UNPKG

@polygonjs/polygonjs

Version:

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

331 lines (330 loc) 11.5 kB
"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; }