@@ -294,11 +294,18 @@
|
|
294
294
|
readonly scripts_immersive_ar: INeedleXRSessionEventReceiver[] = [];
|
295
295
|
readonly coroutines: { [FrameEvent: number]: Array<CoroutineData> } = {}
|
296
296
|
|
297
|
+
/** callbacks called once after the context has been created */
|
297
298
|
readonly post_setup_callbacks: Function[] = [];
|
299
|
+
/** called every frame at the beginning of the frame (after component start events and before earlyUpdate) */
|
298
300
|
readonly pre_update_callbacks: Function[] = [];
|
301
|
+
/** called every frame before rendering (after all component events) */
|
299
302
|
readonly pre_render_callbacks: Array<(frame: XRFrame | null) => void> = [];
|
303
|
+
/** called every frame after rendering (after all component events) */
|
300
304
|
readonly post_render_callbacks: Function[] = [];
|
301
305
|
|
306
|
+
/** called every frame befroe update (this list is emptied every frame) */
|
307
|
+
readonly pre_update_oneshot_callbacks: Function[] = [];
|
308
|
+
|
302
309
|
readonly new_scripts: IComponent[] = [];
|
303
310
|
readonly new_script_start: IComponent[] = [];
|
304
311
|
readonly new_scripts_pre_setup_callbacks: Function[] = [];
|
@@ -410,7 +417,7 @@
|
|
410
417
|
}
|
411
418
|
}
|
412
419
|
}
|
413
|
-
if(debug) console.log("Using Renderer Parameters:", params, this.domElement)
|
420
|
+
if (debug) console.log("Using Renderer Parameters:", params, this.domElement)
|
414
421
|
|
415
422
|
this.renderer = new WebGLRenderer(params);
|
416
423
|
|
@@ -1078,6 +1085,13 @@
|
|
1078
1085
|
this.setCurrentCamera(last);
|
1079
1086
|
}
|
1080
1087
|
|
1088
|
+
if (this.pre_update_oneshot_callbacks) {
|
1089
|
+
for (const i in this.pre_update_oneshot_callbacks) {
|
1090
|
+
this.pre_update_oneshot_callbacks[i]();
|
1091
|
+
}
|
1092
|
+
this.pre_update_oneshot_callbacks.length = 0;
|
1093
|
+
}
|
1094
|
+
|
1081
1095
|
if (this.pre_update_callbacks) {
|
1082
1096
|
for (const i in this.pre_update_callbacks) {
|
1083
1097
|
this.pre_update_callbacks[i]();
|
@@ -6,9 +6,43 @@
|
|
6
6
|
|
7
7
|
const debug = getParam("debuginput");
|
8
8
|
|
9
|
+
|
10
|
+
export const enum PointerType {
|
11
|
+
Mouse = "mouse",
|
12
|
+
Touch = "touch",
|
13
|
+
Controller = "controller",
|
14
|
+
Hand = "hand"
|
15
|
+
}
|
16
|
+
export type PointerTypeNames = EnumToPrimitiveUnion<PointerType>;
|
17
|
+
|
18
|
+
const enum PointerEnumType {
|
19
|
+
PointerDown = "pointerdown",
|
20
|
+
PointerUp = "pointerup",
|
21
|
+
PointerMove = "pointermove",
|
22
|
+
}
|
23
|
+
const enum KeyboardEnumType {
|
24
|
+
KeyDown = "keydown",
|
25
|
+
KeyUp = "keyup",
|
26
|
+
KeyPressed = "keypress"
|
27
|
+
}
|
28
|
+
|
29
|
+
export const enum InputEvents {
|
30
|
+
PointerDown = "pointerdown",
|
31
|
+
PointerUp = "pointerup",
|
32
|
+
PointerMove = "pointermove",
|
33
|
+
KeyDown = "keydown",
|
34
|
+
KeyUp = "keyup",
|
35
|
+
KeyPressed = "keypress"
|
36
|
+
}
|
37
|
+
export type InputEventNames = EnumToPrimitiveUnion<InputEvents>;
|
38
|
+
|
39
|
+
|
40
|
+
|
9
41
|
export declare type NEPointerEventInit = PointerEventInit &
|
10
42
|
{
|
43
|
+
origin: object;
|
11
44
|
pointerId: number;
|
45
|
+
pointerType: PointerTypeNames;
|
12
46
|
mode: XRTargetRayMode,
|
13
47
|
ray?: Ray;
|
14
48
|
/** The control object for this input. In the case of spatial devices the controller,
|
@@ -19,6 +53,12 @@
|
|
19
53
|
|
20
54
|
|
21
55
|
export class NEPointerEvent extends PointerEvent {
|
56
|
+
|
57
|
+
/** The origin of the event contains a reference to the creator of this event.
|
58
|
+
* This can be the Needle Engine input system or e.g. a XR controller
|
59
|
+
*/
|
60
|
+
readonly origin: object;
|
61
|
+
|
22
62
|
/** the browser event that triggered this event (if any) */
|
23
63
|
readonly source: Event | null;
|
24
64
|
|
@@ -36,8 +76,12 @@
|
|
36
76
|
/** true if this event is a double click */
|
37
77
|
isDoubleClick: boolean = false;
|
38
78
|
|
79
|
+
// this is set via the init arguments (we override it here for intellisense to show the string options)
|
80
|
+
override readonly pointerType!: PointerTypeNames;
|
81
|
+
|
39
82
|
constructor(type: InputEvents | InputEventNames, source: Event | null, init: NEPointerEventInit) {
|
40
83
|
super(type, init)
|
84
|
+
this.origin = init.origin;
|
41
85
|
this.source = source;
|
42
86
|
this.mode = init.mode;
|
43
87
|
this.ray = init.ray;
|
@@ -88,35 +132,7 @@
|
|
88
132
|
}
|
89
133
|
|
90
134
|
|
91
|
-
export const enum PointerType {
|
92
|
-
Mouse = "mouse",
|
93
|
-
Touch = "touch",
|
94
|
-
Controller = "controller",
|
95
|
-
Hand = "hand"
|
96
|
-
}
|
97
135
|
|
98
|
-
const enum PointerEnumType {
|
99
|
-
PointerDown = "pointerdown",
|
100
|
-
PointerUp = "pointerup",
|
101
|
-
PointerMove = "pointermove",
|
102
|
-
}
|
103
|
-
const enum KeyboardEnumType {
|
104
|
-
KeyDown = "keydown",
|
105
|
-
KeyUp = "keyup",
|
106
|
-
KeyPressed = "keypress"
|
107
|
-
}
|
108
|
-
|
109
|
-
export const enum InputEvents {
|
110
|
-
PointerDown = "pointerdown",
|
111
|
-
PointerUp = "pointerup",
|
112
|
-
PointerMove = "pointermove",
|
113
|
-
KeyDown = "keydown",
|
114
|
-
KeyUp = "keyup",
|
115
|
-
KeyPressed = "keypress"
|
116
|
-
}
|
117
|
-
export type InputEventNames = EnumToPrimitiveUnion<InputEvents>;
|
118
|
-
|
119
|
-
|
120
136
|
declare type PointerEventListener = (evt: NEPointerEvent) => void;
|
121
137
|
declare type KeyboardEventListener = (evt: NEKeyboardEvent) => void;
|
122
138
|
declare type InputEventListener = PointerEventListener | KeyboardEventListener;
|
@@ -587,7 +603,7 @@
|
|
587
603
|
const id = this.getPointerIndex(touch.identifier)
|
588
604
|
if (debug) showBalloonMessage(`touch start #${id}, identifier:${touch.identifier}`);
|
589
605
|
const space = this.getAndUpdateSpatialObjectForScreenPosition(id, touch.clientX, touch.clientY);
|
590
|
-
const ne = new NEPointerEvent(InputEvents.PointerDown, evt, { mode: "screen", pointerId: id, button: 0, clientX: touch.clientX, clientY: touch.clientY, pointerType: PointerType.Touch, buttonName: "unknown", device: space });
|
606
|
+
const ne = new NEPointerEvent(InputEvents.PointerDown, evt, { origin: this, mode: "screen", pointerId: id, button: 0, clientX: touch.clientX, clientY: touch.clientY, pointerType: PointerType.Touch, buttonName: "unknown", device: space });
|
591
607
|
this.onDown(ne);
|
592
608
|
}
|
593
609
|
}
|
@@ -598,7 +614,7 @@
|
|
598
614
|
const touch = evt.changedTouches[i];
|
599
615
|
const id = this.getPointerIndex(touch.identifier)
|
600
616
|
const space = this.getAndUpdateSpatialObjectForScreenPosition(id, touch.clientX, touch.clientY);
|
601
|
-
const ne = new NEPointerEvent(InputEvents.PointerMove, evt, { mode: "screen", pointerId: id, button: 0, clientX: touch.clientX, clientY: touch.clientY, pointerType: PointerType.Touch, buttonName: "unknown", device: space });
|
617
|
+
const ne = new NEPointerEvent(InputEvents.PointerMove, evt, { origin: this, mode: "screen", pointerId: id, button: 0, clientX: touch.clientX, clientY: touch.clientY, pointerType: PointerType.Touch, buttonName: "unknown", device: space });
|
602
618
|
this.onMove(ne);
|
603
619
|
}
|
604
620
|
}
|
@@ -613,7 +629,7 @@
|
|
613
629
|
|
614
630
|
if (debug) showBalloonMessage(`touch up #${id}, identifier:${touch.identifier}`);
|
615
631
|
const space = this.getAndUpdateSpatialObjectForScreenPosition(id, touch.clientX, touch.clientY);
|
616
|
-
const ne = new NEPointerEvent(InputEvents.PointerUp, evt, { mode: "screen", pointerId: id, button: 0, clientX: touch.clientX, clientY: touch.clientY, pointerType: PointerType.Touch, buttonName: "unknown", device: space });
|
632
|
+
const ne = new NEPointerEvent(InputEvents.PointerUp, evt, { origin: this, mode: "screen", pointerId: id, button: 0, clientX: touch.clientX, clientY: touch.clientY, pointerType: PointerType.Touch, buttonName: "unknown", device: space });
|
617
633
|
this.onUp(ne);
|
618
634
|
}
|
619
635
|
}
|
@@ -631,7 +647,7 @@
|
|
631
647
|
case 2: buttonName = "right"; break;
|
632
648
|
}
|
633
649
|
const space = this.getAndUpdateSpatialObjectForScreenPosition(id, evt.clientX, evt.clientY);
|
634
|
-
const ne = new NEPointerEvent(InputEvents.PointerDown, evt, { mode: "screen", pointerId: 0, button: id, clientX: evt.clientX, clientY: evt.clientY, pointerType: PointerType.Mouse, buttonName: buttonName, device: space });
|
650
|
+
const ne = new NEPointerEvent(InputEvents.PointerDown, evt, { origin: this, mode: "screen", pointerId: 0, button: id, clientX: evt.clientX, clientY: evt.clientY, pointerType: PointerType.Mouse, buttonName: buttonName, device: space });
|
635
651
|
this.onDown(ne);
|
636
652
|
}
|
637
653
|
|
@@ -640,7 +656,7 @@
|
|
640
656
|
if (evt.defaultPrevented) return;
|
641
657
|
const id = evt.button;
|
642
658
|
const space = this.getAndUpdateSpatialObjectForScreenPosition(id, evt.clientX, evt.clientY);
|
643
|
-
const ne = new NEPointerEvent(InputEvents.PointerMove, evt, { mode: "screen", pointerId: 0, button: id, clientX: evt.clientX, clientY: evt.clientY, pointerType: PointerType.Mouse, buttonName: "none", device: space });
|
659
|
+
const ne = new NEPointerEvent(InputEvents.PointerMove, evt, { origin: this, mode: "screen", pointerId: 0, button: id, clientX: evt.clientX, clientY: evt.clientY, pointerType: PointerType.Mouse, buttonName: "none", device: space });
|
644
660
|
this.onMove(ne);
|
645
661
|
}
|
646
662
|
|
@@ -656,7 +672,7 @@
|
|
656
672
|
case 2: buttonName = "right"; break;
|
657
673
|
}
|
658
674
|
const space = this.getAndUpdateSpatialObjectForScreenPosition(id, evt.clientX, evt.clientY);
|
659
|
-
const ne = new NEPointerEvent(InputEvents.PointerUp, evt, { mode: "screen", pointerId: 0, button: id, clientX: evt.clientX, clientY: evt.clientY, pointerType: PointerType.Mouse, buttonName: buttonName, device: space, });
|
675
|
+
const ne = new NEPointerEvent(InputEvents.PointerUp, evt, { origin: this, mode: "screen", pointerId: 0, button: id, clientX: evt.clientX, clientY: evt.clientY, pointerType: PointerType.Mouse, buttonName: buttonName, device: space, });
|
660
676
|
this.onUp(ne);
|
661
677
|
}
|
662
678
|
|
@@ -166,12 +166,14 @@
|
|
166
166
|
addForce(rigidbody: IRigidbody, force: Vec3, wakeup: boolean) {
|
167
167
|
this.validate();
|
168
168
|
const body = this.internal_getRigidbody(rigidbody);
|
169
|
-
body
|
169
|
+
if(body) body.addForce(force, wakeup)
|
170
|
+
else console.warn("Rigidbody doesn't exist: can not apply force");
|
170
171
|
}
|
171
172
|
addImpulse(rigidbody: IRigidbody, force: Vec3, wakeup: boolean) {
|
172
173
|
this.validate();
|
173
174
|
const body = this.internal_getRigidbody(rigidbody);
|
174
|
-
body
|
175
|
+
if (body) body.applyImpulse(force, wakeup);
|
176
|
+
else console.warn("Rigidbody doesn't exist: can not apply impulse");
|
175
177
|
}
|
176
178
|
getLinearVelocity(comp: IRigidbody | ICollider): Vec3 | null {
|
177
179
|
this.validate();
|
@@ -204,13 +206,15 @@
|
|
204
206
|
applyImpulse(rb: IRigidbody, vec: Vec3, wakeup: boolean) {
|
205
207
|
this.validate();
|
206
208
|
const body = this.internal_getRigidbody(rb);
|
207
|
-
body
|
209
|
+
if(body) body.applyImpulse(vec, wakeup);
|
210
|
+
else console.warn("Rigidbody doesn't exist: can not apply impulse");
|
208
211
|
}
|
209
212
|
|
210
213
|
wakeup(rb: IRigidbody) {
|
211
214
|
this.validate();
|
212
215
|
const body = this.internal_getRigidbody(rb);
|
213
|
-
body
|
216
|
+
if(body) body.wakeUp();
|
217
|
+
else console.warn("Rigidbody doesn't exist: can not wake up");
|
214
218
|
}
|
215
219
|
isSleeping(rb: IRigidbody) {
|
216
220
|
this.validate();
|
@@ -220,12 +224,14 @@
|
|
220
224
|
setAngularVelocity(rb: IRigidbody, vec: Vec3, wakeup: boolean) {
|
221
225
|
this.validate();
|
222
226
|
const body = this.internal_getRigidbody(rb);
|
223
|
-
body
|
227
|
+
if(body) body.setAngvel(vec, wakeup);
|
228
|
+
else console.warn("Rigidbody doesn't exist: can not set angular velocity");
|
224
229
|
}
|
225
230
|
setLinearVelocity(rb: IRigidbody, vec: Vec3, wakeup: boolean) {
|
226
231
|
this.validate();
|
227
232
|
const body = this.internal_getRigidbody(rb);
|
228
|
-
body
|
233
|
+
if(body) body.setLinvel(vec, wakeup);
|
234
|
+
else console.warn("Rigidbody doesn't exist: can not set linear velocity");
|
229
235
|
}
|
230
236
|
|
231
237
|
private context?: IContext;
|
@@ -165,17 +165,19 @@
|
|
165
165
|
if (obj.type === "Mesh" && obj.layers.test(mask) && !Gizmos.isGizmo(obj)) {
|
166
166
|
const mesh = obj as Mesh;
|
167
167
|
const geo = mesh.geometry;
|
168
|
-
if (
|
169
|
-
geo.
|
170
|
-
|
171
|
-
if (
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
168
|
+
if (geo) {
|
169
|
+
if (!geo.boundingBox)
|
170
|
+
geo.computeBoundingBox();
|
171
|
+
if (geo.boundingBox) {
|
172
|
+
if (mesh.matrixWorldNeedsUpdate) mesh.updateMatrixWorld();
|
173
|
+
const test = this.tempBoundingBox.copy(geo.boundingBox).applyMatrix4(mesh.matrixWorld);
|
174
|
+
if (sp.intersectsBox(test)) {
|
175
|
+
const wp = getWorldPosition(obj);
|
176
|
+
const dist = wp.distanceTo(sp.center);
|
177
|
+
const int = new SphereIntersection(obj, dist, wp);
|
178
|
+
results.push(int);
|
179
|
+
if (!traverseChildsAfterHit) return;
|
180
|
+
}
|
179
181
|
}
|
180
182
|
}
|
181
183
|
}
|
@@ -1,6 +1,8 @@
|
|
1
1
|
// use for typesafe interface method calls
|
2
2
|
import { Quaternion, type Vector, Vector2, Vector3, Vector4 } from "three";
|
3
3
|
import { type SourceIdentifier } from "./engine_types.js";
|
4
|
+
import { ContextRegistry } from "./engine_context_registry.js";
|
5
|
+
import { type Context } from "./engine_context.js";
|
4
6
|
|
5
7
|
// https://schneidenbach.gitbooks.io/typescript-cookbook/content/nameof-operator.html
|
6
8
|
export const nameofFactory = <T>() => (name: keyof T) => name;
|
@@ -209,12 +211,37 @@
|
|
209
211
|
return obj;
|
210
212
|
}
|
211
213
|
|
214
|
+
/** @returns a promise that resolves after a certain amount of milliseconds
|
215
|
+
* e.g. `await delay(1000)` will wait for 1 second
|
216
|
+
*/
|
212
217
|
export function delay(milliseconds: number): Promise<void> {
|
213
218
|
return new Promise((res, _) => {
|
214
219
|
setTimeout(res, milliseconds);
|
215
220
|
});
|
216
221
|
}
|
217
222
|
|
223
|
+
/** @returns a promise that resolves after a certain amount of frames
|
224
|
+
* e.g. `await delayForFrames(10)` will wait for 10 frames to pass
|
225
|
+
*/
|
226
|
+
export function delayForFrames(frameCount: number, context?: Context): Promise<void> {
|
227
|
+
|
228
|
+
if (frameCount <= 0) return Promise.resolve();
|
229
|
+
if (!context) context = ContextRegistry.Current as Context;
|
230
|
+
if (!context) return Promise.reject("No context");
|
231
|
+
|
232
|
+
const endFrame = context.time.frameCount + frameCount;
|
233
|
+
return new Promise((res, rej) => {
|
234
|
+
if (!context) return rej("No context");
|
235
|
+
const cb = () => {
|
236
|
+
if (context!.time.frameCount >= endFrame) {
|
237
|
+
context!.pre_update_callbacks.splice(context!.pre_update_callbacks.indexOf(cb), 1);
|
238
|
+
res();
|
239
|
+
}
|
240
|
+
}
|
241
|
+
context!.pre_update_callbacks.push(cb);
|
242
|
+
});
|
243
|
+
}
|
244
|
+
|
218
245
|
// 1) if a timeline is exported via menu item the audio clip path is relative to the glb (same folder)
|
219
246
|
// we need to detect that here and build the new audio source path relative to the new glb location
|
220
247
|
// the same is/might be true for any file that is/will be exported via menu item
|
@@ -202,6 +202,8 @@
|
|
202
202
|
this.xr.context.scene.add(this._object);
|
203
203
|
this._ray = new Ray();
|
204
204
|
this.pointerInit = {
|
205
|
+
origin: this,
|
206
|
+
pointerType: this.hand ? "hand" : "controller",
|
205
207
|
pointerId: -1, // < this will be updated in the emitPointerEvent method
|
206
208
|
mode: this.inputSource.targetRayMode,
|
207
209
|
ray: this._ray,
|
@@ -571,14 +573,15 @@
|
|
571
573
|
// that means if the input device is spatial (AR touch on a screen should be handled via touchdown etc still)
|
572
574
|
// Not sure if *this* is enough to determine if the event is spatial or not
|
573
575
|
if (this.xr.mode === "immersive-vr" || this.xr.isPassThrough) {
|
576
|
+
this.pointerInit.origin = this;
|
574
577
|
this.pointerInit.pointerId = this.index * 10 + button;
|
578
|
+
this.pointerInit.pointerType = this.hand ? "hand" : "controller";
|
575
579
|
this.pointerInit.button = button;
|
576
580
|
this.pointerInit.buttonName = buttonName;
|
577
581
|
this.pointerInit.isPrimary = primary;
|
578
582
|
this.pointerInit.mode = this.inputSource.targetRayMode;
|
579
583
|
this.pointerInit.ray = this.ray;
|
580
584
|
this.pointerInit.device = this.object;
|
581
|
-
this.pointerInit.pointerType = this.hand ? PointerType.Hand : PointerType.Controller;
|
582
585
|
|
583
586
|
const prevContext = Context.Current;
|
584
587
|
Context.Current = this.xr.context;
|
@@ -968,6 +968,9 @@
|
|
968
968
|
const emitter = this._particleSystem.emitter;
|
969
969
|
this.context.scene.add(emitter);
|
970
970
|
|
971
|
+
this.inheritVelocity?.awake();
|
972
|
+
this.inheritVelocity.system = this;
|
973
|
+
|
971
974
|
if (debug) {
|
972
975
|
console.log(this);
|
973
976
|
this.gameObject.add(new AxesHelper(1))
|
@@ -1110,6 +1113,8 @@
|
|
1110
1113
|
this._interface.update();
|
1111
1114
|
this.shape.update(this, this.context, this.main.simulationSpace, this.gameObject);
|
1112
1115
|
this.noise.update(this.context);
|
1116
|
+
|
1117
|
+
this.inheritVelocity.system = this;
|
1113
1118
|
this.inheritVelocity?.update(this.context);
|
1114
1119
|
this.velocityOverLifetime.update(this);
|
1115
1120
|
}
|
@@ -1385,11 +1385,18 @@
|
|
1385
1385
|
mode!: ParticleSystemInheritVelocityMode;
|
1386
1386
|
|
1387
1387
|
system!: IParticleSystem;
|
1388
|
-
|
1388
|
+
|
1389
|
+
private _lastWorldPosition: Vector3 | null = null;
|
1389
1390
|
private _velocity: Vector3 = new Vector3();
|
1390
1391
|
private _temp: Vector3 = new Vector3();
|
1391
1392
|
|
1392
|
-
|
1393
|
+
awake() {
|
1394
|
+
this._lastWorldPosition = null!;
|
1395
|
+
this._velocity = new Vector3();
|
1396
|
+
this._temp = new Vector3();
|
1397
|
+
}
|
1398
|
+
|
1399
|
+
update (_context: Context) {
|
1393
1400
|
if (!this.enabled) return;
|
1394
1401
|
if (this.system.worldspace === false) return;
|
1395
1402
|
if (this._lastWorldPosition) {
|
@@ -3,7 +3,7 @@
|
|
3
3
|
import { serializable } from "../../engine/engine_serialization_decorator.js";
|
4
4
|
import { Context } from "../../engine/engine_context.js";
|
5
5
|
import { IComponent, IGameObject } from "../../engine/engine_types.js";
|
6
|
-
import { NeedleXREventArgs, NeedleXRHitTestResult, NeedleXRSession } from "../../engine/engine_xr.js";
|
6
|
+
import { NeedleXRController, NeedleXREventArgs, NeedleXRHitTestResult, NeedleXRSession } from "../../engine/engine_xr.js";
|
7
7
|
import { NEPointerEvent } from "../../engine/engine_input.js";
|
8
8
|
import { getParam } from "../../engine/engine_utils.js";
|
9
9
|
import { destroy } from "../../engine/engine_gameobject.js";
|
@@ -122,6 +122,7 @@
|
|
122
122
|
for (const ret of this._reticle) {
|
123
123
|
destroy(ret);
|
124
124
|
}
|
125
|
+
this._reticle.length = 0;
|
125
126
|
this._isPlacing = true;
|
126
127
|
this.context.input.addEventListener("pointerup", this.onPlaceScene);
|
127
128
|
}
|
@@ -148,7 +149,9 @@
|
|
148
149
|
if (rigObject && rigObject.parent !== this.context.scene) {
|
149
150
|
this.context.scene.add(rigObject);
|
150
151
|
}
|
152
|
+
|
151
153
|
// in pass through mode we want to place the scene using an XR controller
|
154
|
+
let controllersDidHit = false;
|
152
155
|
if (args.xr.isPassThrough && args.xr.controllers.length > 0) {
|
153
156
|
for (const ctrl of args.xr.controllers) {
|
154
157
|
// with this we can only place with the left / first controller right now
|
@@ -156,12 +159,13 @@
|
|
156
159
|
// and then place at the reticle for which the user clicked the place button
|
157
160
|
const hit = ctrl.getHitTest();
|
158
161
|
if (hit) {
|
162
|
+
controllersDidHit = true;
|
159
163
|
this.updateReticleAndHits(args.xr, ctrl.index, hit, args.xr.rigScale);
|
160
164
|
}
|
161
165
|
}
|
162
166
|
}
|
163
|
-
// in screen AR mode we use "camera" hit testing
|
164
|
-
|
167
|
+
// in screen AR mode we use "camera" hit testing (or when using the simulator where controller hit testing is not supported)
|
168
|
+
if (!controllersDidHit) {
|
165
169
|
const hit = args.xr.getHitTest();
|
166
170
|
if (hit) {
|
167
171
|
this.updateReticleAndHits(args.xr, 0, hit, args.xr.rigScale);
|
@@ -246,10 +250,10 @@
|
|
246
250
|
let reticle = this._reticle[0];
|
247
251
|
let hit = this._hits[0];
|
248
252
|
|
249
|
-
if (evt.
|
253
|
+
if (evt.origin instanceof NeedleXRController) {
|
250
254
|
// until we can use hit testing for both controllers and have multple reticles we only allow placement with the first controller
|
251
|
-
reticle = this._reticle[evt.
|
252
|
-
hit = this._hits[evt.
|
255
|
+
reticle = this._reticle[evt.origin.index];
|
256
|
+
hit = this._hits[evt.origin.index];
|
253
257
|
}
|
254
258
|
|
255
259
|
if (!reticle) {
|
@@ -346,7 +350,9 @@
|
|
346
350
|
rigObject.position.multiplyScalar(this.arScale);
|
347
351
|
|
348
352
|
rigObject.updateMatrix();
|
349
|
-
if
|
353
|
+
// if invert forward is disabled we need to invert the forward rotation
|
354
|
+
// we want to look into positive Z direction (if invertForward is enabled we look into negative Z direction)
|
355
|
+
if (!this.invertForward)
|
350
356
|
rigObject.matrix.premultiply(invertForwardMatrix);
|
351
357
|
rigObject.matrix.premultiply(this._startOffset);
|