UNPKG

@polygonjs/polygonjs

Version:

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

240 lines (220 loc) 8 kB
import {BaseNodeType} from './../../_Base'; import {NodeJsonExporterData} from './../../../io/json/export/Node'; import {NodeEvent} from './../../../poly/NodeEvent'; import {NodeContext} from '../../../poly/NodeContext'; import {SceneJsonImporter} from '../../../io/json/import/Scene'; import {JsonExportDispatcher} from '../../../io/json/export/Dispatcher'; import {NodeParamsConfig, ParamTemplate} from '../params/ParamsConfig'; import {PolyNodeDefinition, PolyNodesInputsData} from './PolyNodeDefinition'; import {PolyNodeClassByContext} from './PolyNodeClassByContext'; import {Poly} from '../../../Poly'; import {ParamOptionToAdd} from '../params/ParamsController'; import {ParamType} from '../../../poly/ParamType'; import {PolyNodeDataRegister} from './PolyNodeDataRegister'; import {arrayCompact} from '../../../../core/ArrayUtils'; import {NodeInputsController} from '../io/InputsController'; import {JsonImportDispatcher} from '../../../io/json/import/Dispatcher'; // import {createPolyAnimNode} from '../../anim/utils/poly/createPolyAnimNode'; // import {createPolyGlNode} from '../../gl/utils/poly/createPolyGlNode'; // import {createPolyObjNode} from '../../obj/utils/poly/createPolyObjNode'; // import {createPolySopNode} from '../../sop/utils/poly/createPolySopNode'; import {BaseGlConnectionPoint} from '../io/connections/Gl'; // export const IS_POLY_NODE_BOOLEAN = 'isPolyNode'; type CreatePolyNodeFunction = ( nodeType: string, definition: PolyNodeDefinition, polyNodeControllerClass: typeof PolyNodeController ) => any; export class PolyNodeController { private static _createPolyNodeFunctionByContext: Map<NodeContext, CreatePolyNodeFunction> = new Map(); static registerCreatePolyNodeFunctionForContext( context: NodeContext, createPolyNodeFunction: CreatePolyNodeFunction ) { this._createPolyNodeFunctionByContext.set(context, createPolyNodeFunction); } private static _definitionRegister: Map<NodeContext, Map<string, PolyNodeDefinition>> = new Map(); private _locked = true; constructor(private node: BaseNodeType, private _definition: PolyNodeDefinition) {} initializeNode() { this._initInputs(); // add hooks // this.node.params.onParamsCreated('PolyNodeInit', () => { // this._createParamsFromDefinition(); // }); this.node.lifecycle.onAfterCreated(() => { // this._createParamsFromDefinition(); this.createChildNodesFromDefinition(); }); } locked() { return this._locked; } setLockedState(state: boolean) { if (state == this._locked) { return; } this._locked = state; this.node.emit(NodeEvent.POLY_NODE_LOCK_STATE_UPDATED); } private _initInputs() { const inputsData = this._definition.inputs; if (!inputsData) { return; } const simpleData = inputsData.simple; if (simpleData) { this.node.io.inputs.setCount(simpleData.min, simpleData.max); } } // private _createParamsFromDefinition() { // const paramsData = this._definition.params; // if (!paramsData) { // return; // } // for (let paramData of paramsData) { // paramData.options = paramData.options || {}; // paramData.options.spare = true; // } // this.node.params.updateParams({toAdd: paramsData}); // } static setupParamsConfig(paramsConfig: NodeParamsConfig, data: PolyNodeDefinition) { if (!data.params) { return; } for (const paramData of data.params) { const paramName = paramData.name; const paramType = paramData.type; const initValue = paramData.initValue; const options = paramData.options; (paramsConfig as any)[paramName] = new ParamTemplate(paramType, initValue, options); //ParamConfig.STRING('aa'); } } createChildNodesFromDefinition() { const childrenData = this._definition.nodes; if (!childrenData) { return; } // TODO: this is to avoid creating gl globals and output nodes // but there should be a better way, on a per-node basis. // Especially since it can create problem when loading a scene with gl builders // as those may trigger the creation of globals and output nodes too early, resulting in a broken load const currentSceneLoadedState: boolean = this.node.scene().loadingController.loaded(); if (currentSceneLoadedState) { this.node.scene().loadingController.markAsLoading(); } const oldLockedState = this.locked(); this.setLockedState(false); const sceneImporter = new SceneJsonImporter({}); const dispatcher = new JsonImportDispatcher(); const nodeImporter = dispatcher.dispatchNonPolyNode(this.node); const exporterData: NodeJsonExporterData = { // ...this._definition, type: this.node.type(), polyNode: { locked: true, }, }; nodeImporter.create_nodes(sceneImporter, childrenData, exporterData); const uiData = this._definition.ui; if (uiData) { nodeImporter.processNodesUiData(sceneImporter, uiData); } if (currentSceneLoadedState) { this.node.scene().loadingController.markAsLoaded(); } this.setLockedState(oldLockedState); } static inputsData(node: BaseNodeType): PolyNodesInputsData { if (node.io.inputs.hasNamedInputs()) { const inputs = node.io.inputs as NodeInputsController<NodeContext.GL>; const connectionPoints: BaseGlConnectionPoint[] = []; const namedInputConnectionPoints = inputs.namedInputConnectionPoints(); if (namedInputConnectionPoints) { arrayCompact(namedInputConnectionPoints, connectionPoints); } return { typed: { types: connectionPoints.map((cp) => { return { name: cp.name(), type: cp.type(), }; }), }, }; } else { return { simple: { min: node.io.inputs.minCount(), max: node.io.inputs.maxInputsCount(), names: node.displayedInputNames(), }, }; } } static async polyNodeData(node: BaseNodeType, inputsData?: PolyNodesInputsData): Promise<PolyNodeDefinition> { const dispatcher = new JsonExportDispatcher(); const rootExporter = dispatcher.dispatchNode(node); const nodesData = await rootExporter.data({showPolyNodesData: true}); const uiData = rootExporter.uiData({showPolyNodesData: true}); const nodeInputsData = inputsData || this.inputsData(node); const data: PolyNodeDefinition = { metadata: { version: { polygonjs: '1', }, createdAt: 1, }, nodeContext: node.context(), inputs: nodeInputsData, params: node.params.non_spare .filter((p) => p.parentParam() == null) .map((param) => { const paramData: ParamOptionToAdd<ParamType> = { name: param.name(), type: param.type(), initValue: param.defaultValueSerialized(), rawInput: param.rawInputSerialized(), options: param.options.current(), }; return paramData; }), nodes: nodesData.nodes, ui: uiData.nodes, }; return data; } static _createNodeClass<NC extends NodeContext>( nodeContext: NC, nodeType: string, data: PolyNodeDefinition ): PolyNodeClassByContext[NC] | undefined { const createFunction = this._createPolyNodeFunctionByContext.get(nodeContext); if (!createFunction) { return; } return createFunction(nodeType, data, PolyNodeController) as any; } static createNodeClassAndRegister<NC extends NodeContext>(dataRegister: PolyNodeDataRegister<NC>) { const {node_context, node_type, data} = dataRegister; const nodeClass = this._createNodeClass(node_context, node_type, data); if (nodeClass) { let registerMapForContext = this._definitionRegister.get(node_context); if (!registerMapForContext) { registerMapForContext = new Map(); this._definitionRegister.set(node_context, registerMapForContext); } registerMapForContext.set(node_type, data); Poly.registerNode(nodeClass, 'polyNodes', {polyNode: true}); } else { console.warn('failed to create node from definition', node_context, node_type, data); } } static definition<NC extends NodeContext>(context: NC, type: string) { return this._definitionRegister.get(context)?.get(type); } static register() { return this._definitionRegister; } }