@@ -118,7 +118,7 @@
|
|
118
118
|
}
|
119
119
|
|
120
120
|
onPointerClick(args: PointerEventData) {
|
121
|
-
if (!this.interactable || args.pointerId
|
121
|
+
if (!this.interactable || args.pointerId !== 0) return;
|
122
122
|
|
123
123
|
// Button clicks should only run with left mouse button while using mouse
|
124
124
|
if(args.pointerId !== 0 && this.context.input.getIsMouse(args.pointerId)) return;
|
@@ -14,6 +14,7 @@
|
|
14
14
|
import { $shadowDomOwner } from "./BaseUIComponent.js";
|
15
15
|
import { isDevEnvironment, showBalloonMessage } from "../../engine/debug/index.js";
|
16
16
|
import { Mathf } from "../../engine/engine_math.js";
|
17
|
+
import { isUIObject } from "./Utils.js";
|
17
18
|
|
18
19
|
const debug = getParam("debugeventsystem");
|
19
20
|
|
@@ -222,22 +223,22 @@
|
|
222
223
|
|
223
224
|
// On mouse input has to be always 0 regardless of the button user pressed
|
224
225
|
// because otherwise it would be taken as 3 unique pointers and create OnEnter and OnExit events which is not expected
|
225
|
-
const
|
226
|
+
const id = pointerEvent.pointerType == PointerType.Touch ? pointerEvent.button : 0;
|
226
227
|
const data = new PointerEventData(this.context.input, pointerEvent);
|
227
228
|
|
228
229
|
data.inputSource = this.context.input;
|
229
|
-
data.pointerId =
|
230
|
-
data.isClicked = pointerEvent.type == InputEvents.PointerUp && this.context.input.getPointerClicked(
|
230
|
+
data.pointerId = pointerEvent.button;
|
231
|
+
data.isClicked = pointerEvent.type == InputEvents.PointerUp && this.context.input.getPointerClicked(pointerEvent.button)
|
231
232
|
// using the input type directly instead of input API -> otherwise onMove events can sometimes be getPointerUp() == true
|
232
233
|
data.isDown = pointerEvent.type == InputEvents.PointerDown;
|
233
234
|
data.isUp = pointerEvent.type == InputEvents.PointerUp;
|
234
|
-
data.isPressed = this.context.input.getPointerPressed(
|
235
|
+
data.isPressed = this.context.input.getPointerPressed(pointerEvent.button);
|
235
236
|
|
236
|
-
if (debug && data.isClicked) console.log("CLICK", pointerId);
|
237
|
+
if (debug && data.isClicked) console.log("CLICK", data.pointerId);
|
237
238
|
|
238
239
|
// raycast
|
239
240
|
const options = new RaycastOptions();
|
240
|
-
options.screenPoint = this.context.input.getPointerPositionRC(
|
241
|
+
options.screenPoint = this.context.input.getPointerPositionRC(id)!;
|
241
242
|
|
242
243
|
const hits = this.performRaycast(options);
|
243
244
|
if (!hits) return;
|
@@ -259,17 +260,17 @@
|
|
259
260
|
// pointer has not hit any object to handle
|
260
261
|
|
261
262
|
// thus is not hovering over anything
|
262
|
-
const hoveredData = this.hoveredByID.get(
|
263
|
+
const hoveredData = this.hoveredByID.get(id);
|
263
264
|
if (hoveredData) {
|
264
265
|
this.triggerOnExit(hoveredData.obj, hoveredData.data);
|
265
266
|
}
|
266
|
-
this.hoveredByID.delete(
|
267
|
+
this.hoveredByID.delete(id);
|
267
268
|
|
268
269
|
// if it was up, it means it doesn't should notify things that it down on before
|
269
270
|
if (data.isUp) {
|
270
|
-
const pressedData = this.pressedByID.get(
|
271
|
+
const pressedData = this.pressedByID.get(id);
|
271
272
|
pressedData?.handlers.forEach(h => h.onPointerUp?.call(h, data));
|
272
|
-
this.pressedByID.delete(
|
273
|
+
this.pressedByID.delete(id);
|
273
274
|
}
|
274
275
|
}
|
275
276
|
|
@@ -293,19 +294,32 @@
|
|
293
294
|
* For example if an event component has only an onPointerClick method we don't need to raycast during movement events
|
294
295
|
* */
|
295
296
|
private shouldRaycastObject = (obj: Object3D): RaycastTestObjectReturnType => {
|
297
|
+
|
298
|
+
// check if this object is actually a UI shadow hierarchy object
|
299
|
+
let shadowComponent: Object3D | null = null;
|
300
|
+
const isUI = isUIObject(obj);
|
301
|
+
// if yes we want to grab the actual object that is the owner of the shadow dom
|
302
|
+
// and check that object for the event component
|
303
|
+
if (isUI) {
|
304
|
+
shadowComponent = obj[$shadowDomOwner]?.gameObject;
|
305
|
+
}
|
306
|
+
|
296
307
|
// check if the object was seen previously
|
297
|
-
if (this._testObjectsCache.has(obj)) {
|
308
|
+
if (this._testObjectsCache.has(obj) || (shadowComponent && this._testObjectsCache.has(shadowComponent))) {
|
298
309
|
// if yes we check if it was previously stored as "YES WE NEED TO RAYCAST THIS"
|
299
310
|
const prev = this._testObjectsCache.get(obj)!;
|
300
|
-
if (
|
311
|
+
if (prev === false) return "continue in children"
|
301
312
|
return true;
|
302
313
|
}
|
303
314
|
else {
|
304
315
|
// the object was not yet seen so we test if it has an event component
|
305
|
-
|
316
|
+
let hasEventComponent = hasPointerEventComponent(obj);
|
317
|
+
if (!hasEventComponent && shadowComponent) hasEventComponent = hasPointerEventComponent(shadowComponent);
|
318
|
+
|
306
319
|
if (hasEventComponent) {
|
307
|
-
// console.log("YES RAYCAST", obj.name)
|
308
320
|
// it has an event component: we add it and all its children to the cache
|
321
|
+
// we don't need to do the same for the shadow component hierarchy
|
322
|
+
// because the next object that will be detecting that the shadow owner was already seen
|
309
323
|
this._testObjectsCache.set(obj, true);
|
310
324
|
obj.traverse((o) => {
|
311
325
|
this._testObjectsCache.set(o, true);
|
@@ -353,6 +367,11 @@
|
|
353
367
|
hits = this.sortCandidates(hits);
|
354
368
|
for (const hit of hits) {
|
355
369
|
const { object } = hit;
|
370
|
+
args.point = hit.point;
|
371
|
+
args.normal = hit.normal;
|
372
|
+
args.face = hit.face;
|
373
|
+
args.distance = hit.distance;
|
374
|
+
args.instanceId = hit.instanceId;
|
356
375
|
if (this.handleEventOnObject(object, args)) {
|
357
376
|
return true;
|
358
377
|
}
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { GameObject } from "../Component.js";
|
2
2
|
import { Input, NEPointerEvent } from "../../engine/engine_input.js";
|
3
|
-
import { Object3D } from "three";
|
3
|
+
import { Face, Object3D, Vector3 } from "three";
|
4
4
|
|
5
5
|
export interface IInputEventArgs {
|
6
6
|
get used(): boolean;
|
@@ -33,8 +33,18 @@
|
|
33
33
|
this._event?.stopImmediatePropagation();
|
34
34
|
}
|
35
35
|
|
36
|
+
/** Who initiated this event */
|
36
37
|
inputSource: Input | any;
|
38
|
+
|
39
|
+
/** The object this event hit or interacted with */
|
37
40
|
object!: THREE.Object3D;
|
41
|
+
/** The world position of this event */
|
42
|
+
point?: Vector3;
|
43
|
+
/** The world normal of this event */
|
44
|
+
normal?: Vector3;
|
45
|
+
face?: Face | null;
|
46
|
+
distance?: number;
|
47
|
+
instanceId?: number;
|
38
48
|
|
39
49
|
pointerId: number | undefined;
|
40
50
|
isDown: boolean | undefined;
|
@@ -42,6 +52,9 @@
|
|
42
52
|
isPressed: boolean | undefined;
|
43
53
|
isClicked: boolean | undefined;
|
44
54
|
|
55
|
+
/** mouse button 0 === LEFT, 1 === MIDDLE, 2 === RIGHT */
|
56
|
+
readonly button: number | string;
|
57
|
+
|
45
58
|
private input: Input;
|
46
59
|
|
47
60
|
private _event?: NEPointerEvent;
|
@@ -50,6 +63,7 @@
|
|
50
63
|
constructor(input: Input, event?: NEPointerEvent) {
|
51
64
|
this._event = event;
|
52
65
|
this.input = input;
|
66
|
+
this.button = event?.button ?? 0;
|
53
67
|
}
|
54
68
|
|
55
69
|
clone() {
|