UNPKG

@polygonjs/polygonjs

Version:

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

308 lines (307 loc) 12.5 kB
"use strict"; import { pushOnArrayAtEntry } from "../../../../../core/MapUtils"; import { JsDefinitionType, JsDefinitionTypeMap } from "../../utils/JsDefinition"; import { TypedJsDefinitionCollection } from "../../utils/JsDefinitionCollection"; import { ParamConfigsController } from "../../../../nodes/utils/code/controllers/ParamConfigsController"; import { JsLinesCollectionController } from "./JsLinesCollectionController"; import { CodeFormatter } from "./CodeFormatter"; import { LineType } from "./LineType"; import { arrayUniq } from "../../../../../core/ArrayUtils"; import { triggerableMethodCalls } from "../assemblers/actor/ActorAssemblerUtils"; import { setToArray } from "../../../../../core/SetUtils"; import { JsType } from "../../../../poly/registers/nodes/types/Js"; export class JsCodeBuilder { // _function_declared: Map<ShaderName, Map<string, boolean>> = new Map(); constructor(_nodeTraverser, _rootNodesByShaderName, _assembler) { this._nodeTraverser = _nodeTraverser; this._rootNodesByShaderName = _rootNodesByShaderName; this._assembler = _assembler; // private _id = (nextId += 1); this._param_configs_controller = new ParamConfigsController(); this._param_configs_set_allowed = true; this._lines = /* @__PURE__ */ new Map(); } nodeTraverser() { return this._nodeTraverser; } shaderNames() { return this._nodeTraverser.shaderNames(); } buildFromNodes(rootNodes, paramNodes, setCodeLinesOptions) { this._nodeTraverser.traverse(rootNodes); const nodesByShaderName = /* @__PURE__ */ 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._rootNodesByShaderName(shaderName, rootNodes); for (const rootNode of rootNodesForShader) { pushOnArrayAtEntry(nodesByShaderName, shaderName, rootNode); } } const sorted_node_ids = /* @__PURE__ */ 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(); } const nodesToBeComputed = [...sortedNodes]; if (setCodeLinesOptions == null ? void 0 : setCodeLinesOptions.actor) { const { triggeringNodes, triggerableNodes } = setCodeLinesOptions.actor; nodesToBeComputed.push(...triggeringNodes, ...triggerableNodes); } const codeNodes = nodesToBeComputed.filter((n) => n.type() == JsType.CODE); for (const node of codeNodes) { if (!node.compiled()) { const functionNode = node.functionNode(); if (functionNode) { node.setForbiddenTriggerNodes(functionNode); } node.compile({ triggerFunctionNode: false }); node.clearForbiddenTriggerNodes(); if (node.states.error.active() || !node.compiled()) { const message = `failed to generated code (see node ${node.path()})`; throw new Error(message); } } } this._shadersCollectionController = new JsLinesCollectionController( this.shaderNames(), this.shaderNames()[0], this._assembler ); this.reset(); for (const shaderName of this.shaderNames()) { let notUniqNodes = nodesByShaderName.get(shaderName) || []; const nodes = []; arrayUniq(notUniqNodes, nodes); this._shadersCollectionController.setCurrentShaderName(shaderName); if (nodes) { for (const node of nodes) { node.setLines(this._shadersCollectionController); } } if (setCodeLinesOptions == null ? void 0 : setCodeLinesOptions.actor) { const { triggeringNodes, triggerableNodes } = setCodeLinesOptions.actor; for (const triggeringNode of triggeringNodes) { const _triggerableMethodCalls = triggerableMethodCalls(triggeringNode); triggeringNode.setTriggeringLines(this._shadersCollectionController, _triggerableMethodCalls); } for (const triggerableNode of triggerableNodes) { try { triggerableNode.setTriggerableLines(this._shadersCollectionController); } catch (err) { triggerableNode.states.error.set(`failed to generate code`); throw new Error(`node ${triggerableNode.path()} failed to generated code`); } } } } 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 = err.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); } 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; } reset() { for (let shader_name of this.shaderNames()) { const lines_map = /* @__PURE__ */ new Map(); this._lines.set(shader_name, lines_map); } } param_configs() { return this._param_configs_controller.list() || []; } lines(shader_name, line_type) { var _a; return ((_a = this._lines.get(shader_name)) == null ? void 0 : _a.get(line_type)) || []; } all_lines() { return this._lines; } setParamConfigs(nodes) { 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); } } } } _setCodeLines(nodes, options) { for (const shaderName of this.shaderNames()) { const additionalDefinitions = []; if (this._shadersCollectionController && options && options.otherFragmentShaderCollectionController) { options.otherFragmentShaderCollectionController.traverseDefinitions( shaderName, (definition) => { additionalDefinitions.push(definition); } ); } this._addCodeLines(nodes, shaderName, additionalDefinitions, options); } } _addCodeLines(nodes, shaderName, additionalDefinitions, options) { const allNodes = options && options.actor ? nodes.concat(setToArray(options.actor.triggeringNodes, [])).concat(setToArray(options.actor.triggerableNodes, [])) : nodes; this.addDefinitions(nodes, shaderName, JsDefinitionType.LOCAL_FUNCTION, LineType.DEFINE, additionalDefinitions); this.addDefinitions(allNodes, shaderName, JsDefinitionType.COMPUTED, LineType.MEMBER, additionalDefinitions); this.addDefinitions(nodes, shaderName, JsDefinitionType.CONSTANT, LineType.MEMBER, additionalDefinitions); this.addDefinitions(allNodes, shaderName, JsDefinitionType.REF, LineType.MEMBER, additionalDefinitions); this.addDefinitions( allNodes, shaderName, JsDefinitionType.INIT_FUNCTION, LineType.CONSTRUCTOR, additionalDefinitions ); this.addDefinitions(allNodes, shaderName, JsDefinitionType.WATCH, LineType.CONSTRUCTOR, additionalDefinitions); if (options == null ? void 0 : options.actor.triggeringNodes) { this.addDefinitions( // we currently add all nodes here, // so that nodes which have an intersection output // can have their triggering method added, // even if only the intersection output is used allNodes, //setToArray(options.actor.triggeringNodes), shaderName, JsDefinitionType.TRIGGERING, LineType.BODY, additionalDefinitions ); } if (options == null ? void 0 : options.actor.triggerableNodes) { this.addDefinitions( setToArray(options.actor.triggerableNodes, []), shaderName, JsDefinitionType.TRIGGERABLE, LineType.BODY, additionalDefinitions ); } this.add_code_line_for_nodes_and_line_type(allNodes, shaderName, LineType.BODY); } addDefinitions(nodes, shaderName, definitionType, lineType, additionalDefinitions) { if (!this._shadersCollectionController) { return; } const definitions = []; for (const node of nodes) { let nodeDefinitions = this._shadersCollectionController.definitions(shaderName, node); if (nodeDefinitions) { nodeDefinitions = nodeDefinitions.filter((d) => d.definitionType() == definitionType); for (const definition of nodeDefinitions) { definitions.push(definition); } } } if (additionalDefinitions) { const filteredAdditionalDefinitions = additionalDefinitions.filter( (d) => d.definitionType() == definitionType ); for (const definition of filteredAdditionalDefinitions) { definitions.push(definition); } } if (definitions.length == 0) { return; } const collection = new TypedJsDefinitionCollection(definitions); const uniqDefinitions = collection.uniq(); if (collection.errored) { throw `code builder error: ${collection.error_message}`; } const definitions_by_node_id = /* @__PURE__ */ new Map(); const nodeIds = /* @__PURE__ */ 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); const definitionClass = JsDefinitionTypeMap[definitionType]; definitionClass.gather(definitions, lines_for_shader, lineType); nodeIds.forEach((_, nodeId) => { const definitions2 = definitions_by_node_id.get(nodeId); if (definitions2) { const first_definition = definitions2[0]; if (first_definition) { const comment = CodeFormatter.nodeComment(first_definition.node(), lineType); pushOnArrayAtEntry(lines_for_shader, lineType, comment); for (const definition of definitions2) { 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, shaderName, lineType) { nodes = nodes.filter((node) => { if (this._shadersCollectionController) { const lines = this._shadersCollectionController.bodyLines(shaderName, node); return lines && lines.length > 0; } }); var nodesCount = nodes.length; for (let i = 0; i < nodesCount; i++) { const isLast = i == nodes.length - 1; this.add_code_line_for_node_and_line_type(nodes[i], shaderName, lineType, isLast); } } add_code_line_for_node_and_line_type(node, shaderName, lineType, isLast) { if (!this._shadersCollectionController) { return; } const lines = this._shadersCollectionController.bodyLines(shaderName, node); if (lines && lines.length > 0) { const lines_for_shader = this._lines.get(shaderName); const comment = CodeFormatter.nodeComment(node, lineType); pushOnArrayAtEntry(lines_for_shader, lineType, comment); lines.forEach((line) => { line = CodeFormatter.lineWrap(node, line, lineType); pushOnArrayAtEntry(lines_for_shader, lineType, line); }); if (!(lineType == LineType.BODY && isLast)) { const separator = CodeFormatter.post_line_separator(lineType); pushOnArrayAtEntry(lines_for_shader, lineType, separator); } } } }