@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
167 lines (157 loc) • 6.42 kB
text/typescript
import type {TypedNode, BaseNodeType} from '../../../nodes/_Base';
import type {JsonImportDispatcher} from './Dispatcher';
import type {SceneJsonImporter} from '../../../io/json/import/Scene';
import {NodeContext} from '../../../poly/NodeContext';
import type {NodeJsonExporterData} from '../export/Node';
import {ParamJsonImporter} from './Param';
import {OptimizedNodesJsonImporter} from './OptimizedNodes';
import type {PolyNodeJsonImporter} from './nodes/Poly';
import {Poly} from '../../../Poly';
import type {NodeJsonImporter} from './Node';
import {CoreString} from '../../../../core/String';
import {PolyDictionary} from '../../../../types/GlobalTypes';
import type {NodeCreateOptions} from '../../../nodes/utils/hierarchy/ChildrenController';
type BaseNodeTypeWithIO = TypedNode<NodeContext, any>;
export class NodesJsonImporter<T extends BaseNodeTypeWithIO> {
constructor(protected _node: T, protected dispatcher: JsonImportDispatcher) {}
process_data(sceneImporter: SceneJsonImporter, data?: PolyDictionary<NodeJsonExporterData>) {
if (!data) {
return;
}
if (!(this._node.childrenAllowed() && this._node.childrenController)) {
return;
}
const {optimized_names, non_optimized_names} = OptimizedNodesJsonImporter.child_names_by_optimized_state(data);
const nonOptimizedNodes: BaseNodeTypeWithIO[] = [];
const migrateHelper = sceneImporter.migrateHelper();
for (let nodeName of non_optimized_names) {
const node_data = data[nodeName];
const nodeType = migrateHelper ? migrateHelper.migrateNodeType(this._node, node_data) : node_data.type;
migrateHelper?.migrateParams(this._node, node_data);
const paramsInitValueOverrides = ParamJsonImporter.non_spare_params_data_value(node_data['params']);
const nodeCreateOptions: NodeCreateOptions = {
paramsInitValueOverrides,
nodeName,
};
const loadNodeAttempt = (nodeType: string, nodeCreateOptions: NodeCreateOptions) => {
try {
// try with current type
const node = this._node.createNode(nodeType, nodeCreateOptions);
if (node) {
return node;
}
} catch (e) {
console.error(`error importing node: cannot create with type ${nodeType}`, e);
}
};
let node = loadNodeAttempt(nodeType, nodeCreateOptions);
if (!node) {
// try with camelCased type
const nodeTypeCamelCase = CoreString.camelCase(nodeType);
node = loadNodeAttempt(nodeTypeCamelCase, nodeCreateOptions);
}
if (!node) {
// add Network
const nodeTypeWithNetwork = `${nodeType}Network`;
node = loadNodeAttempt(nodeTypeWithNetwork, nodeCreateOptions);
}
if (node) {
nonOptimizedNodes.push(node);
} else {
const message = `failed to create node with type '${nodeType}' (in '${this._node.path()}')`;
sceneImporter.report.addWarning(message);
Poly.warn(message);
}
// try {
// // try with current type
// const node = this._node.createNode(nodeType, nodeCreateOptions);
// if (node) {
// // node.setName(node_name);
// nonOptimizedNodes.push(node);
// }
// } catch (e) {
// // try with camelCased type
// console.error(`error importing node: cannot create with type ${nodeType}`, e);
// const nodeTypeCamelCase = CoreString.camelCase(nodeType);
// try {
// const node = this._node.createNode(nodeTypeCamelCase, nodeCreateOptions);
// if (node) {
// // node.setName(node_name);
// nonOptimizedNodes.push(node);
// }
// } catch (e) {
// // add Network
// const nodeTypeWithNetwork = `${nodeType}Network`;
// try {
// const node = this._node.createNode(nodeTypeWithNetwork, nodeCreateOptions);
// if (node) {
// // node.setName(node_name);
// nonOptimizedNodes.push(node);
// }
// } catch (e) {
// const message = `failed to create node with type '${nodeType}', '${nodeTypeCamelCase}' or '${nodeTypeWithNetwork}'`;
// scene_importer.report.addWarning(message);
// Poly.warn(message, e);
// }
// }
// }
}
if (optimized_names.length > 0) {
const optimized_nodes_importer = new OptimizedNodesJsonImporter(this._node);
optimized_nodes_importer.process_data(sceneImporter, data);
// ensure that the display node is still created
// as it may not be if the display flag is set to a node that will
// be part of an optimized series of nodes
// for instance
// A -> B > C
// D -> E
// if A, B and C are optimized,
// and D, E are not
// And B has the display flag,
// what will happen is that B will not exist anymore
// and the display flag will end up in either C, D or E
// which can lead to unexpected display in the player
if (this._node.childrenController.context == NodeContext.SOP) {
const nodeNames = Object.keys(data);
let nodeNameWithDisplayFlag: string | undefined = undefined;
for (let nodeName of nodeNames) {
const nodeData = data[nodeName];
if (nodeData.flags?.display) {
nodeNameWithDisplayFlag = nodeName;
}
}
if (nodeNameWithDisplayFlag) {
const existingNodeNames = nonOptimizedNodes.map((n) => n.name());
const optimizedNodes = optimized_nodes_importer.nodes();
for (let optimizedNode of optimizedNodes) {
existingNodeNames.push(optimizedNode.name());
}
if (!existingNodeNames.includes(nodeNameWithDisplayFlag)) {
const parentFullPath = this._node.path();
const nodeFullPath = `${parentFullPath}/${nodeNameWithDisplayFlag}`;
const message = `node '${nodeFullPath}' with display flag has been optimized and does not exist in player mode`;
console.error(message);
}
}
}
}
const importers_by_node_name: Map<string, PolyNodeJsonImporter | NodeJsonImporter<BaseNodeType>> = new Map();
for (let node of nonOptimizedNodes) {
const child_data = data[node.name()];
if (child_data) {
const importer = this.dispatcher.dispatchNode(node);
importers_by_node_name.set(node.name(), importer);
importer.process_data(sceneImporter, data[node.name()]);
} else {
Poly.warn(`possible import error for node ${node.name()}`);
Poly.log('available names are', Object.keys(data).sort(), data);
}
}
for (let node of nonOptimizedNodes) {
const importer = importers_by_node_name.get(node.name());
if (importer) {
importer.process_inputs_data(sceneImporter, data[node.name()]);
}
}
}
}