@threlte/xr 0.0.11 → 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 (87) hide show
  1. package/dist/components/ARButton.svelte +1 -1
  2. package/dist/components/Controller.svelte +69 -79
  3. package/dist/components/Controller.svelte.d.ts +4 -0
  4. package/dist/components/Hand.svelte +25 -58
  5. package/dist/components/Hand.svelte.d.ts +1 -0
  6. package/dist/components/Headset.svelte +4 -7
  7. package/dist/components/XR.svelte +46 -52
  8. package/dist/components/XRButton.svelte +2 -2
  9. package/dist/components/internal/Cursor.svelte +51 -0
  10. package/dist/components/internal/Cursor.svelte.d.ts +19 -0
  11. package/dist/components/internal/PointerCursor.svelte +46 -0
  12. package/dist/components/internal/PointerCursor.svelte.d.ts +18 -0
  13. package/dist/components/internal/ScenePortal.svelte +10 -0
  14. package/dist/components/internal/ScenePortal.svelte.d.ts +16 -0
  15. package/dist/components/internal/ShortRay.svelte +45 -0
  16. package/dist/components/{ShortRay.svelte.d.ts → internal/ShortRay.svelte.d.ts} +4 -2
  17. package/dist/components/internal/TeleportCursor.svelte +51 -0
  18. package/dist/components/internal/TeleportCursor.svelte.d.ts +18 -0
  19. package/dist/components/internal/TeleportRay.svelte +71 -0
  20. package/dist/components/internal/TeleportRay.svelte.d.ts +20 -0
  21. package/dist/hooks/useController.d.ts +0 -4
  22. package/dist/hooks/useController.js +2 -15
  23. package/dist/hooks/useHand.js +2 -0
  24. package/dist/hooks/useHandJoint.d.ts +2 -2
  25. package/dist/hooks/useHeadset.d.ts +2 -1
  26. package/dist/hooks/useHeadset.js +1 -1
  27. package/dist/hooks/useHitTest.d.ts +2 -2
  28. package/dist/hooks/useHitTest.js +16 -6
  29. package/dist/hooks/useTeleport.d.ts +2 -2
  30. package/dist/hooks/useTeleport.js +2 -2
  31. package/dist/index.d.ts +10 -3
  32. package/dist/index.js +13 -2
  33. package/dist/internal/setupControllers.d.ts +1 -0
  34. package/dist/internal/setupControllers.js +69 -0
  35. package/dist/internal/setupHands.d.ts +1 -0
  36. package/dist/internal/setupHands.js +63 -0
  37. package/dist/internal/setupHeadset.d.ts +3 -0
  38. package/dist/internal/{headset.js → setupHeadset.js} +3 -4
  39. package/dist/internal/setupRaf.d.ts +1 -0
  40. package/dist/internal/{updateRaf.js → setupRaf.js} +2 -2
  41. package/dist/internal/stores.d.ts +46 -5
  42. package/dist/internal/stores.js +36 -4
  43. package/dist/internal/useFixed.d.ts +11 -0
  44. package/dist/internal/useFixed.js +17 -0
  45. package/dist/internal/useHandTrackingState.js +1 -1
  46. package/dist/lib/toggleXRSession.js +3 -3
  47. package/dist/plugins/pointerControls/compute.d.ts +3 -0
  48. package/dist/plugins/pointerControls/compute.js +14 -0
  49. package/dist/plugins/pointerControls/context.d.ts +12 -0
  50. package/dist/plugins/pointerControls/context.js +27 -0
  51. package/dist/plugins/pointerControls/hook.d.ts +5 -0
  52. package/dist/plugins/pointerControls/hook.js +24 -0
  53. package/dist/plugins/pointerControls/index.d.ts +27 -0
  54. package/dist/plugins/pointerControls/index.js +54 -0
  55. package/dist/plugins/pointerControls/plugin.d.ts +1 -0
  56. package/dist/plugins/pointerControls/plugin.js +28 -0
  57. package/dist/plugins/pointerControls/setup.d.ts +2 -0
  58. package/dist/plugins/pointerControls/setup.js +203 -0
  59. package/dist/plugins/pointerControls/types.d.ts +62 -0
  60. package/dist/plugins/pointerControls/types.js +11 -0
  61. package/dist/plugins/pointerControls/useComponentEventHandlers.d.ts +4 -0
  62. package/dist/plugins/pointerControls/useComponentEventHandlers.js +15 -0
  63. package/dist/plugins/teleportControls/compute.d.ts +3 -0
  64. package/dist/plugins/teleportControls/compute.js +14 -0
  65. package/dist/plugins/teleportControls/context.d.ts +21 -0
  66. package/dist/plugins/teleportControls/context.js +18 -0
  67. package/dist/plugins/teleportControls/hook.d.ts +7 -0
  68. package/dist/plugins/teleportControls/hook.js +40 -0
  69. package/dist/plugins/teleportControls/index.d.ts +19 -0
  70. package/dist/plugins/teleportControls/index.js +54 -0
  71. package/dist/plugins/teleportControls/plugin.d.ts +4 -0
  72. package/dist/plugins/teleportControls/plugin.js +54 -0
  73. package/dist/plugins/teleportControls/setup.d.ts +2 -0
  74. package/dist/plugins/teleportControls/setup.js +62 -0
  75. package/dist/types.d.ts +10 -8
  76. package/package.json +4 -4
  77. package/dist/components/Ray.svelte +0 -23
  78. package/dist/components/Ray.svelte.d.ts +0 -18
  79. package/dist/components/ShortRay.svelte +0 -32
  80. package/dist/components/TeleportControls.svelte +0 -136
  81. package/dist/components/TeleportControls.svelte.d.ts +0 -54
  82. package/dist/hooks/index.d.ts +0 -7
  83. package/dist/hooks/index.js +0 -7
  84. package/dist/internal/headset.d.ts +0 -3
  85. package/dist/internal/updateRaf.d.ts +0 -1
  86. package/dist/plugins/teleportPlugin.d.ts +0 -1
  87. package/dist/plugins/teleportPlugin.js +0 -41
