@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
301 lines (300 loc) • 11.6 kB
JavaScript
"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]);
// }
}