UNPKG

@polygonjs/polygonjs

Version:

node-based WebGL 3D engine https://polygonjs.com

234 lines (221 loc) 7.54 kB
import {traverseEdges, traverseFaces} from '../CadTraverse'; import type { OpenCascadeInstance, TopoDS_Shape, CADTesselationParams, CachedCADTesselationParams, CadGeometryTypeShape, TopoDS_Edge, TopoDS_Face, BRepMesh_IncrementalMesh, } from '../CadCommon'; import {faceData} from './CadTriangulationFaceUtils'; import {BufferGeometry, BufferAttribute, Object3D, Mesh} from 'three'; import {BaseSopOperation} from '../../../../../engine/operations/sop/_Base'; import {ObjectType} from '../../../Constant'; import {cadMaterialLineGroup, cadMaterialMesh, cadMaterialMeshGroup} from '../CadConstant'; import {cadEdgeToObject3D} from './CadEdge'; import {CadLoaderSync} from '../CadLoaderSync'; import {CadObject} from '../CadObject'; import {objectContentCopyProperties} from '../../../ObjectContent'; import {BaseSopNodeType} from '../../../../../engine/nodes/sop/_Base'; import {SopType} from '../../../../../engine/poly/registers/nodes/types/Sop'; import {CadEntityGroupCollection} from '../CadEntityGroupCollection'; import type {CADGroupSopNode} from '../../../../../engine/nodes/sop/CADGroup'; import {EntityGroupType} from '../../../EntityGroupCollection'; import {withCadException} from '../CadExceptionHandler'; import {arrayPushItems} from '../../../../ArrayUtils'; function cachedTesselationParamsEqual(params1: CachedCADTesselationParams, params2: CachedCADTesselationParams) { return ( params1.linearTolerance == params2.linearTolerance && params1.angularTolerance == params2.angularTolerance && params1.curveAbscissa == params2.curveAbscissa && params1.curveTolerance == params2.curveTolerance ); } const tesselationParamsByShape: WeakMap<TopoDS_Shape, CachedCADTesselationParams> = new WeakMap(); export function cadShapeToObject3D( cadObject: CadObject<CadGeometryTypeShape>, tesselationParams: CADTesselationParams, displayNode: BaseSopNodeType ) { const oc = CadLoaderSync.oc(); const shape = cadObject.cadGeometry(); let cachedParams = tesselationParamsByShape.get(shape); if (cachedParams && !cachedTesselationParamsEqual(cachedParams, tesselationParams)) { oc.BRepTools.Clean(shape, true); } tesselationParamsByShape.set(shape, {...tesselationParams}); const objects: Object3D[] = []; function _processMesh(mesh?: Mesh) { if (mesh) { objectContentCopyProperties(cadObject, mesh); objects.push(mesh); } return mesh; } function _processEdgeObject(edge: TopoDS_Edge) { const edgeObject = cadEdgeToObject3D(edge, tesselationParams); if (edgeObject) { // it seems better to not have shadows from those edges edgeObject.castShadow = false; edgeObject.receiveShadow = false; objects.push(edgeObject); } return edgeObject; } function _displayNodeIsEdgeGroup() { if (displayNode.type() == SopType.CAD_GROUP) { const groupNode = displayNode as CADGroupSopNode; return groupNode.groupType() == EntityGroupType.EDGE; } return false; } function _displayNodeIsFaceGroup() { if (displayNode.type() == SopType.CAD_GROUP) { const groupNode = displayNode as CADGroupSopNode; return groupNode.groupType() == EntityGroupType.FACE; } return false; } if (_displayNodeIsFaceGroup()) { const groupNode = displayNode as CADGroupSopNode; const groupName = groupNode.groupName(); const facesInGroup: TopoDS_Face[] = []; const facesNotInGroup: TopoDS_Face[] = []; CadEntityGroupCollection.traverseEntitiesInGroup({ groupName, groupType: EntityGroupType.FACE, object: cadObject, shape, traverseFunction: traverseFaces, onEntityTraversed: (face, i) => { facesInGroup.push(face); }, onEntityNotInGroupTraversed: (face, i) => { facesNotInGroup.push(face); }, }); const objectInGroup = _createMeshFromFaces(oc, shape, facesInGroup, tesselationParams); const objectNotInGroup = _createMeshFromFaces(oc, shape, facesNotInGroup, tesselationParams); if (objectInGroup) { objectInGroup.material = cadMaterialMeshGroup(); } _processMesh(objectInGroup); _processMesh(objectNotInGroup); } else { if (tesselationParams.displayMeshes) { const mesh = _createMesh(oc, shape, tesselationParams); _processMesh(mesh); } } if (_displayNodeIsEdgeGroup()) { const groupNode = displayNode as CADGroupSopNode; const groupName = groupNode.groupName(); CadEntityGroupCollection.traverseEntitiesInGroup({ groupName, groupType: EntityGroupType.EDGE, object: cadObject, shape, traverseFunction: traverseEdges, onEntityTraversed: (edge, i) => { const edgeObject = _processEdgeObject(edge); if (edgeObject) { edgeObject.material = cadMaterialLineGroup(); } }, onEntityNotInGroupTraversed: _processEdgeObject, }); } else { if (tesselationParams.displayEdges) { traverseEdges(oc, shape, _processEdgeObject); } } return objects; } interface MeshBuffers { positions: number[]; normals: number[]; indices: number[]; } function _createMesh( oc: OpenCascadeInstance, object: TopoDS_Shape, tesselationParams: CADTesselationParams ): Mesh | undefined { const mesher = withCadException<BRepMesh_IncrementalMesh>(oc, () => { const _mesher = new oc.BRepMesh_IncrementalMesh_2( object, tesselationParams.linearTolerance, true, tesselationParams.angularTolerance, true ); return _mesher; }); if (!mesher) { const updatedTesselationParams: CADTesselationParams = {...tesselationParams}; updatedTesselationParams.linearTolerance *= 2; updatedTesselationParams.angularTolerance *= 2; updatedTesselationParams.curveAbscissa *= 2; updatedTesselationParams.curveTolerance *= 2; return _createMesh(oc, object, updatedTesselationParams); } if (mesher.IsDone()) { const positions: number[] = []; const normals: number[] = []; const indices: number[] = []; const buffers: MeshBuffers = {positions, normals, indices}; traverseFaces(oc, object, (face) => { _updateBufferFromFace(oc, face, buffers); }); mesher.delete(); return _createMeshFromBuffers(buffers, tesselationParams); } } function _createMeshFromFaces( oc: OpenCascadeInstance, object: TopoDS_Shape, faces: TopoDS_Face[], tesselationParams: CADTesselationParams ) { const mesher = new oc.BRepMesh_IncrementalMesh_2( object, tesselationParams.linearTolerance, true, tesselationParams.angularTolerance, true ); if (mesher.IsDone()) { const positions: number[] = []; const normals: number[] = []; const indices: number[] = []; const buffers: MeshBuffers = {positions, normals, indices}; for (const face of faces) { _updateBufferFromFace(oc, face, buffers); } mesher.delete(); return _createMeshFromBuffers(buffers, tesselationParams); } } function _updateBufferFromFace(oc: OpenCascadeInstance, face: TopoDS_Face, buffers: MeshBuffers) { const {positions, normals, indices} = buffers; const data = faceData(oc, face, positions.length / 3); if (data) { arrayPushItems(data.positions, positions); arrayPushItems(data.normals, normals); arrayPushItems(data.indices, indices); } } function _createMeshFromBuffers(buffers: MeshBuffers, tesselationParams: CADTesselationParams) { const {positions, normals, indices} = buffers; const geo = new BufferGeometry(); geo.setAttribute('position', new BufferAttribute(new Float32Array(positions), 3)); geo.setAttribute('normal', new BufferAttribute(new Float32Array(normals), 3)); geo.setIndex(indices); return BaseSopOperation.createObject( geo, ObjectType.MESH, cadMaterialMesh(tesselationParams.meshesColor, tesselationParams.wireframe) ); }