@threlte/xr 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ARButton.svelte +2 -1
- package/dist/components/Controller.svelte +30 -36
- package/dist/components/Controller.svelte.d.ts +11 -0
- package/dist/components/Hand.svelte +37 -51
- package/dist/components/Hand.svelte.d.ts +18 -6
- package/dist/components/Headset.svelte +24 -8
- package/dist/components/XR.svelte +1 -1
- package/dist/hooks/index.d.ts +0 -1
- package/dist/hooks/index.js +0 -1
- package/dist/hooks/useTeleport.d.ts +6 -2
- package/dist/hooks/useTeleport.js +18 -18
- package/dist/internal/useHandTrackingState.d.ts +5 -0
- package/dist/internal/useHandTrackingState.js +17 -0
- package/dist/types.d.ts +10 -5
- package/package.json +2 -2
- package/dist/components/Hands.svelte +0 -35
- package/dist/components/Hands.svelte.d.ts +0 -24
- package/dist/hooks/useEvent.d.ts +0 -14
- package/dist/hooks/useEvent.js +0 -33
- package/dist/internal/events.d.ts +0 -5
- package/dist/internal/events.js +0 -26
- package/dist/internal/useBaseReferenceSpace.d.ts +0 -6
- package/dist/internal/useBaseReferenceSpace.js +0 -19
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
mode='immersive-ar'
|
|
19
19
|
sessionInit={{
|
|
20
20
|
domOverlay: typeof document !== 'undefined' ? { root: document.body } : undefined,
|
|
21
|
-
|
|
21
|
+
requiredFeatures: ['plane-detection'],
|
|
22
|
+
optionalFeatures: ['hit-test', 'light-estimation', 'dom-overlay', 'dom-overlay-for-handheld-ar']
|
|
22
23
|
}}
|
|
23
24
|
on:click
|
|
24
25
|
on:error
|
|
@@ -9,13 +9,8 @@ import { onDestroy } from "svelte";
|
|
|
9
9
|
import { XRControllerModelFactory } from "three/examples/jsm/webxr/XRControllerModelFactory";
|
|
10
10
|
import ShortRay from "./ShortRay.svelte";
|
|
11
11
|
import { gaze, left as leftStore, right as rightStore } from "../hooks/useController";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
activeTeleportController,
|
|
16
|
-
isHandTracking,
|
|
17
|
-
pendingTeleportDestination
|
|
18
|
-
} from "../internal/stores";
|
|
12
|
+
import { activeTeleportController, pendingTeleportDestination, isHandTracking } from "../internal/stores";
|
|
13
|
+
import { useHandTrackingState } from "../internal/useHandTrackingState";
|
|
19
14
|
const factory = new XRControllerModelFactory();
|
|
20
15
|
const stores = {
|
|
21
16
|
left: leftStore,
|
|
@@ -33,35 +28,38 @@ const events = [
|
|
|
33
28
|
const eventMap = /* @__PURE__ */ new WeakMap();
|
|
34
29
|
</script>
|
|
35
30
|
|
|
36
|
-
<script>export let left =
|
|
37
|
-
export let right =
|
|
31
|
+
<script>export let left = void 0;
|
|
32
|
+
export let right = void 0;
|
|
33
|
+
export let hand = void 0;
|
|
38
34
|
$:
|
|
39
|
-
|
|
40
|
-
throw new Error("A <Controller> component can only specify one hand.");
|
|
41
|
-
}
|
|
42
|
-
$:
|
|
43
|
-
if (!left && !right) {
|
|
44
|
-
throw new Error("A <Controller> component must specify a hand.");
|
|
45
|
-
}
|
|
46
|
-
$:
|
|
47
|
-
handedness = left ? "left" : "right";
|
|
35
|
+
handedness = left ? "left" : right ? "right" : hand;
|
|
48
36
|
const dispatch = createRawEventDispatcher();
|
|
49
37
|
const { xr } = useThrelte().renderer;
|
|
38
|
+
const handTrackingNow = useHandTrackingState();
|
|
39
|
+
const handleEvent = (event) => {
|
|
40
|
+
if (!handTrackingNow()) {
|
|
41
|
+
dispatch(event.type, event);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
50
44
|
const handleConnected = (event) => {
|
|
51
|
-
const data = event.data;
|
|
52
45
|
const targetData = eventMap.get(event.target);
|
|
53
|
-
if (data.handedness !== handedness || !targetData)
|
|
46
|
+
if (event.data.handedness !== handedness || !targetData)
|
|
54
47
|
return;
|
|
55
|
-
stores[
|
|
56
|
-
|
|
48
|
+
stores[handedness].set({ ...targetData, inputSource: event.data });
|
|
49
|
+
if (!handTrackingNow()) {
|
|
50
|
+
dispatch("connected", event);
|
|
51
|
+
}
|
|
52
|
+
events.forEach((name) => event.target.addEventListener(name, handleEvent));
|
|
57
53
|
};
|
|
58
54
|
const handleDisconnected = (event) => {
|
|
59
55
|
if (event.data.handedness !== handedness)
|
|
60
56
|
return;
|
|
61
|
-
stores[
|
|
62
|
-
|
|
57
|
+
stores[handedness].set(void 0);
|
|
58
|
+
if (!$isHandTracking) {
|
|
59
|
+
dispatch("disconnected", event);
|
|
60
|
+
}
|
|
61
|
+
events.forEach((name) => event.target.removeEventListener(name, handleEvent));
|
|
63
62
|
};
|
|
64
|
-
const handleEvent = (event) => fire(event.type, event);
|
|
65
63
|
for (const index of [0, 1]) {
|
|
66
64
|
const controller = xr.getController(index);
|
|
67
65
|
const grip2 = xr.getControllerGrip(index);
|
|
@@ -69,28 +67,24 @@ for (const index of [0, 1]) {
|
|
|
69
67
|
eventMap.set(controller, { targetRay: controller, model: model2, grip: grip2 });
|
|
70
68
|
controller.addEventListener("connected", handleConnected);
|
|
71
69
|
controller.addEventListener("disconnected", handleDisconnected);
|
|
72
|
-
events.forEach((name) => controller.addEventListener(name, handleEvent));
|
|
73
70
|
}
|
|
74
71
|
$:
|
|
75
|
-
store =
|
|
72
|
+
store = stores[handedness];
|
|
76
73
|
$:
|
|
77
74
|
grip = $store?.grip;
|
|
78
75
|
$:
|
|
79
76
|
targetRay = $store?.targetRay;
|
|
80
77
|
$:
|
|
81
78
|
model = $store?.model;
|
|
82
|
-
for (const type of ["connected", "disconnected", ...events]) {
|
|
83
|
-
useControllerEvent(type, (event) => dispatch(type, event), {
|
|
84
|
-
handedness: left ? "left" : "right"
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
79
|
onDestroy(() => {
|
|
88
80
|
for (const index of [0, 1]) {
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
events.forEach((name) => controller.removeEventListener(name, handleEvent));
|
|
81
|
+
const controller2 = xr.getController(index);
|
|
82
|
+
controller2.removeEventListener("connected", handleConnected);
|
|
83
|
+
controller2.removeEventListener("disconnected", handleDisconnected);
|
|
93
84
|
}
|
|
85
|
+
const controller = $store?.targetRay;
|
|
86
|
+
events.forEach((name) => controller?.removeEventListener(name, handleEvent));
|
|
87
|
+
store.set(void 0);
|
|
94
88
|
});
|
|
95
89
|
</script>
|
|
96
90
|
|
|
@@ -2,9 +2,20 @@ import { SvelteComponent } from "svelte";
|
|
|
2
2
|
import type { XRControllerEvent } from '../types';
|
|
3
3
|
declare const __propDef: {
|
|
4
4
|
props: {
|
|
5
|
+
/** Whether the controller should be matched with the left hand. */
|
|
5
6
|
left: true;
|
|
7
|
+
right?: undefined;
|
|
8
|
+
hand?: undefined;
|
|
6
9
|
} | {
|
|
10
|
+
/** Whether the controller should be matched with the right hand. */
|
|
7
11
|
right: true;
|
|
12
|
+
left?: undefined;
|
|
13
|
+
hand?: undefined;
|
|
14
|
+
} | {
|
|
15
|
+
/** Whether the controller should be matched with the left or right hand. */
|
|
16
|
+
hand: 'left' | 'right';
|
|
17
|
+
left?: undefined;
|
|
18
|
+
right?: undefined;
|
|
8
19
|
};
|
|
9
20
|
slots: {
|
|
10
21
|
default: {};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<script context='module'>import { T, useThrelte, createRawEventDispatcher, useFrame } from "@threlte/core";
|
|
2
2
|
import { XRHandModelFactory } from "three/examples/jsm/webxr/XRHandModelFactory";
|
|
3
|
-
import {
|
|
3
|
+
import { isHandTracking } from "../internal/stores";
|
|
4
|
+
import { useHandTrackingState } from "../internal/useHandTrackingState";
|
|
4
5
|
import { left as leftStore, right as rightStore } from "../hooks/useHand";
|
|
5
|
-
import { useHandEvent } from "../hooks/useEvent";
|
|
6
6
|
import { onDestroy } from "svelte";
|
|
7
7
|
const factory = new XRHandModelFactory();
|
|
8
8
|
const stores = {
|
|
@@ -12,48 +12,38 @@ const stores = {
|
|
|
12
12
|
const eventMap = /* @__PURE__ */ new WeakMap();
|
|
13
13
|
</script>
|
|
14
14
|
|
|
15
|
-
<script>export let left =
|
|
16
|
-
export let right =
|
|
15
|
+
<script>export let left = void 0;
|
|
16
|
+
export let right = void 0;
|
|
17
|
+
export let hand = void 0;
|
|
18
|
+
const handTrackingNow = useHandTrackingState();
|
|
17
19
|
const dispatch = createRawEventDispatcher();
|
|
18
20
|
const { xr } = useThrelte().renderer;
|
|
19
21
|
const space = xr.getReferenceSpace();
|
|
20
22
|
$:
|
|
21
|
-
|
|
22
|
-
throw new Error("A <Hand> component can only specify one hand.");
|
|
23
|
-
}
|
|
24
|
-
$:
|
|
25
|
-
if (!left && !right) {
|
|
26
|
-
throw new Error("A <Hand> component must specify a hand.");
|
|
27
|
-
}
|
|
28
|
-
$:
|
|
29
|
-
handedness = left ? "left" : "right";
|
|
23
|
+
handedness = left ? "left" : right ? "right" : hand;
|
|
30
24
|
const handleConnected = (event) => {
|
|
31
|
-
|
|
32
|
-
const eventHandedness = event.data.handedness;
|
|
33
|
-
if (eventHandedness !== handedness)
|
|
25
|
+
if (event.data.handedness !== handedness)
|
|
34
26
|
return;
|
|
35
|
-
stores[handedness].set({ ...eventMap.get(event.target), inputSource:
|
|
36
|
-
|
|
27
|
+
stores[handedness].set({ ...eventMap.get(event.target), inputSource: event.data.hand });
|
|
28
|
+
if (handTrackingNow()) {
|
|
29
|
+
dispatch("connected", event);
|
|
30
|
+
}
|
|
31
|
+
event.target.addEventListener("pinchstart", handlePinchEvent);
|
|
32
|
+
event.target.addEventListener("pinchend", handlePinchEvent);
|
|
37
33
|
};
|
|
38
34
|
const handleDisconnected = (event) => {
|
|
39
|
-
|
|
40
|
-
if (eventHandedness !== handedness)
|
|
35
|
+
if (event.data.handedness !== handedness)
|
|
41
36
|
return;
|
|
42
37
|
stores[handedness].set(void 0);
|
|
43
|
-
|
|
38
|
+
if ($isHandTracking) {
|
|
39
|
+
dispatch("disconnected", event);
|
|
40
|
+
}
|
|
41
|
+
event.target.removeEventListener("pinchstart", handlePinchEvent);
|
|
42
|
+
event.target.removeEventListener("pinchend", handlePinchEvent);
|
|
44
43
|
};
|
|
45
44
|
const handlePinchEvent = (event) => {
|
|
46
|
-
|
|
45
|
+
dispatch(event.type, event);
|
|
47
46
|
};
|
|
48
|
-
for (const index of [0, 1]) {
|
|
49
|
-
const hand2 = xr.getHand(index);
|
|
50
|
-
const model2 = factory.createHandModel(hand2, "mesh");
|
|
51
|
-
eventMap.set(hand2, { hand: hand2, model: model2 });
|
|
52
|
-
hand2.addEventListener("connected", handleConnected);
|
|
53
|
-
hand2.addEventListener("disconnected", handleDisconnected);
|
|
54
|
-
hand2.addEventListener("pinchstart", handlePinchEvent);
|
|
55
|
-
hand2.addEventListener("pinchend", handlePinchEvent);
|
|
56
|
-
}
|
|
57
47
|
let children;
|
|
58
48
|
const { start, stop } = useFrame(() => {
|
|
59
49
|
const frame = xr.getFrame();
|
|
@@ -74,38 +64,34 @@ $:
|
|
|
74
64
|
stop();
|
|
75
65
|
}
|
|
76
66
|
$:
|
|
77
|
-
store =
|
|
78
|
-
$:
|
|
79
|
-
hand = $store?.hand;
|
|
67
|
+
store = stores[handedness];
|
|
80
68
|
$:
|
|
81
69
|
inputSource = $store?.inputSource;
|
|
82
70
|
$:
|
|
83
71
|
model = $store?.model;
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
"
|
|
87
|
-
|
|
88
|
-
"
|
|
89
|
-
|
|
90
|
-
for (const name of handEvents) {
|
|
91
|
-
useHandEvent(name, (event) => dispatch(name, event), {
|
|
92
|
-
handedness: left ? "left" : "right"
|
|
93
|
-
});
|
|
72
|
+
for (const index of [0, 1]) {
|
|
73
|
+
const hand2 = xr.getHand(index);
|
|
74
|
+
const model2 = factory.createHandModel(hand2, "mesh");
|
|
75
|
+
eventMap.set(hand2, { hand: hand2, model: model2 });
|
|
76
|
+
hand2.addEventListener("connected", handleConnected);
|
|
77
|
+
hand2.addEventListener("disconnected", handleDisconnected);
|
|
94
78
|
}
|
|
95
79
|
onDestroy(() => {
|
|
96
80
|
for (const index of [0, 1]) {
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
hand2.removeEventListener("pinchstart", handlePinchEvent);
|
|
101
|
-
hand2.removeEventListener("pinchend", handlePinchEvent);
|
|
81
|
+
const hand3 = xr.getHand(index);
|
|
82
|
+
hand3.removeEventListener("connected", handleConnected);
|
|
83
|
+
hand3.removeEventListener("disconnected", handleDisconnected);
|
|
102
84
|
}
|
|
85
|
+
const hand2 = stores[handedness].current?.hand;
|
|
86
|
+
hand2?.removeEventListener("pinchstart", handlePinchEvent);
|
|
87
|
+
hand2?.removeEventListener("pinchend", handlePinchEvent);
|
|
88
|
+
stores[handedness].set(void 0);
|
|
103
89
|
});
|
|
104
90
|
</script>
|
|
105
91
|
|
|
106
|
-
{#if hand}
|
|
92
|
+
{#if $store?.hand}
|
|
107
93
|
<T
|
|
108
|
-
is={hand}
|
|
94
|
+
is={$store.hand}
|
|
109
95
|
name='XR hand {handedness}'
|
|
110
96
|
>
|
|
111
97
|
{#if $$slots.default === undefined}
|
|
@@ -2,18 +2,30 @@ import { SvelteComponent } from "svelte";
|
|
|
2
2
|
import type { XRHandEvent } from '../types';
|
|
3
3
|
declare const __propDef: {
|
|
4
4
|
props: {
|
|
5
|
-
/** Whether the XRHand should be matched with the left hand. */
|
|
6
|
-
|
|
5
|
+
/** Whether the XRHand should be matched with the left hand. */
|
|
6
|
+
left: true;
|
|
7
|
+
right?: undefined;
|
|
8
|
+
hand?: undefined;
|
|
9
|
+
} | {
|
|
10
|
+
/** Whether the XRHand should be matched with the right hand. */
|
|
11
|
+
right: true;
|
|
12
|
+
left?: undefined;
|
|
13
|
+
hand?: undefined;
|
|
14
|
+
} | {
|
|
15
|
+
/** Whether the XRHand should be matched with the left or right hand. */
|
|
16
|
+
hand: 'left' | 'right';
|
|
17
|
+
left?: undefined;
|
|
18
|
+
right?: undefined;
|
|
7
19
|
};
|
|
8
20
|
slots: {
|
|
9
21
|
wrist: {};
|
|
10
22
|
default: {};
|
|
11
23
|
};
|
|
12
24
|
events: {
|
|
13
|
-
connected: XRHandEvent<'connected'
|
|
14
|
-
disconnected: XRHandEvent<'disconnected'
|
|
15
|
-
pinchstart: XRHandEvent<'pinchstart'
|
|
16
|
-
pinchend: XRHandEvent<'pinchend'
|
|
25
|
+
connected: XRHandEvent<'connected'>;
|
|
26
|
+
disconnected: XRHandEvent<'disconnected'>;
|
|
27
|
+
pinchstart: XRHandEvent<'pinchstart'>;
|
|
28
|
+
pinchend: XRHandEvent<'pinchend'>;
|
|
17
29
|
};
|
|
18
30
|
};
|
|
19
31
|
export type HandProps = typeof __propDef.props;
|
|
@@ -1,21 +1,37 @@
|
|
|
1
1
|
<script>import { T, HierarchicalObject, useFrame, useThrelte } from "@threlte/core";
|
|
2
2
|
import { Group } from "three";
|
|
3
|
-
import {
|
|
4
|
-
const {
|
|
3
|
+
import { useXR } from "../hooks";
|
|
4
|
+
const { isPresenting } = useXR();
|
|
5
|
+
const { renderer, scene, camera } = useThrelte();
|
|
6
|
+
const { xr } = renderer;
|
|
5
7
|
const group = new Group();
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
const space = baseReferenceSpace.current;
|
|
8
|
+
const immersiveFrame = useFrame(() => {
|
|
9
|
+
const space = xr.getReferenceSpace();
|
|
9
10
|
if (space === null)
|
|
10
11
|
return;
|
|
11
|
-
const
|
|
12
|
-
const pose = frame.getViewerPose(space);
|
|
12
|
+
const pose = xr.getFrame().getViewerPose(space);
|
|
13
13
|
if (pose === void 0)
|
|
14
14
|
return;
|
|
15
15
|
const { position, orientation } = pose.transform;
|
|
16
16
|
group.position.set(position.x, position.y, position.z);
|
|
17
17
|
group.quaternion.set(orientation.x, orientation.y, orientation.z, orientation.w);
|
|
18
|
-
});
|
|
18
|
+
}, { autostart: false });
|
|
19
|
+
$:
|
|
20
|
+
if ($isPresenting) {
|
|
21
|
+
immersiveFrame.start();
|
|
22
|
+
} else {
|
|
23
|
+
immersiveFrame.stop();
|
|
24
|
+
}
|
|
25
|
+
const nonImmersiveFrame = useFrame(() => {
|
|
26
|
+
group.position.copy(camera.current.position);
|
|
27
|
+
group.quaternion.copy(camera.current.quaternion);
|
|
28
|
+
}, { autostart: false });
|
|
29
|
+
$:
|
|
30
|
+
if ($isPresenting === false) {
|
|
31
|
+
nonImmersiveFrame.start();
|
|
32
|
+
} else {
|
|
33
|
+
nonImmersiveFrame.stop();
|
|
34
|
+
}
|
|
19
35
|
</script>
|
|
20
36
|
|
|
21
37
|
<HierarchicalObject
|
|
@@ -46,7 +46,7 @@ const handleVisibilityChange = (event) => {
|
|
|
46
46
|
dispatch("visibilitychange", { ...event, target: $session });
|
|
47
47
|
};
|
|
48
48
|
const handleInputSourcesChange = (event) => {
|
|
49
|
-
$isHandTracking = Object.values(
|
|
49
|
+
$isHandTracking = Object.values(event.session.inputSources).some((source) => source.hand);
|
|
50
50
|
dispatch("inputsourceschange", { ...event, target: $session });
|
|
51
51
|
};
|
|
52
52
|
const handleFramerateChange = (event) => {
|
package/dist/hooks/index.d.ts
CHANGED
package/dist/hooks/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as THREE from 'three';
|
|
2
2
|
/**
|
|
3
|
-
* Returns a callback to teleport the player to a position.
|
|
3
|
+
* Returns a callback to teleport the player from the world origin to a position and optional orientation.
|
|
4
4
|
*
|
|
5
5
|
* @example
|
|
6
6
|
* const teleport = useTeleport()
|
|
@@ -9,5 +9,9 @@ import * as THREE from 'three';
|
|
|
9
9
|
* vec3.set(5, 0, 5)
|
|
10
10
|
*
|
|
11
11
|
* teleport(vec3)
|
|
12
|
+
*
|
|
13
|
+
* const quat = new THREE.Quaternion()
|
|
14
|
+
*
|
|
15
|
+
* teleport(vec3, quat)
|
|
12
16
|
*/
|
|
13
|
-
export declare const useTeleport: () => (position: THREE.Vector3 | THREE.Vector3Tuple,
|
|
17
|
+
export declare const useTeleport: () => (position: THREE.Vector3 | THREE.Vector3Tuple, orientation?: THREE.Quaternion) => void;
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import * as THREE from 'three';
|
|
2
2
|
import { useThrelte } from '@threlte/core';
|
|
3
|
-
import { useBaseReferenceSpace } from '../internal/useBaseReferenceSpace';
|
|
4
3
|
const quaternion = new THREE.Quaternion();
|
|
5
4
|
const offset = { x: 0, y: 0, z: 0 };
|
|
6
5
|
/**
|
|
7
|
-
* Returns a callback to teleport the player to a position.
|
|
6
|
+
* Returns a callback to teleport the player from the world origin to a position and optional orientation.
|
|
8
7
|
*
|
|
9
8
|
* @example
|
|
10
9
|
* const teleport = useTeleport()
|
|
@@ -13,36 +12,37 @@ const offset = { x: 0, y: 0, z: 0 };
|
|
|
13
12
|
* vec3.set(5, 0, 5)
|
|
14
13
|
*
|
|
15
14
|
* teleport(vec3)
|
|
15
|
+
*
|
|
16
|
+
* const quat = new THREE.Quaternion()
|
|
17
|
+
*
|
|
18
|
+
* teleport(vec3, quat)
|
|
16
19
|
*/
|
|
17
20
|
export const useTeleport = () => {
|
|
18
21
|
const { xr } = useThrelte().renderer;
|
|
19
|
-
|
|
22
|
+
let space = xr.getReferenceSpace();
|
|
20
23
|
/**
|
|
21
|
-
* Teleports a player to a position
|
|
24
|
+
* Teleports a player from the world origin to a position and optional orientation.
|
|
22
25
|
*/
|
|
23
|
-
return (position,
|
|
24
|
-
|
|
25
|
-
if (space === null
|
|
26
|
+
return (position, orientation = quaternion) => {
|
|
27
|
+
space ??= xr.getReferenceSpace();
|
|
28
|
+
if (space === null)
|
|
26
29
|
return;
|
|
27
|
-
let x = 0, y = 0, z = 0;
|
|
28
30
|
if (Array.isArray(position)) {
|
|
29
|
-
;
|
|
30
|
-
|
|
31
|
+
offset.x = -position[0];
|
|
32
|
+
offset.y = -position[1];
|
|
33
|
+
offset.z = -position[2];
|
|
31
34
|
}
|
|
32
35
|
else {
|
|
33
|
-
x = position.x;
|
|
34
|
-
y = position.y;
|
|
35
|
-
z = position.z;
|
|
36
|
+
offset.x = -position.x;
|
|
37
|
+
offset.y = -position.y;
|
|
38
|
+
offset.z = -position.z;
|
|
36
39
|
}
|
|
37
|
-
|
|
38
|
-
offset.y = -y;
|
|
39
|
-
offset.z = -z;
|
|
40
|
-
const pose = xr.getFrame().getViewerPose(space);
|
|
40
|
+
const pose = xr.getFrame()?.getViewerPose(space);
|
|
41
41
|
if (pose !== undefined) {
|
|
42
42
|
offset.x += pose.transform.position.x;
|
|
43
43
|
offset.z += pose.transform.position.z;
|
|
44
44
|
}
|
|
45
|
-
const teleportOffset = new XRRigidTransform(offset,
|
|
45
|
+
const teleportOffset = new XRRigidTransform(offset, orientation);
|
|
46
46
|
xr.setReferenceSpace(space.getOffsetReferenceSpace(teleportOffset));
|
|
47
47
|
};
|
|
48
48
|
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useThrelte } from '@threlte/core';
|
|
2
|
+
/**
|
|
3
|
+
* There are some cases where we need to know if hand tracking is now active before an input source
|
|
4
|
+
* connection or disconnection event. This is the way to do that.
|
|
5
|
+
*/
|
|
6
|
+
export const useHandTrackingState = () => {
|
|
7
|
+
const { xr } = useThrelte().renderer;
|
|
8
|
+
return () => {
|
|
9
|
+
let handTracking = false;
|
|
10
|
+
xr.getSession()?.inputSources.forEach((value) => {
|
|
11
|
+
if (value.hand) {
|
|
12
|
+
handTracking = true;
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
return handTracking;
|
|
16
|
+
};
|
|
17
|
+
};
|
package/dist/types.d.ts
CHANGED
|
@@ -10,8 +10,8 @@ export type XRSessionEvent<Type = XRSessionEventType> = THREE.Event & {
|
|
|
10
10
|
};
|
|
11
11
|
export type XRControllerEvent<Type = XRControllerEventType> = THREE.Event & {
|
|
12
12
|
type: Type;
|
|
13
|
-
target:
|
|
14
|
-
data
|
|
13
|
+
target: THREE.Group;
|
|
14
|
+
data: XRInputSource;
|
|
15
15
|
};
|
|
16
16
|
export type XRController = {
|
|
17
17
|
targetRay: THREE.XRTargetRaySpace;
|
|
@@ -24,8 +24,13 @@ export type XRHand = {
|
|
|
24
24
|
model?: XRHandModel;
|
|
25
25
|
inputSource: globalThis.XRHand;
|
|
26
26
|
};
|
|
27
|
-
export type XRHandEvent<Type = XRHandEventType
|
|
27
|
+
export type XRHandEvent<Type = XRHandEventType> = Type extends 'connected' | 'disconnected' ? {
|
|
28
28
|
type: Type;
|
|
29
|
-
target:
|
|
30
|
-
|
|
29
|
+
target: THREE.XRHandSpace;
|
|
30
|
+
data: XRInputSource;
|
|
31
|
+
} : Type extends 'pinchstart' | 'pinchend' ? {
|
|
32
|
+
type: Type;
|
|
33
|
+
handedness: 'left' | 'right';
|
|
34
|
+
target: null;
|
|
35
|
+
} : never;
|
|
31
36
|
export type HitTestCallback = (hitMatrix: THREE.Matrix4, hit: XRHitTestResult) => void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@threlte/xr",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"author": "Micheal Parks <michealparks1989@gmail.com> (https://parks.lol)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"devDependencies": {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"typescript": "^5.1.6",
|
|
19
19
|
"vite": "^4.4.6",
|
|
20
20
|
"vite-plugin-mkcert": "^1.16.0",
|
|
21
|
-
"@threlte/core": "6.0.
|
|
21
|
+
"@threlte/core": "6.0.9"
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
24
24
|
"svelte": ">=4",
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
@component
|
|
3
|
-
Creates XRHand inputs for devices that allow hand tracking.
|
|
4
|
-
-->
|
|
5
|
-
<script>import { createRawEventDispatcher } from "@threlte/core";
|
|
6
|
-
import Hand from "./Hand.svelte";
|
|
7
|
-
export let profile = "mesh";
|
|
8
|
-
const dispatch = createRawEventDispatcher();
|
|
9
|
-
const handedness = [void 0, void 0];
|
|
10
|
-
const setHandedness = (index, event) => {
|
|
11
|
-
if (event.data) {
|
|
12
|
-
handedness[index] = event.data.handedness;
|
|
13
|
-
}
|
|
14
|
-
};
|
|
15
|
-
</script>
|
|
16
|
-
|
|
17
|
-
{#each [0, 1] as index (index)}
|
|
18
|
-
<Hand
|
|
19
|
-
{index}
|
|
20
|
-
{profile}
|
|
21
|
-
on:connected={(event) => {
|
|
22
|
-
setHandedness(index, event)
|
|
23
|
-
dispatch(event.type, event)
|
|
24
|
-
}}
|
|
25
|
-
on:disconnected
|
|
26
|
-
on:pinchstart
|
|
27
|
-
on:pinchend
|
|
28
|
-
>
|
|
29
|
-
{#if handedness[index] === 'left'}
|
|
30
|
-
<slot name='left' />
|
|
31
|
-
{:else if handedness[index] === 'right'}
|
|
32
|
-
<slot name='right' />
|
|
33
|
-
{/if}
|
|
34
|
-
</Hand>
|
|
35
|
-
{/each}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { SvelteComponent } from "svelte";
|
|
2
|
-
import type { XRHandEvent } from '../types';
|
|
3
|
-
declare const __propDef: {
|
|
4
|
-
props: {
|
|
5
|
-
profile?: 'mesh' | 'spheres' | 'boxes' | 'none';
|
|
6
|
-
};
|
|
7
|
-
slots: {
|
|
8
|
-
left: {};
|
|
9
|
-
right: {};
|
|
10
|
-
};
|
|
11
|
-
events: {
|
|
12
|
-
connected: XRHandEvent<'connected', null>;
|
|
13
|
-
disconnected: XRHandEvent<'disconnected', null>;
|
|
14
|
-
pinchstart: XRHandEvent<'pinchstart', THREE.XRHandSpace>;
|
|
15
|
-
pinchend: XRHandEvent<'pinchend', THREE.XRHandSpace>;
|
|
16
|
-
};
|
|
17
|
-
};
|
|
18
|
-
export type HandsProps = typeof __propDef.props;
|
|
19
|
-
export type HandsEvents = typeof __propDef.events;
|
|
20
|
-
export type HandsSlots = typeof __propDef.slots;
|
|
21
|
-
/** Creates XRHand inputs for devices that allow hand tracking. */
|
|
22
|
-
export default class Hands extends SvelteComponent<HandsProps, HandsEvents, HandsSlots> {
|
|
23
|
-
}
|
|
24
|
-
export {};
|
package/dist/hooks/useEvent.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/// <reference types="webxr" />
|
|
2
|
-
import type { XRControllerEvent, XRControllerEventType, XRHandEvent, XRHandEventType } from '../types';
|
|
3
|
-
/**
|
|
4
|
-
* Adds listeners for controller events.
|
|
5
|
-
*/
|
|
6
|
-
export declare const useControllerEvent: (event: XRControllerEventType, handler: (event: XRControllerEvent) => void, { handedness }?: {
|
|
7
|
-
handedness?: XRHandedness;
|
|
8
|
-
}) => void;
|
|
9
|
-
/**
|
|
10
|
-
* Adds listeners for hand events.
|
|
11
|
-
*/
|
|
12
|
-
export declare const useHandEvent: (event: XRHandEventType, handler: (event: XRHandEvent<XRHandEventType, null | THREE.XRHandSpace>) => void, { handedness }?: {
|
|
13
|
-
handedness?: 'left' | 'right';
|
|
14
|
-
}) => void;
|
package/dist/hooks/useEvent.js
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { onDestroy } from 'svelte';
|
|
2
|
-
import { off, on } from '../internal/events';
|
|
3
|
-
/**
|
|
4
|
-
* Adds listeners for controller events.
|
|
5
|
-
*/
|
|
6
|
-
export const useControllerEvent = (event, handler, { handedness } = {}) => {
|
|
7
|
-
const listener = (event, metadata) => {
|
|
8
|
-
if (metadata?.input === 'hand')
|
|
9
|
-
return;
|
|
10
|
-
if (handedness !== undefined && event.data?.handedness !== handedness) {
|
|
11
|
-
return;
|
|
12
|
-
}
|
|
13
|
-
handler(event);
|
|
14
|
-
};
|
|
15
|
-
on(event, listener);
|
|
16
|
-
onDestroy(() => off(event, listener));
|
|
17
|
-
};
|
|
18
|
-
/**
|
|
19
|
-
* Adds listeners for hand events.
|
|
20
|
-
*/
|
|
21
|
-
export const useHandEvent = (event, handler, { handedness } = {}) => {
|
|
22
|
-
const listener = (event, metadata) => {
|
|
23
|
-
if (metadata?.input === 'controller')
|
|
24
|
-
return;
|
|
25
|
-
const eventHandedness = event.handedness ?? event.data?.handedness;
|
|
26
|
-
if (handedness !== undefined && eventHandedness !== handedness) {
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
handler(event);
|
|
30
|
-
};
|
|
31
|
-
on(event, listener);
|
|
32
|
-
onDestroy(() => off(event, listener));
|
|
33
|
-
};
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
type Callback<T = any, O = any> = (event: T, metadata?: O) => void;
|
|
2
|
-
export declare const on: <T, O = any>(name: string, cb: Callback<T, O>) => (() => void);
|
|
3
|
-
export declare const off: <T>(name: string, cb: Callback<T, any>) => void;
|
|
4
|
-
export declare const fire: <T, O = any>(name: string, payload: T, metadata?: O | undefined) => void;
|
|
5
|
-
export {};
|
package/dist/internal/events.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
const events = {};
|
|
2
|
-
export const on = (name, cb) => {
|
|
3
|
-
const fns = events[name];
|
|
4
|
-
if (fns === undefined) {
|
|
5
|
-
events[name] = [cb];
|
|
6
|
-
}
|
|
7
|
-
else {
|
|
8
|
-
fns.push(cb);
|
|
9
|
-
}
|
|
10
|
-
return () => off(name, cb);
|
|
11
|
-
};
|
|
12
|
-
export const off = (name, cb) => {
|
|
13
|
-
const arr = events[name];
|
|
14
|
-
if (arr === undefined)
|
|
15
|
-
return;
|
|
16
|
-
arr.splice(arr.indexOf(cb), 1);
|
|
17
|
-
if (arr.length === 0)
|
|
18
|
-
delete events[name];
|
|
19
|
-
};
|
|
20
|
-
export const fire = (name, payload, metadata) => {
|
|
21
|
-
const fns = events[name];
|
|
22
|
-
if (fns === undefined)
|
|
23
|
-
return;
|
|
24
|
-
for (const fn of fns)
|
|
25
|
-
fn(payload, metadata);
|
|
26
|
-
};
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/// <reference types="webxr" />
|
|
2
|
-
/**
|
|
3
|
-
* Provides a reference space of the world origin,
|
|
4
|
-
* useful for determining offsets using XRReferenceSpace
|
|
5
|
-
*/
|
|
6
|
-
export declare const useBaseReferenceSpace: () => import("@threlte/core").CurrentWritable<XRReferenceSpace | null>;
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { onDestroy } from 'svelte';
|
|
2
|
-
import { useThrelte, currentWritable } from '@threlte/core';
|
|
3
|
-
import { session } from './stores';
|
|
4
|
-
/**
|
|
5
|
-
* Provides a reference space of the world origin,
|
|
6
|
-
* useful for determining offsets using XRReferenceSpace
|
|
7
|
-
*/
|
|
8
|
-
export const useBaseReferenceSpace = () => {
|
|
9
|
-
const { xr } = useThrelte().renderer;
|
|
10
|
-
const baseReferenceSpace = currentWritable(null);
|
|
11
|
-
onDestroy(session.subscribe((value) => {
|
|
12
|
-
if (value === undefined) {
|
|
13
|
-
baseReferenceSpace.set(null);
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
baseReferenceSpace.set(xr.getReferenceSpace());
|
|
17
|
-
}));
|
|
18
|
-
return baseReferenceSpace;
|
|
19
|
-
};
|