@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
182 lines (181 loc) • 6.76 kB
JavaScript
;
import { TypedSopNode } from "./_Base";
import { NodeParamsConfig, ParamConfig } from "../utils/params/ParamsConfig";
import { BufferAttribute, Vector3, Box3 } from "three";
import { triangleGraphFromGeometry } from "../../../core/geometry/modules/three/graph/triangle/TriangleGraphUtils";
import { setToArray } from "../../../core/SetUtils";
import { arrayCopy } from "../../../core/ArrayUtils";
import { triangleEdgePositions } from "../../../core/geometry/modules/three/graph/triangle/TriangleEdge";
import { SopType } from "../../poly/registers/nodes/types/Sop";
import { TypeAssert } from "../../poly/Assert";
const _p0 = new Vector3();
const _p1 = new Vector3();
const _box = new Box3();
const _n = new Vector3();
export var ExtrudeOpenEdgesFilterMethod = /* @__PURE__ */ ((ExtrudeOpenEdgesFilterMethod2) => {
ExtrudeOpenEdgesFilterMethod2["BELOW_Y"] = "belowY";
ExtrudeOpenEdgesFilterMethod2["IN_BBOX"] = "inBbox";
return ExtrudeOpenEdgesFilterMethod2;
})(ExtrudeOpenEdgesFilterMethod || {});
const FILTER_METHODS = [
"belowY" /* BELOW_Y */,
"inBbox" /* IN_BBOX */
];
class ExtrudeOpenEdgesSopParamConfig extends NodeParamsConfig {
constructor() {
super(...arguments);
this.offset = ParamConfig.VECTOR3([0, 1, 0]);
this.filterEdges = ParamConfig.BOOLEAN(false);
/** @param filter method */
this.filterMethod = ParamConfig.INTEGER(FILTER_METHODS.indexOf("belowY" /* BELOW_Y */), {
menu: {
entries: FILTER_METHODS.map((name, value) => ({ name, value }))
},
visibleIf: {
filterEdges: true
}
});
this.bboxMin = ParamConfig.VECTOR3([-1, -1, -1], {
visibleIf: {
filterEdges: true,
filterMethod: FILTER_METHODS.indexOf("inBbox" /* IN_BBOX */)
}
});
this.bboxMax = ParamConfig.VECTOR3([1, 1, 1], {
visibleIf: {
filterEdges: true,
filterMethod: FILTER_METHODS.indexOf("inBbox" /* IN_BBOX */)
}
});
this.yThreshold = ParamConfig.FLOAT(0, {
visibleIf: {
filterEdges: true,
filterMethod: FILTER_METHODS.indexOf("belowY" /* BELOW_Y */)
},
range: [-1, 1],
rangeLocked: [false, false]
});
}
}
const ParamsConfig = new ExtrudeOpenEdgesSopParamConfig();
export class ExtrudeOpenEdgesSopNode extends TypedSopNode {
constructor() {
super(...arguments);
this.paramsConfig = ParamsConfig;
}
static type() {
return SopType.EXTRUDE_OPEN_EDGES;
}
initializeNode() {
this.io.inputs.setCount(1);
}
cook(inputCoreGroups) {
const coreGroup = inputCoreGroups[0];
const objects = coreGroup.threejsObjects();
for (const object of objects) {
this._findAndExtrudeOpenEdges(object);
}
this.setCoreGroup(coreGroup);
}
_findAndExtrudeOpenEdges(object) {
const mesh = object;
const geometry = mesh.geometry;
if (!geometry)
return;
const positionAttribute = geometry.getAttribute("position");
const normalAttribute = geometry.getAttribute("normal");
const index = geometry.getIndex();
if (!(positionAttribute && normalAttribute && index)) {
return;
}
const graph = triangleGraphFromGeometry(mesh.geometry);
if (!graph) {
return;
}
const edgeIds = [];
graph.edgeIds(edgeIds);
const unsharedEdgeIdsSet = /* @__PURE__ */ new Set();
for (const edgeId of edgeIds) {
const edge = graph.edge(edgeId);
if (!edge)
continue;
if (edge.triangleIds.length == 1) {
unsharedEdgeIdsSet.add(edgeId);
}
}
let unsharedEdgeIds = [];
setToArray(unsharedEdgeIdsSet, unsharedEdgeIds);
const newPositionValues = [];
const newNormalValues = [];
const newIndexValues = [];
arrayCopy(positionAttribute.array, newPositionValues);
arrayCopy(normalAttribute.array, newNormalValues);
arrayCopy(index.array, newIndexValues);
if (this.pv.filterEdges) {
const filterMethod = FILTER_METHODS[this.pv.filterMethod];
unsharedEdgeIds = this._filterEdgeIds(graph, unsharedEdgeIds, newPositionValues, filterMethod);
}
for (const edgeId of unsharedEdgeIds) {
this._extrudeEdge(graph, edgeId, newPositionValues, newNormalValues, newIndexValues);
}
geometry.setAttribute("position", new BufferAttribute(new Float32Array(newPositionValues), 3));
geometry.setAttribute("normal", new BufferAttribute(new Float32Array(newNormalValues), 3));
geometry.setIndex(newIndexValues);
}
_filterEdgeIds(graph, edgeIds, positionValues, filterMethod) {
switch (filterMethod) {
case "belowY" /* BELOW_Y */: {
const yThreshold = this.pv.yThreshold;
return edgeIds.filter((edgeId) => {
const edge = graph.edge(edgeId);
if (!edge)
return false;
triangleEdgePositions(edge, positionValues, _p0, _p1);
return _p0.y < yThreshold && _p1.y < yThreshold;
});
}
case "inBbox" /* IN_BBOX */: {
_box.min.copy(this.pv.bboxMin);
_box.max.copy(this.pv.bboxMax);
return edgeIds.filter((edgeId) => {
const edge = graph.edge(edgeId);
if (!edge)
return false;
triangleEdgePositions(edge, positionValues, _p0, _p1);
_p0.add(_p1).divideScalar(2);
return _box.containsPoint(_p0);
});
}
}
TypeAssert.unreachable(filterMethod);
}
_extrudeEdge(graph, edgeId, newPositionValues, newNormalValues, newIndexValues) {
const edge = graph.edge(edgeId);
if (!edge)
return;
const otherTriangleId = edge.triangleIds[0];
const otherTriangle = graph.triangle(otherTriangleId);
const id0Index = otherTriangle.triangle.indexOf(edge.pointIdPair.id0);
const id1Index = otherTriangle.triangle.indexOf(edge.pointIdPair.id1);
const invertRequired = id0Index == 0 && id1Index == 1 || id0Index == 1 && id1Index == 2 || id0Index == 2 && id1Index == 0;
const pointIdPair = edge.pointIdPair;
triangleEdgePositions(edge, newPositionValues, _p0, _p1);
_p0.add(this.pv.offset);
_p1.add(this.pv.offset);
_n.copy(_p1).sub(_p0).cross(this.pv.offset);
const p0Index = newPositionValues.length / 3;
const p1Index = p0Index + 1;
newPositionValues.push(_p0.x, _p0.y, _p0.z);
newPositionValues.push(_p1.x, _p1.y, _p1.z);
if (invertRequired) {
_n.multiplyScalar(-1);
newIndexValues.push(pointIdPair.id1, pointIdPair.id0, p0Index);
newIndexValues.push(pointIdPair.id1, p0Index, p1Index);
} else {
newIndexValues.push(pointIdPair.id0, pointIdPair.id1, p0Index);
newIndexValues.push(p0Index, pointIdPair.id1, p1Index);
}
newNormalValues.push(_n.x, _n.y, _n.z);
newNormalValues.push(_n.x, _n.y, _n.z);
}
}