@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
184 lines (172 loc) • 6.96 kB
text/typescript
import {BaseSceneEventsController} from './_BaseEventsController';
import {PointerEventNode} from '../../../nodes/event/Pointer';
import {Raycaster, Vector2} from 'three';
import {MouseEventType} from '../../../../core/event/MouseEventType';
import {ACCEPTED_POINTER_EVENT_TYPES, PointerEventType} from '../../../../core/event/PointerEventType';
import {TouchEventType} from '../../../../core/event/TouchEventType';
import {ref} from '../../../../core/reactivity/CoreReactivity';
import {createRaycaster} from '../../../../core/RaycastHelper';
import {SceneEventsDispatcher} from './EventsDispatcher';
import {CursorHelper} from '../../../../core/event/CursorHelper';
import type {EventContext} from '../../../../core/event/EventContextType';
export interface RaycasterUpdateOptions {
pointsThreshold: number;
lineThreshold: number;
}
// const methodNameByEventType: Record<PointerEventType, EvaluatorPointerMethod> = {
// // [PointerEventType.click]: 'onClick',
// [PointerEventType.contextmenu]: 'onContextMenu',
// [PointerEventType.pointerdown]: JsType.ON_POINTERDOWN,
// [PointerEventType.pointermove]: 'onPointermove',
// [PointerEventType.pointerup]: JsType.ON_POINTERUP,
// [PointerEventType.touchstart]: JsType.ON_POINTERDOWN,
// [PointerEventType.touchmove]: 'onPointermove',
// [PointerEventType.touchend]: JsType.ON_POINTERUP,
// };
export class PointerEventsController extends BaseSceneEventsController<MouseEvent | TouchEvent, PointerEventNode> {
constructor(dispatcher: SceneEventsDispatcher) {
super(dispatcher);
}
protected override _requireCanvasEventListeners: boolean = true;
private _cursorHelper: CursorHelper = new CursorHelper();
// init to a large value so we don't get a fake intersect
// if there was no interaction
protected _cursor0 = ref<Vector2>(new Vector2(-1000, -1000));
// protected _camera: Camera | undefined;
private _raycaster0 = ref<Raycaster>(createRaycaster());
type() {
return 'pointer';
}
acceptedEventTypes() {
return new Set([...ACCEPTED_POINTER_EVENT_TYPES]);
}
setRaycaster(raycaster: Raycaster) {
// giving a raycaster here is useful to still benefit from mouse events, even if no viewer has been created
this._raycaster0.value = raycaster;
}
override processEvent(eventContext: EventContext<PointerEvent | MouseEvent | TouchEvent>) {
this._cursorHelper.setCursorForCPU(eventContext, this._cursor0.value);
super.processEvent(eventContext);
const {viewer, event} = eventContext;
if (!(event && viewer)) {
console.log('either event or viewer missing');
return;
}
// const camera = viewer.camera();
// this._cursorHelper.setCursorForCPU(eventContext, this._cursor);
// if (camera) {
viewer.raycastersController.setCursor0(this._cursor0.value);
// even though the update is in the render loop
// it may be more up to date to do it here as well
viewer.raycastersController.updateRaycasters();
// this._raycaster = viewer.raycastersController.raycaster0();
// }
const eventType = event.type as PointerEventType | TouchEventType | MouseEventType;
if (eventType == PointerEventType.pointermove || eventType == TouchEventType.touchmove) {
// pointermove is not processed here,
// since callbacks such as onObjectHover
// should be triggered even if the pointer is not moving
// (for instance if instead the object is moving).
// It is therefore triggered via the ActorsManager
return;
}
const mapForEvent = this._actorEvaluatorsByEventNames.get(eventType);
if (!mapForEvent) {
// console.log('no map for eventType', eventType);
return;
}
const eventEmitter = eventContext.emitter;
if (!eventEmitter) {
// console.log('no emitter for context', eventContext);
return;
}
const evaluatorGenerators = mapForEvent.get(eventEmitter);
if (!evaluatorGenerators) {
// console.log('no generators for emitter', eventEmitter);
return;
}
// const methodName = methodNameByEventType[eventType];
if (eventContext.event) {
const actorsManager = this.dispatcher.scene.actorsManager;
switch (eventType) {
case MouseEventType.mousedown: {
actorsManager.rayObjectIntersectionMouseClick.onMousedown(eventContext.event);
return;
}
case MouseEventType.dblclick: {
actorsManager.rayObjectIntersectionDoubleClick.onDoubleClick(eventContext.event);
}
case PointerEventType.pointerdown: {
actorsManager.rayObjectIntersectionClick.onPointerdown(eventContext.event);
actorsManager.rayObjectIntersectionLongPress.onPointerdown(eventContext.event);
actorsManager.rayObjectIntersectionPointerdown.onPointerdown(eventContext.event);
actorsManager.pointerdown.onPointerdown(eventContext.event);
actorsManager.rayObjectIntersectionSwipe.onPointerdown(eventContext.event);
return;
}
case PointerEventType.pointerup: {
// We must not test for isTouchDevice() here
// as we may receive pointerup from a mobile,
// without touchend
// if (!isTouchDevice()) {
actorsManager.rayObjectIntersectionPointerup.onPointerup(eventContext.event);
actorsManager.pointerup.onPointerup(eventContext.event);
return;
// }
}
case PointerEventType.contextmenu: {
actorsManager.rayObjectIntersectionContextmenu.onContextmenu(eventContext.event);
return;
}
case TouchEventType.touchend: {
// we also need to trigger onPointerup after touchend,
// as pointerup appears to not be triggered once the cursor has moved
// We must not test for isTouchDevice() here (see pointerup above)
// if (isTouchDevice()) {
actorsManager.rayObjectIntersectionPointerup.onPointerup(eventContext.event);
actorsManager.pointerup.onPointerup(eventContext.event);
return;
// }
}
}
}
// for (let methodName of methodNames) {
// this.pointerEventsController.addTriggeredEvaluators(evaluatorGenerators, methodName);
// }
// console.log('evaluatorGenerators', evaluatorGenerators);
//
// evaluatorGenerators.forEach((evaluatorGenerator) => {
// evaluatorGenerator.traverseEvaluator((evaluator) => {
// console.log({evaluator}, evaluator.onObjectClick);
// if (evaluator[methodName]) {
// evaluator[methodName]!();
// } else {
// console.log('method not found on object for event type', eventType);
// }
// });
// });
// this.dispatcher.scene.actorsManager.pointerEventsController.setTriggeredNodes(nodesToTrigger);
}
raycaster() {
return this._raycaster0;
}
cursor() {
return this._cursor0;
}
// camera() {
// return this._camera;
// }
updateRaycast(options: RaycasterUpdateOptions) {
// if (!this._raycaster0) {
// return;
// }
const pointsParam = this._raycaster0.value.params.Points;
if (pointsParam) {
pointsParam.threshold = options.pointsThreshold;
}
const lineParam = this._raycaster0.value.params.Line;
if (lineParam) {
lineParam.threshold = options.lineThreshold;
}
}
}