EdgeGeometry Issue after CSG Union operations

Issue

I am using three-csg (import CSG from '../../csg/three-csg') to perform Boolean unions on multiple meshes (created by extrude geometries). However, when I generate edges using THREE.EdgesGeometry, the final mesh still contains edges from the original meshes instead of showing only the correct outer edges of the unioned mesh.

Example Workflow:

1. Mesh1, Mesh2, Mesh3 → Three separate meshes
2. Union Step 1

const goodMesh1 = CSG.union(mesh1, mesh2);

Union Step 2:

const finalMesh = CSG.union(goodMesh1, mesh3);

4. Generating Edges:

const edges = new THREE.EdgesGeometry(finalMesh.geometry);
const lineMaterial = new THREE.LineBasicMaterial({ color: 0x000000 });
const edgeLines = new THREE.LineSegments(edges, lineMaterial);
scene.add(edgeLines);

Problem: The generated edges include those of mesh1 and mesh2, instead of showing only the correct outer edges of finalMesh.

Thing we tried

  1. Custom OutlinePass (library)
  2. Increased threshold values for angle detection in EdgesGeometry.
  3. Converted geometry to non-indexed (geometry.toNonIndexed()).
    4.Tested with async function to wait for complete union operations
  4. Use mergeVertices
  5. used finalMesh.geometry.computeVertexNormals();

Question:

How can I ensure that EdgesGeometry only generates the correct external edges after a CSG.union() operation using three-csg? Is there a way to remove internal faces before generating edges?

Any insights would be appreciated! Thanks

When you did mergeVertices, did you remove the uv and normal attributes first?

3 Likes

Before you get too deep into three-csg, try to use Manifold - the most fast/robust CSG JS library.

Not long ago I explored all JS CSG solutions for my present GemDesign project and found that Manifold is the best.

Here is example how to use it from Three.js: Manifold - three.js Example

2 Likes

Hi Manthrax, tried this solution still no difference.

the line is still visible.

below is the code for reference

handleAngleBlunt(edge, opposingEdge, part) {
    const { edgeOutwardNormal, opposingEdgeOutwardNormal, angleBetween } = this.determineAngleBetweenEdgesCoreGeometry(edge, opposingEdge)
    const angleValidation = (180 - angleBetween) < 89 || (180 - angleBetween) > 91

   
    let bspCornerNext = null
    let opposingNextEdge = null
    let opposingNextEdgeCore = null
    const cornerVertices= this.createCorner(edge, opposingEdge, edgeOutwardNormal, opposingEdgeOutwardNormal, part.inputOrientationUI[0])
    const cornerGeom = new THREE.ExtrudeGeometry(new THREE.Shape(cornerVertices), { bevelEnabled: false, depth: edge.layerOccupation.height })

    if(opposingEdge.nextEdge && opposingEdge.nextEdge.originalgeometry) {
      const nextCorner = this.createCornerGeometry(opposingEdge,opposingEdge.nextEdge, part)
      const cornerMeshNext = this.createMesh(nextCorner)
      const opposingNext =this.createTransformedMesh(opposingEdge.nextEdge.originalgeometry,opposingEdge.nextEdge.originalMatrix, opposingEdge.nextEdge.originalgeometry.isCsg)
      const opposingNextCore =this.createTransformedMesh(opposingEdge.nextEdge.coreGeometry,opposingEdge.nextEdge.coreMatrix, opposingEdge.nextEdge.coreGeometry.isCsg)
      bspCornerNext = CSG.fromMesh(cornerMeshNext)
      opposingNextEdge  = CSG.fromMesh(opposingNext)
      opposingNextEdgeCore  = CSG.fromMesh(opposingNextCore)
      opposingEdge.nextEdge.originalgeometry.isCsg = true
      opposingEdge.nextEdge.coreGeometry.isCsg = true
    }

    const cornerMesh = this.createMesh(cornerGeom)
    const edgeMesh = this.createTransformedMesh(edge.coreGeometry,edge.coreMatrix,edge.coreGeometry.isCsg)
    const oppEdgeMesh =this.createTransformedMesh(opposingEdge.originalgeometry,opposingEdge.originalMatrix, opposingEdge.originalgeometry.isCsg)
    const extendedEdgeMesh =this.createTransformedMesh(opposingEdge.coreGeometry,opposingEdge.coreMatrix,opposingEdge.coreGeometry.isCsg)
    const extendedEdgeMeshWithoutCSG =this.createTransformedMesh(opposingEdge.originalgeometryWithoutCsg, opposingEdge.coreMatrix, true)

    const bspCornerMesh = CSG.fromMesh(cornerMesh)
    const bspEdgeMesh = CSG.fromMesh(edgeMesh)
    const bspOppEdgeMesh = CSG.fromMesh(oppEdgeMesh)
    const bspExtendedEdgeMesh = CSG.fromMesh(extendedEdgeMesh)
    const bspExtendedEdgeMeshWithoutCSG = CSG.fromMesh(extendedEdgeMeshWithoutCSG)

    opposingEdge.originalgeometry.isCsg = true

     if ((180 - angleBetween) > 91 ) {
      // find intersector with next edge and current edge
      const bspIntersector = bspMeshNext.intersect(bspEdgeMesh)
      const finalMesh = bspIntersector.union(bspCornerMesh)
      let bspUnion = finalMesh.union(bspOppEdgeMesh)
      let finalMeshNextEdge = bspUnion

      const nextEdge = this.convertToMesh(finalMeshNextEdge)
      const currentEdge = this.convertToMesh(finalMeshCurrentEdge)
      nextEdge.geometry.deleteAttribute('uv');
      nextEdge.geometry.deleteAttribute('normal');
     // currentEdge.geometry.deleteAttribute('uv');
      //currentEdge.geometry.deleteAttribute('normal');
   
      mergeVertices(nextEdge.geometry);
      mergeVertices(currentEdge.geometry);

      // Recompute normals for better shading
      nextEdge.geometry.computeVertexNormals();
      currentEdge.geometry.computeVertexNormals();
      // Update opposingEdge's coreGeometry
      const opposingGeometry = nextEdge.geometry
      opposingEdge.coreGeometry = opposingGeometry
      // Update currentEdge's coreGeometry
      const edgeGeometry = currentEdge.geometry
      edge.coreGeometry = edgeGeometry

    }

  }

  createTransformedMesh(geometry, matrix, isCsg = false) {
    const mesh = this.createMesh(geometry)
    if(!isCsg) mesh.geometry.applyMatrix4(matrix)
    return mesh
  }

   createMesh(geometry) {
      return new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ color: new THREE.Color('green') }))
    }

Thanks yesbird for you time will try this and get back to you .

1 Like

I had the similar issue and increasing subdivision was the cure, then I switched to Manifold to increase performance, details are here:

mergeVertices returns a new geometry.. it doesn’t merge in place.

And you need to mergeVertices Before you do the CSG.. to make the mesh more watertight/manifold, which is the whole point.

(Not saying its guaranteed to work… but doing it after the fact, and not using the result definitely won’t work. :smiley: )

I suspect you will have the same issues with manifold or mesh-bvh-csg unless you clean up the input meshes.

1 Like