@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
308 lines (307 loc) • 12.5 kB
JavaScript
"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);
}
}
}
}