UNPKG

@polygonjs/polygonjs

Version:

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

209 lines (187 loc) 7.07 kB
/** * 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); } } } } } }