UNPKG

@polygonjs/polygonjs

Version:

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

612 lines (611 loc) 21.3 kB
"use strict"; import { LineType } from "../utils/LineType"; import { VariableConfig } from "../configs/VariableConfig"; import { JsCodeBuilder } from "../utils/CodeBuilder"; import { TypedJsNode } from "../../_Base"; import { JsShaderConfig } from "../configs/ShaderConfig"; import { TypedAssembler } from "../../../utils/shaders/BaseAssembler"; import { JsFunctionName } from "../../../utils/shaders/ShaderName"; import { TypedNodeTraverser } from "../../../utils/shaders/NodeTraverser"; import { JsNodeFinder } from "../utils/NodeFinder"; import { JsType } from "../../../../poly/registers/nodes/types/Js"; import { SopType } from "../../../../poly/registers/nodes/types/Sop"; export const INSERT_MEMBERS_AFTER = "// insert members"; export const INSERT_DEFINE_AFTER = "// insert defines"; export const INSERT_CONSTRUCTOR_AFTER = "// insert after constructor"; export const INSERT_BODY_AFTER = "// insert body"; const INSERT_MEMBER_AFTER_MAP = /* @__PURE__ */ new Map([ // [ShaderName.VERTEX, '#include <common>'], [JsFunctionName.MAIN, INSERT_MEMBERS_AFTER], [JsFunctionName.VELOCITY, INSERT_MEMBERS_AFTER], [JsFunctionName.COLLIDER, INSERT_MEMBERS_AFTER] ]); const INSERT_DEFINE_AFTER_MAP = /* @__PURE__ */ new Map([ // [ShaderName.VERTEX, '#include <common>'], [JsFunctionName.MAIN, INSERT_DEFINE_AFTER], [JsFunctionName.VELOCITY, INSERT_DEFINE_AFTER], [JsFunctionName.COLLIDER, INSERT_DEFINE_AFTER] ]); const INSERT_CONSTRUCTOR_AFTER_MAP = /* @__PURE__ */ new Map([ // [ShaderName.VERTEX, '#include <common>'], [JsFunctionName.MAIN, INSERT_CONSTRUCTOR_AFTER], [JsFunctionName.VELOCITY, INSERT_CONSTRUCTOR_AFTER], [JsFunctionName.COLLIDER, INSERT_CONSTRUCTOR_AFTER] ]); const INSERT_BODY_AFTER_MAP = /* @__PURE__ */ new Map([ // [ShaderName.VERTEX, '#include <color_vertex>'], [JsFunctionName.MAIN, INSERT_BODY_AFTER], [JsFunctionName.VELOCITY, INSERT_BODY_AFTER], [JsFunctionName.COLLIDER, INSERT_BODY_AFTER] ]); const LINES_TO_REMOVE_MAP = /* @__PURE__ */ new Map([ // [ShaderName.VERTEX, ['#include <begin_vertex>', '#include <beginnormal_vertex>']], [JsFunctionName.MAIN, []], [JsFunctionName.VELOCITY, []], [JsFunctionName.COLLIDER, []] ]); const SPACED_LINES = 3; const PER_POINT_PARENT_TYPES = /* @__PURE__ */ new Set([SopType.ACTOR_INSTANCE, SopType.ACTOR_POINT]); export class BaseJsShaderAssembler extends TypedAssembler { constructor(_jsParentNode) { super(); this._jsParentNode = _jsParentNode; this._shaders_by_name = /* @__PURE__ */ new Map(); this._lines = /* @__PURE__ */ new Map(); this._root_nodes = []; this._leaf_nodes = []; this._uniformsTimeDependent = false; this._uniformsResolutionDependent = false; this._computedVarNames = /* @__PURE__ */ new Set(); // // // REGISTERED VARIABLES // // this._registeredVariables = /* @__PURE__ */ new Map(); this._registeredVariablesCountByNode = /* @__PURE__ */ new Map(); // // // REGISTERED FUNCTIONS // // this._registeredFunctions = /* @__PURE__ */ new Map(); } perPoint() { return PER_POINT_PARENT_TYPES.has(this._jsParentNode.type()); } setJsParentNode(parentNode) { this._overridenJsParentNode = parentNode; } currentJsParentNode() { return this._overridenJsParentNode || this._jsParentNode; } addComputedVarName(varName) { if (!this.computedVariablesAllowed()) { return; } this._computedVarNames.add(varName); } registeredAsComputed(varName) { if (varName.trim().length == 0) { console.warn(`attempt to read an empty variable ('${varName}')`); } return this._computedVarNames.has(varName); } computedVariablesAllowed() { return false; } memberReference(varName) { if (this.computedVariablesAllowed()) { return `this.${varName}.value`; } else { return `this.${varName}`; } } compile() { } // abstract defaultEntityIndexVariable(): string; // private get material() { // return (this._material = this._material || this._createMaterial()); // } // async get_material(/*master_assembler?: BaseGlShaderAssembler*/) { // this._material = this._material || this._createMaterial(); // await this._update_material(/*master_assembler*/); // return this._material; // } _template_shader_for_shader_name(shaderName) { var _a, _b, _c; switch (shaderName) { case JsFunctionName.MAIN: return (_a = this.templateShader()) == null ? void 0 : _a.main; case JsFunctionName.VELOCITY: return (_b = this.templateShader()) == null ? void 0 : _b.velocity; case JsFunctionName.COLLIDER: return (_c = this.templateShader()) == null ? void 0 : _c.collider; } } globalsHandler() { var _a; return (_a = this.currentJsParentNode().assemblerController()) == null ? void 0 : _a.globalsHandler(); } compileAllowed() { var _a; return ((_a = this.currentJsParentNode().assemblerController()) == null ? void 0 : _a.globalsHandler()) != null; } shaders_by_name() { return this._shaders_by_name; } // protected createMaterial(): ShaderMaterial | undefined { // return undefined; // } _buildLines() { for (const shaderName of this.shaderNames()) { const template = this._template_shader_for_shader_name(shaderName); if (template) { this._replaceTemplate(template, shaderName); } } } // protected _build_lines_for_shader_name(shader_name: ShaderName){ // const template = this._template_shader() // this._replace_template(template[`${shader_name}Shader`], shader_name) // } set_root_nodes(root_nodes) { this._root_nodes = root_nodes; } templateShader() { return void 0; } _reset() { this._resetRegisteredFunctions(); this._resetRegisteredVariables(); this._computedVarNames.clear(); } updateFunction() { this._reset(); } // protected addUniforms(uniforms: IUniforms) { // for (let param_config of this.param_configs()) { // uniforms[param_config.uniformName()] = param_config.uniform(); // } // if (this.uniformsTimeDependent()) { // uniforms[UniformName.TIME] = uniforms[UniformName.TIME] || { // // type: '1f', // value: this.currentGlParentNode().scene().time(), // }; // } // if (this.uniformsResolutionDependent()) { // uniforms[UniformName.RESOLUTION] = uniforms[UniformName.RESOLUTION] || { // value: new Vector2(1000, 1000), // }; // } // } // // // ROOT NODES AND SHADER NAMES // // rootNodesByShaderName(shaderName, rootNodes) { const list = []; for (const node of rootNodes) { switch (node.type()) { case JsType.PARAM: case JsType.OUTPUT: { list.push(node); break; } case JsType.OUTPUT_AMBIENT_LIGHT: case JsType.OUTPUT_AREA_LIGHT: case JsType.OUTPUT_DIRECTIONAL_LIGHT: case JsType.OUTPUT_HEMISPHERE_LIGHT: case JsType.OUTPUT_POINT_LIGHT: case JsType.OUTPUT_SPOT_LIGHT: { list.push(node); break; } case JsType.ATTRIBUTE: { list.push(node); break; } } } return list; } // leafNodesByShaderName(shaderName: ShaderName): BaseGlNodeType[] { // const list = []; // for (let node of this._leaf_nodes) { // switch (node.type()) { // case GlobalsGlNode.type(): { // list.push(node); // break; // } // case AttributeGlNode.type(): { // break; // } // } // } // return list; // } setNodeLinesGlobals(globalsNode, linesController) { } setNodeLinesOutput(outputNode, linesController) { } setNodeLinesAttribute(attributeNode, linesController) { } // // // CHILDREN NODES PARAMS // // codeBuilder() { return this._codeBuilder = this._codeBuilder || this._createCodeBuilder(); } _createCodeBuilder() { var _a, _b; const computedVariablesAllowed = (_b = (_a = this._jsParentNode.assemblerController()) == null ? void 0 : _a.assembler) == null ? void 0 : _b.computedVariablesAllowed(); const nodeTraverser = new TypedNodeTraverser( this.currentJsParentNode(), this.shaderNames(), (rootNode, shaderName) => { return this.inputNamesForShaderName(rootNode, shaderName); }, { // we do traverse children if computed(()=>{}) is used. // If not, we don't need to traverse children, // and the code is handled by the subnet internal code builder traverseChildren: computedVariablesAllowed == true ? true : false } ); return new JsCodeBuilder( nodeTraverser, (shaderName, rootNodes) => { return this.rootNodesByShaderName(shaderName, rootNodes); }, this ); } buildCodeFromNodes(rootNodes, codeBuilderOptions) { const paramNodes = JsNodeFinder.findParamGeneratingNodes(this.currentJsParentNode()); this.codeBuilder().buildFromNodes(rootNodes, paramNodes, codeBuilderOptions); } allow_new_param_configs() { this.codeBuilder().allow_new_param_configs(); } disallow_new_param_configs() { this.codeBuilder().disallow_new_param_configs(); } builder_param_configs() { return this.codeBuilder().param_configs(); } builder_lines(shader_name, line_type) { return this.codeBuilder().lines(shader_name, line_type); } all_builder_lines() { return this.codeBuilder().all_lines(); } param_configs() { const code_builder = this._param_config_owner || this.codeBuilder(); return code_builder.param_configs(); } set_param_configs_owner(param_config_owner) { this._param_config_owner = param_config_owner; if (this._param_config_owner) { this.codeBuilder().disallow_new_param_configs(); } else { this.codeBuilder().allow_new_param_configs(); } } // // // CHILDREN NODES PARAMS // // static output_input_connection_points() { return [ // new JsConnectionPoint('position', JsConnectionPointType.VEC3), // new JsConnectionPoint('normal', JsConnectionPointType.VEC3), // new JsConnectionPoint('color', JsConnectionPointType.VEC3), // new JsConnectionPoint('alpha', JsConnectionPointType.FLOAT), // new JsConnectionPoint('uv', JsConnectionPointType.VEC2), ]; } add_output_inputs(output_child) { output_child.io.inputs.setNamedInputConnectionPoints(BaseJsShaderAssembler.output_input_connection_points()); } static create_globals_node_output_connections() { return [ // new JsConnectionPoint('position', JsConnectionPointType.VEC3), // new JsConnectionPoint('normal', JsConnectionPointType.VEC3), // new JsConnectionPoint('color', JsConnectionPointType.VEC3), // new JsConnectionPoint('uv', JsConnectionPointType.VEC2), // new JsConnectionPoint(GlobalsOutput.MV_POSITION, JsConnectionPointType.VEC4), // // Maybe I should not add worldPosition, worldNormal, I just now // // as those could add computation overhead when always present in the shader. // // But hopefully in the soon future, they will only be added when the code builder // // adds lines based on connections, as opposed to the whole node // new JsConnectionPoint('worldPosition', JsConnectionPointType.VEC4), // vec4 worldPosition = modelMatrix * vec4( position, 1.0 ); // new JsConnectionPoint('worldNormal', JsConnectionPointType.VEC3), // vec3 worldNormal = normalize( mat3( modelMatrix[0].xyz, modelMatrix[1].xyz, modelMatrix[2].xyz ) * normal ); // // new GlConnectionPoint('I', GlConnectionPointType.VEC3), // vec3 I = worldPosition.xyz - cameraPosition; // new JsConnectionPoint(GlobalsOutput.GL_POSITION, JsConnectionPointType.VEC4), // new JsConnectionPoint(GlobalsOutput.GL_FRAGCOORD, JsConnectionPointType.VEC4), // new JsConnectionPoint('cameraPosition', JsConnectionPointType.VEC3), // new JsConnectionPoint(GlobalsOutput.RESOLUTION, JsConnectionPointType.VEC2), // new JsConnectionPoint(GlobalsOutput.TIME, JsConnectionPointType.FLOAT), ]; } create_globals_node_output_connections() { return BaseJsShaderAssembler.create_globals_node_output_connections(); } add_globals_outputs(globals_node) { globals_node.io.outputs.setNamedOutputConnectionPoints(this.create_globals_node_output_connections()); } allow_attribute_exports() { return false; } // // // CONFIGS // // resetConfigs() { this._reset_shader_configs(); this._reset_variable_configs(); this._resetUniformsTimeDependency(); this._resetUniformsResolutionDependency(); } shaderConfigs() { return this._shader_configs = this._shader_configs || this.create_shader_configs(); } set_shader_configs(shader_configs) { this._shader_configs = shader_configs; } shaderNames() { var _a; return ((_a = this.shaderConfigs()) == null ? void 0 : _a.map((sc) => sc.name())) || []; } _reset_shader_configs() { this._shader_configs = void 0; } create_shader_configs() { return [ // new ShaderConfig(ShaderName.VERTEX, ['position', 'normal', 'uv', VaryingWriteGlNode.INPUT_NAME], []), new JsShaderConfig(JsFunctionName.MAIN, ["color", "alpha"], []) ]; } shader_config(name) { var _a; return (_a = this.shaderConfigs()) == null ? void 0 : _a.filter((sc) => { return sc.name() == name; })[0]; } variable_configs() { return this._variable_configs = this._variable_configs || this.create_variable_configs(); } set_variable_configs(variable_configs) { this._variable_configs = variable_configs; } variable_config(name) { return this.variable_configs().filter((vc) => { return vc.name() == name; })[0]; } static create_variable_configs() { return [ new VariableConfig("d", { // default_from_attribute: true, // default: this.globalsHandler().variable_config_default('position'), // required_definitions: this.globalsHandler().variable_config_required_definitions('position'), prefix: "return " }) // new VariableConfig('normal', { // default_from_attribute: true, // prefix: 'vec3 objectNormal = ', // postLines: ['#ifdef USE_TANGENT', ' vec3 objectTangent = vec3( tangent.xyz );', '#endif'], // }), // new VariableConfig('color', { // prefix: 'diffuseColor.xyz = ', // }), // new VariableConfig('alpha', { // prefix: 'diffuseColor.a = ', // }), // new VariableConfig('uv', { // // default_from_attribute: true, // prefix: 'vUv = ', // // if: GlobalsGeometryHandler.IF_RULE.uv, // }), ]; } create_variable_configs() { return BaseJsShaderAssembler.create_variable_configs(); } _reset_variable_configs() { this._variable_configs = void 0; this.variable_configs(); } inputNamesForShaderName(rootNode, shaderName) { var _a; if (shaderName == JsFunctionName.MAIN) { return (rootNode.io.inputs.namedInputConnectionPoints() || []).map((c) => c.name()); } else { return ((_a = this.shader_config(shaderName)) == null ? void 0 : _a.input_names()) || []; } } // time dependency _resetUniformsTimeDependency() { this._uniformsTimeDependent = false; } setUniformsTimeDependent() { this._uniformsTimeDependent = true; } uniformsTimeDependent() { return this._uniformsTimeDependent; } // resolution dependency _resetUniformsResolutionDependency() { this._uniformsResolutionDependent = false; } setUniformsResolutionDependent() { this._uniformsResolutionDependent = true; } uniformsResolutionDependent() { return this._uniformsResolutionDependent; } _raymarchingLightsWorldCoordsDependent() { return false; } // // // TEMPLATE HOOKS // // insertMemberAfter(shaderName) { return INSERT_MEMBER_AFTER_MAP.get(shaderName); } insertDefineAfter(shaderName) { return INSERT_DEFINE_AFTER_MAP.get(shaderName); } insertConstructorAfter(shaderName) { return INSERT_CONSTRUCTOR_AFTER_MAP.get(shaderName); } insertBodyAfter(shaderName) { return INSERT_BODY_AFTER_MAP.get(shaderName); } // protected insertTriggerAfter(shaderName: ShaderName): string | undefined { // return INSERT_TRIGGER_AFTER_MAP.get(shaderName); // } // protected insertTriggerableAfter(shaderName: ShaderName): string | undefined { // return INSERT_TRIGGERABLE_AFTER_MAP.get(shaderName); // } linesToRemove(shaderName) { return LINES_TO_REMOVE_MAP.get(shaderName); } // // // TEMPLATE CODE REPLACEMENT // // _replaceTemplate(template, shaderName) { const memberLines = this.builder_lines(shaderName, LineType.MEMBER); const constructorLines = this.builder_lines(shaderName, LineType.CONSTRUCTOR); const defineLines = this.builder_lines(shaderName, LineType.DEFINE); const body = this.builder_lines(shaderName, LineType.BODY); let templateLines = template.split("\n"); const newLines = [ // `#define FPS ${ThreeToGl.float(scene.time_controller.fps)}`, // `#define TIME_INCREMENT (1.0/${ThreeToGl.float(scene.time_controller.fps)})`, // `#define FRAME_RANGE_START ${ThreeToGl.float(scene.time_controller.frame_range[0])}`, // `#define FRAME_RANGE_END ${ThreeToGl.float(scene.time_controller.frame_range[1])}`, ]; const lineBeforeMember = this.insertMemberAfter(shaderName); const lineBeforeDefine = this.insertDefineAfter(shaderName); const lineBeforeConstructor = this.insertConstructorAfter(shaderName); const lineBeforeBody = this.insertBodyAfter(shaderName); const linesToRemove = this.linesToRemove(shaderName); let lineBeforeMemberFound = false; let lineBeforeDefineFound = false; let lineBeforeConstructorFound = false; let lineBeforeBodyFoundOnPreviousLine = false; let lineBeforeBodyFound = false; for (const templateLine of templateLines) { if (lineBeforeMemberFound == true) { if (memberLines) { this._insertLines(newLines, memberLines); } lineBeforeMemberFound = false; } if (lineBeforeDefineFound == true) { if (defineLines) { this._insertLines(newLines, defineLines); } lineBeforeDefineFound = false; } if (lineBeforeConstructorFound == true) { if (constructorLines) { this._insertLines(newLines, constructorLines); } lineBeforeConstructorFound = false; } if (lineBeforeBodyFoundOnPreviousLine == true) { if (body) { this._insertLines(newLines, body); } lineBeforeBodyFoundOnPreviousLine = false; } let line_remove_required = false; if (linesToRemove) { for (const line_to_remove of linesToRemove) { if (templateLine.indexOf(line_to_remove) >= 0) { line_remove_required = true; } } } if (!line_remove_required) { newLines.push(templateLine); } else { newLines.push("// removed:"); newLines.push(`//${templateLine}`); } if (lineBeforeDefine && templateLine.indexOf(lineBeforeDefine) >= 0) { lineBeforeDefineFound = true; } if (lineBeforeConstructor && templateLine.indexOf(lineBeforeConstructor) >= 0) { lineBeforeConstructorFound = true; } if (lineBeforeMember && templateLine.indexOf(lineBeforeMember) >= 0) { lineBeforeMemberFound = true; } if (lineBeforeBody && templateLine.indexOf(lineBeforeBody) >= 0) { lineBeforeBodyFoundOnPreviousLine = true; lineBeforeBodyFound = true; } } if (lineBeforeBody) { if (!lineBeforeBodyFound) { console.warn(`line '${lineBeforeBody}' was not found in shader '${shaderName}'`, template, this); } else { } } this._lines.set(shaderName, newLines); } _insertLines(newLines, linesToAdd) { if (linesToAdd.length == 0) { return; } for (let i = 0; i < SPACED_LINES; i++) { newLines.push(""); } for (const lineToAdd of linesToAdd) { newLines.push(lineToAdd); } for (let i = 0; i < SPACED_LINES; i++) { newLines.push(""); } } addVariable(node, variable, varName) { const count = this._registeredVariablesCountByNode.get(node) || 0; this._registeredVariablesCountByNode.set(node, count + 1); const varFullName = varName ? varName : "VAR_" + TypedJsNode.inputVarName(node, count == 0 ? "" : `_${count}`); this._registeredVariables.set(varFullName, variable); return varFullName; } traverseRegisteredVariables(callback) { this._registeredVariables.forEach(callback); } _resetRegisteredVariables() { this._registeredVariables.clear(); this._registeredVariablesCountByNode.clear(); } addFunction(node, namedFunction) { const existingFunctionName = this._registeredFunctions.get(namedFunction.type()); if (existingFunctionName) { return; } this._registeredFunctions.set(namedFunction.type(), namedFunction); } traverseRegisteredFunctions(callback) { this._registeredFunctions.forEach(callback); } _resetRegisteredFunctions() { this._registeredFunctions.clear(); } }