@@ -248,9 +248,14 @@
|
|
248
248
|
const iterator = this._dragHandlers.values();
|
249
249
|
const a = iterator.next().value;
|
250
250
|
const b = iterator.next().value;
|
251
|
-
|
252
|
-
|
253
|
-
|
251
|
+
if (a instanceof DragPointerHandler && b instanceof DragPointerHandler) {
|
252
|
+
const mtHandler = new MultiTouchDragHandler(this, this._targetObject!, a, b);
|
253
|
+
this._dragHandlers.set(this.gameObject, mtHandler);
|
254
|
+
mtHandler.onDragStart(args);
|
255
|
+
}
|
256
|
+
else {
|
257
|
+
console.error("Attempting to construct a MultiTouchDragHandler with invalid DragPointerHandlers. This is likely a bug.", { a, b });
|
258
|
+
}
|
254
259
|
}
|
255
260
|
|
256
261
|
args.use();
|
@@ -605,6 +610,11 @@
|
|
605
610
|
const draggedObject = this.gameObject;
|
606
611
|
const targetObject = this._followObject;
|
607
612
|
|
613
|
+
if (!draggedObject) {
|
614
|
+
console.error("MultiTouchDragHandler has no dragged object. This is likely a bug.");
|
615
|
+
return;
|
616
|
+
}
|
617
|
+
|
608
618
|
targetObject.updateMatrix();
|
609
619
|
targetObject.updateMatrixWorld(true);
|
610
620
|
|
@@ -630,6 +630,13 @@
|
|
630
630
|
}
|
631
631
|
}
|
632
632
|
|
633
|
+
let __isVisionOS: boolean | undefined;
|
634
|
+
/** @returns `true` for VisionOS devices */
|
635
|
+
export function isVisionOS() {
|
636
|
+
if (__isVisionOS !== undefined) return __isVisionOS;
|
637
|
+
return __isVisionOS = (isMacOS() && "xr" in navigator);
|
638
|
+
}
|
639
|
+
|
633
640
|
let __isiOS: boolean | undefined;
|
634
641
|
const iosDevices = ['iPad Simulator', 'iPhone Simulator', 'iPod Simulator', 'iPad', 'iPhone', 'iPod'];
|
635
642
|
|
@@ -161,7 +161,6 @@
|
|
161
161
|
private _hasSelectEvent = false;
|
162
162
|
get hasSelectEvent() { return this._hasSelectEvent; }
|
163
163
|
private _isMxInk = false;
|
164
|
-
private _isMxInkFallback = false;
|
165
164
|
private _isMetaQuestTouchController = false;
|
166
165
|
|
167
166
|
/** Perform a hit test against the XR planes or meshes. shorthand for `xr.getHitTest(controller)`
|
@@ -438,14 +437,6 @@
|
|
438
437
|
rayPositionRaw = getTempVector(t.position);
|
439
438
|
rayQuaternionRaw = getTempQuaternion(t.orientation);
|
440
439
|
|
441
|
-
// HACK: offset for MX Ink on QuestOS v69 and less. This will hopefully not be required with OS v70+ anymore,
|
442
|
-
// when the pen should have its own proper profile and correct ray space.
|
443
|
-
if (this._isMxInk && !this._isMxInkFallback) {
|
444
|
-
const offset = getTempVector(0.013, 0.000, -0.028).applyQuaternion(rayQuaternionRaw);
|
445
|
-
rayPositionRaw.add(offset);
|
446
|
-
this._rayPosition.add(offset);
|
447
|
-
}
|
448
|
-
|
449
440
|
this._rayPositionRaw.copy(rayPositionRaw);
|
450
441
|
this._rayRotationRaw.copy(rayQuaternionRaw);
|
451
442
|
}
|
@@ -759,11 +750,6 @@
|
|
759
750
|
this.getMotionController = fetchProfileCall.then(res => {
|
760
751
|
|
761
752
|
if (!this.connected) return null;
|
762
|
-
if (this._isMxInk && !res.assetPath) {
|
763
|
-
if (debug) console.log("Falling back to custom MX Ink model", res.profile, res.assetPath);
|
764
|
-
res.assetPath = "https://cdn.needle.tools/static/models/controllers/logitech_vr_stylus_v1.3.1_grip_questos68.glb";
|
765
|
-
this._isMxInkFallback = true;
|
766
|
-
}
|
767
753
|
|
768
754
|
this._motioncontroller = new MotionController(
|
769
755
|
this.inputSource,
|
@@ -373,12 +373,20 @@
|
|
373
373
|
static getDefaultSessionInit(mode: Omit<XRSessionMode, "inline">): XRSessionInit {
|
374
374
|
switch (mode) {
|
375
375
|
case "immersive-ar":
|
376
|
+
const arFeatures = ['anchors', 'local-floor', 'layers', 'dom-overlay', 'hit-test', 'unbounded'];
|
377
|
+
// Don't request handtracking by default on VisionOS
|
378
|
+
if (!DeviceUtilities.isVisionOS())
|
379
|
+
arFeatures.push('hand-tracking');
|
376
380
|
return {
|
377
|
-
optionalFeatures:
|
381
|
+
optionalFeatures: arFeatures,
|
378
382
|
}
|
379
383
|
case "immersive-vr":
|
384
|
+
const vrFeatures = ['local-floor', 'bounded-floor', 'high-fixed-foveation-level', 'layers'];
|
385
|
+
// Don't request handtracking by default on VisionOS
|
386
|
+
if (!DeviceUtilities.isVisionOS())
|
387
|
+
vrFeatures.push('hand-tracking');
|
380
388
|
return {
|
381
|
-
optionalFeatures:
|
389
|
+
optionalFeatures: vrFeatures,
|
382
390
|
}
|
383
391
|
default:
|
384
392
|
console.warn("No default session init for mode", mode);
|
@@ -364,7 +364,7 @@
|
|
364
364
|
if (newPlane instanceof Mesh) {
|
365
365
|
disposeObjectResources(newPlane.geometry);
|
366
366
|
newPlane.geometry = this.createGeometry(data);
|
367
|
-
this.makeOccluder(newPlane, newPlane.material, this.occluder);
|
367
|
+
this.makeOccluder(newPlane, newPlane.material, this.occluder && !this.dataTemplate);
|
368
368
|
}
|
369
369
|
else if (newPlane instanceof Group) {
|
370
370
|
// We want to process only one level of children on purpose here
|
@@ -372,7 +372,7 @@
|
|
372
372
|
if (ch instanceof Mesh) {
|
373
373
|
disposeObjectResources(ch.geometry);
|
374
374
|
ch.geometry = this.createGeometry(data);
|
375
|
-
this.makeOccluder(ch, ch.material, this.occluder);
|
375
|
+
this.makeOccluder(ch, ch.material, this.occluder && !this.dataTemplate);
|
376
376
|
}
|
377
377
|
}
|
378
378
|
}
|
@@ -71,6 +71,9 @@
|
|
71
71
|
|
72
72
|
/** @internal */
|
73
73
|
onUpdateXR(args: NeedleXREventArgs): void {
|
74
|
+
// explicit check since we're overriding activeAndEnabled
|
75
|
+
if (!this.enabled) return;
|
76
|
+
|
74
77
|
// try to get the controller
|
75
78
|
const ctrl = args.xr.getController(this.side);
|
76
79
|
if (ctrl) {
|
@@ -113,6 +113,7 @@
|
|
113
113
|
}
|
114
114
|
}
|
115
115
|
}
|
116
|
+
|
116
117
|
onXRControllerRemoved(args: NeedleXRControllerEventArgs): void {
|
117
118
|
console.debug("XR Controller Removed", args.controller.side, args.controller.index);
|
118
119
|
// we need to find the index by the controller because if controller 0 is removed first then args.controller.index 1 will be at index 0
|
@@ -128,6 +129,19 @@
|
|
128
129
|
entry.model = undefined;
|
129
130
|
}
|
130
131
|
}
|
132
|
+
|
133
|
+
onBeforeXR(_mode: XRSessionMode, args: XRSessionInit & { trackedImages: Array<any> }): void {
|
134
|
+
// When a custom hand model is used, we want to ensure we're requesting hand tracking,
|
135
|
+
// even when the platform default doesn't include it (for example, on VisionOS we don't
|
136
|
+
// request hand tracking by default because there's an additional permissions dialogue.
|
137
|
+
if (this.createHandModel && (this.customLeftHand || this.customRightHand)) {
|
138
|
+
args.optionalFeatures = args.optionalFeatures || [];
|
139
|
+
if (!args.optionalFeatures.includes("hand-tracking")) {
|
140
|
+
args.optionalFeatures.push("hand-tracking");
|
141
|
+
}
|
142
|
+
}
|
143
|
+
}
|
144
|
+
|
131
145
|
onLeaveXR(_args: NeedleXREventArgs): void {
|
132
146
|
for (const entry of this._models) {
|
133
147
|
if (!entry) continue;
|
@@ -145,6 +159,7 @@
|
|
145
159
|
}
|
146
160
|
this._models.length = 0;
|
147
161
|
}
|
162
|
+
|
148
163
|
onBeforeRender() {
|
149
164
|
if (!NeedleXRSession.active) return;
|