@@ -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,96 +4,48 @@
4
4
  <script
5
5
 
6
6
  context="module"
7
- >import { T, createRawEventDispatcher, useThrelte } from "@threlte/core";
8
- import { onDestroy } from "svelte";
9
- import { XRControllerModelFactory } from "three/examples/jsm/webxr/XRControllerModelFactory";
10
- import ShortRay from "./ShortRay.svelte";
11
- import { gaze, left as leftStore, right as rightStore } from "../hooks/useController";
12
- import { activeTeleportController, pendingTeleportDestination, isHandTracking } from "../internal/stores";
13
- import { useHandTrackingState } from "../internal/useHandTrackingState";
14
- const factory = new XRControllerModelFactory();
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";
11
+ import PointerCursor from "./internal/PointerCursor.svelte";
12
+ import ShortRay from "./internal/ShortRay.svelte";
13
+ import ScenePortal from "./internal/ScenePortal.svelte";
14
+ import TeleportCursor from "./internal/TeleportCursor.svelte";
15
+ import TeleportRay from "./internal/TeleportRay.svelte";
15
16
  const stores = {
16
17
  left: leftStore,
17
- right: rightStore,
18
- none: gaze
18
+ right: rightStore
19
19
  };
20
- const events = [
21
- "select",
22
- "selectstart",
23
- "selectend",
24
- "squeeze",
25
- "squeezeend",
26
- "squeezestart"
27
- ];
28
- const eventMap = /* @__PURE__ */ new WeakMap();
29
20
  </script>
30
21
 
31
22
  <script>export let left = void 0;
32
23
  export let right = void 0;
33
24
  export let hand = void 0;
34
- $:
35
- handedness = left ? "left" : right ? "right" : hand;
36
25
  const dispatch = createRawEventDispatcher();
