How to create bufferGeometries so that it works with CSG?

I have asked a very similar question recently but that specific issue got solved so I decided it would be more organized to start another thread. And because realized I have no idea how to use bufferGeometry, this is a more broad question.

I am attempting to create a very simple bufferGeometry and perfom CSG operations with it. I read on the three-bvh-csg thread that it supports buffer geometries. But all my attempts lead to hollow results and non-manifold surfaces.The problem is probably in the way I am constructing my buffer geometries, because it works just fine if I use other geometries (like box, extrusion etc). does anyone know what I am doing wrong here?

This is my bufferGeometry code, when I add it to my scene the geometry looks visually fine, all faces are closed etc. :

export const CustomShapeGeometry = () => {
    return (
        <bufferGeometry>
            <bufferAttribute
                attach='attributes-position'
                array={new Float32Array([ 
                    0,0,0, 
                    10,0,0, 
                    10,0,10, 
                    0,0,10,
                    5,5,5
                ])}
                count={5}
                itemSize={3}
            />
            <bufferAttribute
                attach='attributes-uv'
                array={new Float32Array([
                    0,0, 
                    0,1, 
                    0,2, 
                    0,3, 
                    1,0
                ])}
                count={5}
                itemSize={2}
            />
            <bufferAttribute
                attach='attributes-normal'
                array={new Float32Array([
                    0,-1,0,
                    0,-1,0,
                    0,-1,0,
                    0,-1,0,
                    0,1,0
                    ])}
                count={5}
                itemSize={3}
            />
            <bufferAttribute
                attach="index"
                array={new Uint16Array([ 
                    0,1,2, 
                    0,2,3, 
                    0,1,4,
                    1,2,4,
                    2,3,4,
                    3,0,4
                ])}
                count={18}
                itemSize={1}
            />
        </bufferGeometry>
    )
}

And here is my csg code:

export const VolumeSubtraction = () => {
    return (
        <>
        <mesh >
            <meshStandardMaterial color='red' side={THREE.DoubleSide}/>
            <Geometry>
                <Base scale={[10,10,10]} position={[0, 20, 0]}>
                    <boxGeometry/>
                </Base>
                <Intersection position={[0, 20, 0]}>
                     <CustomShapeGeometry/>
                </Intersection>
            </Geometry>
        </mesh>
        </>
    )
}

And the results look like this, as you can see it only intersects the faces, instead of treating it as a solid.

Intersection:
image

Subtraction:
image

And if i use any second geometry more complex than a box it starts to go completely crazy with the faces. For example like this:

Looking over this forum I saw this person with a similar issue as me, their problem was that their geometry was non-manifold, but i have no idea how to find out if that’s the case for me or how to fix it, I am a bit lost. Any help is appreciated!

Note, I do know that I could use a pyramid geometry to perform this without creating the buffer geometry point by point, but my idea is to create more complex geometries from here on, so I was testing the csg on this simpler one.

Update: I have found out what was causing the issue. The triangles need to be set in such a way that all their normals point outward. For some reason I had thought setting the normals in the buffer geometry would take care of that, but not quite. Once I did this to triangles it was fixed:

<bufferAttribute
  attach="index"
  array={new Uint16Array([ 
      0,1,2, 
      0,2,3, 
      0,4,1,
      1,4,2,
      2,4,3,
      3,4,0
  ])}
  count={18}
  itemSize={1}
/>

Now I need to figure out a way to make sure that always happens for more complex geometries. If anyone has any idea how to ensure ouward normals either during the creation of the geometry or once it’s done, I’d appreciate the help.

1 Like

There isn’t a trivial way to ensure outward facing normals or watertight geometry.

Blender has some tools to “recompute normals outside/inside” but even those will fail on some non-convex meshes.

If you know your object is convex… you can compute the normal yourself… by taking the cross product of the edge vectors, and check if the dot product of the normal and one of the vertices is < 0, and that’s an indicator the normal might be pointing inward.

1 Like

Thanks for your response! That makes perfect sense. My geometries are not convex, sometimes they have holes, but it turns out they are even easier than convex. They are always symmetrical – or close to it – on the y axis, meaning that the lower half should always have a normal with a negative y and the upper half should always have a normal with a positive y. By checking for that, and flipping the ones with the wrong y value in the normal, I was able to correct my geometries and get it working.

When looking around though I found this link, which indeed is not trivial, but it might be useful for anyone looking at this thread in the future.

1 Like