UNPKG

polygonjs-engine

Version:

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

320 lines (319 loc) 13.8 kB
import {BaseGlShaderAssembler} from "../_Base"; import {ThreeToGl as ThreeToGl2} from "../../../../../../core/ThreeToGl"; import {ShaderName as ShaderName2} from "../../../../utils/shaders/ShaderName"; import {UniformGLDefinition, VaryingGLDefinition} from "../../../utils/GLDefinition"; import {GlConnectionPointType} from "../../../../utils/io/connections/Gl"; import {MapUtils as MapUtils2} from "../../../../../../core/MapUtils"; import {ShaderMaterial as ShaderMaterial2} from "three/src/materials/ShaderMaterial"; import {GlNodeFinder} from "../../utils/NodeFinder"; export var CustomMaterialName; (function(CustomMaterialName2) { CustomMaterialName2["DISTANCE"] = "customDistanceMaterial"; CustomMaterialName2["DEPTH"] = "customDepthMaterial"; CustomMaterialName2["DEPTH_DOF"] = "customDepthDOFMaterial"; })(CustomMaterialName || (CustomMaterialName = {})); export var GlobalsOutput; (function(GlobalsOutput2) { GlobalsOutput2["TIME"] = "time"; GlobalsOutput2["RESOLUTION"] = "resolution"; GlobalsOutput2["MV_POSITION"] = "mvPosition"; GlobalsOutput2["GL_POSITION"] = "gl_Position"; GlobalsOutput2["GL_FRAGCOORD"] = "gl_FragCoord"; GlobalsOutput2["GL_POINTCOORD"] = "gl_PointCoord"; })(GlobalsOutput || (GlobalsOutput = {})); const FRAGMENT_GLOBALS_OUTPUT = [ GlobalsOutput.GL_FRAGCOORD, GlobalsOutput.GL_POINTCOORD ]; export class ShaderAssemblerMaterial extends BaseGlShaderAssembler { constructor() { super(...arguments); this._assemblers_by_custom_name = new Map(); } create_material() { return new ShaderMaterial2(); } custom_assembler_class_by_custom_name() { return void 0; } _add_custom_materials(material) { const class_by_custom_name = this.custom_assembler_class_by_custom_name(); if (class_by_custom_name) { class_by_custom_name.forEach((assembler_class, custom_name) => { this._add_custom_material(material, custom_name, assembler_class); }); } } _add_custom_material(material, custom_name, assembler_class) { let custom_assembler = this._assemblers_by_custom_name.get(custom_name); if (!custom_assembler) { custom_assembler = new assembler_class(this._gl_parent_node); this._assemblers_by_custom_name.set(custom_name, custom_assembler); } material.custom_materials = material.custom_materials || {}; material.custom_materials[custom_name] = custom_assembler.create_material(); } compile_custom_materials(material) { const class_by_custom_name = this.custom_assembler_class_by_custom_name(); if (class_by_custom_name) { class_by_custom_name.forEach((assembler_class, custom_name) => { if (this._code_builder) { let assembler = this._assemblers_by_custom_name.get(custom_name); if (!assembler) { assembler = new assembler_class(this._gl_parent_node); this._assemblers_by_custom_name.set(custom_name, assembler); } assembler.set_root_nodes(this._root_nodes); assembler.set_param_configs_owner(this._code_builder); assembler.set_shader_configs(this.shader_configs); assembler.set_variable_configs(this.variable_configs()); const custom_material = material.custom_materials[custom_name]; if (custom_material) { assembler.compile_material(custom_material); } } }); } } compile_material(material) { if (!this.compile_allowed()) { return; } const output_nodes = GlNodeFinder.find_output_nodes(this._gl_parent_node); if (output_nodes.length > 1) { this._gl_parent_node.states.error.set("only one output node allowed"); } const varying_nodes = GlNodeFinder.find_varying_nodes(this._gl_parent_node); const root_nodes = output_nodes.concat(varying_nodes); this.set_root_nodes(root_nodes); this._update_shaders(); const new_vertex_shader = this._shaders_by_name.get(ShaderName2.VERTEX); const new_fragment_shader = this._shaders_by_name.get(ShaderName2.FRAGMENT); if (new_vertex_shader && new_fragment_shader) { material.vertexShader = new_vertex_shader; material.fragmentShader = new_fragment_shader; this.add_uniforms(material.uniforms); material.needsUpdate = true; } const scene = this._gl_parent_node.scene(); if (this.uniforms_time_dependent()) { scene.uniforms_controller.add_time_dependent_uniform_owner(material.uuid, material.uniforms); } else { scene.uniforms_controller.remove_time_dependent_uniform_owner(material.uuid); } if (material.custom_materials) { this.compile_custom_materials(material); } } _update_shaders() { this._shaders_by_name = new Map(); this._lines = new Map(); for (let shader_name of this.shader_names) { const template = this._template_shader_for_shader_name(shader_name); if (template) { this._lines.set(shader_name, template.split("\n")); } } if (this._root_nodes.length > 0) { this.build_code_from_nodes(this._root_nodes); this._build_lines(); } for (let shader_name of this.shader_names) { 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) { const input = output_node.io.inputs.named_input(input_name); const var_input = output_node.variable_for_input(input_name); const variable_config = this.variable_config(input_name); let new_var = null; if (input) { new_var = ThreeToGl2.vector3(var_input); } else { if (variable_config.default_from_attribute()) { const connection_point = output_node.io.inputs.named_input_connection_points_by_name(input_name); if (connection_point) { const gl_type = connection_point.type(); const attr_read = this.globals_handler?.read_attribute(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.add_body_lines(output_node, [`#if ${if_condition}`]); } shaders_collection_controller.add_body_lines(output_node, [`${prefix}${new_var}${suffix}`]); if (if_condition) { shaders_collection_controller.add_body_lines(output_node, [`#endif`]); } } } set_node_lines_output(output_node, shaders_collection_controller) { const shader_name = shaders_collection_controller.current_shader_name; const input_names = this.shader_config(shader_name)?.input_names(); if (input_names) { for (let input_name of input_names) { if (output_node.io.inputs.has_named_input(input_name)) { this.add_output_body_line(output_node, shaders_collection_controller, input_name); } } } } set_node_lines_attribute(attribute_node, shaders_collection_controller) { const gl_type = attribute_node.gl_type(); const new_var = this.globals_handler?.read_attribute(attribute_node, gl_type, attribute_node.attribute_name, shaders_collection_controller); const var_name = attribute_node.gl_var_name(attribute_node.output_name); shaders_collection_controller.add_body_lines(attribute_node, [`${gl_type} ${var_name} = ${new_var}`]); } handle_globals_output_name(options) { switch (options.output_name) { case GlobalsOutput.TIME: this.handle_time(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: this.globals_handler?.handle_globals_node(options.globals_node, options.output_name, options.shaders_collection_controller); } } handle_time(options) { const definition = new UniformGLDefinition(options.globals_node, GlConnectionPointType.FLOAT, options.output_name); if (options.globals_shader_name) { MapUtils2.push_on_array_at_entry(options.definitions_by_shader_name, options.globals_shader_name, definition); } const body_line = `float ${options.var_name} = ${options.output_name}`; for (let dependency of options.dependencies) { MapUtils2.push_on_array_at_entry(options.definitions_by_shader_name, dependency, definition); MapUtils2.push_on_array_at_entry(options.body_lines_by_shader_name, dependency, body_line); } options.body_lines.push(body_line); this.set_uniforms_time_dependent(); } handle_resolution(options) { if (options.shader_name == ShaderName2.FRAGMENT) { 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) { MapUtils2.push_on_array_at_entry(options.definitions_by_shader_name, options.globals_shader_name, definition); } for (let dependency of options.dependencies) { MapUtils2.push_on_array_at_entry(options.definitions_by_shader_name, dependency, definition); } this.set_resolution_dependent(); } handle_mvPosition(options) { if (options.shader_name == ShaderName2.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.add_definitions(globals_node, [definition], ShaderName2.VERTEX); shaders_collection_controller.add_body_lines(globals_node, [vertex_body_line], ShaderName2.VERTEX); shaders_collection_controller.add_definitions(globals_node, [definition]); } } handle_gl_Position(options) { if (options.shader_name == ShaderName2.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.add_definitions(globals_node, [definition], ShaderName2.VERTEX); shaders_collection_controller.add_body_lines(globals_node, [vertex_body_line], ShaderName2.VERTEX); shaders_collection_controller.add_definitions(globals_node, [definition]); } } handle_gl_FragCoord(options) { if (options.shader_name == ShaderName2.FRAGMENT) { options.body_lines.push(`vec4 ${options.var_name} = gl_FragCoord`); } } handle_gl_PointCoord(options) { if (options.shader_name == ShaderName2.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.current_shader_name; const shader_config = this.shader_config(shader_name); if (!shader_config) { return; } const dependencies = shader_config.dependencies(); const definitions_by_shader_name = new Map(); const body_lines_by_shader_name = new Map(); const used_output_names = this.used_output_names_for_shader(globals_node, shader_name); for (let output_name of used_output_names) { const var_name = globals_node.gl_var_name(output_name); const globals_shader_name = shaders_collection_controller.current_shader_name; 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.add_definitions(globals_node, definitions, shader_name2); }); body_lines_by_shader_name.forEach((body_lines2, shader_name2) => { shaders_collection_controller.add_body_lines(globals_node, body_lines2, shader_name2); }); shaders_collection_controller.add_body_lines(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 (let name of used_output_names) { if (shader_name == ShaderName2.VERTEX) { if (!FRAGMENT_GLOBALS_OUTPUT.includes(name)) { filtered_names.push(name); } } else { filtered_names.push(name); } } return filtered_names; } }