37
- const { xr } = useThrelte().renderer;
38
- const handTrackingNow = useHandTrackingState();
39
- const handleEvent = (event) => {
40
- if (!handTrackingNow()) {
41
- dispatch(event.type, event);
42
- }
43
- };
44
- const handleConnected = (event) => {
45
- const targetData = eventMap.get(event.target);
46
- if (event.data.handedness !== handedness || !targetData)
47
- return;
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));
53
- };
54
- const handleDisconnected = (event) => {
55
- if (event.data.handedness !== handedness)
56
- return;
57
- stores[handedness].set(void 0);
58
- if (!$isHandTracking) {
59
- dispatch("disconnected", event);
60
- }
61
- events.forEach((name) => event.target.removeEventListener(name, handleEvent));
62
- };
63
- for (const index of [0, 1]) {
64
- const controller = xr.getController(index);
65
- const grip2 = xr.getControllerGrip(index);
66
- const model2 = factory.createControllerModel(grip2);
67
- eventMap.set(controller, { targetRay: controller, model: model2, grip: grip2 });
68
- controller.addEventListener("connected", handleConnected);
69
- controller.addEventListener("disconnected", handleDisconnected);
70
- }
26
+ const handedness = writable(left ? "left" : right ? "right" : hand);
71
27
  $:
72
- store = stores[handedness];
28
+ handedness.set(left ? "left" : right ? "right" : hand);
29
+ controllerDispatchers[$handedness].set(dispatch);
30
+ $:
31
+ controllerDispatchers[$handedness].set(dispatch);
32
+ $:
33
+ store = stores[$handedness];
73
34
  $:
74
35
  grip = $store?.grip;
75
36
  $:
76
37
  targetRay = $store?.targetRay;
77
38
  $:
78
39
  model = $store?.model;
79
- onDestroy(() => {
80
- for (const index of [0, 1]) {
81
- const controller2 = xr.getController(index);
82
- controller2.removeEventListener("connected", handleConnected);
83
- controller2.removeEventListener("disconnected", handleDisconnected);
84
- }
85
- const controller = $store?.targetRay;
86
- events.forEach((name) => controller?.removeEventListener(name, handleEvent));
87
- store.set(void 0);
88
- });
40
+ $:
41
+ hasPointerControls = $pointerState[$handedness].enabled;
42
+ $:
43
+ hasTeleportControls = $teleportState[$handedness].enabled;
89
44
  </script>
90
45
 
