UNPKG

polygonjs-engine

Version:

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

324 lines (298 loc) 10.6 kB
import {TypedNode, BaseNodeType} from '../../../nodes/_Base'; import {SceneJsonImporter} from '../../../io/json/import/Scene'; import {NodeContext} from '../../../poly/NodeContext'; import {NodeJsonExporterData} from '../export/Node'; import {ParamJsonImporter} from './Param'; import {Poly} from '../../../Poly'; import {OperationsComposerSopNode} from '../../../nodes/sop/OperationsComposer'; import {SopOperationContainer} from '../../../../core/operations/container/sop'; import {OPERATIONS_COMPOSER_NODE_TYPE} from '../../../../core/operations/_Base'; import {CoreType} from '../../../../core/Type'; import {PolyDictionary} from '../../../../types/GlobalTypes'; type BaseNodeTypeWithIO = TypedNode<NodeContext, any>; interface RootNodeGenericData { outputs_count: number; non_optimized_count: number; } export class OptimizedNodesJsonImporter<T extends BaseNodeTypeWithIO> { constructor(protected _node: T) {} private _nodes: BaseNodeTypeWithIO[] = []; private _optimized_root_node_names: Set<string> = new Set(); private _operation_containers_by_name: Map<string, SopOperationContainer> = new Map(); nodes() { return this._nodes; } process_data(scene_importer: SceneJsonImporter, data?: PolyDictionary<NodeJsonExporterData>) { if (!data) { return; } if (!(this._node.childrenAllowed() && this._node.childrenController)) { return; } const {optimized_names} = OptimizedNodesJsonImporter.child_names_by_optimized_state(data); this._nodes = []; this._optimized_root_node_names = new Set(); for (let node_name of optimized_names) { if (OptimizedNodesJsonImporter.is_optimized_root_node(data, node_name)) { this._optimized_root_node_names.add(node_name); } } for (let node_name of this._optimized_root_node_names) { const node_data = data[node_name]; const node = this._node.createNode(OPERATIONS_COMPOSER_NODE_TYPE); if (node) { node.setName(node_name); this._nodes.push(node); // ensure the display flag is set accordingly if (node_data.flags?.display) { node.flags?.display?.set(true); } const operation_container = this._create_operation_container( scene_importer, node as OperationsComposerSopNode, node_data, node.name() ); (node as OperationsComposerSopNode).set_output_operation_container( operation_container as SopOperationContainer ); } } for (let node of this._nodes) { const operation_container = (node as OperationsComposerSopNode).output_operation_container(); if (operation_container) { this._node_inputs = []; this._add_optimized_node_inputs( scene_importer, node as OperationsComposerSopNode, data, node.name(), operation_container ); node.io.inputs.setCount(this._node_inputs.length); for (let i = 0; i < this._node_inputs.length; i++) { node.setInput(i, this._node_inputs[i]); } } } } private _node_inputs: BaseNodeType[] = []; private _add_optimized_node_inputs( scene_importer: SceneJsonImporter, node: OperationsComposerSopNode, data: PolyDictionary<NodeJsonExporterData>, node_name: string, current_operation_container: SopOperationContainer ) { const node_data: NodeJsonExporterData = data[node_name]; const inputs_data = node_data['inputs']; if (!inputs_data) { return; } for (let input_data of inputs_data) { if (CoreType.isString(input_data)) { const input_node_data = data[input_data]; if (input_node_data) { if ( OptimizedNodesJsonImporter.is_node_optimized(input_node_data) && !this._optimized_root_node_names.has(input_data) // ensure it is not a root ) { // ensure we do not create multiple operation containers from the same node let operation_container = this._operation_containers_by_name.get(input_data); if (!operation_container) { // if the input is an optimized node, we create an operation and go recursive operation_container = this._create_operation_container( scene_importer, node, input_node_data, input_data ) as SopOperationContainer; if (operation_container) { this._add_optimized_node_inputs( scene_importer, node, data, input_data, operation_container ); } } current_operation_container.add_input(operation_container); } else { // if the input is NOT an optimized node, we set the input to the node const input_node = node.parent()?.node(input_data); if (input_node) { this._node_inputs.push(input_node); const node_input_index = this._node_inputs.length - 1; // node.setInput(node_input_index, input_node as BaseSopNodeType); node.add_input_config(current_operation_container, { operation_input_index: current_operation_container.current_input_index(), node_input_index: node_input_index, }); current_operation_container.increment_input_index(); } } } } } // once the inputs have been set, we can initialize the inputs_clone_state if (node_data.cloned_state_overriden == true) { current_operation_container.override_input_clone_state(node_data.cloned_state_overriden); } } static child_names_by_optimized_state(data: PolyDictionary<NodeJsonExporterData>) { const node_names = Object.keys(data); const optimized_names: string[] = []; const non_optimized_names: string[] = []; for (let node_name of node_names) { const node_data = data[node_name]; const optimized_state = Poly.playerMode() && this.is_node_optimized(node_data); if (optimized_state) { optimized_names.push(node_name); } else { non_optimized_names.push(node_name); } } return {optimized_names, non_optimized_names}; } // private _optimized_names_for_root( // data: PolyDictionary<NodeJsonExporterData>, // current_node_name: string, // current_node_data: NodeJsonExporterData, // input_names: string[] = [] // ) { // input_names.push(current_node_name); // const inputs = current_node_data['inputs']; // if (inputs) { // for (let input_data of inputs) { // if (CoreType.isString(input_data)) { // const input_node_name = input_data; // // if (input_node_name != current_node_name) { // const input_node_data = data[input_node_name]; // if (input_node_data) { // if ( // OptimizedNodesJsonImporter.is_node_optimized(input_node_data) && // !this._is_optimized_root_node(data, input_node_name, input_node_data) // ) { // this._optimized_names_for_root(data, input_node_name, input_node_data, input_names); // } // } // // } // } // } // } // return input_names; // } // a node will be considered optimized root node if: // - it has no output // - at least one output is not optimized (as it if it has 2 outputs, and only 1 is optimized, it will not be considered root) static is_optimized_root_node_generic(data: RootNodeGenericData): boolean { if (data.outputs_count == 0) { return true; } if (data.non_optimized_count > 0) { return true; } return false; } static is_optimized_root_node(data: PolyDictionary<NodeJsonExporterData>, current_node_name: string) { const output_names = this.node_outputs(data, current_node_name); let non_optimized_count = 0; output_names.forEach((node_name) => { const node_data = data[node_name]; if (!this.is_node_optimized(node_data)) { non_optimized_count++; } }); return this.is_optimized_root_node_generic({ outputs_count: output_names.size, non_optimized_count: non_optimized_count, }); } // same algo as is_optimized_root_node, but for a node static is_optimized_root_node_from_node<NC extends NodeContext>(node: TypedNode<NC, any>) { if (!node.flags?.optimize?.active()) { return false; } const output_nodes = node.io.connections.output_connections().map((c) => c.node_dest); let non_optimized_count = 0; for (let output_node of output_nodes) { if (!output_node.flags?.optimize?.active()) { non_optimized_count++; } } return this.is_optimized_root_node_generic({ outputs_count: output_nodes.length, non_optimized_count: non_optimized_count, }); } static node_outputs( data: PolyDictionary<NodeJsonExporterData>, current_node_name: string // current_node_data: NodeJsonExporterData ) { const node_names = Object.keys(data); const output_node_names: Set<string> = new Set(); for (let node_name of node_names) { if (node_name != current_node_name) { const node_data = data[node_name]; const inputs = node_data['inputs']; if (inputs) { for (let input_data of inputs) { if (CoreType.isString(input_data)) { const input_node_name = input_data; if (input_node_name == current_node_name) { output_node_names.add(node_name); } } } } } } return output_node_names; } private _create_operation_container( scene_importer: SceneJsonImporter, node: OperationsComposerSopNode, node_data: NodeJsonExporterData, node_name: string ) { const non_spare_params_data = ParamJsonImporter.non_spare_params_data_value(node_data['params']); const operation_type = OptimizedNodesJsonImporter.operation_type(node_data); const operation_container = this._node.create_operation_container( operation_type, node_name, non_spare_params_data ) as SopOperationContainer; if (operation_container) { // ensure we do not create another operation container from the same node this._operation_containers_by_name.set(node_name, operation_container); // store for path_param resolve when all nodes are created if (operation_container.path_param_resolve_required()) { node.add_operation_container_with_path_param_resolve_required(operation_container); scene_importer.add_operations_composer_node_with_path_param_resolve_required(node); } } return operation_container; } static operation_type(node_data: NodeJsonExporterData) { if (OptimizedNodesJsonImporter.is_node_bypassed(node_data)) { return 'null'; } return node_data['type']; } static is_node_optimized(node_data: NodeJsonExporterData) { const node_flags = node_data['flags']; if (node_flags && node_flags['optimize']) { return true; } return false; } static is_node_bypassed(node_data: NodeJsonExporterData) { const node_flags = node_data['flags']; if (node_flags && node_flags['bypass']) { return true; } return false; } }