UNPKG

@polygonjs/polygonjs

Version:

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

448 lines (407 loc) 14.4 kB
import {TypedNode} from '../../../nodes/_Base'; import {Vector2} from 'three'; import type {JsonImportDispatcher} from './Dispatcher'; import {ParamType} from '../../../poly/ParamType'; import {ParamsUpdateOptions} from '../../../nodes/utils/params/ParamsController'; import {SceneJsonImporter} from '../../../io/json/import/Scene'; import {NodeContext} from '../../../poly/NodeContext'; import {NodeJsonExporterData, NodeJsonExporterUIData, InputData, IoConnectionPointsData} from '../export/Node'; import { ParamJsonExporterData, SimpleParamJsonExporterData, ComplexParamJsonExporterData, } from '../../../nodes/utils/io/IOController'; import type {NodesJsonImporter} from './Nodes'; import {Poly} from '../../../Poly'; import { isString,isNumber,isArray,isBoolean,isObject} from '../../../../core/Type'; // import {CoreString} from '../../../../core/String'; import {PolyDictionary} from '../../../../types/GlobalTypes'; import {VelocityColliderFunctionBody} from '../../../nodes/js/code/assemblers/_Base'; const COMPLEX_PARAM_DATA_KEYS: Readonly<string[]> = ['overriden_options', 'type']; type BaseNodeTypeWithIO = TypedNode<NodeContext, any>; export class NodeJsonImporter<T extends BaseNodeTypeWithIO> { constructor( protected _node: T, private dispatcher: JsonImportDispatcher, protected nodesImporter: NodesJsonImporter<any> ) {} process_data(scene_importer: SceneJsonImporter, data: NodeJsonExporterData) { if (this._node.sceneReadonly()) { scene_importer.report.markAsLoadedWithoutAssemblers(); } this.set_connection_points(data['connection_points']); // rather than having the children creation dependent on the persisted config and player mode, use the childrenAllowed() method // const skip_create_children = Poly.playerMode() && data.persisted_config; if (this._node.childrenAllowed()) { this.create_nodes(scene_importer, data['nodes'], data); } // inputs clone if (this._node.io.inputs.overrideClonedStateAllowed()) { const override = data['cloned_state_overriden']; if (override) { this._node.io.inputs.overrideClonedState(override); } } this.set_flags(data); // params // const spare_params_data = ParamJsonImporter.spare_params_data(data['params']); // this.set_params(spare_params_data); this.set_params(data); if (data.persisted_config) { const shadersData = scene_importer.shadersData(); const jsFunctionBodiesData = scene_importer.jsFunctionBodiesData(); if (shadersData) { let shaders = shadersData[this._node.path()]; // make sure this is never undefined if (!shaders) { shaders = {}; } data.persisted_config.shaders = shaders; } if (jsFunctionBodiesData) { const jsFunctionBodyData = jsFunctionBodiesData[this._node.path()]; if (jsFunctionBodyData) { if (isString(jsFunctionBodyData)) { data.persisted_config.functionBody = jsFunctionBodyData; } else { data.persisted_config.functionBody = jsFunctionBodyData as any as VelocityColliderFunctionBody; } } } this.set_persisted_config(data.persisted_config); } this.setCustomData(data); // already called in create_node() // this._node.lifecycle.set_creation_completed(); } process_inputs_data(scene_importer: SceneJsonImporter, data: NodeJsonExporterData) { const maxInputsCount = data.maxInputsCount; if (maxInputsCount != null) { const minCount = this._node.io.inputs.minCount(); this._node.io.inputs.setCount(minCount, maxInputsCount); } try { this.setInputs(data['inputs']); } catch (err) { const message = (err as ErrorEvent).message || `failed connecting inputs of node ${data['type']}`; scene_importer.report.addWarning(message); console.warn(data['inputs']); } } process_ui_data(scene_importer: SceneJsonImporter, data: NodeJsonExporterUIData) { if (!data) { return; } if (Poly.playerMode()) { return; } const ui_data = this._node.uiData; const pos = data['pos']; if (pos) { const vector = new Vector2().fromArray(pos); ui_data.setPosition(vector); } const comment = data['comment']; if (comment) { ui_data.setComment(comment); } const selection = data['selection']; if (selection) { this.set_selection(selection); } if (this._node.childrenAllowed()) { const nodesData = data['nodes']; if (nodesData) { this.processNodesUiData(scene_importer, nodesData); } } } create_nodes( scene_importer: SceneJsonImporter, data: PolyDictionary<NodeJsonExporterData> | undefined, nodeData: NodeJsonExporterData ) { if (!data) { return; } // const nodes_importer = new NodesJsonImporter(this._node); this.nodesImporter.process_data(scene_importer, data); } set_selection(data?: string[]) { if (this._node.childrenAllowed() && this._node.childrenController) { if (data && data.length > 0) { const selected_nodes: BaseNodeTypeWithIO[] = []; data.forEach((node_name) => { const node = this._node.node(node_name); if (node) { selected_nodes.push(node); } }); this._node.childrenController.selection.set(selected_nodes); } } } set_flags(data: NodeJsonExporterData) { const flags = data['flags']; if (flags) { const bypass = flags['bypass']; if (bypass != null) { this._node.flags?.bypass?.set(bypass); } const display = flags['display']; if (display != null) { this._node.flags?.display?.set(display); } const optimize = flags['optimize']; if (optimize != null) { this._node.flags?.optimize?.set(optimize); } } } set_connection_points(connection_points_data: IoConnectionPointsData | undefined) { if (!connection_points_data) { return; } if (connection_points_data['in']) { this._node.io.saved_connection_points_data.set_in(connection_points_data['in']); } if (connection_points_data['out']) { this._node.io.saved_connection_points_data.set_out(connection_points_data['out']); } if (this._node.io.has_connection_points_controller) { this._node.io.connection_points.update_signature_if_required(); } } private setInputs(inputsData?: InputData[]) { if (!inputsData) { return; } let inputData: InputData; for (let i = 0; i < inputsData.length; i++) { inputData = inputsData[i]; if (inputData && this._node.parent()) { if (isString(inputData)) { const inputNodeName = inputData; const inputNode = this._node.nodeSibling(inputNodeName); this._node.setInput(i, inputNode); } else { const inputNode = this._node.nodeSibling(inputData['node']); let inputIndex = inputData['index']; const inputName = inputData['inputName']; let outputName = inputData['output']; if (inputName != null) { // If we have inputName, try and find the input index matching it. // If we find nothing, we use inputIndex const connectionPoints = this._node.io.inputs.namedInputConnectionPoints(); if (connectionPoints) { const connectionPointIndex = connectionPoints .map((point) => point?.name().toLowerCase()) .indexOf(inputName.toLowerCase()); if (connectionPointIndex >= 0) { inputIndex = connectionPointIndex; } } } if (inputNode != null && outputName != null) { // If we have outtputName, try and find the input index matching it. const connectionPoints = inputNode.io.outputs.namedOutputConnectionPoints(); if (connectionPoints) { for (let connectionPoint of connectionPoints) { if (connectionPoint) { if (connectionPoint.name().toLowerCase() == outputName.toLowerCase()) { outputName = connectionPoint.name(); } } } } } if (inputIndex != null) { this._node.setInput(inputIndex, inputNode, outputName); } } } } } processNodesUiData(scene_importer: SceneJsonImporter, data: PolyDictionary<NodeJsonExporterUIData>) { if (!data) { return; } if (Poly.playerMode()) { return; } const node_names = Object.keys(data); for (let node_name of node_names) { const node = this._node.node(node_name); if (node) { const node_data = data[node_name]; this.dispatcher.dispatchNode(node).process_ui_data(scene_importer, node_data); // node.visit(JsonImporterVisitor).process_ui_data(node_data); } } } // // // PARAMS // // set_params(nodeData: NodeJsonExporterData) { const data = nodeData['params']; if (!data) { return; } const param_names = Object.keys(data); const params_update_options: ParamsUpdateOptions = {}; for (let param_name of param_names) { const param_data = data[param_name] as ComplexParamJsonExporterData<ParamType>; if (param_data != null) { const options = param_data['options']; // const is_spare = options && options['spare'] === true; // make camelCase if required // if (false && param_name.includes('_')) { // param_name = CoreString.camelCase(param_name); // } let param_type = param_data['type']!; const has_param = this._node.params.has_param(param_name); let has_param_and_same_type = false; let param; if (has_param) { param = this._node.params.get(param_name); // we can safely consider same type if param_type is not mentioned if ((param && param.type() == param_type) || param_type == null) { has_param_and_same_type = true; } } if (has_param_and_same_type) { if (this._is_param_data_complex(param_data)) { this._process_param_data_complex(param_name, param_data); } else { this._process_param_data_simple( param_name, param_data as SimpleParamJsonExporterData<ParamType> ); } } else { // it the param is a spare one, // we check if it is currently exists with same type first. // - if it is, we only update the value // - if it's not, we delete it and add it again params_update_options.namesToDelete = params_update_options.namesToDelete || []; params_update_options.namesToDelete.push(param_name); params_update_options.toAdd = params_update_options.toAdd || []; params_update_options.toAdd.push({ name: param_name, type: param_type, initValue: param_data['default_value'] as any, rawInput: param_data['raw_input'] as any, options: options, }); // if (options && param_type) { // if (param_data['default_value']) { // if (has_param) { // this._node.params.delete_param(param_name); // } // param = this._node.add_param(param_type, param_name, param_data['default_value'], options); // if (param) { // JsonImportDispatcher.dispatch_param(param).process_data(param_data); // } // } // } } } } // delete and create the spare params we need to const params_delete_required = params_update_options.namesToDelete && params_update_options.namesToDelete.length > 0; const params_add_required = params_update_options.toAdd && params_update_options.toAdd.length > 0; if (params_delete_required || params_add_required) { this._node.params.updateParams(params_update_options); // update them based on the imported data for (let spare_param of this._node.params.spare) { const param_data = data[spare_param.name()] as ComplexParamJsonExporterData<ParamType>; // JsonImportDispatcher.dispatch_param(spare_param).process_data(param_data); if (!spare_param.parentParam() && param_data) { if (this._is_param_data_complex(param_data)) { this._process_param_data_complex(spare_param.name(), param_data); } else { this._process_param_data_simple( spare_param.name(), param_data as SimpleParamJsonExporterData<ParamType> ); } } } } // those hooks are useful for some gl nodes, // such as the constant, which needs to update its connections // based on another parameter, which will be set just before this._node.params.runOnSceneLoadHooks(); } private _process_param_data_simple(param_name: string, param_data: SimpleParamJsonExporterData<ParamType>) { this._node.params.get(param_name)?.set(param_data); } private _process_param_data_complex(param_name: string, param_data: ComplexParamJsonExporterData<ParamType>) { const param = this._node.params.get(param_name); if (param) { this.dispatcher.dispatchParam(param).process_data(param_data); } // return // const has_param = this._node.params.has_param(param_name); // const param_type = param_data['type']!; // let has_param_and_same_type = false; // let param; // if (has_param) { // param = this._node.params.get(param_name); // // we can safely consider same type if param_type is not mentioned // if ((param && param.type == param_type) || param_type == null) { // has_param_and_same_type = true; // } // } // if (has_param_and_same_type) { // param = this._node.params.get(param_name); // if (param) { // JsonImportDispatcher.dispatch_param(param).process_data(param_data); // // param.visit(JsonImporterVisitor).process_data(param_data); // } // } else { // const options = param_data['options']; // if (options && param_type) { // const is_spare = options['spare'] === true; // if (is_spare && param_data['default_value']) { // if (has_param) { // this._node.params.delete_param(param_name); // } // param = this._node.add_param(param_type, param_name, param_data['default_value'], options); // if (param) { // JsonImportDispatcher.dispatch_param(param).process_data(param_data); // } // } // } // } } private _is_param_data_complex(param_data: ParamJsonExporterData<ParamType>): boolean { // we can test here most param value serialized, except for ramp if ( isString(param_data) || isNumber(param_data) || isArray(param_data) || isBoolean(param_data) ) { return false; } if (isObject(param_data)) { const keys = Object.keys(param_data); for (let complex_key of COMPLEX_PARAM_DATA_KEYS) { if (keys.includes(complex_key)) { return true; } } } return false; } set_persisted_config(persisted_config_data: object) { if (this._node.persisted_config) { this._node.persisted_config.load(persisted_config_data); } } setCustomData(data: NodeJsonExporterData) {} }