91
46
  {#if !$isHandTracking}
92
47
  {#if grip}
93
- <T
94
- is={grip}
95
- name="XR controller grip {handedness}"
96
- >
48
+ <T is={grip}>
97
49
  <slot>
98
50
  <T is={model} />
99
51
  </slot>
@@ -103,16 +55,54 @@ onDestroy(() => {
103
55
  {/if}
104
56
 
105
57
  {#if targetRay}
106
- <T
107
- is={targetRay}
108
- name="XR controller {handedness}"
109
- visible={!$isHandTracking}
110
- >
58
+ <T is={targetRay}>
111
59
  <slot name="target-ray" />
112
- <ShortRay
113
- visible={$activeTeleportController === targetRay &&
114
- $pendingTeleportDestination === undefined}
115
- />
60
+
61
+ {#if hasPointerControls || hasTeleportControls}
62
+ {#if $$slots['pointer-ray']}
63
+ <ShortRay handedness={$handedness}>
64
+ <slot name="pointer-ray" />
65
+ </ShortRay>
66
+ {:else}
67
+ <ShortRay handedness={$handedness} />
68
+ {/if}
69
+ {/if}
116
70
  </T>
117
71
  {/if}
118
72
  {/if}
73
+
74
+ <ScenePortal>
75
+ {#if hasPointerControls}
76
+ {#if $$slots['pointer-cursor']}
77
+ <PointerCursor handedness={$handedness}>
78
+ <slot name="pointer-cursor" />
79
+ </PointerCursor>
80
+ {:else}
81
+ <PointerCursor handedness={$handedness} />
82
+ {/if}
83
+ {/if}
84
+
85
+ {#if hasTeleportControls && targetRay !== undefined}
86
+ {#if $$slots['teleport-ray']}
87
+ <TeleportRay
88
+ {targetRay}
89
+ handedness={$handedness}
90
+ >
91
+ <slot name="teleport-ray" />
92
+ </TeleportRay>
93
+ {:else}
94
+ <TeleportRay
95
+ {targetRay}
96
+ handedness={$handedness}
97
+ />
98
+ {/if}
99
+
100
+ {#if $$slots['teleport-ray']}
101
+ <TeleportCursor handedness={$handedness}>
102
+ <slot name="teleport-cursor" />
103
+ </TeleportCursor>
104
+ {:else}
105
+ <TeleportCursor handedness={$handedness} />
106
+ {/if}
107
+ {/if}
108
+ </ScenePortal>
@@ -21,6 +21,10 @@ declare const __propDef: {
21
21
  default: {};
22
22
  grip: {};
23
23
  'target-ray': {};
24
+ 'pointer-ray': {};
25
+ 'pointer-cursor': {};
26
+ 'teleport-ray': {};
27
+ 'teleport-cursor': {};
24
28
  };
25
29
  events: {
26
30
  connected: XRControllerEvent<'connected'>;
@@ -1,49 +1,26 @@
1
- <script context='module'>import { T, useThrelte, createRawEventDispatcher, useFrame } from "@threlte/core";
2
- import { XRHandModelFactory } from "three/examples/jsm/webxr/XRHandModelFactory";
3
- import { isHandTracking } from "../internal/stores";
4
- import { useHandTrackingState } from "../internal/useHandTrackingState";
1
+ <script context='module'>import { Group } from "three";
2
+ import { T, useThrelte, createRawEventDispatcher, useFrame } from "@threlte/core";
3
+ import { isHandTracking, handDispatchers } from "../internal/stores";
5
4
  import { left as leftStore, right as rightStore } from "../hooks/useHand";
6
- import { onDestroy } from "svelte";
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
13
  <script>export let left = void 0;
16
14
  export let right = void 0;
17
15
  export let hand = void 0;
18
- const handTrackingNow = useHandTrackingState();
19
16
  const dispatch = createRawEventDispatcher();
20
17
  const { xr } = useThrelte().renderer;
21
18
  const space = xr.getReferenceSpace();
19
+ const handedness = writable(left ? "left" : right ? "right" : hand);
22
20
  $:
23
- handedness = left ? "left" : right ? "right" : hand;
24
- const handleConnected = (event) => {
25
- if (event.data.handedness !== handedness)
26
- return;
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);
33
- };
34
- const handleDisconnected = (event) => {
35
- if (event.data.handedness !== handedness)
36
- return;
37
- stores[handedness].set(void 0);
38
- if ($isHandTracking) {
39
- dispatch("disconnected", event);
40
- }
41
- event.target.removeEventListener("pinchstart", handlePinchEvent);
42
- event.target.removeEventListener("pinchend", handlePinchEvent);
43
- };
44
- const handlePinchEvent = (event) => {
45
- dispatch(event.type, event);
46
- };
21
+ handedness.set(left ? "left" : right ? "right" : hand);
22
+ $:
23
+ handDispatchers[$handedness].set(dispatch);
47
24
  let children;
48
25
  const { start, stop } = useFrame(() => {
49
26
  const frame = xr.getFrame();
@@ -58,48 +35,38 @@ const { start, stop } = useFrame(() => {
58
35
  children.quaternion.set(orientation.x, orientation.y, orientation.z, orientation.w);
59
36
  }, { autostart: false });
60
37
  $:
61
- if (($$slots.wrist || $$slots.default) && inputSource) {
38
+ if ($isHandTracking && ($$slots.wrist || $$slots.default) && inputSource) {
62
39
  start();
63
40
  } else {
64
41
  stop();
65
42
  }
66
43
  $:
67
- store = stores[handedness];
44
+ store = stores[$handedness];
68
45
  $:
69
46
  inputSource = $store?.inputSource;
70
47
  $:
71
48
  model = $store?.model;
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);
78
- }
79
- onDestroy(() => {
80
- for (const index of [0, 1]) {
81
- const hand3 = xr.getHand(index);
82
- hand3.removeEventListener("connected", handleConnected);
83
- hand3.removeEventListener("disconnected", handleDisconnected);
84
- }
85
- const hand2 = stores[handedness].current?.hand;
86
- hand2?.removeEventListener("pinchstart", handlePinchEvent);
87
- hand2?.removeEventListener("pinchend", handlePinchEvent);
88
- stores[handedness].set(void 0);
89
- });
90
49
  </script>
91
50
 
92
- {#if $store?.hand}
93
- <T
94
- is={$store.hand}
95
- name='XR hand {handedness}'
96
- >
51
+ {#if $store?.hand && $isHandTracking}
52
+ <T is={$store.hand}>
97
53
  {#if $$slots.default === undefined}
98
54
  <T is={model} />
99
55
  {/if}
56
+ </T>
57
+
58
+ {#if $$slots['target-ray'] !== undefined}
59
+ <T is={$store.targetRay}>
60
+ <slot name='target-ray' />
61
+ </T>
62
+ {/if}
63
+ {/if}
64
+
65
+ {#if $isHandTracking}
66
+ <ScenePortal>
100
67
  <T.Group bind:ref={children}>
101
68
  <slot name='wrist' />
102
69
  <slot />
103
70
  </T.Group>
104
- </T>
71
+ </ScenePortal>
105
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
  };
@@ -1,15 +1,12 @@
1
1
 
2
- <script>import { T, HierarchicalObject, useThrelte } from "@threlte/core";
2
+ <script>import { T } from "@threlte/core";
3
3
  import { useHeadset } from "../hooks/useHeadset";
4
- const { scene } = useThrelte();
4
+ import ScenePortal from "./internal/ScenePortal.svelte";
5
5
  const headset = useHeadset();
6
6
  </script>
7
7
 
8
- <HierarchicalObject
9
- onChildMount={(child) => { scene.add(child) }}
10
- onChildDestroy={(child) => { scene.remove(child) }}
11
- >
8
+ <ScenePortal>
12
9
  <T is={headset}>
13
10
  <slot />
14
11
  </T>
15
- </HierarchicalObject>
12
+ </ScenePortal>
@@ -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 });
@@ -0,0 +1,51 @@
1
+ <script>import { Color, DoubleSide, RawShaderMaterial } from "three";
2
+ import { T } from "@threlte/core";
3
+ export let color = new Color("white");
4
+ export let size = 0.03;
5
+ export let thickness = 0.035;
6
+ const vertexShader = `
7
+ uniform mat4 projectionMatrix;
8
+ uniform mat4 modelViewMatrix;
9
+ attribute vec2 uv;
10
+ attribute vec3 position;
11
+ varying vec2 vUv;
12
+ void main() {
13
+ vUv = uv;
14
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
15
+ }
16
+ `;
17
+ const fragmentShader = `
18
+ precision mediump float;
19
+ uniform float thickness;
20
+ uniform vec3 color;
21
+ varying vec2 vUv;
22
+ void main() {
23
+ float radius = 0.1;
24
+ float dist = length(vUv - vec2(0.5));
25
+ float alpha = 1.0 - step(thickness, abs(distance(vUv, vec2(0.5)) - 0.25));
26
+ gl_FragColor = vec4(color, alpha);
27
+ }
28
+ `;
29
+ const shaderMaterial = new RawShaderMaterial({
30
+ vertexShader,
31
+ fragmentShader,
32
+ uniforms: {
33
+ thickness: { value: thickness },
34
+ color: { value: color }
35
+ },
36
+ side: DoubleSide,
37
+ transparent: true,
38
+ depthTest: false
39
+ });
40
+ $:
41
+ shaderMaterial.uniforms.thickness.value = thickness;
42
+ $:
43
+ shaderMaterial.uniforms.color.value = color;
44
+ </script>
45
+
46
+ <T.Mesh
47
+ scale={size}
48
+ >
49
+ <T.PlaneGeometry />
50
+ <T is={shaderMaterial} />
51
+ </T.Mesh>
@@ -0,0 +1,19 @@
1
+ import { SvelteComponent } from "svelte";
2
+ import { type ColorRepresentation } from 'three';
3
+ declare const __propDef: {
4
+ props: {
5
+ color?: ColorRepresentation;
6
+ size?: number;
7
+ thickness?: number;
8
+ };
9
+ events: {
10
+ [evt: string]: CustomEvent<any>;
11
+ };
12
+ slots: {};
13
+ };
14
+ export type CursorProps = typeof __propDef.props;
15
+ export type CursorEvents = typeof __propDef.events;
16
+ export type CursorSlots = typeof __propDef.slots;
17
+ export default class Cursor extends SvelteComponent<CursorProps, CursorEvents, CursorSlots> {
18
+ }
19
+ export {};
@@ -0,0 +1,46 @@
1
+ <script>import { Group, Vector3, Matrix3 } from "three";
2
+ import { T, useFrame } from "@threlte/core";
3
+ import { pointerIntersection, pointerState } from "../../internal/stores";
4
+ import Cursor from "./Cursor.svelte";
5
+ export let handedness;
6
+ const ref = new Group();
7
+ const vec3 = new Vector3();
8
+ const normalMatrix = new Matrix3();
9
+ const worldNormal = new Vector3();
10
+ $:
11
+ hovering = $pointerState[handedness].hovering;
12
+ $:
13
+ intersection = pointerIntersection[handedness];
14
+ const { start, stop } = useFrame(
15
+ () => {
16
+ if (intersection.current === void 0)
17
+ return;
18
+ const { point, face, object } = intersection.current;
19
+ ref.position.lerp(point, 0.4);
20
+ if (face) {
21
+ normalMatrix.getNormalMatrix(object.matrixWorld);
22
+ worldNormal.copy(face.normal).applyMatrix3(normalMatrix).normalize();
23
+ ref.lookAt(vec3.addVectors(point, worldNormal));
24
+ }
25
+ },
26
+ {
27
+ autostart: false
28
+ }
29
+ );
30
+ $:
31
+ if (hovering) {
32
+ ref.position.copy(intersection.current.point);
33
+ start();
34
+ } else {
35
+ stop();
36
+ }
37
+ </script>
38
+
39
+ <T
40
+ is={ref}
41
+ visible={hovering}
42
+ >
43
+ <slot>
44
+ <Cursor />
45
+ </slot>
46
+ </T>
@@ -0,0 +1,18 @@
1
+ import { SvelteComponent } from "svelte";
2
+ declare const __propDef: {
3
+ props: {
4
+ handedness: 'left' | 'right';
5
+ };
6
+ events: {
7
+ [evt: string]: CustomEvent<any>;
8
+ };
9
+ slots: {
10
+ default: {};
11
+ };
12
+ };
13
+ export type PointerCursorProps = typeof __propDef.props;
14
+ export type PointerCursorEvents = typeof __propDef.events;
15
+ export type PointerCursorSlots = typeof __propDef.slots;
16
+ export default class PointerCursor extends SvelteComponent<PointerCursorProps, PointerCursorEvents, PointerCursorSlots> {
17
+ }
18
+ export {};
@@ -0,0 +1,10 @@
1
+ <script>import { useThrelte, HierarchicalObject } from "@threlte/core";
2
+ const { scene } = useThrelte();
3
+ </script>
4
+
5
+ <HierarchicalObject
6
+ onChildMount={(child) => scene.add(child)}
7
+ onChildDestroy={(child) => scene.remove(child)}
8
+ >
9
+ <slot />
10
+ </HierarchicalObject>