@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
285 lines (284 loc) • 10.5 kB
JavaScript
"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;
}
}