Three.js中提取复杂BufferGeometry外部边缘的技术问询
问题描述
我正在开发一个基于Three.js的区域创建与碰撞检测项目,目前已实现碰撞处理并生成BufferGeometry。我的目标是可视化区域的边界框,以清晰划分区域范围。对于简单几何体,我可以轻松创建包围盒(效果如图所示),但面对复杂几何体时,包围盒无法准确呈现区域的实际复杂形状。
为此,我尝试通过顶点创建LineSegments来高亮几何体边缘,但该方法会显示过多内部线条,我需要过滤这些内部线条,仅保留复杂形状的实际外部轮廓。以下是我尝试提取并显示这些边缘的相关代码:
const calculateZoneCollision = (zone: PotreeVolume) => { const viewer: PotreeViewer = (window as any).viewer; if (!zone) return; zone.updateMatrixWorld(true); const allVolumes = viewer.scene.volumes.filter((volume) => volume.uuid !== zone.uuid); const volumeMeshes = allVolumes.map((v) => { return createMeshFromVolume(v); }); let zoneMesh = createMeshFromVolume(zone); let zoneOBB = createOBBFromMesh(zoneMesh, viewer); let resultMesh; volumeMeshes.forEach((volume) => { volume.updateMatrixWorld(true); let volumeOBB = createOBBFromMesh(volume, viewer); if (zoneOBB.intersectsOBB(volumeOBB)) { console.log(`Collision detected with volume: ${volume.name}`); let originalCSGZone; if (!resultMesh) { originalCSGZone = CSG.fromMesh(zoneMesh); } else { originalCSGZone = CSG.fromMesh(resultMesh); } let csgZone = originalCSGZone.clone(); let csgVolume = CSG.fromMesh(volume); csgZone = csgZone.subtract(csgVolume); const collisionInfo = calculateCollisionInfo(zoneOBB, volumeOBB); const details = getCollisionDetails(zoneOBB, volumeOBB, collisionInfo); if (details) { console.log(`Point of collide: `, details.collisionPoint); console.log(`Side of impact: `, details.side); console.log('Volume: ', zone, zoneMesh, zoneOBB); } const resultGeometry = CSG.toGeometry(csgZone, zoneMesh.matrix); const resultMaterial = new MeshBasicMaterial({ color: 0xff0000, transparent: true, opacity: 0.001 }); resultMesh = new Mesh(resultGeometry, resultMaterial); resultMesh.applyMatrix4(zoneMesh.matrixWorld); const vertices = []; const positionAttribute = resultGeometry.getAttribute('position'); for (let i = 0; i < positionAttribute.count; i++) { const vertex = new Vector3(); vertex.fromBufferAttribute(positionAttribute, i); vertices.push([vertex.x, vertex.y, vertex.z]); } const matrixElements = resultMesh.matrix.elements; } else { console.log(`Collision not detected`); } }); if (resultMesh) { const outerEdges = extractOuterEdges(resultMesh.geometry); outerEdges.applyMatrix4(zoneMesh.matrixWorld); viewer.scene.scene.add(outerEdges); viewer.scene.scene.add(resultMesh); setTimeout(() => { viewer.scene.scene.remove(resultMesh); viewer.scene.scene.remove(outerEdges); }, 160); } }; function extractOuterEdges(geometry) { const indexAttribute = geometry.index; const positionAttribute = geometry.getAttribute('position'); const normalsAttribute = geometry.getAttribute('normal'); const edgeMap = new Map(); const edgeFaces = new Map(); for (let i = 0; i < indexAttribute.count; i += 3) { let indices = [indexAttribute.getX(i), indexAttribute.getX(i + 1), indexAttribute.getX(i + 2)]; let triangleNormals = [ new Vector3(normalsAttribute.getX(i), normalsAttribute.getY(i), normalsAttribute.getZ(i)), new Vector3(normalsAttribute.getX(i + 1), normalsAttribute.getY(i + 1), normalsAttribute.getZ(i + 1)), new Vector3(normalsAttribute.getX(i + 2), normalsAttribute.getY(i + 2), normalsAttribute.getZ(i + 2)), ]; for (let j = 0; j < 3; j++) { let edgeKey = [indices[j], indices[(j + 1) % 3]].sort().join('_'); if (!edgeMap.has(edgeKey)) { edgeMap.set(edgeKey, 1); edgeFaces.set(edgeKey, [triangleNormals[j]]); } else { edgeMap.set(edgeKey, edgeMap.get(edgeKey) + 1); edgeFaces.get(edgeKey).push(triangleNormals[j]); } } } const outerEdgesGeometry = new BufferGeometry(); const vertices = []; edgeMap.forEach((count, key) => { if (count === 1 || (count === 2 && !areNormalsCoPlanar(edgeFaces.get(key)[0], edgeFaces.get(key)[1]))) { let vertexIndices = key.split('_').map(Number); vertices.push( new Vector3().fromBufferAttribute(positionAttribute, vertexIndices[0]), new Vector3().fromBufferAttribute(positionAttribute, vertexIndices[1]) ); } }); outerEdgesGeometry.setFromPoints(vertices); const lineMaterial = new LineBasicMaterial({ color: 0xffffff }); const lineSegments = new LineSegments(outerEdgesGeometry, lineMaterial); return lineSegments; } function areNormalsCoPlanar(normal1, normal2) { return normal1.dot(normal2) > 0.995; // Stricter coplanarity check }
内容的提问来源于stack exchange,提问作者Slaven Soldo
相关产品推荐
相关产品推荐

