UNPKG

@polygonjs/polygonjs

Version:

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

201 lines (183 loc) 6.36 kB
import {DisplayNodeControllerCallbacks, DisplayNodeController} from '../../../utils/DisplayNodeController'; import {SubnetOutputSopNode} from '../../SubnetOutput'; import {CoreGraphNode} from '../../../../../core/graph/CoreGraphNode'; import {TypedSopNode, BaseSopNodeType} from '../../_Base'; import {NodeContext} from '../../../../poly/NodeContext'; import {GeoNodeChildrenMap} from '../../../../poly/registers/nodes/Sop'; import {GeoObjNode} from '../../../../nodes/obj/Geo'; import {NodeParamsConfig} from '../../../utils/params/ParamsConfig'; import {CoreGroup} from '../../../../../core/geometry/Group'; import {Constructor, valueof} from '../../../../../types/GlobalTypes'; import {NodeCreateOptions} from '../../../utils/hierarchy/ChildrenController'; import {BaseNodeClassWithDisplayFlag} from '../../../_Base'; export class SubnetSopNodeLike<T extends NodeParamsConfig> extends TypedSopNode<T> { private _overrideOutputNode: boolean = false; override initializeBaseNode() { super.initializeBaseNode(); this.childrenDisplayController.initializeNode(); // the inputs will be evaluated by the child input nodes this.cookController.disallowInputsEvaluation(); } // display_node and children_display controllers public readonly childrenDisplayController: SopSubnetChildrenDisplayController = new SopSubnetChildrenDisplayController(this); public override readonly displayNodeController: DisplayNodeController = new DisplayNodeController( this, this.childrenDisplayController.displayNodeControllerCallbacks() ); // protected override _childrenControllerContext = NodeContext.SOP; override createNode<S extends keyof GeoNodeChildrenMap>( node_class: S, options?: NodeCreateOptions ): GeoNodeChildrenMap[S]; override createNode<K extends valueof<GeoNodeChildrenMap>>( node_class: Constructor<K>, options?: NodeCreateOptions ): K; override createNode<K extends valueof<GeoNodeChildrenMap>>( node_class: Constructor<K>, options?: NodeCreateOptions ): K { return super.createNode(node_class, options) as K; } override children() { return super.children() as BaseSopNodeType[]; } override nodesByType<K extends keyof GeoNodeChildrenMap>(type: K): GeoNodeChildrenMap[K][] { return super.nodesByType(type) as GeoNodeChildrenMap[K][]; } override async cook(inputCoreGroups: CoreGroup[]) { const childOutputNode = this.outputNode(); if (childOutputNode) { await this._cookFromChildOutputNode(childOutputNode); } else { if (!this._overrideOutputNode) { this.states.error.set('no output node found inside subnet'); } } } private async _cookFromChildOutputNode(childOutputNode: SubnetOutputSopNode | BaseNodeClassWithDisplayFlag) { const container = await childOutputNode.compute(); const coreContent = container.coreContent(); if (coreContent) { this.setCoreGroup(coreContent); } else { if (childOutputNode.states.error.active()) { this.states.error.set(childOutputNode.states.error.message()); } else { this.setObjects([]); } } } outputNode(): SubnetOutputSopNode | BaseNodeClassWithDisplayFlag | undefined { return this._overrideOutputNode ? this.displayNodeController.displayNode() : this.childrenDisplayController.outputNode(); } setOverrideOutputNode(overrideOutputNode: boolean) { if (this._overrideOutputNode == overrideOutputNode) { return; } this._overrideOutputNode = overrideOutputNode; const parent = this.parent(); if (parent) { if (parent instanceof SubnetSopNodeLike) { const parentSubnet = parent as SubnetSopNodeLike<any>; parentSubnet.setOverrideOutputNode(overrideOutputNode); } if (parent instanceof GeoObjNode || parent instanceof SubnetSopNodeLike) { const parentGeoObjNode = parent as SubnetSopNodeLike<any> | GeoObjNode; if (overrideOutputNode) { parentGeoObjNode.displayNodeController.setDisplayNodeOverride(this); } else { parentGeoObjNode.displayNodeController.setDisplayNodeOverride(undefined); } } } this.setDirty(); // this.outputNode()?.setDirty(); } } interface SopSubnetChildrenDisplayControllerOptions { dependsOnDisplayNode: boolean; } const DEFAULT_OPTIONS: SopSubnetChildrenDisplayControllerOptions = { dependsOnDisplayNode: true, }; export class SopSubnetChildrenDisplayController { private _outputNodeNeedsUpdate: boolean = true; private _outputNode: SubnetOutputSopNode | undefined; private _graphNode: CoreGraphNode | undefined; constructor( private node: SubnetSopNodeLike<any>, private options: SopSubnetChildrenDisplayControllerOptions = DEFAULT_OPTIONS ) {} dispose() { this._graphNode?.dispose(); } displayNodeControllerCallbacks(): DisplayNodeControllerCallbacks { return { onDisplayNodeRemove: () => { this.node.setDirty(); }, onDisplayNodeSet: () => { this.node.setDirty(); }, onDisplayNodeUpdate: () => { if (this.node.isDirty()) { return; } this.node.setDirty(); }, }; } outputNode() { if (this._outputNodeNeedsUpdate) { this._updateOutputNode(); } return this._outputNode; } initializeNode() { const displayFlag = this.node.flags?.display; if (displayFlag) { displayFlag.onUpdate(() => { if (displayFlag.active()) { this.node.setDirty(); } }); } this.node.lifecycle.onChildAdd(() => { this._outputNodeNeedsUpdate = true; this.node.setDirty(); }); this.node.lifecycle.onChildRemove(() => { this._outputNodeNeedsUpdate = true; this.node.setDirty(); }); } private _updateOutputNode() { const foundNode = this.node.nodesByType(SubnetOutputSopNode.type())[0]; if ( this._outputNode == null || foundNode == null || this._outputNode.graphNodeId() != foundNode.graphNodeId() ) { if (this._graphNode && this._outputNode) { this._graphNode.removeGraphInput(this._outputNode); } this._outputNode = foundNode; if (this._outputNode && this.options.dependsOnDisplayNode) { this._graphNode = this._graphNode || this._createGraphNode(); this._graphNode.addGraphInput(this._outputNode); } } } private _createGraphNode() { const graphNode = new CoreGraphNode(this.node.scene(), `subnetChildrenDisplayController-${this.node.name()}`); graphNode.addPostDirtyHook('subnetChildrenDisplayController', () => { this.node.setDirty(); }); return graphNode; } }