@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
234 lines (221 loc) • 7.54 kB
text/typescript
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)
);
}