Hi, i can create a mesh on overlaped areas between different models, but still some vertices are missing on the created mesh, as i used three-mesh-bvh library and inbuilt INTERSECTION operation in it. If we consider two meshes, slightly touched (not overlaped) areas are not creating a intersecting mesh. how can i increase accuracy and what specific factors to be considered.
Input meshes need to be manifold and watertight. ( not strictly a requirement but strongly advised )
( and even then there is no guarantee that CSG will work in all cases. It’s a complex algorithm with numerous edge cases. Some implementations are more or less robust, but the more robust ones are generally much slower. )
if (models.length < 2) return;
const evaluator = new Evaluator();
const Intersectionmeshes = [];
const nonIntersectionMeshes = [];
for (let i = 0; i < meshModels.length; i++) {
for (let j = i + 1; j < meshModels.length; j++) {
const meshA = meshModels[i];
const meshB = meshModels[j];
let geometryA = meshA.geometry.clone();
let geometryB = meshB.geometry.clone();
geometryA = weldVertices(geometryA);
geometryB = weldVertices(geometryB);
if (!geometryA.attributes.uv) {
const uvArray = new Float32Array(geometryA.attributes.position.count * 2);
geometryA.setAttribute('uv', new THREE.BufferAttribute(uvArray, 2));
}
geometryA.computeVertexNormals();
if (!geometryB.attributes.uv) {
const uvArray = new Float32Array(geometryB.attributes.position.count * 2);
geometryB.setAttribute('uv', new THREE.BufferAttribute(uvArray, 2));
}
geometryB.computeVertexNormals();
if (!geometryA || !geometryB) {
console.error("Invalid geometry for intersection.");
continue;
}
(geometryA as any).computeBoundsTree();
(geometryB as any).computeBoundsTree();
const brushA = new Brush(geometryA);
const brushB = new Brush(geometryB);
const resultBrush = evaluator.evaluate(brushA, brushB, INTERSECTION);
if (!resultBrush) {
console.error("CSG operation returned a null or undefined result.");
return;
}
if (resultBrush.geometry && resultBrush.geometry.attributes.position.count > 0) {
const parentPriorityA = getParentPriority(meshA.name);
const parentPriorityB = getParentPriority(meshB.name);
let chosenColor;
if (parentPriorityA < parentPriorityB) {
chosenColor = meshA.material.color.getHex();
} else {
chosenColor = meshB.material.color.getHex();
}
const material = new THREE.MeshStandardMaterial({
color: chosenColor,
transparent: true,
depthTest: false,
depthWrite: false,
side: THREE.DoubleSide,
clippingPlanes: clippingPlanes,
});
const resultMesh = new Mesh(resultBrush.geometry, material);
Intersectionmeshes.push(resultMesh);
const edgesGeometry = new THREE.EdgesGeometry(resultBrush.geometry);
const edgesMaterial = new THREE.LineBasicMaterial({ color: 0x000000 });
const edges = new THREE.LineSegments(edgesGeometry, edgesMaterial);
groupRef.current?.add(edges);
const nonIntersectBrushA = evaluator.evaluate(brushA, resultBrush, SUBTRACTION);
const nonIntersectBrushB = evaluator.evaluate(brushB, resultBrush, SUBTRACTION);
if (nonIntersectBrushA.geometry && nonIntersectBrushA.geometry.attributes.position.count > 0) {
const nonIntersectMaterialA = new THREE.MeshStandardMaterial({
color: 0x808080,
});
const nonIntersectMeshA = new THREE.Mesh(nonIntersectBrushA.geometry, nonIntersectMaterialA);
nonIntersectionMeshes.push(nonIntersectMeshA);
}
if (nonIntersectBrushB.geometry && nonIntersectBrushB.geometry.attributes.position.count > 0) {
const nonIntersectMaterialB = new THREE.MeshStandardMaterial({
color: 0x808080,
});
const nonIntersectMeshB = new THREE.Mesh(nonIntersectBrushB.geometry, nonIntersectMaterialB);
nonIntersectionMeshes.push(nonIntersectMeshB);
}
} else {
console.warn(`Empty or invalid result geometry after intersection between models ${i} and ${j}.`);
}
}
}
if (Intersectionmeshes.length > 0) {
Intersectionmeshes.forEach(mesh => groupRef.current?.add(mesh));
setEdgeIntersections(Intersectionmeshes);
nonIntersectionMeshes.forEach(mesh => groupRef.current?.add(mesh));
setnonIntersectionMesh(nonIntersectionMeshes);
}
function weldVertices(geometry: THREE.BufferGeometry): THREE.BufferGeometry {
geometry = geometry.toNonIndexed();
const positionAttribute = geometry.attributes.position as THREE.BufferAttribute;
const vertexMap: Record<string, number[]> = {};
for (let i = 0; i < positionAttribute.count; i++) {
const vertex = new THREE.Vector3(
positionAttribute.getX(i),
positionAttribute.getY(i),
positionAttribute.getZ(i)
);
const key = `${vertex.x.toFixed(5)}_${vertex.y.toFixed(5)}_${vertex.z.toFixed(5)}`;
if (!vertexMap[key]) {
vertexMap[key] = [];
}
vertexMap[key].push(i);
}
Object.values(vertexMap).forEach((indices: number[]) => {
if (indices.length > 1) {
const averageVertex = new THREE.Vector3();
indices.forEach(index => {
averageVertex.add(new THREE.Vector3(
positionAttribute.getX(index),
positionAttribute.getY(index),
positionAttribute.getZ(index)
));
});
averageVertex.divideScalar(indices.length);
indices.forEach(index => {
positionAttribute.setXYZ(index, averageVertex.x, averageVertex.y, averageVertex.z);
});
}
});
geometry.computeVertexNormals();
return geometry;
}
This what i have done for csg intersection, still needs some optimization. Should i need to use GitHub - elalish/manifold: Geometry library for topological robustness for manifold and watertight
Oooh cool… I haven’t seen that library before. Sounds promising especially if it’s used in openscad, etc. Probably worth a shot?
If i want to go with three.js, other than opencascade how can i proceed with manifold @manthrax
I only know of three-mesh-bvh-csg,
that new one you just posted,
and one that I maintain that is a bit old and creaky…
https://manthrax.github.io/THREE-CSGMesh/demos/CSGShinyDemo.html