UNPKG

@polygonjs/polygonjs

Version:

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

399 lines (371 loc) 13.1 kB
import {BaseGlShaderAssembler} from '../_Base'; import TemplateDefault from '../../templates/particles/Default.glsl'; import {AttributeGlNode} from '../../../Attribute'; import {TextureAllocationsController} from '../../utils/TextureAllocationsController'; import {ThreeToGl} from '../../../../../../core/ThreeToGl'; import {BaseGlNodeType} from '../../../_Base'; import {GlobalsGlNode} from '../../../Globals'; import {ShaderName} from '../../../../utils/shaders/ShaderName'; import {OutputGlNode} from '../../../Output'; import {GlConnectionPointType, GlConnectionPoint} from '../../../../utils/io/connections/Gl'; import {UniformGLDefinition} from '../../../utils/GLDefinition'; import {GlobalsTextureHandler} from '../../globals/Texture'; import {ShadersCollectionController} from '../../utils/ShadersCollectionController'; import {SubnetOutputGlNode} from '../../../SubnetOutput'; import {GlNodeTraverser} from '../../../../utils/shaders/GlNodeTraverser'; export class ShaderAssemblerParticles extends BaseGlShaderAssembler { private _textureAllocationsController: TextureAllocationsController | undefined; override templateShader() { return undefined; } protected override _template_shader_for_shader_name(shader_name: ShaderName) { return TemplateDefault; } // async get_shaders(){ // await this.update_shaders() // return this._shaders_by_name // } override compile() { this.setup_shader_names_and_variables(); this._updateShaders(); } override rootNodesByShaderName(shader_name: ShaderName, rootNodes: BaseGlNodeType[]): BaseGlNodeType[] { // return this._root_nodes const list = []; for (const node of rootNodes) { switch (node.type()) { case SubnetOutputGlNode.type(): case OutputGlNode.type(): { list.push(node); break; } // list.push(node); // break; // } case AttributeGlNode.type(): { const attrib_name = (node as AttributeGlNode).attributeName(); const variable = this._textureAllocationsController?.variable(attrib_name); if (variable && variable.allocation()) { const allocation_shader_name = variable.allocation()?.shaderName(); if (allocation_shader_name == shader_name) { list.push(node); } } break; } // case GlType.ADJACENT_POINTS_ATTRIB_SMOOTH: { // list.push(node); // break; // } } } return list; } // override leaf_nodes_by_shader_name(shader_name: ShaderName): BaseGlNodeType[] { // const list = []; // for (let node of this._leaf_nodes) { // switch (node.type()) { // case GlobalsGlNode.type(): { // list.push(node); // break; // } // case AttributeGlNode.type(): { // const attrib_name: string = (node as AttributeGlNode).attributeName(); // const variable = this._textureAllocationsController?.variable(attrib_name); // if (variable && variable.allocation()) { // const allocation_shader_name = variable.allocation()?.shaderName(); // if (allocation_shader_name == shader_name) { // list.push(node); // } // } // break; // } // } // } // return list; // } setup_shader_names_and_variables() { // here we need 2 traversers: // 1: the first one is shallow and does not traverse children, just like the materials // 2: the second one does traverse the children, // and is necessary to find the attribute nodes that may be inside subnets // so that we can allocate the texture variables const node_traverser_shallow = new GlNodeTraverser( this.currentGlParentNode(), this.shaderNames(), (root_node, shader_name) => { return this.inputNamesForShaderName(root_node, shader_name); } ); const node_traverser_deep = new GlNodeTraverser( this.currentGlParentNode(), this.shaderNames(), (root_node, shader_name) => { return this.inputNamesForShaderName(root_node, shader_name); }, {traverseChildren: true} ); this._leaf_nodes = node_traverser_shallow.leavesFromNodes(this._root_nodes); const leafNodesForTextureAllocations = node_traverser_deep.leavesFromNodes(this._root_nodes); // for (let node of this._root_nodes) { // await node.params.eval_all(); // } // for (let node of this._leaf_nodes) { // await node.params.eval_all(); // } this._textureAllocationsController = new TextureAllocationsController(); this._textureAllocationsController.allocateConnectionsFromRootNodes( this._root_nodes, leafNodesForTextureAllocations ); // const globalsHandler = new GlobalsTextureHandler() // this.setAssemblerGlobalsHandler(globalsHandler) if (this.globalsHandler()) { ((<unknown>this.globalsHandler()) as GlobalsTextureHandler)?.set_texture_allocations_controller( this._textureAllocationsController ); } this._reset_shader_configs(); } private _updateShaders() { this._shaders_by_name.clear(); this._lines.clear(); for (const shader_name of this.shaderNames()) { const template = this._template_shader_for_shader_name(shader_name); this._lines.set(shader_name, template.split('\n')); } if (this._root_nodes.length > 0) { // the code builder needs to be reset here, // as otherwise it will not know that the shader names may have changed this._resetCodeBuilder(); this.buildCodeFromNodes(this._root_nodes); this._buildLines(); } // this._material.uniforms = this.build_uniforms(template_shader) 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')); } } } // // // CHILDREN NODES PARAMS // // override add_output_inputs(output_child: OutputGlNode) { // output_child.add_param(ParamType.VECTOR3, 'position', [0, 0, 0]); // output_child.add_param(ParamType.VECTOR3, 'velocity', [0, 0, 0]); output_child.io.inputs.setNamedInputConnectionPoints([ new GlConnectionPoint('position', GlConnectionPointType.VEC3), new GlConnectionPoint('velocity', GlConnectionPointType.VEC3), ]); } override add_globals_outputs(globals_node: GlobalsGlNode) { globals_node.io.outputs.setNamedOutputConnectionPoints([ new GlConnectionPoint('position', GlConnectionPointType.VEC3), new GlConnectionPoint('velocity', GlConnectionPointType.VEC3), // new TypedNamedConnectionPoint('acceleration', ConnectionPointType.VEC3), new GlConnectionPoint('time', GlConnectionPointType.FLOAT), ]); } override allow_attribute_exports() { return true; } textureAllocationsController() { return (this._textureAllocationsController = this._textureAllocationsController || new TextureAllocationsController()); } // // // CONFIGS // // override create_shader_configs() { return this._textureAllocationsController?.createShaderConfigs() || []; // [ // new ShaderConfig('position', ['position'], []), // // new ShaderConfig('fragment', ['color', 'alpha'], ['vertex']), // ] } override create_variable_configs() { return [ // new VariableConfig('position', { // default: 'vec3( position )', // prefix: 'vec3 transformed = ' // }), ]; } override shaderNames(): ShaderName[] { return this.textureAllocationsController().shaderNames() || []; } override inputNamesForShaderName(root_node: BaseGlNodeType, shader_name: ShaderName) { return this.textureAllocationsController().inputNamesForShaderName(root_node, shader_name) || []; // return this.shader_config(shader_name).input_names() } // // // TEMPLATE HOOKS // // protected override insertDefineAfter(shader_name: ShaderName) { return '// INSERT DEFINE'; } protected override insertBodyAfter(shader_name: ShaderName) { return '// INSERT BODY'; } protected override linesToRemove(shader_name: ShaderName) { return ['// INSERT DEFINE', '// INSERT BODY']; } // // // TEMPLATE CODE REPLACEMENT // // add_export_body_line( export_node: BaseGlNodeType, input_name: string, input: BaseGlNodeType, variable_name: string, shaders_collection_controller: ShadersCollectionController ) { if (input) { const var_input = export_node.variableForInput(input_name); const new_var = ThreeToGl.vector3(var_input); if (new_var) { const texture_variable = this.textureAllocationsController().variable(variable_name); // if we are in the texture this variable is allocated to, we write it back const shader_name = shaders_collection_controller.currentShaderName(); if (texture_variable && texture_variable.allocation()?.shaderName() == shader_name) { const component = texture_variable.component(); const line = `gl_FragColor.${component} = ${new_var}`; shaders_collection_controller.addBodyLines(export_node, [line], shader_name); } } } } override set_node_lines_output( output_node: BaseGlNodeType, shaders_collection_controller: ShadersCollectionController ) { const shader_name = shaders_collection_controller.currentShaderName(); const input_names = this.textureAllocationsController().inputNamesForShaderName(output_node, shader_name); if (input_names) { for (const input_name of input_names) { const input = output_node.io.inputs.named_input(input_name); if (input) { const variable_name = input_name; this.add_export_body_line( output_node, input_name, input, variable_name, shaders_collection_controller ); } else { // position reads the default attribute position // or maybe there is no need? // if(input_name == 'position'){ // this.globalsHandler().read_attribute(output_node, 'vec3', 'position') // } } } } } override setNodeLinesAttribute( attribute_node: AttributeGlNode, shaders_collection_controller: ShadersCollectionController ) { if (attribute_node.isImporting()) { const gl_type = attribute_node.glType(); const attribute_name = attribute_node.attributeName(); const new_value = this.globalsHandler()?.readAttribute( attribute_node, gl_type, attribute_name, shaders_collection_controller ); const var_name = attribute_node.glVarName(attribute_node.outputName()); const body_line = `${gl_type} ${var_name} = ${new_value}`; shaders_collection_controller.addBodyLines(attribute_node, [body_line]); // re-export to ensure it is available on next frame const texture_variable = this.textureAllocationsController().variable(attribute_name); const shader_name = shaders_collection_controller.currentShaderName(); if (texture_variable && texture_variable.allocation()?.shaderName() == shader_name) { const variable = this.textureAllocationsController().variable(attribute_name); if (variable) { const component = variable.component(); const body_line = `gl_FragColor.${component} = ${var_name}`; shaders_collection_controller.addBodyLines(attribute_node, [body_line]); } } // this.add_import_body_line( // attribute_node, // shader_name, // Attribute.output_name(), // attribute_node.attribute_name() // ) } if (attribute_node.isExporting()) { const input = attribute_node.connected_input_node(); if (input) { const variable_name = attribute_node.attributeName(); this.add_export_body_line( attribute_node, attribute_node.inputName(), input, variable_name, shaders_collection_controller ); } } } override set_node_lines_globals( globals_node: GlobalsGlNode, shaders_collection_controller: ShadersCollectionController ) { for (const output_name of globals_node.io.outputs.used_output_names()) { switch (output_name) { case 'time': this._handle_globals_time(globals_node, output_name, shaders_collection_controller); break; default: this._handle_globals_default(globals_node, output_name, shaders_collection_controller); } } } private _handle_globals_time( globals_node: GlobalsGlNode, output_name: string, shaders_collection_controller: ShadersCollectionController ) { const definition = new UniformGLDefinition(globals_node, GlConnectionPointType.FLOAT, output_name); shaders_collection_controller.addDefinitions(globals_node, [definition]); const var_name = globals_node.glVarName(output_name); const body_line = `float ${var_name} = ${output_name}`; shaders_collection_controller.addBodyLines(globals_node, [body_line]); this.setUniformsTimeDependent(); } private _handle_globals_default( globals_node: GlobalsGlNode, output_name: string, shaders_collection_controller: ShadersCollectionController ) { const output_connection_point = globals_node.io.outputs.namedOutputConnectionPointsByName(output_name); if (output_connection_point) { const gl_type = output_connection_point.type(); const attrib_read = this.globalsHandler()?.readAttribute( globals_node, gl_type, output_name, shaders_collection_controller ); if (attrib_read) { const var_name = globals_node.glVarName(output_name); const body_line = `${gl_type} ${var_name} = ${attrib_read}`; shaders_collection_controller.addBodyLines(globals_node, [body_line]); } } } }