UNPKG

@polygonjs/polygonjs

Version:

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

301 lines (300 loc) 11.6 kB
"use strict"; import { ObjectTransformMode } from "./../../../core/TransformSpace"; import { SopType } from "./../../poly/registers/nodes/types/Sop"; import { TypedSopNode } from "./_Base"; import { BaseCoreObject } from "../../../core/geometry/entities/object/BaseCoreObject"; import { CoreInstancer } from "../../../core/geometry/Instancer"; import { SopCopyStamp } from "./utils/CopyStamp"; import { Matrix4 } from "three"; import { NodeParamsConfig, ParamConfig } from "../utils/params/ParamsConfig"; import { InputCloneMode } from "../../poly/InputCloneMode"; import { isBooleanTrue } from "../../../core/BooleanValue"; import { filterCoreObjectsFromCoreGroup, filterObjectsWithGroup, filterThreejsCoreObjectsFromCoreGroup } from "../../../core/geometry/Mask"; import { CoreTransform, RotationOrder, TRANSFORM_TARGET_TYPES, TransformTargetType } from "../../../core/Transform"; import { OBJECT_TRANSFORM_SPACE_MENU_ENTRIES, OBJECT_TRANSFORM_SPACES } from "../../../core/TransformSpace"; import { coreObjectClassFactory } from "../../../core/geometry/CoreObjectFactory"; import { pointsFromCoreObjects } from "../../../core/geometry/entities/point/CorePointUtils"; class CopySopParamsConfig extends NodeParamsConfig { constructor() { super(...arguments); /** @param select which objects are copied */ this.srcGroup = ParamConfig.STRING("", { objectMask: true }); /** @param select which objects the src objects are copied onto */ this.templateGroup = ParamConfig.STRING("", { objectMask: { inputIndex: 1 } }); /** @param copies count, used when the second input is not given */ this.count = ParamConfig.INTEGER(1, { range: [1, 20], rangeLocked: [true, false] }); /** @param translate each copy */ this.t = ParamConfig.VECTOR3([0, 0, 0]); /** @param rotate each copy */ this.r = ParamConfig.VECTOR3([0, 0, 0]); /** @param scale each copy */ this.s = ParamConfig.VECTOR3([1, 1, 1]); /** @param scale multiplier for each copy */ this.scale = ParamConfig.FLOAT(1); /** @param transforms every input object each on a single input point */ this.transformOnly = ParamConfig.BOOLEAN(0); /** @param defines if the objects or the geometries are transformed */ this.transformMode = ParamConfig.INTEGER(TRANSFORM_TARGET_TYPES.indexOf(TransformTargetType.OBJECT), { menu: { entries: TRANSFORM_TARGET_TYPES.map((name, value) => ({ name, value })) } }); /** @param defines how the objects are transformed */ this.objectTransformSpace = ParamConfig.INTEGER(0, { visibleIf: { transformMode: TRANSFORM_TARGET_TYPES.indexOf(TransformTargetType.OBJECT) }, menu: { entries: OBJECT_TRANSFORM_SPACE_MENU_ENTRIES } }); /** @param toggles on to copy attributes from the input points to the created objects. Note that the vertex attributes from the points become object attributes */ this.copyAttributes = ParamConfig.BOOLEAN(0); /** @param names of attributes to copy */ this.attributesToCopy = ParamConfig.STRING("", { visibleIf: { copyAttributes: true } }); /** @param toggle on to use the `copy` expression, which allows to change how the left input is evaluated for each point */ this.useCopyExpr = ParamConfig.BOOLEAN(1); } } const ParamsConfig = new CopySopParamsConfig(); export class CopySopNode extends TypedSopNode { constructor() { super(...arguments); this.paramsConfig = ParamsConfig; this._attribNamesToCopy = []; this._objects = []; this._instancer = new CoreInstancer(); this._instanceMatrix = new Matrix4(); // // // ACCUMULATE TRANSFORM // // this._coreTransform = new CoreTransform(); this._transformAccumulatedMatrix = new Matrix4(); this._transformMatrix = new Matrix4(); } static type() { return SopType.COPY; } initializeNode() { this.io.inputs.setCount(1, 2); this.io.inputs.initInputsClonedState([InputCloneMode.FROM_NODE, InputCloneMode.NEVER]); } setTransformMode(transformMode) { this.p.transformMode.set(TRANSFORM_TARGET_TYPES.indexOf(transformMode)); } setObjectTransformSpace(transformSpace) { this.p.objectTransformSpace.set(OBJECT_TRANSFORM_SPACES.indexOf(transformSpace)); } async cook(inputCoreGroups) { if (!isBooleanTrue(this.pv.useCopyExpr)) { this.stampNode().reset(); } const coreGroup0 = inputCoreGroups[0]; if (!this.io.inputs.hasInput(1)) { await this.cookWithoutTemplate(coreGroup0); return; } const coreGroup1 = inputCoreGroups[1]; if (!coreGroup1) { this.states.error.set("second input invalid"); return; } await this.cookWithTemplate(coreGroup0, coreGroup1); this.stampNode().reset(); } async cookWithTemplate(instanceCoreGroup, templateCoreGroup) { this._objects = []; const templateCoreObjects = filterThreejsCoreObjectsFromCoreGroup(templateCoreGroup, { group: this.pv.templateGroup }); const templatePoints = []; pointsFromCoreObjects(templateCoreObjects, templatePoints); this._instancer.setCoreGroup(templateCoreGroup); this._attribNamesToCopy = templateCoreGroup.pointAttribNamesMatchingMask(this.pv.attributesToCopy); await this._copyMovedObjectsOnTemplatePoints(instanceCoreGroup, templatePoints); this.setObjects(this._objects); } // https://stackoverflow.com/questions/24586110/resolve-promises-one-after-another-i-e-in-sequence async _copyMovedObjectsOnTemplatePoints(instanceCoreGroup, templatePoints) { this._initAccumulatedTransform(); for (let pointIndex = 0; pointIndex < templatePoints.length; pointIndex++) { await this._copyMovedObjectOnTemplatePoint(instanceCoreGroup, templatePoints, pointIndex); this._accumulateTransform(); } } async _copyMovedObjectOnTemplatePoint(instanceCoreGroup, templatePoints, point_index) { this._instancer.matrixFromPoint(templatePoints[point_index], this._instanceMatrix); const templatePoint = templatePoints[point_index]; if (isBooleanTrue(this.pv.useCopyExpr)) { this.stampNode().setPoint(templatePoint); } const movedObjects = await this._getMovedObjectsForTemplatePoint(instanceCoreGroup, point_index); for (const movedObject of movedObjects) { if (isBooleanTrue(this.pv.copyAttributes)) { this._copyAttributesGromTemplate(movedObject, templatePoint); } if (isBooleanTrue(this.pv.transformOnly)) { movedObject.applyMatrix4(this._instanceMatrix); } else { this._applyMatrixToObject(movedObject, this._instanceMatrix); } this._applyAccumulatedTransform(movedObject); this._objects.push(movedObject); } } async _getMovedObjectsForTemplatePoint(instanceCoreGroup, pointIndex) { const stampedInstanceCoreGroup = await this._stampInstanceGroupIfRequired(instanceCoreGroup); if (stampedInstanceCoreGroup) { const getObjectsForTransformOnly = () => { const objects = filterObjectsWithGroup(stampedInstanceCoreGroup, { group: this.pv.srcGroup }); const object = objects[pointIndex]; if (object) { return [coreObjectClassFactory(object).clone(object)]; } else { return []; } }; const movedObjects = isBooleanTrue(this.pv.transformOnly) ? ( // TODO: why is doing a transform slower than cloning the input?? getObjectsForTransformOnly() ) : filterObjectsWithGroup(stampedInstanceCoreGroup.clone(), { group: this.pv.srcGroup }); return movedObjects; } else { return []; } } async _stampInstanceGroupIfRequired(instanceCoreGroup) { const container0 = await this.containerController.requestInputContainer(0); if (container0) { const coreGroup0 = container0.coreContent(); if (coreGroup0) { return coreGroup0; } else { return; } } else { this.states.error.set(`input failed for index ${this.stampValue()}`); return; } } async _copyMovedObjectsForEachInstance(instanceCoreGroup) { this._initAccumulatedTransform(); for (let i = 0; i < this.pv.count; i++) { await this._copyMovedObjectsForInstance(instanceCoreGroup, i); this._accumulateTransform(); } } async _copyMovedObjectsForInstance(instanceCoreGroup, i) { if (isBooleanTrue(this.pv.useCopyExpr)) { this.stampNode().setGlobalIndex(i); } const stamptedInstanceCoreGroup = await this._stampInstanceGroupIfRequired(instanceCoreGroup); if (stamptedInstanceCoreGroup) { const srcCoreObjects = filterCoreObjectsFromCoreGroup(stamptedInstanceCoreGroup, { group: this.pv.srcGroup }); for (const coreObject of srcCoreObjects) { const clonedObject = coreObject.clone().object(); if (clonedObject) { this._applyAccumulatedTransform(clonedObject); this._objects.push(clonedObject); } } } } // TODO: what if I combine both param_count and stamping?! async cookWithoutTemplate(instanceCoreGroup) { this._objects = []; await this._copyMovedObjectsForEachInstance(instanceCoreGroup); this.setObjects(this._objects); } _copyAttributesGromTemplate(object, templatePoint) { this._attribNamesToCopy.forEach((attribName, i) => { const attribValue = templatePoint.attribValue(attribName); BaseCoreObject.addAttribute(object, attribName, attribValue); }); } // // // STAMP // // stampValue(attribName) { return this.stampNode().value(attribName); } stampNode() { return this._stampNode = this._stampNode || this._createStampNode(); } _createStampNode() { const stampNode = new SopCopyStamp(this.scene()); stampNode.setForbiddenTriggerNodes(this); return stampNode; } dispose() { super.dispose(); if (this._stampNode) { this._stampNode.dispose(); } } _initAccumulatedTransform() { const pv = this.pv; this._transformMatrix = this._coreTransform.matrix(pv.t, pv.r, pv.s, pv.scale, RotationOrder.XYZ); this._transformAccumulatedMatrix.identity(); } _accumulateTransform() { this._transformAccumulatedMatrix.multiply(this._transformMatrix); } _applyMatrixToObject(object, matrix) { coreObjectClassFactory(object).applyMatrix( object, matrix, TRANSFORM_TARGET_TYPES[this.pv.transformMode], OBJECT_TRANSFORM_SPACES[this.pv.objectTransformSpace], ObjectTransformMode.MULT ); } _applyAccumulatedTransform(object) { this._applyMatrixToObject(object, this._transformAccumulatedMatrix); } // // // MATRIX OPERATIONS // // // private _applyMatrixToObjectOrGeometry(object: ObjectContent<CoreObjectType>, matrix: Matrix4) { // const transformMode = TRANSFORM_MODES[this.pv.transformMode]; // switch (transformMode) { // case TransformMode.OBJECT: { // this._applyMatrixToObject(object, matrix); // return; // } // case TransformMode.GEOMETRY: { // const geometry = (object as Object3DWithGeometry).geometry; // if (geometry) { // geometry.applyMatrix4(matrix); // } // return; // } // } // TypeAssert.unreachable(transformMode); // } // private _object_position = new Vector3(); // private _applyMatrixToObject(object: ObjectContent<CoreObjectType>, matrix: Matrix4) { // applyTransformWithSpaceToObject(object, matrix, OBJECT_TRANSFORM_SPACES[this.pv.objectTransformSpace]); // } }