UNPKG

@polygonjs/polygonjs

Version:

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

336 lines (307 loc) 12.1 kB
import {BaseGlConnectionPoint} from './../io/connections/Gl'; import {CoreGraph} from '../../../../core/graph/CoreGraph'; import {pushOnArrayAtEntry} from '../../../../core/MapUtils'; import {ShaderNameByContextMap} from './ShaderName'; import {TypedNode} from '../../_Base'; import {NodeContext, BaseNodeByContextMap, NetworkChildNodeType} from '../../../poly/NodeContext'; // import {NodeTypeMap} from '../../../containers/utils/ContainerMap'; import {CoreGraphNodeId} from '../../../../core/graph/CoreGraph'; import {arrayUniq, arrayDifference, arrayCompact} from '../../../../core/ArrayUtils'; // type NumberByString = Map<string, number>; type NumberByCoreGraphNodeId = Map<CoreGraphNodeId, number>; // type BaseNodeTypeByString = Map<string, BaseNodeType>; // type BooleanByString = Map<string, boolean>; type BooleanByCoreGraphNodeId = Map<CoreGraphNodeId, boolean>; type BooleanByStringByShaderName<NC extends NodeContext> = Map<ShaderNameByContextMap[NC], BooleanByCoreGraphNodeId>; type StringArrayByString = Map<CoreGraphNodeId, CoreGraphNodeId[]>; type InputNamesByShaderNameMethod<NC extends NodeContext> = ( root_node: BaseNodeByContextMap[NC], shader_name: ShaderNameByContextMap[NC] ) => string[]; interface NodeTraverserOptions { traverseChildren?: boolean; } export class TypedNodeTraverser<NC extends NodeContext> { protected _leaves_graph_id: BooleanByStringByShaderName<NC> = new Map(); protected _graph_ids_by_shader_name: BooleanByStringByShaderName<NC> = new Map(); private _outputs_by_graph_id: StringArrayByString = new Map(); private _depth_by_graph_id: NumberByCoreGraphNodeId = new Map(); private _graph_id_by_depth: Map<number, CoreGraphNodeId[]> = new Map(); protected _graph: CoreGraph; protected _shaderName!: ShaderNameByContextMap[NC]; // private _subnets_by_id: BaseNodeTypeByString = new Map(); constructor( private _parent_node: TypedNode<NC, any>, private _shader_names: ShaderNameByContextMap[NC][], private _inputNamesForShaderNameMethod: InputNamesByShaderNameMethod<NC>, private _options?: NodeTraverserOptions ) { this._graph = this._parent_node.scene().graph; } private _traverseChildren() { return this._options?.traverseChildren || false; } private reset() { this._leaves_graph_id.clear(); this._graph_ids_by_shader_name.clear(); this._outputs_by_graph_id.clear(); this._depth_by_graph_id.clear(); this._graph_id_by_depth.clear(); // this._subnets_by_id.clear(); this._shader_names.forEach((shader_name) => { this._graph_ids_by_shader_name.set(shader_name, new Map()); }); } shaderNames() { return this._shader_names; } inputNamesForShaderName(root_node: BaseNodeByContextMap[NC], shader_name: ShaderNameByContextMap[NC]) { return this._inputNamesForShaderNameMethod(root_node, shader_name); } traverse(rootNodes: BaseNodeByContextMap[NC][]) { this.reset(); for (const shaderName of this.shaderNames()) { this._leaves_graph_id.set(shaderName, new Map()); } for (const shaderName of this.shaderNames()) { this._shaderName = shaderName; for (const rootNode of rootNodes) { this._findLeavesFromRootNode(rootNode); this._setNodesDepth(); } } // graph_ids.forEach((graph_id) => { this._depth_by_graph_id.forEach((depth: number, graph_id: CoreGraphNodeId) => { if (depth != null) { // this._graph_id_by_depth.set(depth, this._graph_id_by_depth.get(depth) || []); // this._graph_id_by_depth.get(depth)?.push(graph_id); pushOnArrayAtEntry(this._graph_id_by_depth, depth, graph_id); } }); } nodesForShaderName(shaderName: ShaderNameByContextMap[NC]) { const depths: number[] = []; this._graph_id_by_depth.forEach((value: CoreGraphNodeId[], key: number) => { depths.push(key); }); depths.sort((a, b) => a - b); const nodes: BaseNodeByContextMap[NC][] = []; const node_id_used_state: Map<CoreGraphNodeId, boolean> = new Map(); depths.forEach((depth) => { const graph_ids_for_depth = this._graph_id_by_depth.get(depth); if (graph_ids_for_depth) { graph_ids_for_depth.forEach((graph_id: CoreGraphNodeId) => { const is_present = this._graph_ids_by_shader_name.get(shaderName)?.get(graph_id); if (is_present) { const node = this._graph.nodeFromId(graph_id) as BaseNodeByContextMap[NC]; this._addNodesWithChildren(node, node_id_used_state, nodes); } }); } }); return nodes; } sortedNodes() { const depths: number[] = []; this._graph_id_by_depth.forEach((ids: CoreGraphNodeId[], depth: number) => { depths.push(depth); }); depths.sort((a, b) => a - b); const nodes: BaseNodeByContextMap[NC][] = []; const node_id_used_state: Map<CoreGraphNodeId, boolean> = new Map(); depths.forEach((depth) => { const graph_ids_for_depth = this._graph_id_by_depth.get(depth); if (graph_ids_for_depth) { for (const graph_id of graph_ids_for_depth) { const node = this._graph.nodeFromId(graph_id) as BaseNodeByContextMap[NC]; if (node) { this._addNodesWithChildren(node, node_id_used_state, nodes); } } } }); return nodes; } private _addNodesWithChildren( node: BaseNodeByContextMap[NC], node_id_used_state: Map<CoreGraphNodeId, boolean>, accumulated_nodes: BaseNodeByContextMap[NC][] // shader_name?: ShaderName ) { if (!node_id_used_state.get(node.graphNodeId())) { accumulated_nodes.push(node); node_id_used_state.set(node.graphNodeId(), true); } // if (node.type() == NetworkChildNodeType.INPUT) { // console.log('_addNodesWithChildren', node); // const parent = node.parent(); // if (parent) { // const nodes_with_same_parent_as_subnet_input = this._sortedNodesForShaderNameForParent( // parent, // shader_name // ); // for (let child_node of nodes_with_same_parent_as_subnet_input) { // if (child_node.graphNodeId() != node.graphNodeId()) { // this._addNodesWithChildren(child_node, node_id_used_state, accumulated_nodes, shader_name); // } // } // } // } } // private _sortedNodesForShaderNameForParent(parent: BaseNodeType, shader_name?: ShaderName) { // const depths: number[] = []; // this._graph_id_by_depth.forEach((value: CoreGraphNodeId[], key: number) => { // depths.push(key); // }); // depths.sort((a, b) => a - b); // const nodes: BaseNodeByContextMap[NC][] = []; // depths.forEach((depth) => { // const graph_ids_for_depth = this._graph_id_by_depth.get(depth); // if (graph_ids_for_depth) { // graph_ids_for_depth.forEach((graph_id: CoreGraphNodeId) => { // const is_present = shader_name // ? this._graph_ids_by_shader_name.get(shader_name)?.get(graph_id) // : true; // if (is_present) { // const node = this._graph.nodeFromId(graph_id) as BaseNodeByContextMap[NC]; // if (node.parent() == parent) { // nodes.push(node); // } // } // }); // } // }); // const first_node = nodes[0]; // if (parent.context() == first_node.context()) { // nodes.push(parent as BaseNodeByContextMap[NC]); // } // return nodes; // } private _findLeavesFromRootNode(rootNode: BaseNodeByContextMap[NC]) { this._graph_ids_by_shader_name.get(this._shaderName)?.set(rootNode.graphNodeId(), true); // if rootNode is a subnet, traverse its children output nodes instead if (rootNode.childrenAllowed() && this._traverseChildren()) { const outputNode = rootNode.childrenController?.outputNode() as BaseNodeByContextMap[NC] | undefined; if (outputNode) { this._findLeavesFromRootNode(outputNode); return; } } // const inputNames = this.inputNamesForShaderName(rootNode, this._shaderName); if (inputNames) { for (const inputName of inputNames) { const input = rootNode.io.inputs.named_input(inputName) as BaseNodeByContextMap[NC]; if (input) { pushOnArrayAtEntry(this._outputs_by_graph_id, input.graphNodeId(), rootNode.graphNodeId()); this._findLeaves(input); } } } this._outputs_by_graph_id.forEach((outputs: CoreGraphNodeId[], graph_id: CoreGraphNodeId) => { const uniqIds: number[] = []; arrayUniq(outputs, uniqIds); this._outputs_by_graph_id.set(graph_id, uniqIds); }); } private _blockedInputNames: Map<string, string[]> | undefined; setBlockedInputNames(nodeType: string, inputNames: string[]) { this._blockedInputNames = this._blockedInputNames || new Map(); this._blockedInputNames.set(nodeType, inputNames); } protected _findLeaves(node: BaseNodeByContextMap[NC]) { this._graph_ids_by_shader_name.get(this._shaderName)?.set(node.graphNodeId(), true); const inputs = this._findInputs(node) as BaseNodeByContextMap[NC][]; const compactInputs: BaseNodeByContextMap[NC][] = []; arrayCompact(inputs, compactInputs); const inputGraphIds: number[] = []; arrayUniq( compactInputs.map((n) => n.graphNodeId()), inputGraphIds ); const uniqueInputs = inputGraphIds.map((graph_id) => this._graph.nodeFromId(graph_id) ) as BaseNodeByContextMap[NC][]; if (uniqueInputs.length > 0) { for (const input of uniqueInputs) { pushOnArrayAtEntry(this._outputs_by_graph_id, input.graphNodeId(), node.graphNodeId()); this._findLeaves(input); } } else { this._leaves_graph_id.get(this._shaderName)!.set(node.graphNodeId(), true); } } getNodeInputs(node: BaseNodeByContextMap[NC]) { if (this._blockedInputNames == null || !this._blockedInputNames.has(node.type())) { return node.io.inputs.inputs(); } else { const blockedInputNames = this._blockedInputNames.get(node.type()) as string[]; const inputConnectionPoints = node.io.inputs.namedInputConnectionPoints() as BaseGlConnectionPoint[]; const inputConnectionPointNames = inputConnectionPoints.map((c) => c.name()); const allowedInputNames: string[] = []; arrayDifference(inputConnectionPointNames, blockedInputNames, allowedInputNames); const inputs = allowedInputNames.map((inputName) => { const inputIndex = node.io.inputs.getNamedInputIndex(inputName); return node.io.inputs.input(inputIndex); }); return inputs; } } private _findInputs(node: BaseNodeByContextMap[NC]) { if (this._traverseChildren()) { if (node.type() == NetworkChildNodeType.INPUT) { const parent = node.parent() as BaseNodeByContextMap[NC]; return parent ? this.getNodeInputs(parent) : []; // return node.parent()?.io.inputs.inputs() || []; } else { if (node.childrenAllowed()) { // this._subnets_by_id.set(node.graphNodeId(), node); const outputNode = node.childrenController?.outputNode(); return [outputNode]; } else { return this.getNodeInputs(node); //node.io.inputs.inputs(); } } } else { return this.getNodeInputs(node); //node.io.inputs.inputs(); } } private _setNodesDepth() { this._leaves_graph_id.forEach((booleans_by_graph_id, shader_name) => { booleans_by_graph_id.forEach((boolean, graph_id) => { this._setNodeDepth(graph_id); }); }); } private _setNodeDepth(graph_id: CoreGraphNodeId, depth: number = 0) { /* adjust graph depth by hierarchical depth meaning that nodes inside a subnet should add their depth to the parent (and a multiplier) so that nodes outside of a subnet do not have a depth that ends up between the depths of 2 subnet children. */ // let depth_offset = 0; // const node = this._graph.node_from_id(graph_id) as BaseNodeType; // if (node.type == NetworkChildNodeType.INPUT) { // const parent = node.parent; // if (parent) { // depth_offset = parent.children().length * 10; // } // } // depth += depth_offset; /* end hierarchical depth adjustment */ const current_depth = this._depth_by_graph_id.get(graph_id); if (current_depth != null) { this._depth_by_graph_id.set(graph_id, Math.max(current_depth, depth)); } else { this._depth_by_graph_id.set(graph_id, depth); } const output_ids = this._outputs_by_graph_id.get(graph_id); if (output_ids) { output_ids.forEach((output_id) => { this._setNodeDepth(output_id, depth + 1); }); } } }