@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
206 lines (185 loc) • 6.3 kB
text/typescript
import {BaseInputEventNodeType} from '../../../nodes/event/_BaseInput';
import {SceneEventsDispatcher} from './EventsDispatcher';
import {EventData, EventType} from '../../../../core/event/EventData';
import {PointerEventType} from '../../../../core/event/PointerEventType';
import {CoreEventEmitter} from '../../../../core/event/CoreEventEmitter';
import {addToSetAtEntry} from '../../../../core/MapUtils';
import {ActorEvaluatorGenerator} from '../../../nodes/js/code/assemblers/actor/ActorEvaluatorGenerator';
import type {EventContext} from '../../../../core/event/EventContextType';
export abstract class BaseSceneEventsController<
E extends Event,
T extends BaseInputEventNodeType
// JsNodeType extends BaseUserInputJsNodeType
> {
private _activeEventDatas: EventData[] = [];
private _activeEventDataTypes: Set<string> = new Set();
protected _eventNodes: Set<T> = new Set();
protected _requireCanvasEventListeners: boolean = false;
protected _actorEvaluators: Set<ActorEvaluatorGenerator> = new Set();
// protected _actorEventNames: Set<string> = new Set();
protected _actorEvaluatorsByEventNames: Map<string, Map<CoreEventEmitter, Set<ActorEvaluatorGenerator>>> =
new Map();
constructor(protected dispatcher: SceneEventsDispatcher) {}
registerEventNode(node: T) {
this._eventNodes.add(node);
this.updateViewerEventListeners();
}
unregisterEventNode(node: T) {
this._eventNodes.delete(node);
this.updateViewerEventListeners();
}
registerEvaluatorGenerator(evaluator: ActorEvaluatorGenerator) {
this._actorEvaluators.add(evaluator);
this._updateActorEvaluatorCache();
this.updateViewerEventListeners();
}
unregisterEvaluatorGenerator(evaluator: ActorEvaluatorGenerator) {
this._actorEvaluators.delete(evaluator);
this._updateActorEvaluatorCache();
this.updateViewerEventListeners();
}
private _updateActorEvaluatorCache() {
// this._actorEventNames.clear();
this._actorEvaluatorsByEventNames.clear();
this._actorEvaluators.forEach((evaluator) => {
const eventDatas = evaluator.eventDatas;
if (eventDatas) {
eventDatas.forEach((eventData) => {
const eventName = eventData.type;
const emitter = eventData.emitter;
let mapForEventName = this._actorEvaluatorsByEventNames.get(eventName);
if (!mapForEventName) {
mapForEventName = new Map();
this._actorEvaluatorsByEventNames.set(eventName, mapForEventName);
}
addToSetAtEntry(mapForEventName, emitter, evaluator);
});
}
// const nodeEventNames = node.userInputEventNames();
// const emitter = node.eventEmitter();
// for (let eventName of nodeEventNames) {
// // this._actorEventNames.add(eventName);
// let mapForEventName = this._actorNodesByEventNames.get(eventName);
// if (!mapForEventName) {
// mapForEventName = new Map();
// this._actorNodesByEventNames.set(eventName, mapForEventName);
// }
// MapUtils.addToSetAtEntry(mapForEventName, emitter, node);
// }
});
}
abstract type(): string;
abstract acceptedEventTypes(): Set<string>;
// abstract accepts_event(event: Event): boolean;
processEvent(eventContext: EventContext<E>) {
if (this._activeEventDatas.length == 0) {
return;
}
const eventType = eventContext.event?.type;
if (eventType) {
// The check here if the eventType is active
// is not necessary for canvas events (pointer, mouse, keyboard)
// but currently necessary for scene events (such as tick)
if (!this._activeEventDataTypes.has(eventType)) {
return;
}
}
this._eventNodes.forEach((node) => {
node.processEvent(eventContext);
});
}
updateViewerEventListeners() {
this._updateActiveEventTypes();
if (this._requireCanvasEventListeners) {
this.dispatcher.scene.viewersRegister.traverseViewers((viewer) => {
viewer.eventsController().updateEvents(this);
});
}
}
activeEventDatas() {
return this._activeEventDatas;
}
private _updateActiveEventTypes() {
const _storeEventData = (eventData: EventData) => {
this._activeEventDatas.push(eventData);
this._activeEventDataTypes.add(eventData.type);
};
const _reset = () => {
this._activeEventDatas.splice(0, this._activeEventDatas.length);
this._activeEventDataTypes.clear();
};
const _actorEventDatas = () => {
let eventTypeByEmitter: Map<CoreEventEmitter, Set<EventType>> = new Map();
this._actorEvaluatorsByEventNames.forEach((mapForEventName, eventName) => {
mapForEventName.forEach((nodes, emitter) => {
nodes.forEach((node) => {
addToSetAtEntry(eventTypeByEmitter, emitter, eventName);
});
});
});
const eventDatas: EventData[] = [];
eventTypeByEmitter.forEach((eventNames, emitter) => {
for (const eventName of eventNames) {
const eventData: EventData = {
type: eventName,
emitter,
};
eventDatas.push(eventData);
}
});
return eventDatas;
};
const _updateActorNodesEventData = () => {
const actorEventDatas = _actorEventDatas();
if (actorEventDatas) {
for (const data of actorEventDatas) {
_storeEventData(data);
}
}
};
const _updateEventNodesEventData = () => {
const activeNodeEventTypesState: Set<EventData> = new Set();
this._eventNodes.forEach((node) => {
if (node.parent()) {
const nodeActiveEventDatas = node.activeEventDatas();
for (const data of nodeActiveEventDatas) {
activeNodeEventTypesState.add(data);
}
}
});
activeNodeEventTypesState.forEach((state, data) => {
_storeEventData(data);
});
};
_reset();
// we always need the pointermove and touchmove
// in order to always update the cursor
_storeEventData({
type: PointerEventType.pointermove,
emitter: CoreEventEmitter.DOCUMENT,
});
_storeEventData({
type: PointerEventType.touchmove,
emitter: CoreEventEmitter.DOCUMENT,
});
_updateActorNodesEventData();
_updateEventNodesEventData();
}
}
export type BaseSceneEventsControllerType = BaseSceneEventsController<
Event,
BaseInputEventNodeType
// BaseUserInputJsNodeType
>;
export class BaseSceneEventsControllerClass extends BaseSceneEventsController<
Event,
BaseInputEventNodeType
// BaseUserInputJsNodeType
> {
type() {
return '';
}
acceptedEventTypes(): Set<string> {
return new Set<string>();
}
}