UNPKG

@polygonjs/polygonjs

Version:

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

321 lines (290 loc) 11 kB
import {BaseGlNodeType} from '../../_Base'; import {TypedNodeTraverser} from '../../../utils/shaders/NodeTraverser'; import {pushOnArrayAtEntry} from '../../../../../core/MapUtils'; import {ShaderName} from '../../../utils/shaders/ShaderName'; import {GLDefinitionType, BaseGLDefinition} from '../../utils/GLDefinition'; import {TypedGLDefinitionCollection} from '../../utils/GLDefinitionCollection'; import {ParamConfigsController} from '../../../../nodes/utils/code/controllers/ParamConfigsController'; import {ShadersCollectionController} from './ShadersCollectionController'; import {CodeFormatter} from './CodeFormatter'; import {LineType} from './LineType'; import {GlParamConfig} from './GLParamConfig'; import {ParamType} from '../../../../poly/ParamType'; import {NodeContext} from '../../../../poly/NodeContext'; import {CoreGraphNodeId} from '../../../../../core/graph/CoreGraph'; import {arrayUniq} from '../../../../../core/ArrayUtils'; import {TypedAssembler} from '../../../utils/shaders/BaseAssembler'; type RootNodesForShaderMethod = (shader_name: ShaderName, rootNodes: BaseGlNodeType[]) => BaseGlNodeType[]; // let nextId = 1; export interface CodeBuilderSetCodeLinesOptions { otherFragmentShaderCollectionController?: ShadersCollectionController; } export class CodeBuilder { // private _id = (nextId += 1); private _param_configs_controller: ParamConfigsController<GlParamConfig<ParamType>> = new ParamConfigsController(); private _param_configs_set_allowed: boolean = true; private _shadersCollectionController: ShadersCollectionController | undefined; private _lines: Map<ShaderName, Map<LineType, string[]>> = new Map(); // _function_declared: Map<ShaderName, Map<string, boolean>> = new Map(); constructor( private _nodeTraverser: TypedNodeTraverser<NodeContext.GL>, private _root_nodes_for_shader_method: RootNodesForShaderMethod, private _assembler: TypedAssembler<NodeContext.GL> ) {} nodeTraverser() { return this._nodeTraverser; } shaderNames() { return this._nodeTraverser.shaderNames(); } buildFromNodes( rootNodes: BaseGlNodeType[], paramNodes: BaseGlNodeType[], setCodeLinesOptions?: CodeBuilderSetCodeLinesOptions ) { this._nodeTraverser.traverse(rootNodes); const nodesByShaderName: Map<ShaderName, BaseGlNodeType[]> = new Map(); for (const shaderName of this.shaderNames()) { const nodes = this._nodeTraverser.nodesForShaderName(shaderName); nodesByShaderName.set(shaderName, nodes); } const sortedNodes = this._nodeTraverser.sortedNodes(); for (const shaderName of this.shaderNames()) { const rootNodesForShader = this._root_nodes_for_shader_method(shaderName, rootNodes); for (const rootNode of rootNodesForShader) { pushOnArrayAtEntry(nodesByShaderName, shaderName, rootNode); } } // ensure nodes are not added if already present const sorted_node_ids: Map<CoreGraphNodeId, boolean> = new Map(); for (const node of sortedNodes) { sorted_node_ids.set(node.graphNodeId(), true); } for (const rootNode of rootNodes) { if (!sorted_node_ids.get(rootNode.graphNodeId())) { sortedNodes.push(rootNode); sorted_node_ids.set(rootNode.graphNodeId(), true); } } for (const node of sortedNodes) { node.reset_code(); } for (const node of paramNodes) { node.reset_code(); } // for (let node of sorted_nodes) { // await node.params.eval_all(); // } // const param_promises = sorted_nodes.map(node=>{ // return node.eval_all_params() // }) // await Promise.all(param_promises) this._shadersCollectionController = new ShadersCollectionController( this.shaderNames(), this.shaderNames()[0], this._assembler ); this.reset(); for (const shaderName of this.shaderNames()) { const nodes: BaseGlNodeType[] = []; const nonUniqNodes = nodesByShaderName.get(shaderName); if (nonUniqNodes) { arrayUniq(nonUniqNodes, nodes); } this._shadersCollectionController.setCurrentShaderName(shaderName); if (nodes) { for (const node of nodes) { node.setLines(this._shadersCollectionController); } } } // set param configs if (this._param_configs_set_allowed) { for (const param_node of paramNodes) { try { param_node.states.error.clear(); param_node.setParamConfigs(); } catch (err) { const message: string = (err as any).message || 'failed to create spare param'; param_node.states.error.set(message); throw new Error(`${param_node.name()} cannot create spare parameter`); } } this.setParamConfigs(paramNodes); } // finalize this._setCodeLines(sortedNodes, setCodeLinesOptions); } shadersCollectionController() { return this._shadersCollectionController; } disallow_new_param_configs() { this._param_configs_set_allowed = false; } allow_new_param_configs() { this._param_configs_set_allowed = true; } private reset() { for (const shader_name of this.shaderNames()) { const lines_map = new Map(); // for (let line_type of LINE_TYPES) { // lines_map.set(line_type, []); // } this._lines.set(shader_name, lines_map); // this._function_declared.set(shader_name, new Map()); } } param_configs() { return this._param_configs_controller.list() || []; } lines(shader_name: ShaderName, line_type: LineType) { return this._lines.get(shader_name)?.get(line_type) || []; } all_lines() { return this._lines; } setParamConfigs(nodes: BaseGlNodeType[]) { this._param_configs_controller.reset(); for (const node of nodes) { const param_configs = node.param_configs(); if (param_configs) { for (const param_config of param_configs) { this._param_configs_controller.push(param_config); } } } } private _setCodeLines(nodes: BaseGlNodeType[], options?: CodeBuilderSetCodeLinesOptions) { for (const shaderName of this.shaderNames()) { const additionalDefinitions: BaseGLDefinition[] = []; if (shaderName == ShaderName.FRAGMENT) { if (this._shadersCollectionController && options && options.otherFragmentShaderCollectionController) { // for (let shaderName of options.otherShadersCollectionController.shaderNames()) { // this._linesControllerByShaderName.set(shaderName, new LinesController(shaderName)); options.otherFragmentShaderCollectionController.traverseDefinitions( ShaderName.FRAGMENT, (definition: BaseGLDefinition) => { additionalDefinitions.push(definition); } ); // } } } this._addCodeLines(nodes, shaderName, additionalDefinitions); } } private _addCodeLines(nodes: BaseGlNodeType[], shaderName: ShaderName, additionalDefinitions?: BaseGLDefinition[]) { this.addDefinitions(nodes, shaderName, GLDefinitionType.PRECISION, LineType.DEFINE, additionalDefinitions); this.addDefinitions( nodes, shaderName, GLDefinitionType.FUNCTION, LineType.FUNCTION_DECLARATION, additionalDefinitions ); this.addDefinitions(nodes, shaderName, GLDefinitionType.UNIFORM, LineType.DEFINE, additionalDefinitions); this.addDefinitions(nodes, shaderName, GLDefinitionType.VARYING, LineType.DEFINE, additionalDefinitions); this.addDefinitions(nodes, shaderName, GLDefinitionType.ATTRIBUTE, LineType.DEFINE, additionalDefinitions); this.add_code_line_for_nodes_and_line_type(nodes, shaderName, LineType.BODY); } private addDefinitions( nodes: BaseGlNodeType[], shaderName: ShaderName, definitionType: GLDefinitionType, lineType: LineType, additionalDefinitions?: BaseGLDefinition[] ) { if (!this._shadersCollectionController) { return; } const definitions: BaseGLDefinition[] = []; for (const node of nodes) { let nodeDefinitions = this._shadersCollectionController.definitions(shaderName, node); if (nodeDefinitions) { nodeDefinitions = nodeDefinitions.filter((d) => d.definition_type == definitionType); for (const definition of nodeDefinitions) { definitions.push(definition); } } } if (additionalDefinitions) { const filteredAdditionalDefinitions = additionalDefinitions.filter( (d) => d.definition_type == definitionType ); for (const definition of filteredAdditionalDefinitions) { definitions.push(definition); } } if (definitions.length > 0) { const collection = new TypedGLDefinitionCollection<GLDefinitionType>(definitions); const uniqDefinitions = collection.uniq(); if (collection.errored) { // TODO: handle error throw `code builder error: ${collection.error_message}`; } const definitions_by_node_id: Map<CoreGraphNodeId, BaseGLDefinition[]> = new Map(); const nodeIds: Map<CoreGraphNodeId, boolean> = new Map(); for (const definition of uniqDefinitions) { const nodeId = definition.node.graphNodeId(); if (!nodeIds.has(nodeId)) { nodeIds.set(nodeId, true); } pushOnArrayAtEntry(definitions_by_node_id, nodeId, definition); } const lines_for_shader = this._lines.get(shaderName)!; nodeIds.forEach((_, nodeId) => { const definitions = definitions_by_node_id.get(nodeId); if (definitions) { const first_definition = definitions[0]; if (first_definition) { const comment = CodeFormatter.nodeComment(first_definition.node, lineType); pushOnArrayAtEntry(lines_for_shader, lineType, comment); for (const definition of definitions) { const line = CodeFormatter.lineWrap(first_definition.node, definition.line, lineType); pushOnArrayAtEntry(lines_for_shader, lineType, line); } const separator = CodeFormatter.post_line_separator(lineType); pushOnArrayAtEntry(lines_for_shader, lineType, separator); } } }); } } add_code_line_for_nodes_and_line_type(nodes: BaseGlNodeType[], shader_name: ShaderName, line_type: LineType) { nodes = nodes.filter((node) => { if (this._shadersCollectionController) { const lines = this._shadersCollectionController.bodyLines(shader_name, node); return lines && lines.length > 0; } }); var nodes_count = nodes.length; for (let i = 0; i < nodes_count; i++) { const is_last = i == nodes.length - 1; this.add_code_line_for_node_and_line_type(nodes[i], shader_name, line_type, is_last); } } add_code_line_for_node_and_line_type( node: BaseGlNodeType, shader_name: ShaderName, line_type: LineType, is_last: boolean ): void { if (!this._shadersCollectionController) { return; } const lines = this._shadersCollectionController.bodyLines(shader_name, node); if (lines && lines.length > 0) { const lines_for_shader = this._lines.get(shader_name)!; const comment = CodeFormatter.nodeComment(node, line_type); pushOnArrayAtEntry(lines_for_shader, line_type, comment); lines.forEach((line) => { line = CodeFormatter.lineWrap(node, line, line_type); pushOnArrayAtEntry(lines_for_shader, line_type, line); }); if (!(line_type == LineType.BODY && is_last)) { const separator = CodeFormatter.post_line_separator(line_type); pushOnArrayAtEntry(lines_for_shader, line_type, separator); } } } }