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