UNPKG

@polygonjs/polygonjs

Version:

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

249 lines (241 loc) 9 kB
import {BaseSopNodeType} from '../nodes/sop/_Base'; import {PolyScene} from '../scene/PolyScene'; import {CoreObjectType, ObjectContent} from '../../core/geometry/ObjectContent'; import {Object3D} from 'three'; import {Poly} from '../Poly'; import {updateObjectChildrenCountRef} from '../../core/reactivity/ObjectHierarchyReactivity'; import {ON_OBJECT_BEFORE_DELETE} from '../../core/geometry/Event'; import {deregisterGeneratorsForObject} from '../nodes/js/code/assemblers/actor/ActorEvaluatorGenerator'; enum HandlerName { ADD = 'onObjectAddHookHandlerNodeIds', REMOVE = 'onObjectRemoveHookHandlerNodeIds', } // enum CompletedHandlerName { // ADD = 'onObjectAddHookHandlerNodeIds_Completed', // REMOVE = 'onObjectRemoveHookHandlerNodeIds_Completed', // } // function _completedHandlerName(handlerName: HandlerName) { // switch (handlerName) { // case HandlerName.ADD: // return CompletedHandlerName.ADD; // case HandlerName.REMOVE: // return CompletedHandlerName.REMOVE; // } // } interface HookHandler { graphNodeId(): number; updateObjectOnAdd(object: ObjectContent<CoreObjectType>, parent: ObjectContent<CoreObjectType>): void; updateObjectOnRemove(object: ObjectContent<CoreObjectType>, parent: ObjectContent<CoreObjectType>): void; } function assignHookHandler(object: ObjectContent<CoreObjectType>, node: HookHandler, handlerName: HandlerName) { let ids = hookHandlers(object, handlerName); if (!ids) { ids = []; object.userData[handlerName] = ids; } const id = node.graphNodeId(); if (!ids.includes(id)) { ids.push(id); } } type ObjectOnAddRemoveCallback = (object: ObjectContent<CoreObjectType>) => void; const onAddRemoveCallbackByObject = new WeakMap< ObjectContent<CoreObjectType>, Map<HandlerName, Set<ObjectOnAddRemoveCallback>> >(); function assignOnAddRemoveHook( object: ObjectContent<CoreObjectType>, handlerName: HandlerName, hook: ObjectOnAddRemoveCallback ) { let mapForObject = onAddRemoveCallbackByObject.get(object); if (!mapForObject) { mapForObject = new Map(); onAddRemoveCallbackByObject.set(object, mapForObject); } let callbacksForHandlerName = mapForObject.get(handlerName); if (!callbacksForHandlerName) { callbacksForHandlerName = new Set(); mapForObject.set(handlerName, callbacksForHandlerName); } callbacksForHandlerName.add(hook); } function runOnAddRemoveHooks(object: ObjectContent<CoreObjectType>, handlerName: HandlerName) { const mapForObject = onAddRemoveCallbackByObject.get(object); if (!mapForObject) { return; } const callbacksForHandlerName = mapForObject.get(handlerName); if (!callbacksForHandlerName) { return; } for (const callback of callbacksForHandlerName) { callback(object); } } // function removeHookHandler(object: ObjectContent<CoreObjectType>, node: HookHandler, handlerName: HandlerName) { // let ids = hookHandlers(object, handlerName); // if (ids) { // const index = ids.indexOf(node.graphNodeId()); // ids.splice(index, 1); // } // } // function assignCompletedHookHandler( // object: ObjectContent<CoreObjectType>, // node: HookHandler, // handlerName: HandlerName // ) { // const completedHandlerName = _completedHandlerName(handlerName); // let ids = completedHookHandlers(object, completedHandlerName); // if (!ids) { // ids = []; // object.userData[completedHandlerName] = ids; // } // const id = node.graphNodeId(); // if (!ids.includes(id)) { // ids.push(id); // } // } function hookHandlers(object: ObjectContent<CoreObjectType>, handlerName: HandlerName) { return object.userData[handlerName] as number[] | undefined; } // function completedHookHandlers(object: ObjectContent<CoreObjectType>, handlerName: CompletedHandlerName) { // return object.userData[handlerName] as number[] | undefined; // } function runHooks(scene: PolyScene, parent: ObjectContent<CoreObjectType>, handlerName: HandlerName) { const children = parent.children; for (const child of children) { child.traverse((grandChild) => { runHookOnObject(grandChild, scene, handlerName); }); } } function runHookOnObject(object: ObjectContent<CoreObjectType>, scene: PolyScene, handlerName: HandlerName) { runOnAddRemoveHooks(object, handlerName); if (object.parent) { const ids = hookHandlers(object, handlerName); if (!ids) { return; } // const completedHandlersIds = completedHookHandlers(object, _completedHandlerName(handlerName)); // if (completedHandlersIds) { // for (const id of completedHandlersIds) { // const index = ids.indexOf(id); // if (index >= 0) { // ids.splice(index, 1); // } // } // } for (const id of ids) { const node = scene.graph.nodeFromId(id) as BaseSopNodeType | undefined; if (node && !node.disposed()) { // when traversing the scene to run the hooks // we must be very careful to note update the hierarchy // with .add and .remove, as this will make .traverse unpredictable. // Instead, children must be replaced directly with: // // const index = parent.children.indexOf(oldObject); // parent.children[index] = newObject; // newObject.parent = parent; // we need to remove the handler before processing the object, // to avoid cases where it might be processed multiple times. // This can happen on scene load, and also when multiple objects are added via the same node, // and only one is new. This will still trigger the hook on the already-added objects // UPDATE: this impacts the particles, which are not generated correctly // removeHookHandler(object, node, handlerName); switch (handlerName) { case HandlerName.ADD: { node.updateObjectOnAdd(object, object.parent); break; } case HandlerName.REMOVE: { node.updateObjectOnRemove(object, object.parent); break; } } // assignCompletedHookHandler(object, node, handlerName); } } } } export function replaceChild(parent: Object3D, oldObject: Object3D, newObject: Object3D) { // new object replaces old Object directly, // and not using .remove and .add, // as this would make traversing the scenes from the hooks handler // unpredictable. const index = parent.children.indexOf(oldObject); if (index < 0) { console.warn('could not find object to replace'); return; } parent.children[index] = newObject; newObject.parent = parent; // parent.remove(object); // parent.add(CSSObject); } export function removeFromParent(scene: PolyScene, object: ObjectContent<CoreObjectType>) { const parent = object.parent; if (!parent) { return; } object.dispatchEvent(ON_OBJECT_BEFORE_DELETE); deregisterGeneratorsForObject(object); Poly.onObjectsAddRemoveHooks.runOnRemoveHookOnObject(scene, object); parent.remove(object); updateObjectChildrenCountRef(parent); } export function addToParent( scene: PolyScene, parent: ObjectContent<CoreObjectType>, child: ObjectContent<CoreObjectType> ) { parent.add(child); updateObjectChildrenCountRef(parent); Poly.onObjectsAddRemoveHooks.runOnAddHookOnObject(scene, child); } // export function replaceChildWithCallbackObject( // parent: Object3D, // oldObject: Object3D, // newObjectCallback: () => Object3D // ) { // const index = parent.children.indexOf(oldObject); // const newObject = newObjectCallback(); // parent.children[index] = newObject; // newObject.parent = parent; // } export function copyBasicObjectProperties(srcObject: Object3D, destObject: Object3D) { destObject.name = srcObject.name; destObject.matrixAutoUpdate = srcObject.matrixAutoUpdate; destObject.frustumCulled = srcObject.frustumCulled; destObject.layers = srcObject.layers; destObject.position.copy(srcObject.position); destObject.quaternion.copy(srcObject.quaternion); destObject.scale.copy(srcObject.scale); destObject.matrix.copy(srcObject.matrix); } export class PolyOnObjectsAddRemoveHooksController { assignOnAddHookHandler(object: ObjectContent<CoreObjectType>, node: HookHandler) { assignHookHandler(object, node, HandlerName.ADD); } assignOnRemoveHookHandler(object: ObjectContent<CoreObjectType>, node: HookHandler) { assignHookHandler(object, node, HandlerName.REMOVE); } assignOnAddHookCallback(object: ObjectContent<CoreObjectType>,callback: ObjectOnAddRemoveCallback) { assignOnAddRemoveHook(object, HandlerName.ADD, callback); } assignOnRemoveHookCallback(object: ObjectContent<CoreObjectType>,callback: ObjectOnAddRemoveCallback) { assignOnAddRemoveHook(object, HandlerName.REMOVE, callback); } runOnAddHooks(scene: PolyScene, parent: ObjectContent<CoreObjectType>) { runHooks(scene, parent, HandlerName.ADD); } runOnRemoveHooks(scene: PolyScene, parent: ObjectContent<CoreObjectType>) { runHooks(scene, parent, HandlerName.REMOVE); } runOnAddHookOnObject(scene: PolyScene, object: ObjectContent<CoreObjectType>) { runHookOnObject(object, scene, HandlerName.ADD); } runOnRemoveHookOnObject(scene: PolyScene, object: ObjectContent<CoreObjectType>) { runHookOnObject(object, scene, HandlerName.REMOVE); } }