@threlte/xr 0.0.12 → 0.0.13

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.
Files changed (37) hide show
  1. package/dist/components/ARButton.svelte +1 -1
  2. package/dist/components/Controller.svelte +24 -83
  3. package/dist/components/Hand.svelte +27 -64
  4. package/dist/components/Hand.svelte.d.ts +1 -0
  5. package/dist/components/XR.svelte +46 -52
  6. package/dist/components/XRButton.svelte +2 -2
  7. package/dist/components/internal/PointerCursor.svelte +7 -3
  8. package/dist/components/internal/TeleportCursor.svelte +7 -3
  9. package/dist/hooks/useController.js +2 -4
  10. package/dist/hooks/useHand.js +2 -0
  11. package/dist/hooks/useHandJoint.d.ts +2 -2
  12. package/dist/hooks/useHeadset.d.ts +2 -1
  13. package/dist/hooks/useHeadset.js +1 -1
  14. package/dist/hooks/useHitTest.d.ts +2 -2
  15. package/dist/hooks/useHitTest.js +16 -6
  16. package/dist/hooks/useTeleport.d.ts +2 -2
  17. package/dist/hooks/useTeleport.js +2 -2
  18. package/dist/internal/setupControllers.d.ts +1 -0
  19. package/dist/internal/setupControllers.js +69 -0
  20. package/dist/internal/setupHands.d.ts +1 -0
  21. package/dist/internal/setupHands.js +63 -0
  22. package/dist/internal/setupHeadset.d.ts +3 -0
  23. package/dist/internal/{headset.js → setupHeadset.js} +2 -3
  24. package/dist/internal/setupRaf.d.ts +1 -0
  25. package/dist/internal/{updateRaf.js → setupRaf.js} +2 -2
  26. package/dist/internal/stores.d.ts +22 -7
  27. package/dist/internal/stores.js +15 -9
  28. package/dist/internal/useHandTrackingState.js +1 -1
  29. package/dist/lib/toggleXRSession.js +3 -3
  30. package/dist/plugins/pointerControls/types.d.ts +13 -12
  31. package/dist/plugins/teleportControls/context.d.ts +7 -6
  32. package/dist/plugins/teleportControls/hook.d.ts +5 -4
  33. package/dist/plugins/teleportControls/index.d.ts +1 -1
  34. package/dist/types.d.ts +10 -8
  35. package/package.json +4 -4
  36. package/dist/internal/headset.d.ts +0 -3
  37. package/dist/internal/updateRaf.d.ts +0 -1
@@ -17,7 +17,7 @@
17
17
  sessionInit={{
18
18
  domOverlay: typeof document !== 'undefined' ? { root: document.body } : undefined,
19
19
  requiredFeatures: ['plane-detection'],
20
- optionalFeatures: ['hit-test', 'light-estimation', 'dom-overlay', 'dom-overlay-for-handheld-ar']
20
+ optionalFeatures: ['local-floor', 'bounded-floor', 'hand-tracking', 'layers', 'hit-test']
21
21
  }}
22
22
  {...$$restProps}
23
23
  mode='immersive-ar'
@@ -4,76 +4,33 @@
4
4
  <script
5
5
 
6
6
  context="module"
7
- >import { XRControllerModelFactory } from "three/examples/jsm/webxr/XRControllerModelFactory";
8
- import { onDestroy } from "svelte";
9
- import { T, createRawEventDispatcher, useThrelte } from "@threlte/core";
10
- import { gaze, left as leftStore, right as rightStore } from "../hooks/useController";
11
- import { isHandTracking, pointerState, teleportState } from "../internal/stores";
12
- import { useHandTrackingState } from "../internal/useHandTrackingState";
7
+ >import { writable } from "svelte/store";
8
+ import { T, createRawEventDispatcher } from "@threlte/core";
9
+ import { left as leftStore, right as rightStore } from "../hooks/useController";
10
+ import { isHandTracking, pointerState, teleportState, controllerDispatchers } from "../internal/stores";
13
11
  import PointerCursor from "./internal/PointerCursor.svelte";
14
12
  import ShortRay from "./internal/ShortRay.svelte";
15
13
  import ScenePortal from "./internal/ScenePortal.svelte";
16
14
  import TeleportCursor from "./internal/TeleportCursor.svelte";
17
15
  import TeleportRay from "./internal/TeleportRay.svelte";
18
- const factory = new XRControllerModelFactory();
19
16
  const stores = {
20
17
  left: leftStore,
21
- right: rightStore,
22
- none: gaze
18
+ right: rightStore
23
19
  };
24
- const events = [
25
- "select",
26
- "selectstart",
27
- "selectend",
28
- "squeeze",
29
- "squeezeend",
30
- "squeezestart"
31
- ];
32
- const eventMap = /* @__PURE__ */ new WeakMap();
33
20
  </script>
34
21
 
35
22
  <script>export let left = void 0;
36
23
  export let right = void 0;
37
24
  export let hand = void 0;
38
- $:
39
- handedness = left ? "left" : right ? "right" : hand;
40
25
  const dispatch = createRawEventDispatcher();
41
- const { xr } = useThrelte().renderer;
42
- const handTrackingNow = useHandTrackingState();
43
- const handleEvent = (event) => {
44
- if (!handTrackingNow()) {
45
- dispatch(event.type, event);
46
- }
47
- };
48
- const handleConnected = (event) => {
49
- const targetData = eventMap.get(event.target);
50
- if (event.data.handedness !== handedness || !targetData)
51
- return;
52
- stores[handedness].set({ ...targetData, inputSource: event.data });
53
- if (!handTrackingNow()) {
54
- dispatch("connected", event);
55
- }
56
- events.forEach((name) => event.target.addEventListener(name, handleEvent));
57
- };
58
- const handleDisconnected = (event) => {
59
- if (event.data.handedness !== handedness)
60
- return;
61
- stores[handedness].set(void 0);
62
- if (!$isHandTracking) {
63
- dispatch("disconnected", event);
64
- }
65
- events.forEach((name) => event.target.removeEventListener(name, handleEvent));
66
- };
67
- for (const index of [0, 1]) {
68
- const targetRay2 = xr.getController(index);
69
- const grip2 = xr.getControllerGrip(index);
70
- const model2 = factory.createControllerModel(grip2);
71
- eventMap.set(targetRay2, { targetRay: targetRay2, model: model2, grip: grip2 });
72
- targetRay2.addEventListener("connected", handleConnected);
73
- targetRay2.addEventListener("disconnected", handleDisconnected);
74
- }
26
+ const handedness = writable(left ? "left" : right ? "right" : hand);
27
+ $:
28
+ handedness.set(left ? "left" : right ? "right" : hand);
29
+ controllerDispatchers[$handedness].set(dispatch);
30
+ $:
31
+ controllerDispatchers[$handedness].set(dispatch);
75
32
  $:
76
- store = stores[handedness];
33
+ store = stores[$handedness];
77
34
  $:
78
35
  grip = $store?.grip;
79
36
  $:
@@ -81,27 +38,14 @@ $:
81
38
  $:
82
39
  model = $store?.model;
83
40
  $:
84
- hasPointerControls = $pointerState[handedness].enabled;
41
+ hasPointerControls = $pointerState[$handedness].enabled;
85
42
  $:
