UNPKG

@polygonjs/polygonjs

Version:

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

278 lines (277 loc) 10.4 kB
"use strict"; import { QuadSopNode } from "./_BaseQuad"; import { NodeParamsConfig, ParamConfig } from "../utils/params/ParamsConfig"; import { SopType } from "../../poly/registers/nodes/types/Sop"; import { QuadGeometry } from "../../../core/geometry/modules/quad/QuadGeometry"; import { Vector3, BufferAttribute } from "three"; import { Attribute } from "../../../core/geometry/Attribute"; import { InputCloneMode } from "../../poly/InputCloneMode"; import { QuadObject } from "../../../core/geometry/modules/quad/QuadObject"; import { sample } from "../../../core/ArrayUtils"; import { objectContentCopyProperties } from "../../../core/geometry/ObjectContent"; import { getFirstEdgeIdBetweenTriangles, sortEdgesFromLargestToSmallest, triangleGraphFindExpandedEdge, triangleGraphFindNextLargest, triangleGraphFromGeometry } from "../../../core/geometry/modules/three/graph/triangle/TriangleGraphUtils"; import { triangleEdgeLength } from "../../../core/geometry/modules/three/graph/triangle/TriangleEdge"; const _v3 = new Vector3(); const _p0 = new Vector3(); const _p1 = new Vector3(); const _p2 = new Vector3(); const _p3 = new Vector3(); class QuadrangulateSopParamsConfig extends NodeParamsConfig { constructor() { super(...arguments); /** @param quadsCount */ // quadsCount = ParamConfig.INTEGER(1, { // range: [0, 1000], // rangeLocked: [true, false], // }); /** @param regular */ this.regular = ParamConfig.BOOLEAN(1); /** @param test */ this.granular = ParamConfig.BOOLEAN(0, { visibleIf: { regular: 0 } }); /** @param irregularAmount */ this.irregularAmount = ParamConfig.FLOAT(1, { range: [0, 1], rangeLocked: [true, true], visibleIf: { granular: 1, regular: 0 } }); /** @param subdivide */ this.subdivide = ParamConfig.BOOLEAN(1, { visibleIf: { regular: 0 } }); /** @param seed */ this.seed = ParamConfig.INTEGER(0, { range: [-100, 100], rangeLocked: [false, false], visibleIf: { regular: 0 } }); } } const ParamsConfig = new QuadrangulateSopParamsConfig(); export class QuadrangulateSopNode extends QuadSopNode { constructor() { super(...arguments); this.paramsConfig = ParamsConfig; } static type() { return SopType.QUADRANGULATE; } initializeNode() { this.io.inputs.setCount(1); this.io.inputs.initInputsClonedState(InputCloneMode.NEVER); } cook(inputCoreGroups) { const coreGroup = inputCoreGroups[0]; const threejsObjects = coreGroup.threejsObjects(); const newQuadObjects = []; for (const object of threejsObjects) { const geometry = object.geometry; if (geometry) { const newQuadObject = this._processGeometry(geometry); if (newQuadObject) { objectContentCopyProperties(object, newQuadObject); newQuadObject.geometry.applyMatrix(object.matrix); newQuadObjects.push(newQuadObject); } } } this.setQuadObjects(newQuadObjects); } _processGeometry(geometry) { const graph = triangleGraphFromGeometry(geometry); if (!graph) { return; } const positionAttribute = geometry.getAttribute(Attribute.POSITION); if (!positionAttribute) { return; } const positions = geometry.getAttribute(Attribute.POSITION).array; const { regular, granular, irregularAmount, subdivide, seed } = this.pv; const quadGeometry = new QuadGeometry(); const newPositionArray = [...positionAttribute.clone().array]; const quadIndices = []; const edgeIds = []; graph.edgeIds(edgeIds); const edgeLengthById = /* @__PURE__ */ new Map(); for (const edgeId of edgeIds) { const edge = graph.edge(edgeId); if (edge) { edgeLengthById.set(edgeId, triangleEdgeLength(edge, positions)); } } sortEdgesFromLargestToSmallest(edgeIds, edgeLengthById); const edgeCenterIndexByEdgeIndices = /* @__PURE__ */ new Map(); const _findOrCreateEdgeCenterIndex = (i0, i1) => { const key0 = i0 < i1 ? i0 : i1; const key1 = i0 < i1 ? i1 : i0; let edgeCenterIndexByEdgeIndex = edgeCenterIndexByEdgeIndices.get(key0); if (!edgeCenterIndexByEdgeIndex) { edgeCenterIndexByEdgeIndex = /* @__PURE__ */ new Map(); edgeCenterIndexByEdgeIndices.set(key0, edgeCenterIndexByEdgeIndex); } let edgeCenterIndex = edgeCenterIndexByEdgeIndex.get(key1); if (edgeCenterIndex == null) { _p0.fromArray(newPositionArray, i0 * 3); _p1.fromArray(newPositionArray, i1 * 3); _v3.copy(_p0).add(_p1).multiplyScalar(0.5); edgeCenterIndex = newPositionArray.length / 3; _v3.toArray(newPositionArray, newPositionArray.length); edgeCenterIndexByEdgeIndex.set(key1, edgeCenterIndex); } return edgeCenterIndex; }; const _completeQuadObject = () => { if (regular == false && subdivide) { graph.traverseTriangles((triangle) => { const i0 = triangle.triangle[0]; const i1 = triangle.triangle[1]; const i2 = triangle.triangle[2]; _p0.fromArray(newPositionArray, i0 * 3); _p1.fromArray(newPositionArray, i1 * 3); _p2.fromArray(newPositionArray, i2 * 3); _v3.copy(_p0).add(_p1).add(_p2).divideScalar(3); const iCenter = newPositionArray.length / 3; _v3.toArray(newPositionArray, newPositionArray.length); const i01 = _findOrCreateEdgeCenterIndex(i0, i1); const i12 = _findOrCreateEdgeCenterIndex(i1, i2); const i20 = _findOrCreateEdgeCenterIndex(i2, i0); quadIndices.push(i0, i01, iCenter, i20); quadIndices.push(i1, i12, iCenter, i01); quadIndices.push(i2, i20, iCenter, i12); }); } const position = new BufferAttribute(new Float32Array(newPositionArray), 3); quadGeometry.setAttribute(Attribute.POSITION, position); quadGeometry.setIndex(quadIndices); const quadObject = new QuadObject(quadGeometry); return quadObject; }; let _preparedNextEdgeId; const visitedEdgeIds = /* @__PURE__ */ new Set(); const _randomSample = (step) => { return sample(edgeIds, seed + step); }; const _prepareNextEdgeId = (startEdgeId, step) => { if (regular) { return triangleGraphFindNextLargest(edgeLengthById, graph, startEdgeId, visitedEdgeIds); } else { return triangleGraphFindExpandedEdge( edgeLengthById, graph, startEdgeId, // edgeIds, seed, step, irregularAmount, visitedEdgeIds, _randomSample ); } }; const _nextEdgeIdWithRegularMethod = () => { const foundEdgeId = _preparedNextEdgeId != null ? _preparedNextEdgeId : getFirstEdgeIdBetweenTriangles(graph, edgeIds); if (foundEdgeId != null) { const index = edgeIds.indexOf(foundEdgeId); if (index >= 0) { edgeIds.splice(index, 1); } } return foundEdgeId; }; const _nextEdgeIdWithIrregularMethod = (step) => { const _sampleFromIrregularity = () => { return _preparedNextEdgeId != null ? _preparedNextEdgeId : _randomSample(step); }; const edgeId = granular == true ? _sampleFromIrregularity() : _randomSample(step); if (edgeId != null) { const index = edgeIds.indexOf(edgeId); if (index < 0) { console.log("bad edge found", edgeId, [...edgeIds].sort().join(", ")); throw "internal error"; } edgeIds.splice(index, 1); } return edgeId; }; let i = 0; while (edgeIds.length > 0) { i++; const edgeId = regular ? _nextEdgeIdWithRegularMethod() : _nextEdgeIdWithIrregularMethod(i); if (edgeId == null) { return _completeQuadObject(); } visitedEdgeIds.add(edgeId); const _prepareNextEdgeIdIfRequired = () => { if (regular == true || granular == true && regular == false) { _preparedNextEdgeId = _prepareNextEdgeId( edgeId, i + 1 /* we add 1 as we are computing for the next step. This is necessary to match the behavior when granular is off */ ); } }; const edge = graph.edge(edgeId); if (!edge) { _prepareNextEdgeIdIfRequired(); continue; } const triangleIds = edge.triangleIds; const triangle0 = graph.triangle(triangleIds[0]); const triangle1 = graph.triangle(triangleIds[1]); if (!triangle0 || !triangle1) { _prepareNextEdgeIdIfRequired(); continue; } _prepareNextEdgeIdIfRequired(); graph.removeTriangle(triangle0.id); graph.removeTriangle(triangle1.id); const i0 = triangle0.triangle.find( (index) => index != edge.pointIdPair.id0 && index != edge.pointIdPair.id1 ); const i2 = triangle1.triangle.find( (index) => index != edge.pointIdPair.id0 && index != edge.pointIdPair.id1 ); const triangle0UnsharedIndexIndex = triangle0.triangle.indexOf(i0); const triangle1UnsharedIndexIndex = triangle1.triangle.indexOf(i2); const i1 = triangle0.triangle[(triangle0UnsharedIndexIndex + 1) % 3]; const i3 = triangle1.triangle[(triangle1UnsharedIndexIndex + 1) % 3]; if (regular == true || subdivide == false) { quadIndices.push(i0, i1, i2, i3); } else { _p0.fromArray(newPositionArray, i0 * 3); _p1.fromArray(newPositionArray, i1 * 3); _p2.fromArray(newPositionArray, i2 * 3); _p3.fromArray(newPositionArray, i3 * 3); _v3.copy(_p0).add(_p1).add(_p2).add(_p3).multiplyScalar(0.25); const iCenter = newPositionArray.length / 3; _v3.toArray(newPositionArray, newPositionArray.length); const i01 = _findOrCreateEdgeCenterIndex(i0, i1); const i12 = _findOrCreateEdgeCenterIndex(i1, i2); const i23 = _findOrCreateEdgeCenterIndex(i2, i3); const i30 = _findOrCreateEdgeCenterIndex(i3, i0); quadIndices.push(i0, i01, iCenter, i30); quadIndices.push(i1, i12, iCenter, i01); quadIndices.push(i2, i23, iCenter, i12); quadIndices.push(i3, i30, iCenter, i23); } } return _completeQuadObject(); } }