UNPKG

@polygonjs/polygonjs

Version:

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

239 lines (225 loc) 10.6 kB
import {Object3D} from 'three'; import {PolyScene} from '../PolyScene'; import {ActorManualTriggersController} from './actors/ManualTriggersController'; import {ActorKeyboardEventsController} from './actors/ActorsKeyboardEventsController'; import {JsType} from '../../poly/registers/nodes/types/Js'; import {EvaluatorMethodName, EVALUATOR_METHOD_NAMES} from '../../nodes/js/code/assemblers/actor/ActorEvaluator'; import {ActorEvaluatorGenerator} from '../../nodes/js/code/assemblers/actor/ActorEvaluatorGenerator'; // import {ActorPointerEventsController} from './actors/ActorsPointerEventsController'; import {AssemblerControllerNode} from '../../nodes/js/code/Controller'; import {JsAssemblerActor} from '../../nodes/js/code/assemblers/actor/ActorAssembler'; import {ActorCompilationController} from '../../../core/actor/ActorCompilationController'; import {ObjectContent, CoreObjectType} from '../../../core/geometry/ObjectContent'; import {hierarchyTraverse} from '../../../core/geometry/util/HierarchyTraverse'; import {Poly} from '../../Poly'; import {RayObjectIntersectionsHoverController} from './actors/rayObjectIntersection/RayObjectIntersectionsHoverController'; import {RayObjectIntersectionsPointerdownController} from './actors/rayObjectIntersection/RayObjectIntersectionsPointerdownController'; import {RayObjectIntersectionsPointerupController} from './actors/rayObjectIntersection/RayObjectIntersectionsPointerupController'; import {RayObjectIntersectionsClickController} from './actors/rayObjectIntersection/RayObjectIntersectionsClickController'; import {RayObjectIntersectionsMouseClickController} from './actors/rayObjectIntersection/RayObjectIntersectionsMouseClickController'; import {RayObjectIntersectionsContextmenuController} from './actors/rayObjectIntersection/RayObjectIntersectionsContextmenuController'; import {RayObjectIntersectionsLongPressController} from './actors/rayObjectIntersection/RayObjectIntersectionsLongPressController'; import {RayObjectIntersectionsSwipeController} from './actors/rayObjectIntersection/RayObjectIntersectionsSwipeController'; import {PointerdownController} from './actors/rayObjectIntersection/PointerdownController'; import {PointerupController} from './actors/rayObjectIntersection/PointerupController'; import {RayObjectIntersectionsDoubleClickController} from './actors/rayObjectIntersection/RayObjectIntersectionsDoubleClickController'; const ACTOR_BUILDER_NODE_IDS_KEY = 'actorBuilderNodeIds'; enum EventHandlerType { instant = 'instant', onTick = 'onTick', } const EVENT_MAP_LOGIC: Record<EvaluatorMethodName, EventHandlerType> = { [JsType.ON_KEY]: EventHandlerType.onTick, [JsType.ON_KEYDOWN]: EventHandlerType.onTick, [JsType.ON_KEYPRESS]: EventHandlerType.onTick, [JsType.ON_KEYUP]: EventHandlerType.onTick, // [JsType.ON_MANUAL_TRIGGER]: EventHandlerType.instant, [JsType.ON_MAPBOX_CAMERA_MOVE]: EventHandlerType.onTick, [JsType.ON_MAPBOX_CAMERA_MOVE_START]: EventHandlerType.onTick, [JsType.ON_MAPBOX_CAMERA_MOVE_END]: EventHandlerType.onTick, [JsType.ON_OBJECT_ATTRIBUTE_UPDATE]: EventHandlerType.onTick, // ['onClick']: EventHandlerType.onTick, ['onContextMenu']: EventHandlerType.onTick, [JsType.ON_OBJECT_DISPATCH_EVENT]: EventHandlerType.instant, // TODO ['onPointermove']: EventHandlerType.onTick, // [JsType.ON_OBJECT_POINTERDOWN]: EventHandlerType.onTick, // [JsType.ON_OBJECT_POINTERUP]: EventHandlerType.onTick, [JsType.ON_PERFORMANCE_CHANGE]: EventHandlerType.instant, [JsType.ON_POINTERDOWN]: EventHandlerType.onTick, [JsType.ON_POINTERUP]: EventHandlerType.onTick, [JsType.ON_SCENE_PAUSE]: EventHandlerType.instant, [JsType.ON_SCENE_PLAY]: EventHandlerType.instant, [JsType.ON_SCENE_RESET]: EventHandlerType.instant, [JsType.ON_TICK]: EventHandlerType.onTick, [JsType.ON_VIDEO_EVENT]: EventHandlerType.onTick, [JsType.ON_WEBXR_CONTROLLER_EVENT]: EventHandlerType.onTick, // TODO }; const ON_TICK_METHOD_NAMES: Set<EvaluatorMethodName> = new Set( EVALUATOR_METHOD_NAMES.filter((methodName) => EVENT_MAP_LOGIC[methodName] == EventHandlerType.onTick) ); const INSTANT_METHOD_NAMES: Set<EvaluatorMethodName> = new Set( EVALUATOR_METHOD_NAMES.filter((methodName) => EVENT_MAP_LOGIC[methodName] == EventHandlerType.instant) ); if (0 + 0) { console.log({ON_TICK_METHOD_NAMES, INSTANT_METHOD_NAMES}); } export abstract class ActorBuilderNode extends AssemblerControllerNode<JsAssemblerActor> { public abstract readonly compilationController: ActorCompilationController; } export class ActorsManager { private _keyboardEventsController: ActorKeyboardEventsController | undefined; private _manualTriggerController: ActorManualTriggersController | undefined; constructor(public readonly scene: PolyScene) {} registerEvaluatorGenerator(evaluatorGenerator: ActorEvaluatorGenerator) { this.scene.eventsDispatcher.registerEvaluatorGenerator(evaluatorGenerator); } unregisterEvaluatorGenerator(evaluatorGenerator: ActorEvaluatorGenerator) { this.scene.eventsDispatcher.unregisterEvaluatorGenerator(evaluatorGenerator); } assignActorBuilder(object: ObjectContent<CoreObjectType>, node: ActorBuilderNode) { let ids = this.objectActorNodeIds(object); if (!ids) { ids = []; object.userData[ACTOR_BUILDER_NODE_IDS_KEY] = ids; } const id = node.graphNodeId(); if (!ids.includes(id)) { ids.push(id); } Poly.onObjectsAddRemoveHooks.assignOnRemoveHookHandler(object, node); } objectActorNodeIds(object: ObjectContent<CoreObjectType>) { return object.userData[ACTOR_BUILDER_NODE_IDS_KEY] as number[] | undefined; } /* * * EVENTS * */ get keyboardEventsController() { return (this._keyboardEventsController = this._keyboardEventsController || new ActorKeyboardEventsController(this)); } get manualTriggerController() { return (this._manualTriggerController = this._manualTriggerController || new ActorManualTriggersController(this)); } public readonly rayObjectIntersectionClick = new RayObjectIntersectionsClickController(this); public readonly rayObjectIntersectionDoubleClick = new RayObjectIntersectionsDoubleClickController(this); public readonly rayObjectIntersectionMouseClick = new RayObjectIntersectionsMouseClickController(this); public readonly rayObjectIntersectionContextmenu = new RayObjectIntersectionsContextmenuController(this); public readonly rayObjectIntersectionHover = new RayObjectIntersectionsHoverController(this); public readonly rayObjectIntersectionLongPress = new RayObjectIntersectionsLongPressController(this); public readonly rayObjectIntersectionPointerdown = new RayObjectIntersectionsPointerdownController(this); public readonly rayObjectIntersectionPointerup = new RayObjectIntersectionsPointerupController(this); public readonly rayObjectIntersectionSwipe = new RayObjectIntersectionsSwipeController(this); public readonly pointerdown = new PointerdownController(this); public readonly pointerup = new PointerupController(this); /* * * PUBLIC METHODS * */ tick() { this.rayObjectIntersectionHover.process(); this._keyboardEventsController?.runTriggers(); hierarchyTraverse(this.scene.threejsScene(), (object) => { this.triggerEventNodes(object, 'onPointermove'); this._onEventTickBound(object); }); } runOnEventSceneReset() { this._onEventSceneResetTraverse(); } runOnEventScenePlay() { this._onEventScenePlayTraverse(); } runOnEventScenePause() { this._onEventScenePauseTraverse(); } runOnEventPerformanceChange() { this._onEventPerformanceChangeTraverse(); } /* * * PRIVATE METHODS * */ // tick private _onEventTickBound = this._onEventTick.bind(this); private _onEventTick(object: Object3D) { this.triggerEventNodes(object, JsType.ON_TICK); } // reset private _onEventSceneResetBound = this._onEventSceneReset.bind(this); private _onEventSceneReset(object: Object3D) { this.triggerEventNodes(object, JsType.ON_SCENE_RESET); } private _onEventSceneResetTraverse() { hierarchyTraverse(this.scene.threejsScene(), this._onEventSceneResetBound); } // play private _onEventScenePlayBound = this._onEventScenePlay.bind(this); private _onEventScenePlay(object: Object3D) { this.triggerEventNodes(object, JsType.ON_SCENE_PLAY); } private _onEventScenePlayTraverse() { hierarchyTraverse(this.scene.threejsScene(), this._onEventScenePlayBound); } // pause private _onEventScenePauseBound = this._onEventScenePause.bind(this); private _onEventScenePause(object: Object3D) { this.triggerEventNodes(object, JsType.ON_SCENE_PAUSE); } private _onEventScenePauseTraverse() { hierarchyTraverse(this.scene.threejsScene(), this._onEventScenePauseBound); } // performanceChange private _onEventPerformanceChangeBound = this._onEventPerformanceChange.bind(this); private _onEventPerformanceChange(object: Object3D) { this.triggerEventNodes(object, JsType.ON_PERFORMANCE_CHANGE); } private _onEventPerformanceChangeTraverse() { hierarchyTraverse(this.scene.threejsScene(), this._onEventPerformanceChangeBound); } // triggerEventNodes(object: Object3D, methodName: EvaluatorMethodName) { const nodeIds = this.objectActorNodeIds(object); if (!nodeIds) { return; } for (const nodeId of nodeIds) { // we need to test that the object has a parent, // as it could have multiple actor attached, // and if the first one from nodeIds triggers the object removal // from the hierarchy, going through the following actor nodes // will regenerate an evaluator for an object that is not in the hierarchy anymore if (object.parent == null) { return; } const node = this.scene.graph.nodeFromId(nodeId) as ActorBuilderNode | undefined; if (node) { this.triggerEventNode(node, object, methodName); } } } triggerEventNode(node: ActorBuilderNode, object: Object3D, methodName: EvaluatorMethodName) { const evaluatorGenerator = node.compilationController.evaluatorGenerator(); this._triggerEvaluatorGenerator(evaluatorGenerator, object, methodName); } private _triggerEvaluatorGenerator( evaluatorGenerator: ActorEvaluatorGenerator, object: Object3D, methodName: EvaluatorMethodName ) { // we can't yet create evaluators only when the method requested here exists, // as this fails for onObjectClick methods, which are called from the relevant event controller // if (onlyIfMethodExists == true && !evaluatorGenerator.hasExpectedEvaluatorMethodName(methodName)) { // return; // } const evaluator = evaluatorGenerator.findOrCreateEvaluator(object); if ((evaluator as any)[methodName]) { (evaluator as any)[methodName]!(); } } }