86
- hasTeleportControls = $teleportState[handedness].enabled;
87
- onDestroy(() => {
88
- for (const index of [0, 1]) {
89
- const controller2 = xr.getController(index);
90
- controller2.removeEventListener("connected", handleConnected);
91
- controller2.removeEventListener("disconnected", handleDisconnected);
92
- }
93
- const controller = $store?.targetRay;
94
- events.forEach((name) => controller?.removeEventListener(name, handleEvent));
95
- store.set(void 0);
96
- });
43
+ hasTeleportControls = $teleportState[$handedness].enabled;
97
44
  </script>
98
45
 
99
46
  {#if !$isHandTracking}
100
47
  {#if grip}
101
- <T
102
- is={grip}
103
- name="XR controller grip {handedness}"
104
- >
48
+ <T is={grip}>
105
49
  <slot>
106
50
  <T is={model} />
107
51
  </slot>
@@ -111,19 +55,16 @@ onDestroy(() => {
111
55
  {/if}
112
56
 
113
57
  {#if targetRay}
114
- <T
115
- is={targetRay}
116
- name="XR controller {handedness}"
117
- >
58
+ <T is={targetRay}>
118
59
  <slot name="target-ray" />
119
60
 
120
61
  {#if hasPointerControls || hasTeleportControls}
121
62
  {#if $$slots['pointer-ray']}
122
- <ShortRay {handedness}>
63
+ <ShortRay handedness={$handedness}>
123
64
  <slot name="pointer-ray" />
124
65
  </ShortRay>
125
66
  {:else}
126
- <ShortRay {handedness} />
67
+ <ShortRay handedness={$handedness} />
127
68
  {/if}
128
69
  {/if}
129
70
  </T>
@@ -133,11 +74,11 @@ onDestroy(() => {
133
74
  <ScenePortal>
134
75
  {#if hasPointerControls}
135
76
  {#if $$slots['pointer-cursor']}
136
- <PointerCursor {handedness}>
77
+ <PointerCursor handedness={$handedness}>
137
78
  <slot name="pointer-cursor" />
138
79
  </PointerCursor>
139
80
  {:else}
140
- <PointerCursor {handedness} />
81
+ <PointerCursor handedness={$handedness} />
141
82
  {/if}
142
83
  {/if}
143
84
 
@@ -145,23 +86,23 @@ onDestroy(() => {
145
86
  {#if $$slots['teleport-ray']}
146
87
  <TeleportRay
147
88
  {targetRay}
148
- {handedness}
89
+ handedness={$handedness}
149
90
  >
150
91
  <slot name="teleport-ray" />
151
92
  </TeleportRay>
152
93
  {:else}
153
94
  <TeleportRay
154
95
  {targetRay}
155
- {handedness}
96
+ handedness={$handedness}
156
97
  />
157
98
  {/if}
158
99
 
159
100
  {#if $$slots['teleport-ray']}
160
- <TeleportCursor {handedness}>
101
+ <TeleportCursor handedness={$handedness}>
161
102
  <slot name="teleport-cursor" />
162
103
  </TeleportCursor>
163
104
  {:else}
164
- <TeleportCursor {handedness} />
105
+ <TeleportCursor handedness={$handedness} />
165
106
  {/if}
166
107
  {/if}
167
108
  </ScenePortal>
@@ -1,50 +1,26 @@
1
- <script context='module'>import { onDestroy } from "svelte";
2
- import { XRHandModelFactory } from "three/examples/jsm/webxr/XRHandModelFactory";
1
+ <script context='module'>import { Group } from "three";
3
2
  import { T, useThrelte, createRawEventDispatcher, useFrame } from "@threlte/core";
4
- import { isHandTracking } from "../internal/stores";
5
- import { useHandTrackingState } from "../internal/useHandTrackingState";
3
+ import { isHandTracking, handDispatchers } from "../internal/stores";
6
4
  import { left as leftStore, right as rightStore } from "../hooks/useHand";
7
- const factory = new XRHandModelFactory();
5
+ import ScenePortal from "./internal/ScenePortal.svelte";
6
+ import { writable } from "svelte/store";
8
7
  const stores = {
9
8
  left: leftStore,
10
9
  right: rightStore
11
10
  };
12
- const eventMap = /* @__PURE__ */ new WeakMap();
13
11
  </script>
14
12
 
15
- <script>import ScenePortal from "./internal/ScenePortal.svelte";
16
- export let left = void 0;
13
+ <script>export let left = void 0;
17
14
  export let right = void 0;
18
15
  export let hand = void 0;
19
- const handTrackingNow = useHandTrackingState();
20
16
  const dispatch = createRawEventDispatcher();
21
17
  const { xr } = useThrelte().renderer;
22
18
  const space = xr.getReferenceSpace();
19
+ const handedness = writable(left ? "left" : right ? "right" : hand);
23
20
  $:
24
- handedness = left ? "left" : right ? "right" : hand;
25
- const handleConnected = (event) => {
26
- if (event.data.handedness !== handedness)
27
- return;
28
- stores[handedness].set({ ...eventMap.get(event.target), inputSource: event.data.hand });
29
- if (handTrackingNow()) {
30
- dispatch("connected", event);
31
- }
32
- event.target.addEventListener("pinchstart", handlePinchEvent);
33
- event.target.addEventListener("pinchend", handlePinchEvent);
34
- };
35
- const handleDisconnected = (event) => {
36
- if (event.data.handedness !== handedness)
37
- return;
38
- stores[handedness].set(void 0);
39
- if ($isHandTracking) {
40
- dispatch("disconnected", event);
41
- }
42
- event.target.removeEventListener("pinchstart", handlePinchEvent);
43
- event.target.removeEventListener("pinchend", handlePinchEvent);
44
- };
45
- const handlePinchEvent = (event) => {
46
- dispatch(event.type, event);
47
- };
21
+ handedness.set(left ? "left" : right ? "right" : hand);
22
+ $:
23
+ handDispatchers[$handedness].set(dispatch);
48
24
  let children;
49
25
  const { start, stop } = useFrame(() => {
50
26
  const frame = xr.getFrame();
@@ -59,51 +35,38 @@ const { start, stop } = useFrame(() => {
59
35
  children.quaternion.set(orientation.x, orientation.y, orientation.z, orientation.w);
60
36
  }, { autostart: false });
61
37
  $:
62
- if (($$slots.wrist || $$slots.default) && inputSource) {
38
+ if ($isHandTracking && ($$slots.wrist || $$slots.default) && inputSource) {
63
39
  start();
64
40
  } else {
65
41
  stop();
66
42
  }
67
43
  $:
68
- store = stores[handedness];
44
+ store = stores[$handedness];
69
45
  $:
70
46
  inputSource = $store?.inputSource;
71
47
  $:
72
48
  model = $store?.model;
73
- for (const index of [0, 1]) {
74
- const hand2 = xr.getHand(index);
75
- const model2 = factory.createHandModel(hand2, "mesh");
76
- eventMap.set(hand2, { hand: hand2, model: model2 });
77
- hand2.addEventListener("connected", handleConnected);
78
- hand2.addEventListener("disconnected", handleDisconnected);
79
- }
80
- onDestroy(() => {
81
- for (const index of [0, 1]) {
82
- const hand3 = xr.getHand(index);
83
- hand3.removeEventListener("connected", handleConnected);
84
- hand3.removeEventListener("disconnected", handleDisconnected);
85
- }
86
- const hand2 = stores[handedness].current?.hand;
87
- hand2?.removeEventListener("pinchstart", handlePinchEvent);
88
- hand2?.removeEventListener("pinchend", handlePinchEvent);
89
- stores[handedness].set(void 0);
90
- });
91
49
  </script>
92
50
 
93
- {#if $store?.hand}
94
- <T
95
- is={$store.hand}
96
- name='XR hand {handedness}'
97
- >
51
+ {#if $store?.hand && $isHandTracking}
52
+ <T is={$store.hand}>
98
53
  {#if $$slots.default === undefined}
99
54
  <T is={model} />
100
55
  {/if}
101
56
  </T>
57
+
58
+ {#if $$slots['target-ray'] !== undefined}
59
+ <T is={$store.targetRay}>
60
+ <slot name='target-ray' />
61
+ </T>
62
+ {/if}
102
63
  {/if}
103
64
 
104
- <ScenePortal>
105
- <T.Group bind:ref={children}>
106
- <slot name='wrist' />
107
- <slot />
108
- </T.Group>
109
- </ScenePortal>
65
+ {#if $isHandTracking}
66
+ <ScenePortal>
67
+ <T.Group bind:ref={children}>
68
+ <slot name='wrist' />
69
+ <slot />
70
+ </T.Group>
71
+ </ScenePortal>
72
+ {/if}
@@ -18,6 +18,7 @@ declare const __propDef: {
18
18
  right?: undefined;
19
19
  };
20
20
  slots: {
21
+ 'target-ray': {};
21
22
  wrist: {};
22
23
  default: {};
23
24
  };
@@ -17,33 +17,38 @@ This should be placed within a Threlte `<Canvas />`.
17
17
  ```
18
18
 
19
19
  -->
20
- <script>import { onDestroy } from "svelte";
21
- import { createRawEventDispatcher, useThrelte } from "@threlte/core";
20
+ <script>import { onMount } from "svelte";
21
+ import { createRawEventDispatcher, useThrelte, watch } from "@threlte/core";
22
22
  import {
23
- initialized,
24
23
  isHandTracking,
25
24
  isPresenting,
26
25
  referenceSpaceType,
27
26
  session,
28
27
  xr as xrStore
29
28
  } from "../internal/stores";
30
- import { updateRaf } from "../internal/updateRaf";
31
- import { useUpdateHeadset } from "../internal/headset";
29
+ import { setupRaf } from "../internal/setupRaf";
30
+ import { setupHeadset } from "../internal/setupHeadset";
31
+ import { setupControllers } from "../internal/setupControllers";
32
+ import { setupHands } from "../internal/setupHands";
32
33
  export let foveation = 1;
33
34
  export let frameRate = void 0;
34
35
  export let referenceSpace = "local-floor";
35
36
  const dispatch = createRawEventDispatcher();
36
- updateRaf();
37
37
  const { renderer, frameloop } = useThrelte();
38
38
  const { xr } = renderer;
39
- const handleSessionStart = (event) => {
40
- $isPresenting = true;
41
- dispatch("sessionstart", { ...event, target: $session });
39
+ let originalFrameloop = $frameloop;
40
+ setupRaf();
41
+ setupHeadset();
42
+ setupControllers();
43
+ setupHands();
44
+ const handleSessionStart = () => {
45
+ isPresenting.set(true);
46
+ dispatch("sessionstart", { type: "sessionstart", target: $session });
42
47
  };
43
- const handleSessionEnd = (event) => {
44
- dispatch("sessionend", { ...event, target: $session });
45
- $isPresenting = false;
46
- $session = void 0;
48
+ const handleSessionEnd = () => {
49
+ dispatch("sessionend", { type: "sessionend", target: $session });
50
+ isPresenting.set(false);
51
+ session.set(void 0);
47
52
  };
48
53
  const handleVisibilityChange = (event) => {
49
54
  dispatch("visibilitychange", { ...event, target: $session });
@@ -63,14 +68,7 @@ const updateTargetFrameRate = (frameRate2) => {
63
68
  } catch {
64
69
  }
65
70
  };
66
- const cleanupSession = (currentSession) => {
67
- if (currentSession === void 0)
68
- return;
69
- currentSession.removeEventListener("visibilitychange", handleVisibilityChange);
70
- currentSession.removeEventListener("inputsourceschange", handleInputSourcesChange);
71
- currentSession.removeEventListener("frameratechange", handleFramerateChange);
72
- };
73
- const updateSession = async (currentSession) => {
71
+ watch(session, (currentSession) => {
74
72
  if (currentSession === void 0)
75
73
  return;
76
74
  currentSession.addEventListener("visibilitychange", handleVisibilityChange);
@@ -78,44 +76,40 @@ const updateSession = async (currentSession) => {
78
76
  currentSession.addEventListener("frameratechange", handleFramerateChange);
79
77
  xr.setFoveation(foveation);
80
78
  updateTargetFrameRate(frameRate);
81
- };
82
- let lastSession;
83
- $initialized = true;
84
- $xrStore = xr;
85
- xr.enabled = true;
86
- xr.addEventListener("sessionstart", handleSessionStart);
87
- xr.addEventListener("sessionend", handleSessionEnd);
88
- useUpdateHeadset();
89
- onDestroy(() => {
90
- $initialized = false;
91
- $xrStore = void 0;
92
- xr.enabled = false;
93
- xr.removeEventListener("sessionstart", handleSessionStart);
94
- xr.removeEventListener("sessionend", handleSessionEnd);
79
+ return () => {
80
+ currentSession.removeEventListener("visibilitychange", handleVisibilityChange);
81
+ currentSession.removeEventListener("inputsourceschange", handleInputSourcesChange);
82
+ currentSession.removeEventListener("frameratechange", handleFramerateChange);
83
+ };
95
84
  });
96
- $: {
97
- xr.setReferenceSpaceType(referenceSpace);
98
- $referenceSpaceType = referenceSpace;
99
- }
100
- $:
101
- if (lastSession !== $session) {
102
- cleanupSession(lastSession);
103
- updateSession($session);
104
- lastSession = $session;
105
- }
106
- let originalFrameloop = $frameloop;
107
- $frameloop = "always";
108
- $:
109
- if ($isPresenting) {
110
- originalFrameloop = $frameloop;
111
- $frameloop = "always";
85
+ watch(isPresenting, (presenting) => {
86
+ if (presenting) {
87
+ originalFrameloop = frameloop.current;
88
+ frameloop.set("always");
112
89
  } else {
113
- $frameloop = originalFrameloop;
90
+ frameloop.set(originalFrameloop);
114
91
  }
92
+ });
93
+ onMount(() => {
94
+ $xrStore = xr;
95
+ xr.enabled = true;
96
+ xr.addEventListener("sessionstart", handleSessionStart);
97
+ xr.addEventListener("sessionend", handleSessionEnd);
98
+ return () => {
99
+ $xrStore = void 0;
100
+ xr.enabled = false;
101
+ xr.removeEventListener("sessionstart", handleSessionStart);
102
+ xr.removeEventListener("sessionend", handleSessionEnd);
103
+ };
104
+ });
115
105
  $:
116
106
  updateTargetFrameRate(frameRate);
117
107
  $:
118
108
  xr.setFoveation(foveation);
109
+ $: {
110
+ xr.setReferenceSpaceType(referenceSpace);
111
+ $referenceSpaceType = referenceSpace;
112
+ }
119
113
  </script>
120
114
 
121
115
  {#if $isPresenting}
@@ -21,14 +21,14 @@ display info about your WebXR session. This is aliased by `ARButton` and
21
21
  <script>import { createEventDispatcher } from "svelte";
22
22
  import { getXRSupportState } from "../lib/getXRSupportState";
23
23
  import { toggleXRSession } from "../lib/toggleXRSession";
24
- import { session, initialized } from "../internal/stores";
24
+ import { session, xr } from "../internal/stores";
25
25
  export let mode;
26
26
  export let sessionInit = void 0;
27
27
  export let force = void 0;
28
28
  export let styled = true;
29
29
  const dispatch = createEventDispatcher();
30
30
  const handleButtonClick = async (nativeEvent, state) => {
31
- if (!$initialized) {
31
+ if (!$xr) {
32
32
  throw new Error("The <XR> component was not created. This is required to start an XR session.");
33
33
  }
34
34
  dispatch("click", { state, nativeEvent });
@@ -1,10 +1,12 @@
1
- <script>import { Group, Vector3 } from "three";
1
+ <script>import { Group, Vector3, Matrix3 } from "three";
2
2
  import { T, useFrame } from "@threlte/core";
3
3
  import { pointerIntersection, pointerState } from "../../internal/stores";
4
4
  import Cursor from "./Cursor.svelte";
5
5
  export let handedness;
6
6
  const ref = new Group();
7
7
  const vec3 = new Vector3();
8
+ const normalMatrix = new Matrix3();
9
+ const worldNormal = new Vector3();
8
10
  $:
9
11
  hovering = $pointerState[handedness].hovering;
10
12
  $:
@@ -13,10 +15,12 @@ const { start, stop } = useFrame(
13
15
  () => {
14
16
  if (intersection.current === void 0)
15
17
  return;
16
- const { point, face } = intersection.current;
18
+ const { point, face, object } = intersection.current;
17
19
  ref.position.lerp(point, 0.4);
18
20
  if (face) {
19
- ref.lookAt(vec3.addVectors(point, face.normal));
21
+ normalMatrix.getNormalMatrix(object.matrixWorld);
22
+ worldNormal.copy(face.normal).applyMatrix3(normalMatrix).normalize();
23
+ ref.lookAt(vec3.addVectors(point, worldNormal));
20
24
  }
21
25
  },
22
26
  {
@@ -1,21 +1,25 @@
1
1
  <script>import { spring } from "svelte/motion";
2
- import { Group, Vector3 } from "three";
2
+ import { Group, Matrix3, Vector3 } from "three";
3
3
  import { T, useFrame } from "@threlte/core";
4
4
  import { teleportIntersection } from "../../internal/stores";
5
5
  import Cursor from "./Cursor.svelte";
6
6
  export let handedness;
7
7
  const ref = new Group();
8
8
  const vec3 = new Vector3();
9
+ const normalMatrix = new Matrix3();
10
+ const worldNormal = new Vector3();
9
11
  $:
10
12
  intersection = teleportIntersection[handedness];
11
13
  const { start, stop } = useFrame(
12
14
  () => {
13
15
  if (intersection.current === void 0)
14
16
  return;
15
- const { point, face } = intersection.current;
17
+ const { point, face, object } = intersection.current;
16
18
  ref.position.lerp(point, 0.4);
17
19
  if (face) {
18
- ref.lookAt(vec3.addVectors(point, face.normal));
20
+ normalMatrix.getNormalMatrix(object.matrixWorld);
21
+ worldNormal.copy(face.normal).applyMatrix3(normalMatrix).normalize();
22
+ ref.lookAt(vec3.addVectors(point, worldNormal));
19
23
  }
20
24
  },
21
25
  {
@@ -2,10 +2,6 @@ import { currentWritable } from '@threlte/core';
2
2
  export const left = currentWritable(undefined);
3
3
  export const right = currentWritable(undefined);
4
4
  export const gaze = currentWritable(undefined);
5
- const gamepadLeft = currentWritable(undefined);
6
- const gamepadRight = currentWritable(undefined);
7
- left.subscribe((value) => gamepadLeft.set(value?.inputSource.gamepad));
8
- right.subscribe((value) => gamepadRight.set(value?.inputSource.gamepad));
9
5
  /**
10
6
  * Provides a reference to a current XRController, filtered by handedness.
11
7
  */
@@ -17,5 +13,7 @@ export const useController = (handedness) => {
17
13
  return right;
18
14
  case 'none':
19
15
  return gaze;
16
+ default:
17
+ throw new Error('useController handedness must be left, right, or none.');
20
18
  }
21
19
  };
@@ -10,5 +10,7 @@ export const useHand = (handedness) => {
10
10
  return left;
11
11
  case 'right':
12
12
  return right;
13
+ default:
14
+ throw new Error('useHand handedness must be left or right.');
13
15
  }
14
16
  };
@@ -1,6 +1,6 @@
1
- import type * as THREE from 'three';
1
+ import type { XRJointSpace } from 'three';
2
2
  import type { HandJoints } from '../lib/handJoints';
3
3
  /**
4
4
  * Provides a reference to a requested hand joint, once available.
5
5
  */
6
- export declare const useHandJoint: (handedness: 'left' | 'right', joint: HandJoints) => import("@threlte/core").CurrentWritable<THREE.XRJointSpace | undefined>;
6
+ export declare const useHandJoint: (handedness: 'left' | 'right', joint: HandJoints) => import("@threlte/core").CurrentWritable<XRJointSpace | undefined>;
@@ -1 +1,2 @@
1
- export declare const useHeadset: () => Readonly<THREE.Group>;
1
+ import type { Group } from 'three';
2
+ export declare const useHeadset: () => Readonly<Group>;
@@ -1,4 +1,4 @@
1
- import { headset } from '../internal/headset';
1
+ import { headset } from '../internal/setupHeadset';
2
2
  export const useHeadset = () => {
3
3
  return headset;
4
4
  };
@@ -1,6 +1,6 @@
1
1
  /// <reference types="webxr" />
2
- import * as THREE from 'three';
3
- export type HitTestCallback = (hitMatrix: THREE.Matrix4, hit: XRHitTestResult | undefined) => void;
2
+ import { Matrix4 } from 'three';
3
+ export type HitTestCallback = (hitMatrix: Matrix4, hit: XRHitTestResult | undefined) => void;
4
4
  export type UseHitTestOptions = {
5
5
  /**
6
6
  * The ray source when performing hit testing.
@@ -1,4 +1,4 @@
1
- import * as THREE from 'three';
1
+ import { Matrix4 } from 'three';
2
2
  import { useThrelte, useFrame, watch, currentWritable } from '@threlte/core';
3
3
  import { useXR } from './useXR';
4
4
  import { useController } from './useController';
@@ -17,12 +17,14 @@ export const useHitTest = (hitTestCallback, options = {}) => {
17
17
  const source = options.source ?? 'viewer';
18
18
  const { xr } = useThrelte().renderer;
19
19
  const xrState = useXR();
20
- const hitMatrix = new THREE.Matrix4();
20
+ const hitMatrix = new Matrix4();
21
21
  let hitTestSource = currentWritable(undefined);
22
22
  if (source === 'viewer') {
23
23
  watch(xrState.session, async (session) => {
24
- if (session === undefined)
24
+ if (session === undefined) {
25
+ hitTestSource.set(undefined);
25
26
  return;
27
+ }
26
28
  const space = await session.requestReferenceSpace('viewer');
27
29
  hitTestSource.set(await session.requestHitTestSource?.({ space }));
28
30
  });
@@ -31,14 +33,18 @@ export const useHitTest = (hitTestCallback, options = {}) => {
31
33
  const controller = useController(source === 'leftInput' ? 'left' : 'right');
32
34
  const hand = useController(source === 'leftInput' ? 'left' : 'right');
33
35
  watch([xrState.session, controller], async ([session, input]) => {
34
- if (input === undefined || session === undefined)
36
+ if (input === undefined || session === undefined) {
37
+ hitTestSource.set(undefined);
35
38
  return;
39
+ }
36
40
  const space = input.inputSource.targetRaySpace;
37
41
  hitTestSource.set(await session.requestHitTestSource?.({ space }));
38
42
  });
39
43
  watch([xrState.session, hand], async ([session, input]) => {
40
- if (input === undefined || session === undefined)
44
+ if (input === undefined || session === undefined) {
45
+ hitTestSource.set(undefined);
41
46
  return;
47
+ }
42
48
  const space = input.inputSource.targetRaySpace;
43
49
  hitTestSource.set(await session.requestHitTestSource?.({ space }));
44
50
  });
@@ -56,7 +62,11 @@ export const useHitTest = (hitTestCallback, options = {}) => {
56
62
  hitMatrix.fromArray(pose.transform.matrix);
57
63
  hitTestCallback(hitMatrix, hit);
58
64
  }, { autostart: false });
59
- watch(hitTestSource, (testSource) => {
65
+ watch([xrState.isPresenting, hitTestSource], ([isPresenting, testSource]) => {
66
+ if (!isPresenting) {
67
+ stop();
68
+ return;
69
+ }
60
70
  if (testSource === undefined) {
61
71
  stop();
62
72
  // Execute callback one last time to inform consumers of no hits.
@@ -1,4 +1,4 @@
1
- import * as THREE from 'three';
1
+ import { Quaternion, type Vector3, type Vector3Tuple } from 'three';
2
2
  /**
3
3
  * Returns a callback to teleport the player from the world origin to a position and optional orientation.
4
4
  *
@@ -14,4 +14,4 @@ import * as THREE from 'three';
14
14
  *
15
15
  * teleport(vec3, quat)
16
16
  */
17
- export declare const useTeleport: () => (position: THREE.Vector3 | THREE.Vector3Tuple, orientation?: THREE.Quaternion) => void;
17
+ export declare const useTeleport: () => (position: Vector3 | Vector3Tuple, orientation?: Quaternion) => void;
@@ -1,6 +1,6 @@
1
- import * as THREE from 'three';
1
+ import { Quaternion } from 'three';
2
2
  import { useThrelte } from '@threlte/core';
3
- const quaternion = new THREE.Quaternion();
3
+ const quaternion = new Quaternion();
4
4
  const offset = { x: 0, y: 0, z: 0 };
5
5
  /**
6
6
  * Returns a callback to teleport the player from the world origin to a position and optional orientation.
@@ -0,0 +1 @@
1
+ export declare const setupControllers: () => void;
@@ -0,0 +1,69 @@
1
+ import { XRControllerModelFactory } from 'three/examples/jsm/webxr/XRControllerModelFactory';
2
+ import { useThrelte } from '@threlte/core';
3
+ import { onMount } from 'svelte';
4
+ import { useHandTrackingState } from './useHandTrackingState';
5
+ import { gaze, left, right } from '../hooks/useController';
6
+ import { controllerDispatchers } from './stores';
7
+ export const setupControllers = () => {
8
+ const factory = new XRControllerModelFactory();
9
+ const stores = { left, right, none: gaze };
10
+ const { xr } = useThrelte().renderer;
11
+ const hasHands = useHandTrackingState();
12
+ const controllers = [xr.getController(0), xr.getController(1)];
13
+ const indexMap = new Map();
14
+ controllers.forEach((targetRay, index) => {
15
+ indexMap.set(targetRay, {
16
+ targetRay,
17
+ grip: xr.getControllerGrip(index),
18
+ model: factory.createControllerModel(targetRay)
19
+ });
20
+ });
21
+ onMount(() => {
22
+ const dispatch = (event) => {
23
+ if (hasHands())
24
+ return;
25
+ const { data } = event;
26
+ controllerDispatchers[data.handedness]?.current?.(event.type, event);
27
+ };
28
+ function handleConnected(event) {
29
+ const { model, targetRay, grip } = indexMap.get(this);
30
+ const { data: inputSource } = event;
31
+ stores[event.data.handedness].set({
32
+ inputSource,
33
+ targetRay,
34
+ grip,
35
+ model
36
+ });
37
+ dispatch(event);
38
+ }
39
+ const handleDisconnected = (event) => {
40
+ dispatch(event);
41
+ stores[event.data.handedness].set(undefined);
42
+ };
43
+ for (const targetRay of controllers) {
44
+ targetRay.addEventListener('connected', handleConnected);
45
+ targetRay.addEventListener('disconnected', handleDisconnected);
46
+ targetRay.addEventListener('select', dispatch);
47
+ targetRay.addEventListener('selectstart', dispatch);
48
+ targetRay.addEventListener('selectend', dispatch);
49
+ targetRay.addEventListener('squeeze', dispatch);
50
+ targetRay.addEventListener('squeezestart', dispatch);
51
+ targetRay.addEventListener('squeezeend', dispatch);
52
+ }
53
+ return () => {
54
+ for (const targetRay of controllers) {
55
+ targetRay.removeEventListener('connected', handleConnected);
56
+ targetRay.removeEventListener('disconnected', handleDisconnected);
57
+ targetRay.removeEventListener('select', dispatch);
58
+ targetRay.removeEventListener('selectstart', dispatch);
59
+ targetRay.removeEventListener('selectend', dispatch);
60
+ targetRay.removeEventListener('squeeze', dispatch);
61
+ targetRay.removeEventListener('squeezestart', dispatch);
62
+ targetRay.removeEventListener('squeezeend', dispatch);
63
+ }
64
+ stores.left.set(undefined);
65
+ stores.right.set(undefined);
66
+ stores.none.set(undefined);
67
+ };
68
+ });
69
+ };
@@ -0,0 +1 @@
1
+ export declare const setupHands: () => void;
@@ -0,0 +1,63 @@
1
+ import { XRHandModelFactory } from 'three/examples/jsm/webxr/XRHandModelFactory';
2
+ import { useThrelte } from '@threlte/core';
3
+ import { onMount } from 'svelte';
4
+ import { left, right } from '../hooks/useHand';
5
+ import { useHandTrackingState } from './useHandTrackingState';
6
+ import { handDispatchers } from './stores';
7
+ export const setupHands = () => {
8
+ const factory = new XRHandModelFactory();
9
+ const stores = { left, right };
10
+ const { xr } = useThrelte().renderer;
11
+ const hasHands = useHandTrackingState();
12
+ const handSpaces = [xr.getHand(0), xr.getHand(1)];
13
+ const map = new Map();
14
+ handSpaces.forEach((handSpace, index) => {
15
+ map.set(handSpace, {
16
+ hand: handSpace,
17
+ targetRay: xr.getController(index),
18
+ model: factory.createHandModel(handSpace, 'mesh')
19
+ });
20
+ });
21
+ onMount(() => {
22
+ const dispatch = (event) => {
23
+ if (!hasHands())
24
+ return;
25
+ const handEvent = event;
26
+ const handedness = ('handedness' in handEvent) ? handEvent.handedness : handEvent.data.handedness;
27
+ handDispatchers[handedness]?.current?.(event.type, event);
28
+ };
29
+ function handleConnected(event) {
30
+ const hand = this;
31
+ const { model, targetRay } = map.get(this);
32
+ const { data } = event;
33
+ const { handedness, hand: inputSource } = data;
34
+ stores[handedness].set({
35
+ hand,
36
+ model,
37
+ inputSource,
38
+ targetRay
39
+ });
40
+ dispatch(event);
41
+ }
42
+ const handleDisconnected = (event) => {
43
+ dispatch(event);
44
+ stores[event.data.handedness].set(undefined);
45
+ };
46
+ for (const handSpace of handSpaces) {
47
+ handSpace.addEventListener('connected', handleConnected);
48
+ handSpace.addEventListener('disconnected', handleDisconnected);
49
+ handSpace.addEventListener('pinchstart', dispatch);
50
+ handSpace.addEventListener('pinchend', dispatch);
51
+ }
52
+ return () => {
53
+ for (const handSpace of handSpaces) {
54
+ handSpace.removeEventListener('connected', handleConnected);
55
+ handSpace.removeEventListener('disconnected', handleDisconnected);
56
+ handSpace.removeEventListener('pinchstart', dispatch);
57
+ handSpace.removeEventListener('pinchend', dispatch);
58
+ }
59
+ stores.left.set(undefined);
60
+ stores.right.set(undefined);
61
+ };
62
+ });
63
+ };
@@ -0,0 +1,3 @@
1
+ import { Group } from 'three';
2
+ export declare const headset: Group<import("three").Object3DEventMap>;
3
+ export declare const setupHeadset: () => void;
@@ -2,9 +2,8 @@ import { Group } from 'three';
2
2
  import { useThrelte, useFrame, watch } from '@threlte/core';
3
3
  import { useXR } from '../hooks/useXR';
4
4
  export const headset = new Group();
5
- export const useUpdateHeadset = () => {
5
+ export const setupHeadset = () => {
6
6
  const { renderer, camera } = useThrelte();
7
- const xrState = useXR();
8
7
  const { xr } = renderer;
9
8
  const immersiveFrame = useFrame(() => {
10
9
  const space = xr.getReferenceSpace();
@@ -23,7 +22,7 @@ export const useUpdateHeadset = () => {
23
22
  headset.position.copy(camera.current.position);
24
23
  headset.quaternion.copy(camera.current.quaternion);
25
24
  }, { autostart: false, invalidate: false });
26
- watch(xrState.isPresenting, (isPresenting) => {
25
+ watch(useXR().isPresenting, (isPresenting) => {
27
26
  if (isPresenting) {
28
27
  immersiveFrame.start();
29
28
  nonImmersiveFrame.stop();
@@ -0,0 +1 @@
1
+ export declare const setupRaf: () => void;
@@ -3,7 +3,7 @@ import { set_raf } from 'svelte/internal';
3
3
  import { onDestroy } from 'svelte';
4
4
  import { watch } from '@threlte/core';
5
5
  import { session } from './stores';
6
- export const updateRaf = () => {
6
+ export const setupRaf = () => {
7
7
  if (typeof window === 'undefined')
8
8
  return;
9
9
  const browserRaf = (fn) => requestAnimationFrame(fn);
@@ -11,7 +11,7 @@ export const updateRaf = () => {
11
11
  set_raf((fn) => currentRaf.fn(fn));
12
12
  watch(session, (session) => {
13
13
  if (session) {
14
- currentRaf.fn = (fn) => (session.requestAnimationFrame(fn));
14
+ currentRaf.fn = (fn) => session.requestAnimationFrame(fn);
15
15
  }
16
16
  else {
17
17
  currentRaf.fn = browserRaf;
@@ -1,11 +1,26 @@
1
1
  /// <reference types="webxr" />
2
- /// <reference types="svelte" />
3
- export declare const initialized: import("svelte/store").Writable<boolean>;
2
+ import type { WebXRManager, Intersection } from 'three';
4
3
  export declare const isPresenting: import("@threlte/core").CurrentWritable<boolean>;
5
4
  export declare const isHandTracking: import("@threlte/core").CurrentWritable<boolean>;
6
5
  export declare const session: import("@threlte/core").CurrentWritable<XRSession | undefined>;
7
6
  export declare const referenceSpaceType: import("@threlte/core").CurrentWritable<XRReferenceSpaceType | undefined>;
8
- export declare const xr: import("@threlte/core").CurrentWritable<import("three").WebXRManager | undefined>;
7
+ export declare const xr: import("@threlte/core").CurrentWritable<WebXRManager | undefined>;
8
+ export declare const controllerDispatchers: {
9
+ left: import("@threlte/core").CurrentWritable<((<Type extends string>(type: Type, payload?: unknown) => void) & {
10
+ hasEventListener: <Type_1 extends string>(type: Type_1) => boolean;
11
+ }) | undefined>;
12
+ right: import("@threlte/core").CurrentWritable<((<Type extends string>(type: Type, payload?: unknown) => void) & {
13
+ hasEventListener: <Type_1 extends string>(type: Type_1) => boolean;
14
+ }) | undefined>;
15
+ };
16
+ export declare const handDispatchers: {
17
+ left: import("@threlte/core").CurrentWritable<((<Type extends string>(type: Type, payload?: unknown) => void) & {
18
+ hasEventListener: <Type_1 extends string>(type: Type_1) => boolean;
19
+ }) | undefined>;
20
+ right: import("@threlte/core").CurrentWritable<((<Type extends string>(type: Type, payload?: unknown) => void) & {
21
+ hasEventListener: <Type_1 extends string>(type: Type_1) => boolean;
22
+ }) | undefined>;
23
+ };
9
24
  export declare const teleportState: import("@threlte/core").CurrentWritable<{
10
25
  left: {
11
26
  enabled: boolean;
@@ -17,8 +32,8 @@ export declare const teleportState: import("@threlte/core").CurrentWritable<{
17
32
  };
18
33
  }>;
19
34
  export declare const teleportIntersection: {
20
- left: import("@threlte/core").CurrentWritable<import("three").Intersection<import("three").Object3D<import("three").Event>> | undefined>;
21
- right: import("@threlte/core").CurrentWritable<import("three").Intersection<import("three").Object3D<import("three").Event>> | undefined>;
35
+ left: import("@threlte/core").CurrentWritable<Intersection<import("three").Object3D<import("three").Object3DEventMap>> | undefined>;
36
+ right: import("@threlte/core").CurrentWritable<Intersection<import("three").Object3D<import("three").Object3DEventMap>> | undefined>;
22
37
  };
23
38
  export declare const pointerState: import("@threlte/core").CurrentWritable<{
24
39
  left: {
@@ -31,6 +46,6 @@ export declare const pointerState: import("@threlte/core").CurrentWritable<{
31
46
  };
32
47
  }>;
33
48
  export declare const pointerIntersection: {
34
- left: import("@threlte/core").CurrentWritable<import("three").Intersection<import("three").Object3D<import("three").Event>> | undefined>;
35
- right: import("@threlte/core").CurrentWritable<import("three").Intersection<import("three").Object3D<import("three").Event>> | undefined>;
49
+ left: import("@threlte/core").CurrentWritable<Intersection<import("three").Object3D<import("three").Object3DEventMap>> | undefined>;
50
+ right: import("@threlte/core").CurrentWritable<Intersection<import("three").Object3D<import("three").Object3DEventMap>> | undefined>;
36
51
  };
@@ -1,36 +1,42 @@
1
- import { writable } from 'svelte/store';
2
1
  import { currentWritable } from '@threlte/core';
3
- export const initialized = writable(false);
4
2
  export const isPresenting = currentWritable(false);
5
3
  export const isHandTracking = currentWritable(false);
6
4
  export const session = currentWritable(undefined);
7
5
  export const referenceSpaceType = currentWritable(undefined);
8
6
  export const xr = currentWritable(undefined);
7
+ export const controllerDispatchers = {
8
+ left: currentWritable(undefined),
9
+ right: currentWritable(undefined)
10
+ };
11
+ export const handDispatchers = {
12
+ left: currentWritable(undefined),
13
+ right: currentWritable(undefined)
14
+ };
9
15
  export const teleportState = currentWritable({
10
16
  left: {
11
17
  enabled: false,
12
- hovering: false,
18
+ hovering: false
13
19
  },
14
20
  right: {
15
21
  enabled: false,
16
- hovering: false,
17
- },
22
+ hovering: false
23
+ }
18
24
  });
19
25
  export const teleportIntersection = {
20
26
  left: currentWritable(undefined),
21
- right: currentWritable(undefined),
27
+ right: currentWritable(undefined)
22
28
  };
23
29
  export const pointerState = currentWritable({
24
30
  left: {
25
31
  enabled: false,
26
- hovering: false,
32
+ hovering: false
27
33
  },
28
34
  right: {
29
35
  enabled: false,
30
- hovering: false,
36
+ hovering: false
31
37
  }
32
38
  });
33
39
  export const pointerIntersection = {
34
40
  left: currentWritable(undefined),
35
- right: currentWritable(undefined),
41
+ right: currentWritable(undefined)
36
42
  };
@@ -7,7 +7,7 @@ export const useHandTrackingState = () => {
7
7
  const { xr } = useThrelte().renderer;
8
8
  return () => {
9
9
  let handTracking = false;
10
- xr.getSession()?.inputSources.forEach((value) => {
10
+ xr.getSession()?.inputSources?.forEach((value) => {
11
11
  if (value.hand) {
12
12
  handTracking = true;
13
13
  }
@@ -21,12 +21,12 @@ export const toggleXRSession = async (sessionMode, sessionInit, force) => {
21
21
  session.set(undefined);
22
22
  return;
23
23
  }
24
- // Otherwise enter a session
25
- const options = getXRSessionOptions(referenceSpaceType.current, sessionInit);
26
- const nextSession = await navigator.xr.requestSession(sessionMode, options);
27
24
  if (xr.current === undefined) {
28
25
  throw new Error('An <XR> component was not created when attempting to toggle a session.');
29
26
  }
27
+ // Otherwise enter a session
28
+ const options = getXRSessionOptions(referenceSpaceType.current, sessionInit);
29
+ const nextSession = await navigator.xr.requestSession(sessionMode, options);
30
30
  await xr.current.setSession(nextSession);
31
31
  session.set(nextSession);
32
32
  return nextSession;
@@ -1,45 +1,46 @@
1
+ import type { Intersection as ThreeIntersection, Object3D, Vector3, Ray, Raycaster, Event } from 'three';
1
2
  import type { CurrentWritable } from '@threlte/core';
2
3
  import type { ComputeFunction } from './compute';
3
4
  export type Properties<T> = Pick<T, {
4
5
  [K in keyof T]: T[K] extends (_: any) => any ? never : K;
5
6
  }[keyof T]>;
6
- export interface Intersection extends THREE.Intersection {
7
+ export interface Intersection extends ThreeIntersection {
7
8
  /** The event source (the object which registered the handler) */
8
- eventObject: THREE.Object3D;
9
+ eventObject: Object3D;
9
10
  }
10
11
  export interface IntersectionEvent extends Intersection {
11
12
  /** The event source (the object which registered the handler) */
12
- eventObject: THREE.Object3D;
13
+ eventObject: Object3D;
13
14
  /** An array of intersections */
14
15
  intersections: Intersection[];
15
16
  /** Normalized event coordinates */
16
- pointer: THREE.Vector3;
17
+ pointer: Vector3;
17
18
  /** Delta between first click and this event */
18
19
  delta: number;
19
20
  /** The ray that pierced it */
20
- ray: THREE.Ray;
21
+ ray: Ray;
21
22
  /** stopPropagation will stop underlying handlers from firing */
22
23
  stopPropagation: () => void;
23
24
  /** The original host event */
24
- nativeEvent: THREE.Event | undefined;
25
+ nativeEvent: Event | undefined;
25
26
  /** If the event was stopped by calling stopPropagation */
26
27
  stopped: boolean;
27
28
  }
28
- export type FilterFunction = (items: THREE.Intersection[], state: ControlsContext, handState: HandContext) => THREE.Intersection[];
29
+ export type FilterFunction = (items: Intersection[], state: ControlsContext, handState: HandContext) => Intersection[];
29
30
  export type ControlsContext = {
30
- interactiveObjects: THREE.Object3D[];
31
- raycaster: THREE.Raycaster;
31
+ interactiveObjects: Object3D[];
32
+ raycaster: Raycaster;
32
33
  compute: ComputeFunction;
33
34
  filter?: FilterFunction | undefined;
34
35
  };
35
36
  export type HandContext = {
36
37
  hand: 'left' | 'right';
37
38
  enabled: CurrentWritable<boolean>;
38
- pointer: CurrentWritable<THREE.Vector3>;
39
+ pointer: CurrentWritable<Vector3>;
39
40
  pointerOverTarget: CurrentWritable<boolean>;
40
- lastEvent: THREE.Event | undefined;
41
+ lastEvent: Event | undefined;
41
42
  initialClick: [x: number, y: number, z: number];
42
- initialHits: THREE.Object3D[];
43
+ initialHits: Object3D[];
43
44
  hovered: Map<string, IntersectionEvent>;
44
45
  };
45
46
  export interface PointerCaptureTarget {
@@ -1,18 +1,19 @@
1
+ import type { Mesh, Raycaster, Intersection } from 'three';
1
2
  import type { CurrentWritable, createRawEventDispatcher } from '@threlte/core';
2
3
  export type ComputeFunction = (context: Context, handContext: HandContext) => void;
3
4
  export interface Context {
4
- interactiveObjects: THREE.Mesh[];
5
- surfaces: Map<string, THREE.Mesh>;
6
- blockers: Map<string, THREE.Mesh>;
7
- dispatchers: WeakMap<THREE.Mesh, ReturnType<typeof createRawEventDispatcher>>;
8
- raycaster: THREE.Raycaster;
5
+ interactiveObjects: Mesh[];
6
+ surfaces: Map<string, Mesh>;
7
+ blockers: Map<string, Mesh>;
8
+ dispatchers: WeakMap<Mesh, ReturnType<typeof createRawEventDispatcher>>;
9
+ raycaster: Raycaster;
9
10
  compute: ComputeFunction;
10
11
  }
11
12
  export interface HandContext {
12
13
  hand: 'left' | 'right';
13
14
  enabled: CurrentWritable<boolean>;
14
15
  active: CurrentWritable<boolean>;
15
- hovered: CurrentWritable<THREE.Intersection | undefined>;
16
+ hovered: CurrentWritable<Intersection | undefined>;
16
17
  }
17
18
  export declare const getHandContext: (hand: 'left' | 'right') => HandContext;
18
19
  export declare const setHandContext: (hand: 'left' | 'right', context: HandContext) => void;
@@ -1,6 +1,7 @@
1
+ import type { Mesh } from 'three';
1
2
  export declare const useTeleportControls: () => {
2
- addSurface: (mesh: THREE.Mesh) => void;
3
- removeSurface: (mesh: THREE.Mesh) => void;
4
- addBlocker: (mesh: THREE.Mesh) => void;
5
- removeBlocker: (mesh: THREE.Mesh) => void;
3
+ addSurface: (mesh: Mesh) => void;
4
+ removeSurface: (mesh: Mesh) => void;
5
+ addBlocker: (mesh: Mesh) => void;
6
+ removeBlocker: (mesh: Mesh) => void;
6
7
  };
@@ -14,6 +14,6 @@ export interface TeleportControlsOptions {
14
14
  }
15
15
  export declare const teleportControls: (handedness: 'left' | 'right', options?: TeleportControlsOptions) => {
16
16
  enabled: import("@threlte/core").CurrentWritable<boolean>;
17
- hovered: import("@threlte/core").CurrentWritable<import("three").Intersection<import("three").Object3D<import("three").Event>> | undefined>;
17
+ hovered: import("@threlte/core").CurrentWritable<import("three").Intersection<import("three").Object3D<import("three").Object3DEventMap>> | undefined>;
18
18
  active: import("@threlte/core").CurrentWritable<boolean>;
19
19
  };
package/dist/types.d.ts CHANGED
@@ -1,32 +1,34 @@
1
1
  /// <reference types="webxr" />
2
+ import type { Event, Group, XRTargetRaySpace, XRGripSpace, XRHandSpace } from 'three';
2
3
  import type { XRControllerModel } from 'three/examples/jsm/webxr/XRControllerModelFactory';
3
4
  import type { XRHandModel } from 'three/examples/jsm/webxr/XRHandModelFactory';
4
5
  export type XRSessionEventType = 'sessionstart' | 'sessionend' | 'visibilitychange' | 'frameratechange';
5
6
  export type XRControllerEventType = 'select' | 'selectstart' | 'selectend' | 'squeeze' | 'squeezeend' | 'squeezestart' | 'disconnected' | 'connected';
6
7
  export type XRHandEventType = 'pinchstart' | 'pinchend' | 'connected' | 'disconnected';
7
- export type XRSessionEvent<Type = XRSessionEventType> = THREE.Event & {
8
+ export type XRSessionEvent<Type = XRSessionEventType> = Event & {
8
9
  type: Type;
9
10
  target: XRSession;
10
11
  };
11
- export type XRControllerEvent<Type = XRControllerEventType> = THREE.Event & {
12
+ export type XRControllerEvent<Type = XRControllerEventType> = Event & {
12
13
  type: Type;
13
- target: THREE.Group;
14
+ target: Group;
14
15
  data: XRInputSource;
15
16
  };
16
17
  export type XRController = {
17
- targetRay: THREE.XRTargetRaySpace;
18
- grip: THREE.XRGripSpace;
19
- model?: XRControllerModel;
18
+ targetRay: XRTargetRaySpace;
19
+ grip: XRGripSpace;
20
+ model?: XRControllerModel | undefined;
20
21
  inputSource: XRInputSource;
21
22
  };
22
23
  export type XRHand = {
23
- hand: THREE.XRHandSpace;
24
+ targetRay: XRTargetRaySpace;
25
+ hand: XRHandSpace;
24
26
  model?: XRHandModel;
25
27
  inputSource: globalThis.XRHand;
26
28
  };
27
29
  export type XRHandEvent<Type = XRHandEventType> = Type extends 'connected' | 'disconnected' ? {
28
30
  type: Type;
29
- target: THREE.XRHandSpace;
31
+ target: XRHandSpace;
30
32
  data: XRInputSource;
31
33
  } : Type extends 'pinchstart' | 'pinchend' ? {
32
34
  type: Type;
package/package.json CHANGED
@@ -1,24 +1,24 @@
1
1
  {
2
2
  "name": "@threlte/xr",
3
- "version": "0.0.12",
3
+ "version": "0.0.13",
4
4
  "author": "Micheal Parks <michealparks1989@gmail.com> (https://parks.lol)",
5
5
  "license": "MIT",
6
6
  "devDependencies": {
7
7
  "@sveltejs/adapter-auto": "^2.1.0",
8
8
  "@sveltejs/kit": "^1.22.3",
9
9
  "@sveltejs/package": "^2.2.0",
10
- "@types/three": "^0.155.1",
10
+ "@types/three": "^0.158.3",
11
11
  "autoprefixer": "^10.4.14",
12
12
  "postcss": "^8.4.27",
13
13
  "publint": "^0.1.16",
14
14
  "svelte": "^4.1.1",
15
15
  "svelte-check": "^3.4.6",
16
- "three": "^0.154.0",
16
+ "three": "^0.158.0",
17
17
  "tslib": "^2.6.0",
18
18
  "typescript": "^5.1.6",
19
19
  "vite": "^4.4.6",
20
20
  "vite-plugin-mkcert": "^1.16.0",
21
- "@threlte/core": "6.1.0"
21
+ "@threlte/core": "6.1.1"
22
22
  },
23
23
  "peerDependencies": {
24
24
  "svelte": ">=4",
@@ -1,3 +0,0 @@
1
- import { Group } from 'three';
2
- export declare const headset: Group;
3
- export declare const useUpdateHeadset: () => void;
@@ -1 +0,0 @@
1
- export declare const updateRaf: () => void;