@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
209 lines (187 loc) • 7.07 kB
text/typescript
/**
* Processes the faces of the input geometry
*
*
*/
import {Mesh, BufferAttribute, BufferGeometry, Vector3} from 'three';
import {TypedSopNode} from './_Base';
import {CoreGroup} from '../../../core/geometry/Group';
import {CoreFace} from '../../../core/geometry/modules/three/CoreFace';
import {ThreejsPointArray3} from '../../../core/geometry/modules/three/Common';
import {InputCloneMode} from '../../poly/InputCloneMode';
import {NodeParamsConfig, ParamConfig} from '../utils/params/ParamsConfig';
import {arrayChunk, rangeWithEnd} from '../../../core/ArrayUtils';
import {isBooleanTrue} from '../../../core/BooleanValue';
import {corePointClassFactory} from '../../../core/geometry/CoreObjectFactory';
import {ThreejsPoint} from '../../../core/geometry/modules/three/ThreejsPoint';
import {ThreejsPrimitiveTriangle} from '../../../core/geometry/modules/three/ThreejsPrimitiveTriangle';
const dummyMesh = new Mesh();
enum FaceAttribName {
CENTER = 'faceCenter',
ID = 'faceId',
POSITION = 'position',
}
const _faceCenter = new Vector3();
const _position = new Vector3();
const _newPosition = new Vector3();
class FaceSopParamsConfig extends NodeParamsConfig {
/** @param makes faces unique */
makeFacesUnique = ParamConfig.BOOLEAN(0);
/** @param adds a vector3 attribute that represents the center of a face */
addFaceCenterAttribute = ParamConfig.BOOLEAN(0, {
visibleIf: {makeFacesUnique: 1},
});
/** @param add an id attribute for each face */
addFaceId = ParamConfig.BOOLEAN(0, {
visibleIf: {makeFacesUnique: 1},
});
/** @param allows to transform each face */
transform = ParamConfig.BOOLEAN(0, {
visibleIf: {makeFacesUnique: 1},
});
/** @param scales the faces indepedently */
scale = ParamConfig.FLOAT(1, {
visibleIf: {makeFacesUnique: 1, transform: 1},
});
}
const ParamsConfig = new FaceSopParamsConfig();
export class FaceSopNode extends TypedSopNode<FaceSopParamsConfig> {
override paramsConfig = ParamsConfig;
static override type() {
return 'face';
}
override initializeNode() {
this.io.inputs.setCount(1);
this.io.inputs.initInputsClonedState(InputCloneMode.FROM_NODE);
}
private _face = new CoreFace();
private _points: ThreejsPointArray3 = [
new ThreejsPoint(dummyMesh, 0),
new ThreejsPoint(dummyMesh, 0),
new ThreejsPoint(dummyMesh, 0),
];
override cook(input_contents: CoreGroup[]) {
const core_group = input_contents[0];
if (isBooleanTrue(this.pv.makeFacesUnique)) {
this._makeFacesUnique(core_group);
// we can only add face_center attrib
// if the faces have been split
// otherwise a point may belong to multiple faces
if (isBooleanTrue(this.pv.addFaceCenterAttribute)) {
this._addFaceCenterAttribute(core_group);
}
if (isBooleanTrue(this.pv.addFaceId)) {
this._addFaceId(core_group);
}
if (isBooleanTrue(this.pv.transform)) {
this._transformFaces(core_group);
}
}
this.setCoreGroup(core_group);
}
private _makeFacesUnique(core_group: CoreGroup) {
const objects = core_group.threejsObjects();
for (const object of objects) {
if ((object as Mesh).isMesh) {
const geometry = (object as Mesh).geometry as BufferGeometry;
const index = geometry.index?.array;
if (index) {
const faces = arrayChunk([...index], 3);
const pointsCount = faces.length * 3;
for (const attrib_name of Object.keys(geometry.attributes)) {
const attrib = geometry.attributes[attrib_name] as BufferAttribute;
const attrib_size = attrib.itemSize;
const new_values = new Float32Array(pointsCount * attrib_size);
let new_value_index = 0;
faces.forEach((face) => {
face.forEach((index) => {
for (let i = 0; i < attrib_size; i++) {
const current_value = attrib.array[index * attrib_size + i];
new_values[new_value_index] = current_value;
new_value_index += 1;
}
});
});
geometry.setAttribute(attrib_name, new BufferAttribute(new_values, attrib_size));
}
const newIndices = rangeWithEnd(pointsCount);
geometry.setIndex(newIndices);
}
}
}
}
private _addFaceCenterAttribute(coreGroup: CoreGroup) {
// let faces: CoreFace[], face: CoreFace, points: CorePoint[], point: CorePoint;
const coreObjects = coreGroup.threejsCoreObjects();
for (const coreObject of coreObjects) {
const object = coreObject.object();
const corePointClass = corePointClassFactory(object);
if ((object as Mesh).isMesh) {
if (!corePointClass.hasAttribute(object, FaceAttribName.CENTER)) {
corePointClass.addNumericAttribute(object, FaceAttribName.CENTER, 3, -1);
}
const facesCount = ThreejsPrimitiveTriangle.entitiesCount(object);
this._face.setGeometry((object as Mesh).geometry);
for (let fi = 0; fi < facesCount; fi++) {
// face = faces[fi];
this._face.setIndex(fi);
this._face.center(_faceCenter);
this._face.points(this._points);
for (const point of this._points) {
point.setAttribValue(FaceAttribName.CENTER, _faceCenter);
}
}
}
}
}
private _addFaceId(coreGroup: CoreGroup) {
const coreObjects = coreGroup.threejsCoreObjects();
for (const coreObject of coreObjects) {
const object = coreObject.object();
const corePointClass = corePointClassFactory(object);
if ((object as Mesh).isMesh) {
// const faces = core_geometry.faces();
// const points_count = core_geometry.pointsCount();
if (!corePointClass.hasAttribute(object, FaceAttribName.ID)) {
corePointClass.addNumericAttribute(object, FaceAttribName.ID, 1, -1);
}
const facesCount = ThreejsPrimitiveTriangle.entitiesCount(object);
this._face.setGeometry((object as Mesh).geometry);
for (let i = 0; i < facesCount; i++) {
this._face.setIndex(i);
this._face.points(this._points);
for (const point of this._points) {
point.setAttribValue(FaceAttribName.ID, i);
}
}
}
}
}
private _transformFaces(coreGroup: CoreGroup) {
const scale = this.pv.scale;
const coreObjects = coreGroup.threejsCoreObjects();
for (const coreObject of coreObjects) {
const object = coreObject.object();
const corePointClass = corePointClassFactory(object);
if ((object as Mesh).isMesh) {
if (!corePointClass.hasAttribute(object, FaceAttribName.POSITION)) {
corePointClass.addNumericAttribute(object, FaceAttribName.POSITION, 3, -1);
}
const facesCount = ThreejsPrimitiveTriangle.entitiesCount(object);
this._face.setGeometry((object as Mesh).geometry);
for (let fi = 0; fi < facesCount; fi++) {
this._face.setIndex(fi);
this._face.center(_faceCenter);
this._face.points(this._points);
for (const point of this._points) {
point.position(_position);
_newPosition.x = _position.x * scale + _faceCenter.x * (1 - scale);
_newPosition.y = _position.y * scale + _faceCenter.y * (1 - scale);
_newPosition.z = _position.z * scale + _faceCenter.z * (1 - scale);
point.setAttribValue(FaceAttribName.POSITION, _newPosition);
}
}
}
}
}
}