UNPKG

@polygonjs/polygonjs

Version:

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

425 lines (424 loc) 17.1 kB
"use strict"; import { BaseGlShaderAssembler } from "../_Base"; import { ThreeToGl } from "../../../../../../core/ThreeToGl"; import { ShaderName } from "../../../../utils/shaders/ShaderName"; import { UniformGLDefinition, VaryingGLDefinition } from "../../../utils/GLDefinition"; import { GlConnectionPointType } from "../../../../utils/io/connections/Gl"; import { pushOnArrayAtEntry } from "../../../../../../core/MapUtils"; import { Material } from "three"; import { GlNodeFinder } from "../../utils/NodeFinder"; import { assignOnBeforeCompileDataAndFunction } from "./OnBeforeCompile"; import { GlobalsOutput } from "./common/GlobalOutput"; const FRAGMENT_GLOBALS_OUTPUT = [ /*GlobalsOutput.GL_POSITION,*/ GlobalsOutput.GL_FRAGCOORD, GlobalsOutput.GL_POINTCOORD ]; const COMPILE_CUSTOM_MATERIALS = true; export class ShaderAssemblerMaterial extends BaseGlShaderAssembler { constructor() { super(...arguments); this._assemblersByCustomName = /* @__PURE__ */ new Map(); this._filterFragmentShaderCallbacks = /* @__PURE__ */ new Map(); // private _additionalUniformNames:string[]=[] // setAdditionalUniformNames(uniformNames:string[]){ // this._additionalUniformNames = [...uniformNames] // } this._additionalTextureUniforms = {}; } createMaterial() { return new Material(); } customAssemblerClassByCustomName() { return void 0; } traverseCustomAssemblers(callback) { this._assemblersByCustomName.forEach(callback); } _addCustomMaterials(material) { const map = this.customAssemblerClassByCustomName(); if (map) { map.forEach((assemblerClass, customName) => { this._addCustomMaterial(material, customName, assemblerClass); }); } } _addCustomMaterial(material, customName, assemblerClass) { material.customMaterials = material.customMaterials || {}; const matNode = this.currentGlParentNode(); const matBuilderNode = matNode; if (matBuilderNode.customMaterialRequested && matBuilderNode.customMaterialRequested(customName) == false) { delete material.customMaterials[customName]; return; } let customAssembler = this._assemblersByCustomName.get(customName); if (!customAssembler) { customAssembler = new assemblerClass(this.currentGlParentNode()); this._assemblersByCustomName.set(customName, customAssembler); } const mat = customAssembler.createMaterial(); mat.name = customName; material.customMaterials[customName] = mat; } compileCustomMaterials(material, parentAssembler) { const matNode = this.currentGlParentNode(); const matBuilderNode = matNode; const class_by_custom_name = this.customAssemblerClassByCustomName(); if (class_by_custom_name) { class_by_custom_name.forEach( (assemblerClass, customName) => { if (matBuilderNode.customMaterialRequested && matBuilderNode.customMaterialRequested(customName) == false) { delete material.customMaterials[customName]; return; } else { if (!(customName in material.customMaterials)) { this._addCustomMaterial(material, customName, assemblerClass); } } if (this._codeBuilder) { let assembler = this._assemblersByCustomName.get(customName); if (!assembler) { assembler = new assemblerClass(this.currentGlParentNode()); this._assemblersByCustomName.set(customName, assembler); } assembler._setAdditionalTextureUniforms(this._additionalTextureUniforms); assembler.set_root_nodes(this._root_nodes); assembler.setGlParentNode(parentAssembler.currentGlParentNode()); assembler.set_param_configs_owner(this._codeBuilder); assembler.set_shader_configs(this.shaderConfigs()); assembler.set_variable_configs(this.variable_configs()); const custom_material = material.customMaterials[customName]; if (custom_material) { assembler.setFilterFragmentShaderMethodOwner(this); assembler.compileMaterial(custom_material); assembler.setFilterFragmentShaderMethodOwner(void 0); } } } ); } } _resetFilterFragmentShaderCallbacks() { this._filterFragmentShaderCallbacks.clear(); } _addFilterFragmentShaderCallback(callbackName, callback) { this._filterFragmentShaderCallbacks.set(callbackName, callback); } _removeFilterFragmentShaderCallback(callbackName) { this._filterFragmentShaderCallbacks.delete(callbackName); } setFilterFragmentShaderMethodOwner(owner) { this._filterFragmentShaderMethodOwner = owner; } filterFragmentShader(fragmentShader) { this._filterFragmentShaderCallbacks.forEach((callback, callbackName) => { fragmentShader = callback(fragmentShader); }); return fragmentShader; } processFilterFragmentShader(fragmentShader) { if (this._filterFragmentShaderMethodOwner) { return this._filterFragmentShaderMethodOwner.filterFragmentShader(fragmentShader); } else { return this.filterFragmentShader(fragmentShader); } } onBeforeCompileData() { return this._onBeforeCompileData; } clearAdditionalTextureUniforms() { this._additionalTextureUniforms = {}; } addAdditionalTextureUniforms(uniformName, uniform) { this._additionalTextureUniforms[uniformName] = uniform; } _setAdditionalTextureUniforms(uniforms) { this.clearAdditionalTextureUniforms(); const uniformNames = Object.keys(uniforms); for (const uniformName of uniformNames) { this._additionalTextureUniforms[uniformName] = uniforms[uniformName]; } } compileMaterial(material, codeBuilderOptions) { if (!this.compileAllowed()) { return; } const outputNodes = GlNodeFinder.findOutputNodes(this.currentGlParentNode()); if (outputNodes.length == 0) { this.currentGlParentNode().states.error.set("one output node is required"); } if (outputNodes.length > 1) { this.currentGlParentNode().states.error.set("only one output node allowed"); } const varyingNodes = GlNodeFinder.findVaryingNodes(this.currentGlParentNode()); const rootNodes = outputNodes.concat(varyingNodes); this.set_root_nodes(rootNodes); this.updateShaders(codeBuilderOptions); this.prepareOnBeforeCompileData(material); if (COMPILE_CUSTOM_MATERIALS) { if (material.customMaterials) { this.compileCustomMaterials(material, this); } } } prepareOnBeforeCompileData(material) { const scene = this.currentGlParentNode().scene(); const vertexShader = this._shaders_by_name.get(ShaderName.VERTEX); const fragmentShader = this._shaders_by_name.get(ShaderName.FRAGMENT); if (vertexShader && fragmentShader) { const processedFragmentShader = this.processFilterFragmentShader(fragmentShader); this._onBeforeCompileData = { vertexShader, fragmentShader: processedFragmentShader, paramConfigs: this.param_configs(), additionalTextureUniforms: this._additionalTextureUniforms, timeDependent: this.uniformsTimeDependent(), resolutionDependent: this.uniformsResolutionDependent(), raymarchingLightsWorldCoordsDependent: this._raymarchingLightsWorldCoordsDependent() }; assignOnBeforeCompileDataAndFunction(scene, material, this._onBeforeCompileData); material.needsUpdate = true; } } updateShaders(codeBuilderOptions) { this._shaders_by_name.clear(); this._lines.clear(); for (const shaderName of this.shaderNames()) { const template = this._template_shader_for_shader_name(shaderName); if (template) { this._lines.set(shaderName, template.split("\n")); } } if (this._root_nodes.length > 0) { this.buildCodeFromNodes(this._root_nodes, codeBuilderOptions); this._buildLines(); } for (const shader_name of this.shaderNames()) { const lines = this._lines.get(shader_name); if (lines) { this._shaders_by_name.set(shader_name, lines.join("\n")); } } } shadow_assembler_class_by_custom_name() { return {}; } add_output_body_line(output_node, shaders_collection_controller, input_name) { var _a; const input = output_node.io.inputs.named_input(input_name); const var_input = output_node.variableForInput(input_name); const variable_config = this.variable_config(input_name); let new_var = null; if (input) { new_var = ThreeToGl.vector3(var_input); } else { if (variable_config.default_from_attribute()) { const connection_point = output_node.io.inputs.namedInputConnectionPointsByName(input_name); if (connection_point) { const gl_type = connection_point.type(); const attr_read = (_a = this.globalsHandler()) == null ? void 0 : _a.readAttribute( output_node, gl_type, input_name, shaders_collection_controller ); if (attr_read) { new_var = attr_read; } } } else { const variable_config_default = variable_config.default(); if (variable_config_default) { new_var = variable_config_default; } } } if (new_var) { const prefix = variable_config.prefix(); const suffix = variable_config.suffix(); const if_condition = variable_config.if_condition(); if (if_condition) { shaders_collection_controller.addBodyLines(output_node, [`#if ${if_condition}`]); } shaders_collection_controller.addBodyLines(output_node, [`${prefix}${new_var}${suffix}`]); const postLines = variable_config.postLines(); if (postLines) { shaders_collection_controller.addBodyLines(output_node, postLines); } if (if_condition) { shaders_collection_controller.addBodyLines(output_node, [`#endif`]); } } } set_node_lines_output(output_node, shadersCollectionController) { var _a; const shader_name = shadersCollectionController.currentShaderName(); const input_names = (_a = this.shader_config(shader_name)) == null ? void 0 : _a.input_names(); if (input_names) { for (const input_name of input_names) { if (output_node.io.inputs.has_named_input(input_name)) { this.add_output_body_line(output_node, shadersCollectionController, input_name); } } } } setNodeLinesAttribute(attribute_node, shadersCollectionController) { var _a; const gl_type = attribute_node.glType(); const new_var = (_a = this.globalsHandler()) == null ? void 0 : _a.readAttribute( attribute_node, gl_type, attribute_node.attributeName(), shadersCollectionController ); const var_name = attribute_node.glVarName(attribute_node.outputName()); shadersCollectionController.addBodyLines(attribute_node, [`${gl_type} ${var_name} = ${new_var}`]); } handle_globals_output_name(options) { var _a; switch (options.output_name) { case GlobalsOutput.TIME: this.handleTime(options); return; case GlobalsOutput.RESOLUTION: this.handle_resolution(options); return; case GlobalsOutput.MV_POSITION: this.handle_mvPosition(options); return; case GlobalsOutput.GL_POSITION: this.handle_gl_Position(options); return; case GlobalsOutput.GL_FRAGCOORD: this.handle_gl_FragCoord(options); return; case GlobalsOutput.GL_POINTCOORD: this.handle_gl_PointCoord(options); return; default: (_a = this.globalsHandler()) == null ? void 0 : _a.handleGlobalsNode( options.globals_node, options.output_name, options.shaders_collection_controller ); } } handleTime(options) { const definition = new UniformGLDefinition( options.globals_node, GlConnectionPointType.FLOAT, options.output_name ); if (options.globals_shader_name) { pushOnArrayAtEntry(options.definitions_by_shader_name, options.globals_shader_name, definition); } const body_line = `float ${options.var_name} = ${options.output_name}`; for (const dependency of options.dependencies) { pushOnArrayAtEntry(options.definitions_by_shader_name, dependency, definition); pushOnArrayAtEntry(options.body_lines_by_shader_name, dependency, body_line); } options.body_lines.push(body_line); this.setUniformsTimeDependent(); } handle_resolution(options) { options.body_lines.push(`vec2 ${options.var_name} = resolution`); const definition = new UniformGLDefinition( options.globals_node, GlConnectionPointType.VEC2, options.output_name ); if (options.globals_shader_name) { pushOnArrayAtEntry(options.definitions_by_shader_name, options.globals_shader_name, definition); } for (const dependency of options.dependencies) { pushOnArrayAtEntry(options.definitions_by_shader_name, dependency, definition); } this.setUniformsResolutionDependent(); } handle_mvPosition(options) { if (options.shader_name == ShaderName.FRAGMENT) { const globals_node = options.globals_node; const shaders_collection_controller = options.shaders_collection_controller; const definition = new VaryingGLDefinition(globals_node, GlConnectionPointType.VEC4, options.var_name); const vertex_body_line = `${options.var_name} = modelViewMatrix * vec4(position, 1.0)`; shaders_collection_controller.addDefinitions(globals_node, [definition], ShaderName.VERTEX); shaders_collection_controller.addBodyLines(globals_node, [vertex_body_line], ShaderName.VERTEX); shaders_collection_controller.addDefinitions(globals_node, [definition]); } } handle_gl_Position(options) { if (options.shader_name == ShaderName.FRAGMENT) { const globals_node = options.globals_node; const shaders_collection_controller = options.shaders_collection_controller; const definition = new VaryingGLDefinition(globals_node, GlConnectionPointType.VEC4, options.var_name); const vertex_body_line = `${options.var_name} = projectionMatrix * modelViewMatrix * vec4(position, 1.0)`; shaders_collection_controller.addDefinitions(globals_node, [definition], ShaderName.VERTEX); shaders_collection_controller.addBodyLines(globals_node, [vertex_body_line], ShaderName.VERTEX); shaders_collection_controller.addDefinitions(globals_node, [definition]); } } handle_gl_FragCoord(options) { if (options.shader_name == ShaderName.FRAGMENT) { options.body_lines.push(`vec4 ${options.var_name} = gl_FragCoord`); } } handle_gl_PointCoord(options) { if (options.shader_name == ShaderName.FRAGMENT) { options.body_lines.push(`vec2 ${options.var_name} = gl_PointCoord`); } else { options.body_lines.push(`vec2 ${options.var_name} = vec2(0.0, 0.0)`); } } set_node_lines_globals(globals_node, shaders_collection_controller) { const body_lines = []; const shader_name = shaders_collection_controller.currentShaderName(); const shader_config = this.shader_config(shader_name); if (!shader_config) { return; } const dependencies = shader_config.dependencies(); const definitions_by_shader_name = /* @__PURE__ */ new Map(); const body_lines_by_shader_name = /* @__PURE__ */ new Map(); const used_output_names = this.used_output_names_for_shader(globals_node, shader_name); for (const output_name of used_output_names) { const var_name = globals_node.glVarName(output_name); const globals_shader_name = shaders_collection_controller.currentShaderName(); const options = { globals_node, shaders_collection_controller, output_name, globals_shader_name, definitions_by_shader_name, body_lines, var_name, shader_name, dependencies, body_lines_by_shader_name }; this.handle_globals_output_name(options); } definitions_by_shader_name.forEach((definitions, shader_name2) => { shaders_collection_controller.addDefinitions(globals_node, definitions, shader_name2); }); body_lines_by_shader_name.forEach((body_lines2, shader_name2) => { shaders_collection_controller.addBodyLines(globals_node, body_lines2, shader_name2); }); shaders_collection_controller.addBodyLines(globals_node, body_lines); } used_output_names_for_shader(globals_node, shader_name) { const used_output_names = globals_node.io.outputs.used_output_names(); const filtered_names = []; for (const name of used_output_names) { if (shader_name == ShaderName.VERTEX) { if (!FRAGMENT_GLOBALS_OUTPUT.includes(name)) { filtered_names.push(name); } } else { filtered_names.push(name); } } return filtered_names; } }