UNPKG

@polygonjs/polygonjs

Version:

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

209 lines (208 loc) 7.91 kB
"use strict"; import { QuadSopNode } from "./_BaseQuad"; import { NodeParamsConfig, ParamConfig } from "../utils/params/ParamsConfig"; import { SopType } from "../../poly/registers/nodes/types/Sop"; import { InputCloneMode } from "../../poly/InputCloneMode"; import { Vector2, Vector3, Vector4, BufferAttribute } from "three"; import { addToSetAtEntry } from "../../../core/MapUtils"; class QuadFuseSopParamsConfig extends NodeParamsConfig { constructor() { super(...arguments); /** @param tolerance */ this.tolerance = ParamConfig.FLOAT(0.1); } } const ParamsConfig = new QuadFuseSopParamsConfig(); export class QuadFuseSopNode extends QuadSopNode { constructor() { super(...arguments); this.paramsConfig = ParamsConfig; } static type() { return SopType.QUAD_FUSE; } initializeNode() { this.io.inputs.setCount(1); this.io.inputs.initInputsClonedState(InputCloneMode.FROM_NODE); } cook(inputCoreGroups) { const coreGroup = inputCoreGroups[0]; const objects = coreGroup.quadObjects(); if (!objects) { this.states.error.set(`no quad objects found`); return; } const tolerance = this.pv.tolerance; for (const object of objects) { mergeFaces(object.geometry, tolerance); } this.setQuadObjects(objects); } // private _fuseObject(quadObject: QuadObject) { // // const geometry = quadObject.geometry; // // if (!geometry) { // // return; // // } // // const position = geometry.attributes['position']; // // const pointsCount = position.count; // // const positions = position.array; // // for (let i = 0; i < pointsCount; i++) { // // _pos.fromArray(positions, i * 3); // // _plane.projectPoint(_pos, _projectedPos); // // _delta.copy(_pos).sub(_projectedPos); // // _projectedPos.sub(_delta); // // _projectedPos.toArray(positions, i * 3); // // } // // // if ((object as Mesh).isMesh) { // // quadObjectInverse(quadObject); // // } // } } const tmpV2 = new Vector2(); const tmpV3 = new Vector3(); const tmpV4 = new Vector4(); class QuadPointPosition { constructor(positionAttribute, index, tolerance) { this.positionAttribute = positionAttribute; this.index = index; this.originalPosition = new Vector3(); this.snappedPosition = new Vector3(); this.originalPosition.fromBufferAttribute(positionAttribute, this.index); roundedPos(positionAttribute, this.index, this.snappedPosition, tolerance); this.snappedKey = `${this.snappedPosition.x}:${this.snappedPosition.y}:${this.snappedPosition.z}`; } addAttribValue(geometry, attribName, targetArray) { const attribute = geometry.attributes[attribName]; switch (attribute.itemSize) { case 1: { const val = attribute.getX(this.index); targetArray.push(val); break; } case 2: { tmpV2.fromBufferAttribute(attribute, this.index); tmpV2.toArray(targetArray, targetArray.length); break; } case 3: { tmpV3.fromBufferAttribute(attribute, this.index); tmpV3.toArray(targetArray, targetArray.length); break; } case 4: { tmpV4.fromBufferAttribute(attribute, this.index); tmpV4.toArray(targetArray, targetArray.length); break; } } } } class Face { constructor(a, b, c, d) { this.a = a; this.b = b; this.c = c; this.d = d; } } function averagePosition(positions, target) { target.set(0, 0, 0); positions.forEach((position) => { target.add(position.originalPosition); }); target.divideScalar(positions.size); } function roundedPos(position, index, target, tolerance) { target.fromBufferAttribute(position, index); if (tolerance > 0) { target.x = Math.round(target.x / tolerance) * tolerance; target.y = Math.round(target.y / tolerance) * tolerance; target.z = Math.round(target.z / tolerance) * tolerance; } } function isFaceCollapsed(face) { return face.a.snappedKey == face.b.snappedKey || face.a.snappedKey == face.c.snappedKey || face.a.snappedKey == face.d.snappedKey || face.b.snappedKey == face.c.snappedKey || face.b.snappedKey == face.d.snappedKey || face.c.snappedKey == face.d.snappedKey; } const tmpAttribute = new BufferAttribute(new Float32Array(0), 0); const _positions = [ new QuadPointPosition(tmpAttribute, 0, 0.1), new QuadPointPosition(tmpAttribute, 0, 0.1), new QuadPointPosition(tmpAttribute, 0, 0.1), new QuadPointPosition(tmpAttribute, 0, 0.1) ]; export function mergeFaces(geometry, tolerance) { const index = geometry.index; const indexArray = index; const positionAttribute = geometry.attributes["position"]; const positionsCount = positionAttribute.count; const facesCount = indexArray.length / 4; const positions = new Array(positionsCount); const faces = new Array(facesCount); const pointsBySnappedPos = /* @__PURE__ */ new Map(); const firstPointBySnappedPos = /* @__PURE__ */ new Map(); const averagePosBySnappedKey = /* @__PURE__ */ new Map(); const newIndexBySnappedKey = /* @__PURE__ */ new Map(); const newPositions = []; const newIndices = []; const newAttributeValues = {}; const otherAttributeNames = Object.keys(geometry.attributes).filter((attribName) => attribName != "position"); const otherAttributeNamesCount = otherAttributeNames.length; for (let k = 0; k < otherAttributeNamesCount; k++) { const otherAttributeName = otherAttributeNames[k]; newAttributeValues[otherAttributeName] = []; } for (let i = 0; i < positionsCount; i++) { const position = new QuadPointPosition(positionAttribute, i, tolerance); positions[i] = position; addToSetAtEntry(pointsBySnappedPos, position.snappedKey, position); if (!firstPointBySnappedPos.has(position.snappedKey)) { firstPointBySnappedPos.set(position.snappedKey, position); } } pointsBySnappedPos.forEach((points, snappedKey) => { const averageV3 = new Vector3(); averagePosition(points, averageV3); averagePosBySnappedKey.set(snappedKey, averageV3); }); for (let i = 0; i < facesCount; i++) { const a = positions[indexArray[i * 4]]; const b = positions[indexArray[i * 4 + 1]]; const c = positions[indexArray[i * 4 + 2]]; const d = positions[indexArray[i * 4 + 3]]; const face = new Face(a, b, c, d); faces[i] = face; } const remainingFaces = faces.filter((face) => !isFaceCollapsed(face)); const remainingFacesCount = remainingFaces.length; for (let i = 0; i < remainingFacesCount; i++) { const face = remainingFaces[i]; _positions[0] = face.a; _positions[1] = face.b; _positions[2] = face.c; _positions[3] = face.d; for (let j = 0; j < 4; j++) { const position = _positions[j]; let newIndex = newIndexBySnappedKey.get(position.snappedKey); const averagePos = averagePosBySnappedKey.get(position.snappedKey); if (newIndex == null) { newIndex = newPositions.length / 3; newIndexBySnappedKey.set(position.snappedKey, newIndex); averagePos.toArray(newPositions, newPositions.length); const firstPoint = firstPointBySnappedPos.get(position.snappedKey); for (let k = 0; k < otherAttributeNamesCount; k++) { const otherAttribName = otherAttributeNames[k]; firstPoint.addAttribValue(geometry, otherAttribName, newAttributeValues[otherAttribName]); } } newIndices.push(newIndex); } } geometry.setAttribute("position", new BufferAttribute(new Float32Array(newPositions), 3)); for (let k = 0; k < otherAttributeNamesCount; k++) { const attribName = otherAttributeNames[k]; const attribute = geometry.attributes[attribName]; const newValues = newAttributeValues[attribName]; geometry.setAttribute(attribName, new BufferAttribute(new Float32Array(newValues), attribute.itemSize)); } geometry.setIndex(newIndices); }