UNPKG

@polygonjs/polygonjs

Version:

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

221 lines (199 loc) 6.86 kB
/** * Creates a plane. * * @remarks * This node is similar to the Color and Normal SOPs, and can update the vertex positions with expressions * */ import {BufferGeometry, TypedArray, BufferAttribute, Mesh} from 'three'; import {TypedSopNode} from './_Base'; import {CoreGroup} from '../../../core/geometry/Group'; import type {ThreejsCoreObject} from '../../../core/geometry/modules/three/ThreejsCoreObject'; import {BaseCorePoint, CorePoint} from '../../../core/geometry/entities/point/CorePoint'; import {InputCloneMode} from '../../poly/InputCloneMode'; import {BooleanParam} from '../../params/Boolean'; import {FloatParam} from '../../params/Float'; const POSITION_ATTRIB_NAME = 'position'; type ValueArrayByName = Map<string, number[]>; type ComponentOffset = 0 | 1 | 2; import {NodeParamsConfig, ParamConfig} from '../utils/params/ParamsConfig'; import {isBooleanTrue} from '../../../core/BooleanValue'; import {SopType} from '../../poly/registers/nodes/types/Sop'; import {pointsFromObject} from '../../../core/geometry/entities/point/CorePointUtils'; import {CoreObjectType} from '../../../core/geometry/ObjectContent'; class PointSopParamsConfig extends NodeParamsConfig { /** @param toggle on to update the x component */ updateX = ParamConfig.BOOLEAN(0); /** @param expression the x component */ x = ParamConfig.FLOAT('@P.x', { visibleIf: {updateX: 1}, expression: {forEntities: true}, }); /** @param toggle on to update the y component */ updateY = ParamConfig.BOOLEAN(0); /** @param expression the y component */ y = ParamConfig.FLOAT('@P.y', { visibleIf: {updateY: 1}, expression: {forEntities: true}, }); /** @param toggle on to update the z component */ updateZ = ParamConfig.BOOLEAN(0); /** @param expression the z component */ z = ParamConfig.FLOAT('@P.z', { visibleIf: {updateZ: 1}, expression: {forEntities: true}, }); /** @param toggle on to update the normals */ updateNormals = ParamConfig.BOOLEAN(1); } const ParamsConfig = new PointSopParamsConfig(); export class PointSopNode extends TypedSopNode<PointSopParamsConfig> { override paramsConfig = ParamsConfig; static override type() { return SopType.POINT; } private _x_arrays_by_geometry_uuid: ValueArrayByName = new Map(); private _y_arrays_by_geometry_uuid: ValueArrayByName = new Map(); private _z_arrays_by_geometry_uuid: ValueArrayByName = new Map(); override initializeNode() { this.io.inputs.setCount(1); this.io.inputs.initInputsClonedState(InputCloneMode.FROM_NODE); } override async cook(inputCoreGroups: CoreGroup[]) { const coreGroup = inputCoreGroups[0]; await this._evalExpressionsForCoreGroup(coreGroup); } async _evalExpressionsForCoreGroup(coreGroup: CoreGroup) { const coreObjects = coreGroup.threejsCoreObjects(); for (let i = 0; i < coreObjects.length; i++) { await this._evalExpressionsForCoreObject(coreObjects[i]); } if (isBooleanTrue(this.pv.updateNormals)) { const objects = coreGroup.threejsObjectsWithGeo(); for (const object of objects) { if ((object as Mesh).isMesh) { object.geometry.computeVertexNormals(); } } } const geometries = coreGroup.geometries(); for (const geometry of geometries) { geometry.computeBoundingBox(); } // needs update required for when no cloning if (!this.io.inputs.cloneRequired(0)) { const geometries = coreGroup.geometries(); for (const geometry of geometries) { const attrib = geometry.getAttribute(POSITION_ATTRIB_NAME) as BufferAttribute; attrib.needsUpdate = true; } } this.setCoreGroup(coreGroup); } async _evalExpressionsForCoreObject(coreObject: ThreejsCoreObject) { const object = coreObject.object(); const geometry = (object as Mesh).geometry as BufferGeometry; const points: CorePoint<CoreObjectType>[] = []; pointsFromObject(object, points); const array = (geometry.getAttribute(POSITION_ATTRIB_NAME) as BufferAttribute).array; const tmp_array_x = await this._updateFromParam( geometry, points, this.p.updateX, this.p.x, this.pv.x, this._x_arrays_by_geometry_uuid, 0 ); const tmp_array_y = await this._updateFromParam( geometry, points, this.p.updateY, this.p.y, this.pv.y, this._y_arrays_by_geometry_uuid, 1 ); const tmp_array_z = await this._updateFromParam( geometry, points, this.p.updateZ, this.p.z, this.pv.z, this._z_arrays_by_geometry_uuid, 2 ); if (tmp_array_x) { this._commitTmpValues(tmp_array_x, array, 0); } if (tmp_array_y) { this._commitTmpValues(tmp_array_y, array, 1); } if (tmp_array_z) { this._commitTmpValues(tmp_array_z, array, 2); } } private async _updateFromParam( geometry: BufferGeometry, points: BaseCorePoint[], do_update_param: BooleanParam, value_param: FloatParam, param_value: number, arrays_by_geometry_uuid: ValueArrayByName, offset: ComponentOffset ) { const do_update = do_update_param; const param = value_param; let tmpArray = this._initArrayIfRequired(geometry, arrays_by_geometry_uuid, points.length, offset); if (do_update.value) { if (param.hasExpression() && param.expressionController && param.expressionController.entitiesDependent()) { await param.expressionController.computeExpressionForPoints(points, (point, value) => { tmpArray[point.index()] = value; }); } else { let point; for (let i = 0; i < points.length; i++) { point = points[i]; tmpArray[point.index()] = param_value; } } } return tmpArray; } private _initArrayIfRequired( geometry: BufferGeometry, arrays_by_geometry_uuid: ValueArrayByName, points_count: number, offset: ComponentOffset ) { const uuid = geometry.uuid; const current_array = arrays_by_geometry_uuid.get(uuid); if (current_array) { // only create new array if we need more point, or as soon as the length is different? if (current_array.length < points_count) { const new_array = this._array_for_component(geometry, points_count, offset); arrays_by_geometry_uuid.set(uuid, new_array); return new_array; } else { return current_array; } } else { const new_array = this._array_for_component(geometry, points_count, offset); arrays_by_geometry_uuid.set(uuid, new_array); return new_array; } } private _array_for_component(geometry: BufferGeometry, pointsCount: number, offset: ComponentOffset) { const new_array = new Array<number>(pointsCount); const src_array = (geometry.getAttribute(POSITION_ATTRIB_NAME) as BufferAttribute).array; for (let i = 0; i < new_array.length; i++) { new_array[i] = src_array[i * 3 + offset]; } return new_array; } private _commitTmpValues(tmp_array: number[], targetArray: TypedArray, offset: number) { for (let i = 0; i < tmp_array.length; i++) { targetArray[i * 3 + offset] = tmp_array[i]; } } }