@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
331 lines (330 loc) • 11.5 kB
JavaScript
"use strict";
import { Vector3 } from "three";
import { NodeParamsConfig } from "../utils/params/ParamsConfig";
import { NodeContext } from "../../poly/NodeContext";
import { InputCloneMode } from "../../poly/InputCloneMode";
import { SopType } from "../../poly/registers/nodes/types/Sop";
import { isBoolean, isNumberValid, isColor, isVector, isArray, isFunction } from "../../../core/Type";
import { Poly } from "../../Poly";
import { ThreejsCoreObject } from "../../../core/geometry/modules/three/ThreejsCoreObject";
import { DEFAULT as DEFAULT_TESSELATION_PARAMS } from "../../../core/geometry/modules/tet/utils/TesselationParamsConfig";
import { CoreSoftBodyAttribute, SoftBodyIdAttribute } from "../../../core/softBody/SoftBodyAttribute";
import {
createOrFindSoftBodyController,
softBodyControllerNodeIdFromObject
} from "../../../core/softBody/SoftBodyControllerRegister";
import { TetSopNode } from "./_BaseTet";
import { SoftBodyPersistedConfig } from "../js/code/assemblers/softBody/SoftBodyPersistedConfig";
import { AssemblerName } from "../../poly/registers/assemblers/_BaseRegister";
import { JsNodeFinder } from "../js/code/utils/NodeFinder";
class TetSoftBodySolverSopParamsConfig extends NodeParamsConfig {
}
const ParamsConfig = new TetSoftBodySolverSopParamsConfig();
export class TetSoftBodySolverSopNode extends TetSopNode {
constructor() {
super(...arguments);
this.paramsConfig = ParamsConfig;
this._nextId = 0;
this._tetEmbedByThreejsObjectEphemeralId = /* @__PURE__ */ new Map();
this.persisted_config = new SoftBodyPersistedConfig(this);
this._assemblerController = this._createAssemblerController();
this._childrenControllerContext = NodeContext.JS;
this._evaluationGlobals = {
position: new Vector3(),
velocity: new Vector3(),
time: 0,
delta: 0
};
this._paramConfigs = [];
this._functionCreationArgs = {
velocity: [],
collider: []
};
this._functionEvalArgs = {
velocity: { args: [], argsCountBeforeParams: 0 },
collider: { args: [], argsCountBeforeParams: 0 }
};
this._function = {
velocity: void 0,
collider: void 0
};
this._functionArgsWithParams = {
velocity: [],
collider: []
};
}
static type() {
return SopType.TET_SOFT_BODY_SOLVER;
}
assemblerController() {
return this._assemblerController;
}
usedAssembler() {
return AssemblerName.JS_SOFT_BODY;
}
_createAssemblerController() {
return Poly.assemblersRegister.assembler(this, this.usedAssembler());
}
initializeNode() {
this.io.inputs.setCount(1);
this.io.inputs.initInputsClonedState(InputCloneMode.ALWAYS);
}
createNode(node_class, options) {
return super.createNode(node_class, options);
}
children() {
return super.children();
}
nodesByType(type) {
return super.nodesByType(type);
}
childrenAllowed() {
if (this.assemblerController()) {
return super.childrenAllowed();
}
return false;
}
sceneReadonly() {
return this.assemblerController() == null;
}
async cook(inputCoreGroups) {
this.compileIfRequired();
const inputTetObjects = inputCoreGroups[0].tetObjects();
if (inputTetObjects) {
const newThreejsObjects = [];
for (const tetObject of inputTetObjects) {
const highResObject = await this._highResObject(tetObject);
const threejsObjectsFromTetObject = tetObject.toObject3D({
...DEFAULT_TESSELATION_PARAMS,
displayTetMesh: false,
displayOuterMesh: true
});
if (threejsObjectsFromTetObject) {
const lowResObject = isArray(threejsObjectsFromTetObject) ? threejsObjectsFromTetObject[0] : threejsObjectsFromTetObject;
const displayedObject = highResObject ? highResObject : lowResObject;
ThreejsCoreObject.addAttribute(
displayedObject,
SoftBodyIdAttribute.SOLVER_NODE,
this.graphNodeId()
);
const nextId = this._nextId++;
ThreejsCoreObject.addAttribute(displayedObject, SoftBodyIdAttribute.EPHEMERAL_ID, nextId);
const tetEmbed = {
tetObject,
lowResObject,
highResObject
};
this._tetEmbedByThreejsObjectEphemeralId.set(nextId, tetEmbed);
Poly.onObjectsAddRemoveHooks.assignOnAddHookHandler(displayedObject, this);
newThreejsObjects.push(displayedObject);
}
}
this.setObjects(newThreejsObjects);
} else {
this.states.error.set(`no tet objects found in input`);
this.setObjects([]);
}
}
async _highResObject(tetObject) {
var _a;
const highResNodeId = CoreSoftBodyAttribute.getTetEmbedHighResNodeId(tetObject);
if (highResNodeId == null) {
return;
}
const node = this.scene().graph.nodeFromId(highResNodeId);
if (!node) {
return;
}
const container = await node.compute();
if (!container) {
return;
}
const threejsObjects = (_a = container.coreContent()) == null ? void 0 : _a.threejsObjects();
if (!threejsObjects) {
return;
}
return threejsObjects[0];
}
updateObjectOnAdd(object, parent) {
const solverNodeId = ThreejsCoreObject.attribValue(object, SoftBodyIdAttribute.SOLVER_NODE);
if (solverNodeId != null) {
if (solverNodeId != this.graphNodeId()) {
return;
}
const ephemeralId = ThreejsCoreObject.attribValue(object, SoftBodyIdAttribute.EPHEMERAL_ID);
if (ephemeralId == null) {
console.error("no ephemeralId found on object", object);
}
const tetEmbed = this._tetEmbedByThreejsObjectEphemeralId.get(ephemeralId);
if (!tetEmbed) {
console.error("no tetObject found from object", object);
return;
}
createOrFindSoftBodyController(this.scene(), this, {
tetEmbed,
threejsObjectInSceneTree: object
});
}
}
compileIfRequired() {
var _a;
if ((_a = this.assemblerController()) == null ? void 0 : _a.compileRequired()) {
this.compile();
}
}
updateSceneGlobals(stepsCount, dt) {
this._evaluationGlobals.time = this.scene().time() + stepsCount * dt;
this._evaluationGlobals.delta = dt;
this._functionArgsWithParams.velocity[2] = this._evaluationGlobals.time;
this._functionArgsWithParams.collider[2] = this._evaluationGlobals.time;
this._functionArgsWithParams.velocity[3] = this._evaluationGlobals.delta;
this._functionArgsWithParams.collider[3] = this._evaluationGlobals.delta;
}
setPositionGlobals(position) {
this._evaluationGlobals.position.copy(position);
}
setPointGlobals(position, velocity) {
this._evaluationGlobals.position.copy(position);
this._evaluationGlobals.velocity.copy(velocity);
}
function() {
return this._function;
}
functionData() {
return this._functionData;
}
compile() {
const assemblerController = this.assemblerController();
if (!assemblerController) {
return;
}
const outputNodes = JsNodeFinder.findOutputNodes(this);
if (outputNodes.length == 0) {
this.states.error.set("one output node is required");
return;
}
if (outputNodes.length > 1) {
this.states.error.set("only one output node allowed");
return;
}
const outputNode = outputNodes[0];
if (outputNode) {
const paramNodes = JsNodeFinder.findParamGeneratingNodes(this);
const rootNodes = outputNodes.concat(paramNodes);
assemblerController.assembler.set_root_nodes(rootNodes);
assemblerController.assembler.updateFunction();
const functionData = assemblerController.assembler.functionData();
if (!functionData) {
this.states.error.set("failed to compile ");
return;
}
this.updateFromFunctionData(functionData);
}
assemblerController.post_compile();
}
updateFromFunctionData(functionData) {
this._functionData = functionData;
const { functionBody, variableNames, variablesByName, functionNames, functionsByName, paramConfigs } = this._functionData;
const _createFunctionArgs = (functionBody2, type) => {
const wrappedBody = `
try {
${functionBody2}
} catch(e) {
_setErrorFromError(e)
return 0;
}`;
const _setErrorFromError = (e) => {
this.states.error.set(e.message);
};
const variables = [];
const functions = [];
for (const variableName of variableNames) {
const variable = variablesByName[variableName];
variables.push(variable);
}
for (const functionName of functionNames) {
const _func = functionsByName[functionName];
functions.push(_func);
}
this._paramConfigs = [...paramConfigs];
const paramConfigNames = paramConfigs.map((pc) => pc.uniformName());
paramConfigs.forEach((p) => p.applyToNode(this));
this._functionCreationArgs[type] = [
"position",
"velocity",
"time",
"delta",
"_setErrorFromError",
...variableNames,
...functionNames,
...paramConfigNames,
wrappedBody
];
this._functionEvalArgs[type].args = [
this._evaluationGlobals.position,
this._evaluationGlobals.velocity,
this._evaluationGlobals.time,
this._evaluationGlobals.delta,
_setErrorFromError,
...variables,
...functions
// paramConfigs are added dynamically during cook
];
this._functionEvalArgs[type].argsCountBeforeParams = this._functionEvalArgs[type].args.length;
try {
this._function[type] = new Function(...this._functionCreationArgs[type]);
} catch (e) {
console.warn(e);
this.states.error.set("failed to compile");
}
};
_createFunctionArgs(functionBody.velocity, "velocity");
_createFunctionArgs(functionBody.collider, "collider");
}
functionEvalArgsWithParamConfigs() {
const _args = (type) => {
const argsData = this._functionEvalArgs[type];
const list = argsData.args;
let i = argsData.argsCountBeforeParams;
for (const paramConfig of this._paramConfigs) {
const paramName = paramConfig.name();
const spareParam = this.params.get(paramName);
if (spareParam && spareParam.value != null) {
if (isBoolean(spareParam.value) || isNumberValid(spareParam.value) || isColor(spareParam.value) || isVector(spareParam.value)) {
list[i] = spareParam.value;
i++;
} else {
console.warn(`spareParam not found but type not yet copied to function args:'${paramName}'`);
}
} else {
console.warn(`spareParam not found:'${paramName}'`);
}
}
return list;
};
this._functionArgsWithParams.velocity = _args("velocity");
this._functionArgsWithParams.collider = _args("collider");
return this._functionArgsWithParams;
}
}
export function getSoftBodyControllerNodeFromSolverObject(solverObject, scene) {
const nodeId = softBodyControllerNodeIdFromObject(solverObject);
if (nodeId == null) {
return;
}
const graphNode = scene.graph.nodeFromId(nodeId);
if (!graphNode) {
return;
}
const node = isFunction(graphNode.context) ? graphNode : null;
if (!node) {
return;
}
if (node.context() != NodeContext.SOP) {
return;
}
if (node.type() != SopType.TET_SOFT_BODY_SOLVER) {
return;
}
return node;
}