UNPKG

@polygonjs/polygonjs

Version:

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

182 lines (181 loc) 6.76 kB
"use strict"; import { TypedSopNode } from "./_Base"; import { NodeParamsConfig, ParamConfig } from "../utils/params/ParamsConfig"; import { BufferAttribute, Vector3, Box3 } from "three"; import { triangleGraphFromGeometry } from "../../../core/geometry/modules/three/graph/triangle/TriangleGraphUtils"; import { setToArray } from "../../../core/SetUtils"; import { arrayCopy } from "../../../core/ArrayUtils"; import { triangleEdgePositions } from "../../../core/geometry/modules/three/graph/triangle/TriangleEdge"; import { SopType } from "../../poly/registers/nodes/types/Sop"; import { TypeAssert } from "../../poly/Assert"; const _p0 = new Vector3(); const _p1 = new Vector3(); const _box = new Box3(); const _n = new Vector3(); export var ExtrudeOpenEdgesFilterMethod = /* @__PURE__ */ ((ExtrudeOpenEdgesFilterMethod2) => { ExtrudeOpenEdgesFilterMethod2["BELOW_Y"] = "belowY"; ExtrudeOpenEdgesFilterMethod2["IN_BBOX"] = "inBbox"; return ExtrudeOpenEdgesFilterMethod2; })(ExtrudeOpenEdgesFilterMethod || {}); const FILTER_METHODS = [ "belowY" /* BELOW_Y */, "inBbox" /* IN_BBOX */ ]; class ExtrudeOpenEdgesSopParamConfig extends NodeParamsConfig { constructor() { super(...arguments); this.offset = ParamConfig.VECTOR3([0, 1, 0]); this.filterEdges = ParamConfig.BOOLEAN(false); /** @param filter method */ this.filterMethod = ParamConfig.INTEGER(FILTER_METHODS.indexOf("belowY" /* BELOW_Y */), { menu: { entries: FILTER_METHODS.map((name, value) => ({ name, value })) }, visibleIf: { filterEdges: true } }); this.bboxMin = ParamConfig.VECTOR3([-1, -1, -1], { visibleIf: { filterEdges: true, filterMethod: FILTER_METHODS.indexOf("inBbox" /* IN_BBOX */) } }); this.bboxMax = ParamConfig.VECTOR3([1, 1, 1], { visibleIf: { filterEdges: true, filterMethod: FILTER_METHODS.indexOf("inBbox" /* IN_BBOX */) } }); this.yThreshold = ParamConfig.FLOAT(0, { visibleIf: { filterEdges: true, filterMethod: FILTER_METHODS.indexOf("belowY" /* BELOW_Y */) }, range: [-1, 1], rangeLocked: [false, false] }); } } const ParamsConfig = new ExtrudeOpenEdgesSopParamConfig(); export class ExtrudeOpenEdgesSopNode extends TypedSopNode { constructor() { super(...arguments); this.paramsConfig = ParamsConfig; } static type() { return SopType.EXTRUDE_OPEN_EDGES; } initializeNode() { this.io.inputs.setCount(1); } cook(inputCoreGroups) { const coreGroup = inputCoreGroups[0]; const objects = coreGroup.threejsObjects(); for (const object of objects) { this._findAndExtrudeOpenEdges(object); } this.setCoreGroup(coreGroup); } _findAndExtrudeOpenEdges(object) { const mesh = object; const geometry = mesh.geometry; if (!geometry) return; const positionAttribute = geometry.getAttribute("position"); const normalAttribute = geometry.getAttribute("normal"); const index = geometry.getIndex(); if (!(positionAttribute && normalAttribute && index)) { return; } const graph = triangleGraphFromGeometry(mesh.geometry); if (!graph) { return; } const edgeIds = []; graph.edgeIds(edgeIds); const unsharedEdgeIdsSet = /* @__PURE__ */ new Set(); for (const edgeId of edgeIds) { const edge = graph.edge(edgeId); if (!edge) continue; if (edge.triangleIds.length == 1) { unsharedEdgeIdsSet.add(edgeId); } } let unsharedEdgeIds = []; setToArray(unsharedEdgeIdsSet, unsharedEdgeIds); const newPositionValues = []; const newNormalValues = []; const newIndexValues = []; arrayCopy(positionAttribute.array, newPositionValues); arrayCopy(normalAttribute.array, newNormalValues); arrayCopy(index.array, newIndexValues); if (this.pv.filterEdges) { const filterMethod = FILTER_METHODS[this.pv.filterMethod]; unsharedEdgeIds = this._filterEdgeIds(graph, unsharedEdgeIds, newPositionValues, filterMethod); } for (const edgeId of unsharedEdgeIds) { this._extrudeEdge(graph, edgeId, newPositionValues, newNormalValues, newIndexValues); } geometry.setAttribute("position", new BufferAttribute(new Float32Array(newPositionValues), 3)); geometry.setAttribute("normal", new BufferAttribute(new Float32Array(newNormalValues), 3)); geometry.setIndex(newIndexValues); } _filterEdgeIds(graph, edgeIds, positionValues, filterMethod) { switch (filterMethod) { case "belowY" /* BELOW_Y */: { const yThreshold = this.pv.yThreshold; return edgeIds.filter((edgeId) => { const edge = graph.edge(edgeId); if (!edge) return false; triangleEdgePositions(edge, positionValues, _p0, _p1); return _p0.y < yThreshold && _p1.y < yThreshold; }); } case "inBbox" /* IN_BBOX */: { _box.min.copy(this.pv.bboxMin); _box.max.copy(this.pv.bboxMax); return edgeIds.filter((edgeId) => { const edge = graph.edge(edgeId); if (!edge) return false; triangleEdgePositions(edge, positionValues, _p0, _p1); _p0.add(_p1).divideScalar(2); return _box.containsPoint(_p0); }); } } TypeAssert.unreachable(filterMethod); } _extrudeEdge(graph, edgeId, newPositionValues, newNormalValues, newIndexValues) { const edge = graph.edge(edgeId); if (!edge) return; const otherTriangleId = edge.triangleIds[0]; const otherTriangle = graph.triangle(otherTriangleId); const id0Index = otherTriangle.triangle.indexOf(edge.pointIdPair.id0); const id1Index = otherTriangle.triangle.indexOf(edge.pointIdPair.id1); const invertRequired = id0Index == 0 && id1Index == 1 || id0Index == 1 && id1Index == 2 || id0Index == 2 && id1Index == 0; const pointIdPair = edge.pointIdPair; triangleEdgePositions(edge, newPositionValues, _p0, _p1); _p0.add(this.pv.offset); _p1.add(this.pv.offset); _n.copy(_p1).sub(_p0).cross(this.pv.offset); const p0Index = newPositionValues.length / 3; const p1Index = p0Index + 1; newPositionValues.push(_p0.x, _p0.y, _p0.z); newPositionValues.push(_p1.x, _p1.y, _p1.z); if (invertRequired) { _n.multiplyScalar(-1); newIndexValues.push(pointIdPair.id1, pointIdPair.id0, p0Index); newIndexValues.push(pointIdPair.id1, p0Index, p1Index); } else { newIndexValues.push(pointIdPair.id0, pointIdPair.id1, p0Index); newIndexValues.push(p0Index, pointIdPair.id1, p1Index); } newNormalValues.push(_n.x, _n.y, _n.z); newNormalValues.push(_n.x, _n.y, _n.z); } }