UNPKG

@polygonjs/polygonjs

Version:

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

256 lines (238 loc) 8.03 kB
import { BaseJsShaderAssembler, INSERT_DEFINE_AFTER, INSERT_BODY_AFTER, INSERT_MEMBERS_AFTER, INSERT_CONSTRUCTOR_AFTER, VelocityColliderFunctionData, SpareParamOptions, } from '../_Base'; import {RegisterableVariable} from '../_BaseJsPersistedConfigUtils'; import {ThreeToGl} from '../../../../../../core/ThreeToGl'; import {JsShaderConfig} from '../../configs/ShaderConfig'; import {VariableConfig} from '../../configs/VariableConfig'; import {JsFunctionName} from '../../../../utils/shaders/ShaderName'; import {OutputJsNode} from '../../../Output'; import {GlobalsJsNode} from '../../../Globals'; import {JsConnectionPointType, JsConnectionPoint} from '../../../../utils/io/connections/Js'; import {JsLinesCollectionController} from '../../utils/JsLinesCollectionController'; import {Vector3} from 'three'; import {NamedFunctionMap} from '../../../../../poly/registers/functions/All'; import {ParamOptions} from '../../../../../params/utils/OptionsController'; import {PrettierController} from '../../../../../../core/code/PrettierController'; export enum SoftBodyVariable { P = 'position', V = 'velocity', COLLISION_SDF = 'collisionSDF', // TIME = 'time', DELTA = 'delta', } const TEMPLATE_VELOCITY = ` ${INSERT_DEFINE_AFTER} ${INSERT_MEMBERS_AFTER} ${INSERT_CONSTRUCTOR_AFTER} const SoftBodyVelocity = function(){ ${INSERT_BODY_AFTER} `; const CLOSE_CLASS_DEFINITION_VELOCITY = `}; return SoftBodyVelocity;`; const TEMPLATE_COLLIDER = ` ${INSERT_DEFINE_AFTER} ${INSERT_MEMBERS_AFTER} ${INSERT_CONSTRUCTOR_AFTER} const SoftBodyCollider = function(){ ${INSERT_BODY_AFTER} `; const CLOSE_CLASS_DEFINITION_COLLIDER = `}; return SoftBodyCollider;`; export class JsAssemblerSoftBody extends BaseJsShaderAssembler { makeFunctionNodeDirtyOnChange() { return true; } defaultObjectVariable(): string { return 'null'; } defaultObject3DMaterialVariable(): string { return 'null'; } defaultPrimitiveGraph(): string { return 'null'; } override templateShader() { return { velocity: TEMPLATE_VELOCITY, collider: TEMPLATE_COLLIDER, }; } override spareParamsOptions(options: SpareParamOptions) { const _options: ParamOptions = { spare: true, // computeOnDirty: true, // not needed if cook option is not set cook: false, // for Softbody, the node must not recook // important for texture nodes // that compute after being found by the nodepath param dependentOnFoundNode: true, }; return _options; } functionData(): VelocityColliderFunctionData | undefined { const _buildFunctionBody = (functionName: JsFunctionName, closeDef: string) => { const bodyLines = this._shaders_by_name.get(functionName) || TEMPLATE_VELOCITY; const functionBodyElements = [ bodyLines, // triggerableFunctionLines.join('\n'), // triggerFunctionLines.join('\n'), closeDef, ]; const functionBody = PrettierController.formatJs(functionBodyElements.join('\n')); return functionBody; }; const functionBodyVelocity = _buildFunctionBody(JsFunctionName.VELOCITY, CLOSE_CLASS_DEFINITION_VELOCITY); const functionBodyCollider = _buildFunctionBody(JsFunctionName.COLLIDER, CLOSE_CLASS_DEFINITION_COLLIDER); if (!(functionBodyVelocity && functionBodyCollider)) { return; } const variableNames: string[] = []; const functionNames: Array<keyof NamedFunctionMap> = []; const variablesByName: Record<string, RegisterableVariable> = {}; const functionsByName: Record<string, Function> = {}; this.traverseRegisteredVariables((variable, varName) => { variableNames.push(varName); variablesByName[varName] = variable; }); this.traverseRegisteredFunctions((namedFunction) => { functionNames.push(namedFunction.type() as keyof NamedFunctionMap); functionsByName[namedFunction.type()] = namedFunction.func.bind(namedFunction); }); const paramConfigs = this.param_configs(); return { functionBody: { velocity: functionBodyVelocity, collider: functionBodyCollider, }, variableNames, variablesByName, functionNames, functionsByName, paramConfigs: [...paramConfigs], }; } override updateFunction() { super.updateFunction(); this._lines = new Map(); this._shaders_by_name = new Map(); const shaderNames = this.shaderNames(); if (this._root_nodes.length > 0) { this.buildCodeFromNodes(this._root_nodes); this._buildLines(); } for (const shaderName of shaderNames) { const lines = this._lines.get(shaderName); if (lines) { this._shaders_by_name.set(shaderName, lines.join('\n')); } } } // // // CHILDREN NODES PARAMS // // override add_output_inputs(output_child: OutputJsNode) { output_child.io.inputs.setNamedInputConnectionPoints([ new JsConnectionPoint(SoftBodyVariable.V, JsConnectionPointType.VECTOR3), new JsConnectionPoint(SoftBodyVariable.COLLISION_SDF, JsConnectionPointType.FLOAT), ]); } override add_globals_outputs(globals_node: GlobalsJsNode) { globals_node.io.outputs.setNamedOutputConnectionPoints([ new JsConnectionPoint(SoftBodyVariable.P, JsConnectionPointType.VECTOR3), new JsConnectionPoint(SoftBodyVariable.V, JsConnectionPointType.VECTOR3), new JsConnectionPoint(SoftBodyVariable.TIME, JsConnectionPointType.FLOAT), new JsConnectionPoint(SoftBodyVariable.DELTA, JsConnectionPointType.FLOAT), ]); } // // // CONFIGS // // override create_shader_configs() { return [ new JsShaderConfig(JsFunctionName.VELOCITY, [SoftBodyVariable.V], []), new JsShaderConfig(JsFunctionName.COLLIDER, [SoftBodyVariable.COLLISION_SDF], []), ]; } override create_variable_configs() { return [ new VariableConfig(SoftBodyVariable.V, { prefix: 'return ', }), new VariableConfig(SoftBodyVariable.COLLISION_SDF, { prefix: 'return ', }), ]; } override setNodeLinesOutput(outputNode: OutputJsNode, linesController: JsLinesCollectionController) { const inputNames = this.inputNamesForShaderName( outputNode, linesController.currentShaderName() ) as SoftBodyVariable[]; if (inputNames) { for (const inputName of inputNames) { const input = outputNode.io.inputs.named_input(inputName); const glVar = outputNode.variableForInput(linesController, inputName); switch (inputName) { case SoftBodyVariable.V: { const _defaultVar = () => { const tmpVarName = linesController.addVariable(outputNode, new Vector3(0, 0.1, 0)); return `return ${tmpVarName}`; }; const bodyLine = input ? `return ${ThreeToGl.any(glVar)}` : _defaultVar(); linesController._addBodyLines(outputNode, [bodyLine]); break; } case SoftBodyVariable.COLLISION_SDF: { const bodyLine = input ? `return ${ThreeToGl.any(glVar)}` : `return 100`; linesController._addBodyLines(outputNode, [bodyLine]); break; } } } } } override setNodeLinesGlobals(globalsNode: GlobalsJsNode, shadersCollectionController: JsLinesCollectionController) { const shaderName = shadersCollectionController.currentShaderName(); const shaderConfig = this.shader_config(shaderName); if (!shaderConfig) { return; } const bodyLines: string[] = []; const usedOutputNames = globalsNode.io.outputs.used_output_names(); for (const outputName of usedOutputNames) { const varName = globalsNode.jsVarName(outputName); switch (outputName) { case 'position': { shadersCollectionController.addVariable(globalsNode, new Vector3(), varName); bodyLines.push(`${varName}.copy(${outputName})`); break; } case SoftBodyVariable.V: { shadersCollectionController.addVariable(globalsNode, new Vector3(), varName); bodyLines.push(`${varName}.copy(${outputName})`); break; } case SoftBodyVariable.TIME: { bodyLines.push(`const ${varName} = ${SoftBodyVariable.TIME}`); break; } case SoftBodyVariable.DELTA: { bodyLines.push(`const ${varName} = ${SoftBodyVariable.DELTA}`); break; } } } shadersCollectionController._addBodyLines(globalsNode, bodyLines); } }