@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
399 lines (371 loc) • 13.1 kB
text/typescript
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]);
}
}
}
}