UNPKG

@polygonjs/polygonjs

Version:

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

285 lines (284 loc) 10.5 kB
"use strict"; import { Vector3, Triangle, Quaternion } from "three"; import { QuadSopNode } from "./_BaseQuad"; import { NodeParamsConfig, ParamConfig } from "../utils/params/ParamsConfig"; import { SopType } from "../../poly/registers/nodes/types/Sop"; import { Attribute } from "../../../core/geometry/Attribute"; import { setToArray } from "../../../core/SetUtils"; import { InputCloneMode } from "../../poly/InputCloneMode"; import { TypeAssert } from "../../poly/Assert"; import { typedArrayCopy } from "../../../core/ArrayUtils"; const _current = new Vector3(); const _neighbourAverage = new Vector3(); const _neighbour = new Vector3(); const _p0 = new Vector3(); const _p1 = new Vector3(); const _p2 = new Vector3(); const _p3 = new Vector3(); const _delta0 = new Vector3(); const _delta1 = new Vector3(); const _delta2 = new Vector3(); const _delta3 = new Vector3(); const _currentDelta0 = new Vector3(); const _currentDelta1 = new Vector3(); const _currentDelta2 = new Vector3(); const _currentDelta3 = new Vector3(); const _center = new Vector3(); const _triangle = new Triangle(); const _triangleNormal = new Vector3(); const _q = new Quaternion(); const _average = new Vector3(); export var QuadSmoothMode = /* @__PURE__ */ ((QuadSmoothMode2) => { QuadSmoothMode2["SQUARIFY"] = "squarify"; QuadSmoothMode2["AVERAGE"] = "average"; return QuadSmoothMode2; })(QuadSmoothMode || {}); export const QUAD_SMOOTH_MODES = ["average" /* AVERAGE */, "squarify" /* SQUARIFY */]; class QuadSmoothSopParamsConfig extends NodeParamsConfig { constructor() { super(...arguments); /** @param mode */ this.mode = ParamConfig.INTEGER(QUAD_SMOOTH_MODES.indexOf("squarify" /* SQUARIFY */), { menu: { entries: QUAD_SMOOTH_MODES.map((name, value) => { return { name, value }; }) } }); /** @param iterations */ this.iterations = ParamConfig.INTEGER(50, { range: [0, 100], rangeLocked: [true, false] }); /** @param strength */ this.strength = ParamConfig.FLOAT(1, { range: [0, 1], rangeLocked: [true, true] }); } } const ParamsConfig = new QuadSmoothSopParamsConfig(); export class QuadSmoothSopNode extends QuadSopNode { constructor() { super(...arguments); this.paramsConfig = ParamsConfig; } static type() { return SopType.QUAD_SMOOTH; } initializeNode() { this.io.inputs.setCount(1); this.io.inputs.initInputsClonedState(InputCloneMode.FROM_NODE); } cook(inputCoreGroups) { const coreGroup = inputCoreGroups[0]; const quadObjects = coreGroup.quadObjects(); if (quadObjects) { for (const object of quadObjects) { this._smoothQuadGeometry(object.geometry); } } this.setCoreGroup(coreGroup); } setMode(method) { this.p.mode.set(QUAD_SMOOTH_MODES.indexOf(method)); } mode() { return QUAD_SMOOTH_MODES[this.pv.mode]; } _smoothQuadGeometry(geometry) { const mode = this.mode(); switch (mode) { case "average" /* AVERAGE */: return this._smoothQuadGeometryWithAverage(geometry); case "squarify" /* SQUARIFY */: return this._smoothQuadGeometryWithSquarify(geometry); } TypeAssert.unreachable(mode); } _smoothQuadGeometryWithAverage(geometry) { const position = geometry.attributes[Attribute.POSITION]; if (!position) { return; } const tmpPositionArray0 = typedArrayCopy(position.array, new Float32Array(position.array.length)); const tmpPositionArray1 = typedArrayCopy(tmpPositionArray0, new Float32Array(position.array.length)); const index = geometry.index; const quadsCount = geometry.quadsCount(); const adjacentIdByIndexWithSets = /* @__PURE__ */ new Map(); const quadCountByEdge = /* @__PURE__ */ new Map(); const _addQuadCount = (quadIndex, i0, i1) => { let quadCountByEdgeEntry = quadCountByEdge.get(i0); if (!quadCountByEdgeEntry) { quadCountByEdgeEntry = /* @__PURE__ */ new Map(); quadCountByEdge.set(i0, quadCountByEdgeEntry); } let quadIndices = quadCountByEdgeEntry.get(i1); if (!quadIndices) { quadIndices = /* @__PURE__ */ new Set(); } quadIndices.add(quadIndex); quadCountByEdgeEntry.set(i1, quadIndices); }; const _addAdjacency = (quadIndex, current, adjacent) => { let adjacentIds = adjacentIdByIndexWithSets.get(current); if (!adjacentIds) { adjacentIds = /* @__PURE__ */ new Set(); adjacentIdByIndexWithSets.set(current, adjacentIds); } adjacentIds.add(adjacent); _addQuadCount(quadIndex, current, adjacent); _addQuadCount(quadIndex, adjacent, current); }; for (let i = 0; i < quadsCount; i++) { const i4 = i * 4; const i0 = index[i4 + 0]; const i1 = index[i4 + 1]; const i2 = index[i4 + 2]; const i3 = index[i4 + 3]; _addAdjacency(i, i0, i3); _addAdjacency(i, i0, i1); _addAdjacency(i, i1, i0); _addAdjacency(i, i1, i2); _addAdjacency(i, i2, i1); _addAdjacency(i, i2, i3); _addAdjacency(i, i3, i2); _addAdjacency(i, i3, i0); } const adjacentIdByIndex = /* @__PURE__ */ new Map(); adjacentIdByIndexWithSets.forEach((adjacentIds, index2) => { adjacentIdByIndex.set(index2, setToArray(adjacentIds, [])); }); adjacentIdByIndexWithSets.clear(); const pointsOnUnsharedEdges = /* @__PURE__ */ new Set(); adjacentIdByIndex.forEach((_, index2) => { const quadCountByEdgeEntry = quadCountByEdge.get(index2); if (!quadCountByEdgeEntry) { return; } quadCountByEdgeEntry.forEach((quadIndices, key1) => { if (quadIndices.size == 1) { pointsOnUnsharedEdges.add(index2); } }); }); const iterations = this.pv.iterations; const strength = this.pv.strength; const lerp = 1 - strength; let previousPositionArray = tmpPositionArray0; let nextPositionArray = tmpPositionArray1; for (let i = 0; i < iterations; i++) { adjacentIdByIndex.forEach((adjacentIds, index2) => { if (pointsOnUnsharedEdges.has(index2)) { return; } _current.fromArray(previousPositionArray, index2 * 3); _neighbourAverage.set(0, 0, 0); for (const adjacentId of adjacentIds) { _neighbour.fromArray(previousPositionArray, adjacentId * 3); _neighbourAverage.add(_neighbour); } _neighbourAverage.divideScalar(adjacentIds.length); _neighbourAverage.lerp(_current, lerp); _neighbourAverage.toArray(nextPositionArray, index2 * 3); }); const tmp = nextPositionArray; nextPositionArray = previousPositionArray; previousPositionArray = tmp; } position.array = previousPositionArray; } _smoothQuadGeometryWithSquarify(geometry) { const position = geometry.attributes[Attribute.POSITION]; if (!position) { return; } const iterations = this.pv.iterations; const strength = this.pv.strength; const positionArray = position.array; const pointsCount = positionArray.length / 3; const deltas = new Array(positionArray.length).fill(0); const deltasCount = new Array(pointsCount).fill(0); const index = geometry.index; const indicesCount = index.length; let previousPositionArray = positionArray; let nextPositionArray = positionArray; for (let i = 0; i < iterations; i++) { deltas.fill(0); for (let q = 0; q < indicesCount; q += 4) { const i0 = index[q + 0]; const i1 = index[q + 1]; const i2 = index[q + 2]; const i3 = index[q + 3]; const i0_3 = i0 * 3; const i1_3 = i1 * 3; const i2_3 = i2 * 3; const i3_3 = i3 * 3; _p0.fromArray(previousPositionArray, i0_3); _p1.fromArray(previousPositionArray, i1_3); _p2.fromArray(previousPositionArray, i2_3); _p3.fromArray(previousPositionArray, i3_3); _delta0.copy(_p0); _delta1.copy(_p1); _delta2.copy(_p2); _delta3.copy(_p3); _triangle.a.copy(_p0); _triangle.b.copy(_p1); _triangle.c.copy(_p2); _triangle.getNormal(_triangleNormal); _center.copy(_p0).add(_p1).add(_p2).add(_p3).multiplyScalar(0.25); _p0.sub(_center); _p1.sub(_center); _p2.sub(_center); _p3.sub(_center); _q.setFromAxisAngle(_triangleNormal, -Math.PI * 0.5); _p1.applyQuaternion(_q); _q.setFromAxisAngle(_triangleNormal, -Math.PI * 1); _p2.applyQuaternion(_q); _q.setFromAxisAngle(_triangleNormal, -Math.PI * 1.5); _p3.applyQuaternion(_q); _average.copy(_p0).add(_p1).add(_p2).add(_p3).multiplyScalar(0.25); _p0.lerp(_average, strength); _p1.lerp(_average, strength); _p2.lerp(_average, strength); _p3.lerp(_average, strength); _q.setFromAxisAngle(_triangleNormal, +Math.PI * 0.5); _p1.applyQuaternion(_q); _q.setFromAxisAngle(_triangleNormal, +Math.PI * 1); _p2.applyQuaternion(_q); _q.setFromAxisAngle(_triangleNormal, +Math.PI * 1.5); _p3.applyQuaternion(_q); _p0.add(_center); _p1.add(_center); _p2.add(_center); _p3.add(_center); _delta0.sub(_p0).multiplyScalar(-1); _delta1.sub(_p1).multiplyScalar(-1); _delta2.sub(_p2).multiplyScalar(-1); _delta3.sub(_p3).multiplyScalar(-1); _currentDelta0.fromArray(deltas, i0_3); _currentDelta1.fromArray(deltas, i1_3); _currentDelta2.fromArray(deltas, i2_3); _currentDelta3.fromArray(deltas, i3_3); _delta0.add(_currentDelta0).toArray(deltas, i0_3); _delta1.add(_currentDelta1).toArray(deltas, i1_3); _delta2.add(_currentDelta2).toArray(deltas, i2_3); _delta3.add(_currentDelta3).toArray(deltas, i3_3); deltasCount[i0]++; deltasCount[i1]++; deltasCount[i2]++; deltasCount[i3]++; } for (let i2 = 0; i2 < pointsCount; i2++) { const deltaCount = deltasCount[i2]; if (deltaCount > 0) { _current.fromArray(previousPositionArray, i2 * 3); _currentDelta0.fromArray(deltas, i2 * 3).divideScalar(deltaCount); _current.add(_currentDelta0); _current.toArray(nextPositionArray, i2 * 3); } } } position.array = previousPositionArray; } }