Three-bvh-csg: A library for performing fast CSG operations!

I am thinking of using this library for CSG operations on three.js mesh objects, each being composed of multiple water-tight and properly oriented bodies. That is, the buffer geometry defining each mesh consists of multiple bodies. Will the CSG operation results be valid?..Thanks…OLDMAN

That should be fine - there’s no requirement that the mesh encapsulate a single coherent volume.

To generate that object I use the two rotated .gltf models as Brushes. On screen - looks great! but I want to give viewer the option to export the static model to .gltf/.glb and this is where the problem starts. I would like the exported model to look the same as the one generated by csg, on screen (The blue cut edge is important!). but what I get is 2 meshes in Threejs editor/viewer or one mesh with 2 textures in Blender interpretation. Do you have any idea how to export my pitcher correctly?

1 Like

The csg operation just produces a mesh so there shouldn’t be anything special about about exporting it. And without an example it won’t really be possible to help. I recommend making a new forum thread demonstrating the issue with gltf export with a jsfiddle.

Thanks for the reply. I’m sure I’ve messed something up in the code. I’ll do as you suggest

the evaluator result mesh from when exported using GLTFExporter does not export as expected (useGroups:true)

when the glb is opened in blender here are the things i noticed

  • there is one mesh with both materials , and there are two copies of the entire geometry overlapping each other

  • the triangles which are out of the drawRange in the viewer become visible in the glb


GLTFExporter does not export as expected

the triangles which are out of the drawRange in the viewer become visible in the glb

If GLTFExporter does not properly respect draw range the this is an exporter issue. As I mentioned above please make a new thread demonstrating the gltf exporter problem or make a three.js issue.


will confirm the drawRange issue then report on three js github (made a thread to discuss this)
update:- confirmed that gltfExported does not export drawRange and there are no plans to support it in the future

here’s how the meshes currently export , i think the geometry.groups case has not been fully implemented in GltfExporter



Made a custom solution , working good so far

sandbox is here bvh csg export - CodeSandbox

method breakdown

  • clone the evaluator result geometry
  • splice the geometry.groups array so it contains only one material’s groups in each geometry
  • use mergeGroups from BufferGeometryUtils to get the new geometry
  • make a new mesh with the new geometry correct material

these new meshes exports as it looks in gltf :partying_face:

amazing work with the library! even with all this extra logic it’s still runs smoothly/realtime ! :ok_hand:


it looks great! thank you for the solution.

1 Like

I have started using the updated 0.0.3 version of this library. In general, the number of just-touch problems seem to have decreased. But not disappeared. Still get a number of

`TriangleClipper: Coplanar clip not handled`

messages as I repeatedly call the Boolean intersector.


Is that just the way it is…or are there remedies I can try?



If you’e having issues please check your package versions. three-bvh-csg is on v0.0.8. Likewise three-mesh-bvh has been updated quite a bit, as well.

1 Like

Thank you.

I downloaded and installed version 0.0.8. The earlier mentioned error no longer appears. This is very good!


My sole use of this library is for performing mesh-pair intersections. I do this in a loop over and over again with appropriate rigid body displacements to one or the other mesh each time. The computed volume of intersection gives me what I need to know: are they touching, are they partially intersecting, is one inside the other, etc.

I’m finding that, as before, I still get some computed intersection volumes to be negative. That is, the volume of the mesh (space) that is common to both initial meshes. I’m not sure how to interpret this - it’s probably bad news :slight_smile:

One thing I do is reorient one member of the pair just a very small bit before each intersection - in the hope that this might avert a just-touch or similar bad case. I can’t claim that this prevents negative intersection volumes entirely. However it does generally seem to improve my end result.

My main question is why some intersection volumes are turning out negative.

Happy to hear your thoughts…


If this volume is a signed volume, then it is OK to be negative – this just depends on the chosen orientation. Take the absolute value to get the normal volume.

If you have questions please make a separate thread with a live example. Or if you think there’s a bug please make an issue at the repository.

I’ve just released version 0.0.10! This version uses a new feature of three-mesh-bvh that enables BVH storage that indirectly references the geometry index rather than modifying it. This means that geometry groups no longer require separate BVH roots resulting in faster clip performance. And a new operations API enables you to retrieve the results of multiple CSG operations on the same two brushes at once with significantly reduced overhead compared to running two operations.

v0.0.10 release notes here

BVH Performance

This complex case involving 100 spheres clipping into the bunny model with a SUBTRACTION operation and unique materials per sphere takes ~30% less time in the new release (~2130ms down from ~3000ms on my machine)

Multi Operation

With the new operation evaluation API you can get two separate CSG results with one function call which lets you quickly slice and core bunnies to see what’s inside. Performing a dual operation can provide the second result 80% faster compared to performing another separate operation:

const evaluator = new Evaluator();
   [ result1, result2 ],


I’ve released version v0.0.11! This version has some more performance improvements that can bring up to a 25% performance boost with dynamic operations (after BVH and half edge have been generated), as well as “Hollow”-type operations.

v0.0.11 release notes here

Hollow Operations

Hollow operations (including HOLLOW_SUBTRACTION and HOLLOW_INTERSECTION) are ones that do not yield a water-tight result. The second brush must be water tight but the first can be a soup of triangles and the result will include the triangles within or outside the second brush’s volume.


I’ve just released v0.0.16! This one is primarily some bug and triangulation fixes including one issue that was causing unnecessarily large bounding boxes when a mesh contains degenerate triangles.

v0.0.16 Release Notes




Cool library and thanks for the updates. What are your thoughts on using add operations to create visible watermarks on complex models? Could it be done in a way that would produce a “good” looking result?