UNPKG

@polygonjs/polygonjs

Version:

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

246 lines (245 loc) 8.11 kB
"use strict"; import { NodeContext } from "../../poly/NodeContext"; import { NodeParamsConfig, ParamConfig } from "../utils/params/ParamsConfig"; import { SopType } from "../../poly/registers/nodes/types/Sop"; import { AssemblerName } from "../../poly/registers/assemblers/_BaseRegister"; import { Poly } from "../../Poly"; import { JsNodeFinder } from "../js/code/utils/NodeFinder"; import { Box3, Vector3 } from "three"; import { InputCloneMode } from "../../poly/InputCloneMode"; import { SDFLoader } from "../../../core/geometry/modules/sdf/SDFLoader"; import { TypedSopNode } from "./_Base"; import { ModuleName } from "../../poly/registers/modules/Common"; import { SDFObject } from "../../../core/geometry/modules/sdf/SDFObject"; import { isArray, isBoolean, isNumberValid, isColor, isVector } from "../../../core/Type"; import { SDFPersistedConfig } from "../js/code/assemblers/sdf/SDFPersistedConfig"; const _box3 = new Box3(); const box = { min: [-1, -1, -1], max: [1, 1, 1] }; class SDFBuilderSopParamsConfig extends NodeParamsConfig { constructor() { super(...arguments); /** @param stepSize */ this.stepSize = ParamConfig.FLOAT(0.1, { range: [0.01, 1], rangeLocked: [true, false] }); /** @param level */ this.level = ParamConfig.FLOAT(0, { range: [-1, 1], rangeLocked: [false, false] }); /** @param min bound */ this.min = ParamConfig.VECTOR3([-1, -1, -1]); /** @param max bound */ this.max = ParamConfig.VECTOR3([1, 1, 1]); /** @param linear Tolerance */ this.facetAngle = ParamConfig.FLOAT(45, { range: [0.01, 180], rangeLocked: [true, false] }); /** @param meshes color */ this.meshesColor = ParamConfig.COLOR([1, 1, 1]); /** @param wireframe */ this.wireframe = ParamConfig.BOOLEAN(false, { // we need the separator for spare params separatorAfter: true }); } } const ParamsConfig = new SDFBuilderSopParamsConfig(); export class SDFBuilderSopNode extends TypedSopNode { constructor() { super(...arguments); this.paramsConfig = ParamsConfig; this.persisted_config = new SDFPersistedConfig(this); this._assemblerController = this._createAssemblerController(); // static PARAM_CALLBACK_reset(node: ParticlesSystemGpuSopNode) { // node.PARAM_CALLBACK_reset(); // } // PARAM_CALLBACK_reset() { // // this.gpu_controller.reset_gpu_compute_and_set_dirty(); // } // private _reset_material_if_dirty_bound = this._reset_material_if_dirty.bind(this); this._childrenControllerContext = NodeContext.JS; this._position = new Vector3(); this._paramConfigs = []; this._functionCreationArgs = []; this._functionEvalArgs = []; } static type() { return SopType.SDF_BUILDER; } requiredModules() { return [ModuleName.SDF]; } assemblerController() { return this._assemblerController; } usedAssembler() { return AssemblerName.JS_SDF; } _createAssemblerController() { return Poly.assemblersRegister.assembler(this, this.usedAssembler()); } // private _on_create_prepare_material_bound = this._on_create_prepare_material.bind(this); initializeNode() { this.io.inputs.setCount(0, 1); this.io.inputs.initInputsClonedState(InputCloneMode.NEVER); } 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) { const manifold = await SDFLoader.core(); const coreGroup = inputCoreGroups[0]; if (coreGroup) { coreGroup.boundingBox(_box3); _box3.min.toArray(box.min); _box3.max.toArray(box.max); } else { this.pv.min.toArray(box.min); this.pv.max.toArray(box.max); } this.compileIfRequired(); const _func = this._function; if (_func) { const args = this.functionEvalArgsWithParamConfigs(); const convertedFunction = (p) => { this._position.fromArray(p); return -1 * _func(...args); }; const geometry = manifold.Manifold.levelSet(convertedFunction, box, this.pv.stepSize, this.pv.level); const sdfObject = new SDFObject(geometry); const results = sdfObject.toObject3D(this.pv); if (results) { if (isArray(results)) { this.setObjects(results); } else { this.setObjects([results]); } } else { this.setObjects([]); } } else { this.setObjects([]); } } compileIfRequired() { var _a; if ((_a = this.assemblerController()) == null ? void 0 : _a.compileRequired()) { this.compile(); } } 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 wrappedBody = ` try { ${functionBody} } 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 = [ "position", "_setErrorFromError", ...variableNames, ...functionNames, ...paramConfigNames, wrappedBody ]; this._functionEvalArgs = [ this._position, _setErrorFromError, ...variables, ...functions // paramConfigs are added dynamically during cook ]; try { this._function = new Function(...this._functionCreationArgs); } catch (e) { console.warn(e); this.states.error.set("failed to compile"); } } functionEvalArgsWithParamConfigs() { const list = [...this._functionEvalArgs]; 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.push(spareParam.value); } else { console.warn(`spareParam not found but type not yet copied to function args:'${paramName}'`); } } else { console.warn(`spareParam not found:'${paramName}'`); } } return list; } }