polygonjs-engine
Version:
node-based webgl 3D engine https://polygonjs.com
301 lines (272 loc) • 10.2 kB
text/typescript
import {CoreGraph} from '../../../../core/graph/CoreGraph';
import {MapUtils} from '../../../../core/MapUtils';
import {ShaderName} from './ShaderName';
import {TypedNode, BaseNodeType} from '../../_Base';
import {NodeContext, NetworkChildNodeType} from '../../../poly/NodeContext';
import {NodeTypeMap} from '../../../containers/utils/ContainerMap';
import {CoreGraphNodeId} from '../../../../core/graph/CoreGraph';
import {ArrayUtils} 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 = Map<ShaderName, BooleanByCoreGraphNodeId>;
type StringArrayByString = Map<CoreGraphNodeId, CoreGraphNodeId[]>;
type InputNamesByShaderNameMethod<NC extends NodeContext> = (
root_node: NodeTypeMap[NC],
shader_name: ShaderName
) => string[];
export class TypedNodeTraverser<NC extends NodeContext> {
private _leaves_graph_id: BooleanByStringByShaderName = new Map();
private _graph_ids_by_shader_name: BooleanByStringByShaderName = 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();
private _graph: CoreGraph;
private _shader_name!: ShaderName;
// private _subnets_by_id: BaseNodeTypeByString = new Map();
constructor(
private _parent_node: TypedNode<NC, any>,
private _shader_names: ShaderName[],
private _input_names_for_shader_name_method: InputNamesByShaderNameMethod<NC>
) {
this._graph = this._parent_node.scene().graph;
}
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());
});
}
shader_names() {
return this._shader_names;
}
input_names_for_shader_name(root_node: NodeTypeMap[NC], shader_name: ShaderName) {
return this._input_names_for_shader_name_method(root_node, shader_name);
}
traverse(root_nodes: NodeTypeMap[NC][]) {
this.reset();
for (let shader_name of this.shader_names()) {
this._leaves_graph_id.set(shader_name, new Map());
}
for (let shader_name of this.shader_names()) {
this._shader_name = shader_name;
for (let root_node of root_nodes) {
this.find_leaves_from_root_node(root_node);
this.set_nodes_depth();
}
}
// 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);
MapUtils.push_on_array_at_entry(this._graph_id_by_depth, depth, graph_id);
}
});
}
leaves_from_nodes(nodes: NodeTypeMap[NC][]) {
this._shader_name = ShaderName.LEAVES_FROM_NODES_SHADER;
this._graph_ids_by_shader_name.set(this._shader_name, new Map());
this._leaves_graph_id.set(this._shader_name, new Map());
for (let node of nodes) {
this.find_leaves(node);
}
const node_ids: CoreGraphNodeId[] = [];
this._leaves_graph_id.get(this._shader_name)?.forEach((value: boolean, key: CoreGraphNodeId) => {
node_ids.push(key);
});
return this._graph.nodes_from_ids(node_ids) as NodeTypeMap[NC][];
}
nodes_for_shader_name(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: NodeTypeMap[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(shader_name)?.get(graph_id);
if (is_present) {
const node = this._graph.node_from_id(graph_id) as NodeTypeMap[NC];
this.add_nodes_with_children(node, node_id_used_state, nodes, shader_name);
}
});
}
});
return nodes;
}
sorted_nodes() {
const depths: number[] = [];
this._graph_id_by_depth.forEach((ids: CoreGraphNodeId[], depth: number) => {
depths.push(depth);
});
depths.sort((a, b) => a - b);
const nodes: NodeTypeMap[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 (let graph_id of graph_ids_for_depth) {
const node = this._graph.node_from_id(graph_id) as NodeTypeMap[NC];
if (node) {
this.add_nodes_with_children(node, node_id_used_state, nodes);
}
}
}
});
return nodes;
}
add_nodes_with_children(
node: NodeTypeMap[NC],
node_id_used_state: Map<CoreGraphNodeId, boolean>,
accumulated_nodes: NodeTypeMap[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) {
const parent = node.parent();
if (parent) {
const nodes_with_same_parent_as_subnet_input = this.sorted_nodes_for_shader_name_for_parent(
parent,
shader_name
);
for (let child_node of nodes_with_same_parent_as_subnet_input) {
if (child_node.graphNodeId() != node.graphNodeId()) {
this.add_nodes_with_children(child_node, node_id_used_state, accumulated_nodes, shader_name);
}
}
}
}
}
sorted_nodes_for_shader_name_for_parent(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: NodeTypeMap[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.node_from_id(graph_id) as NodeTypeMap[NC];
if (node.parent() == parent) {
nodes.push(node);
}
}
});
}
});
const first_node = nodes[0];
if (parent.nodeContext() == first_node.nodeContext()) {
nodes.push(parent as NodeTypeMap[NC]);
}
return nodes;
}
private find_leaves_from_root_node(root_node: NodeTypeMap[NC]) {
this._graph_ids_by_shader_name.get(this._shader_name)?.set(root_node.graphNodeId(), true);
const input_names = this.input_names_for_shader_name(root_node, this._shader_name);
if (input_names) {
for (let input_name of input_names) {
const input = root_node.io.inputs.named_input(input_name) as NodeTypeMap[NC];
if (input) {
MapUtils.push_on_array_at_entry(
this._outputs_by_graph_id,
input.graphNodeId(),
root_node.graphNodeId()
);
this.find_leaves(input);
}
}
}
this._outputs_by_graph_id.forEach((outputs: CoreGraphNodeId[], graph_id: CoreGraphNodeId) => {
this._outputs_by_graph_id.set(graph_id, ArrayUtils.uniq(outputs));
});
}
private find_leaves(node: NodeTypeMap[NC]) {
this._graph_ids_by_shader_name.get(this._shader_name)?.set(node.graphNodeId(), true);
const inputs = this._find_inputs_or_children(node) as NodeTypeMap[NC][];
const compact_inputs: NodeTypeMap[NC][] = ArrayUtils.compact(inputs);
const input_graph_ids = ArrayUtils.uniq(compact_inputs.map((n) => n.graphNodeId()));
const unique_inputs = input_graph_ids.map((graph_id) =>
this._graph.node_from_id(graph_id)
) as NodeTypeMap[NC][];
if (unique_inputs.length > 0) {
for (let input of unique_inputs) {
MapUtils.push_on_array_at_entry(this._outputs_by_graph_id, input.graphNodeId(), node.graphNodeId());
this.find_leaves(input);
}
} else {
this._leaves_graph_id.get(this._shader_name)!.set(node.graphNodeId(), true);
}
}
private _find_inputs_or_children(node: NodeTypeMap[NC]) {
if (node.type() == NetworkChildNodeType.INPUT) {
return node.parent()?.io.inputs.inputs() || [];
} else {
if (node.childrenAllowed()) {
// this._subnets_by_id.set(node.graphNodeId(), node);
const output_node = node.childrenController?.output_node();
return [output_node];
} else {
return node.io.inputs.inputs();
}
}
}
private set_nodes_depth() {
this._leaves_graph_id.forEach((booleans_by_graph_id, shader_name) => {
booleans_by_graph_id.forEach((boolean, graph_id) => {
this.set_node_depth(graph_id);
});
});
}
private set_node_depth(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.set_node_depth(output_id, depth + 1);
});
}
}
}