@threlte/xr 1.0.7 → 1.1.0

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 (71) hide show
  1. package/dist/components/ARButton.svelte.d.ts +11 -24
  2. package/dist/components/Controller.svelte +51 -52
  3. package/dist/components/Controller.svelte.d.ts +7 -5
  4. package/dist/components/Hand.svelte +35 -41
  5. package/dist/components/Hand.svelte.d.ts +6 -4
  6. package/dist/components/Headset.svelte +16 -7
  7. package/dist/components/Headset.svelte.d.ts +8 -18
  8. package/dist/components/VRButton.svelte.d.ts +5 -18
  9. package/dist/components/XR.svelte +50 -50
  10. package/dist/components/XR.svelte.d.ts +24 -22
  11. package/dist/components/XRButton.svelte +6 -6
  12. package/dist/components/XRButton.svelte.d.ts +26 -23
  13. package/dist/components/internal/Cursor.svelte +2 -1
  14. package/dist/components/internal/Cursor.svelte.d.ts +4 -2
  15. package/dist/components/internal/PointerCursor.svelte +32 -18
  16. package/dist/components/internal/PointerCursor.svelte.d.ts +5 -3
  17. package/dist/components/internal/ShortRay.svelte +6 -6
  18. package/dist/components/internal/ShortRay.svelte.d.ts +5 -3
  19. package/dist/components/internal/TeleportCursor.svelte +28 -17
  20. package/dist/components/internal/TeleportCursor.svelte.d.ts +5 -3
  21. package/dist/components/internal/TeleportRay.svelte +26 -20
  22. package/dist/components/internal/TeleportRay.svelte.d.ts +5 -3
  23. package/dist/hooks/currentReadable.svelte.d.ts +5 -0
  24. package/dist/hooks/currentReadable.svelte.js +11 -0
  25. package/dist/hooks/useController.svelte.d.ts +13 -0
  26. package/dist/hooks/useController.svelte.js +22 -0
  27. package/dist/hooks/useHand.svelte.d.ts +12 -0
  28. package/dist/hooks/{useHand.js → useHand.svelte.js} +8 -5
  29. package/dist/hooks/{useHandJoint.d.ts → useHandJoint.svelte.d.ts} +1 -1
  30. package/dist/hooks/useHandJoint.svelte.js +21 -0
  31. package/dist/hooks/useHeadset.js +1 -1
  32. package/dist/hooks/useHitTest.svelte.js +67 -0
  33. package/dist/hooks/useTeleport.js +1 -2
  34. package/dist/hooks/useXR.d.ts +6 -7
  35. package/dist/hooks/useXR.js +10 -12
  36. package/dist/index.d.ts +4 -4
  37. package/dist/index.js +4 -4
  38. package/dist/internal/raf.d.ts +1 -0
  39. package/dist/internal/raf.js +2 -0
  40. package/dist/internal/setupControllers.js +13 -14
  41. package/dist/internal/setupHands.js +8 -9
  42. package/dist/internal/{setupHeadset.js → setupHeadset.svelte.js} +6 -6
  43. package/dist/internal/setupRaf.svelte.js +17 -0
  44. package/dist/internal/state.svelte.d.ts +51 -0
  45. package/dist/internal/state.svelte.js +40 -0
  46. package/dist/internal/useHandTrackingState.js +10 -7
  47. package/dist/lib/getXRSessionOptions.d.ts +1 -1
  48. package/dist/lib/toggleXRSession.d.ts +3 -3
  49. package/dist/lib/toggleXRSession.js +3 -3
  50. package/dist/plugins/pointerControls/compute.js +2 -6
  51. package/dist/plugins/pointerControls/index.js +4 -10
  52. package/dist/plugins/pointerControls/{setup.js → setup.svelte.js} +21 -22
  53. package/dist/plugins/teleportControls/compute.js +2 -6
  54. package/dist/plugins/teleportControls/index.js +4 -10
  55. package/dist/plugins/teleportControls/{setup.js → setup.svelte.js} +9 -10
  56. package/package.json +8 -8
  57. package/dist/components/internal/ScenePortal.svelte +0 -23
  58. package/dist/components/internal/ScenePortal.svelte.d.ts +0 -20
  59. package/dist/hooks/useController.d.ts +0 -9
  60. package/dist/hooks/useController.js +0 -19
  61. package/dist/hooks/useHand.d.ts +0 -8
  62. package/dist/hooks/useHandJoint.js +0 -20
  63. package/dist/hooks/useHitTest.js +0 -79
  64. package/dist/internal/setupRaf.js +0 -16
  65. package/dist/internal/stores.d.ts +0 -43
  66. package/dist/internal/stores.js +0 -42
  67. /package/dist/hooks/{useHitTest.d.ts → useHitTest.svelte.d.ts} +0 -0
  68. /package/dist/internal/{setupHeadset.d.ts → setupHeadset.svelte.d.ts} +0 -0
  69. /package/dist/internal/{setupRaf.d.ts → setupRaf.svelte.d.ts} +0 -0
  70. /package/dist/plugins/pointerControls/{setup.d.ts → setup.svelte.d.ts} +0 -0
  71. /package/dist/plugins/teleportControls/{setup.d.ts → setup.svelte.d.ts} +0 -0
@@ -1,23 +1,6 @@
1
- import { type Snippet } from 'svelte';
1
+ import type { Snippet } from 'svelte';
2
2
  import type { XRSessionEvent } from '../types';
3
- /**
4
- * `<XR />` is a WebXR manager that configures your scene for XR rendering and interaction.
5
- *
6
- * This should be placed within a Threlte `<Canvas />`.
7
- *
8
- * ```svelte
9
- * <XR
10
- * foveation={1}
11
- * frameRate={90}
12
- * referenceSpace='local-floor'
13
- * onsessionstart={(event: XREvent<XRManagerEvent>) => {}}
14
- * onsessionend={(event: XREvent<XRManagerEvent>) => {}}
15
- * onvisibilitychange={(event: XREvent<XRSessionEvent>) => {}}
16
- * oninputsourceschange={(event: XREvent<XRSessionEvent>) => {}}
17
- * />
18
- * ```
19
- */
20
- declare const Xr: import("svelte").Component<{
3
+ interface Props {
21
4
  /**
22
5
  * Enables foveated rendering. Default is `1`, the three.js default.
23
6
  *
@@ -40,12 +23,31 @@ declare const Xr: import("svelte").Component<{
40
23
  fallback?: Snippet;
41
24
  children?: Snippet;
42
25
  /** Called as an XRSession is requested */
43
- onsessionstart?: (event: XRSessionEvent<"sessionstart">) => void;
26
+ onsessionstart?: (event: XRSessionEvent<'sessionstart'>) => void;
44
27
  /** Called after an XRSession is terminated */
45
- onsessionend?: (event: XRSessionEvent<"sessionend">) => void;
28
+ onsessionend?: (event: XRSessionEvent<'sessionend'>) => void;
46
29
  /** Called when an XRSession is hidden or unfocused. */
47
30
  onvisibilitychange?: (event: globalThis.XRSessionEvent) => void;
48
31
  /** Called when available inputsources change */
49
32
  oninputsourceschange?: (event: globalThis.XRSessionEvent) => void;
50
- }, {}, "">;
33
+ }
34
+ /**
35
+ * `<XR />` is a WebXR manager that configures your scene for XR rendering and interaction.
36
+ *
37
+ * This should be placed within a Threlte `<Canvas />`.
38
+ *
39
+ * ```svelte
40
+ * <XR
41
+ * foveation={1}
42
+ * frameRate={90}
43
+ * referenceSpace='local-floor'
44
+ * onsessionstart={(event: XREvent<XRManagerEvent>) => {}}
45
+ * onsessionend={(event: XREvent<XRManagerEvent>) => {}}
46
+ * onvisibilitychange={(event: XREvent<XRSessionEvent>) => {}}
47
+ * oninputsourceschange={(event: XREvent<XRSessionEvent>) => {}}
48
+ * />
49
+ * ```
50
+ */
51
+ declare const Xr: import("svelte").Component<Props, {}, "">;
52
+ type Xr = ReturnType<typeof Xr>;
51
53
  export default Xr;
@@ -21,7 +21,7 @@ display info about your WebXR session. This is aliased by `ARButton` and
21
21
  import type { HTMLButtonAttributes } from 'svelte/elements'
22
22
  import { getXRSupportState } from '../lib/getXRSupportState'
23
23
  import { toggleXRSession } from '../lib/toggleXRSession'
24
- import { session, xr } from '../internal/stores'
24
+ import { isPresenting, xr } from '../internal/state.svelte'
25
25
  import type { Snippet } from 'svelte'
26
26
 
27
27
  type Props = HTMLButtonAttributes & {
@@ -57,7 +57,7 @@ display info about your WebXR session. This is aliased by `ARButton` and
57
57
  type SupportState = 'unsupported' | 'insecure' | 'blocked' | 'supported'
58
58
 
59
59
  const handleButtonClick = async (nativeEvent: MouseEvent, state: SupportState) => {
60
- if (!$xr) {
60
+ if (!xr.current) {
61
61
  throw new Error(
62
62
  'The <XR> component was not created. This is required to start an XR session.'
63
63
  )
@@ -75,7 +75,7 @@ display info about your WebXR session. This is aliased by `ARButton` and
75
75
  }
76
76
  }
77
77
 
78
- let modeText = $derived(
78
+ const modeText = $derived(
79
79
  {
80
80
  'immersive-vr': 'VR',
81
81
  'immersive-ar': 'AR',
@@ -83,7 +83,7 @@ display info about your WebXR session. This is aliased by `ARButton` and
83
83
  }[mode]
84
84
  )
85
85
 
86
- let style = $derived(
86
+ const style = $derived(
87
87
  styled
88
88
  ? `
89
89
  position: absolute;
@@ -111,7 +111,7 @@ display info about your WebXR session. This is aliased by `ARButton` and
111
111
  {style}
112
112
  >
113
113
  {#if children}
114
- {@render children?.({ state })}
114
+ {@render children({ state })}
115
115
  {:else if state === 'unsupported'}
116
116
  {modeText} unsupported
117
117
  {:else if state === 'insecure'}
@@ -119,7 +119,7 @@ display info about your WebXR session. This is aliased by `ARButton` and
119
119
  {:else if state === 'blocked'}
120
120
  {modeText} blocked
121
121
  {:else if state === 'supported'}
122
- {$session ? 'Exit' : 'Enter'} {modeText}
122
+ {isPresenting.current ? 'Exit' : 'Enter'} {modeText}
123
123
  {/if}
124
124
  </button>
125
125
  {/await}
@@ -1,24 +1,7 @@
1
1
  import type { HTMLButtonAttributes } from 'svelte/elements';
2
2
  import type { Snippet } from 'svelte';
3
- /**
4
- * `<XRButton />` is an HTML `<button />` that can be used to init and
5
- * display info about your WebXR session. This is aliased by `ARButton` and
6
- * `VRButton` with sensible session defaults.
7
- *
8
- * ```svelte
9
- * <XRButton
10
- * mode={'immersive-ar' | 'immersive-vr' | 'inline'}
11
- * sessionInit={{
12
- * optionalFeatures: ['local-floor', 'bounded-floor', 'hand-tracking', 'layers']
13
- * }}
14
- * force={'enter' | 'exit' | undefined}
15
- * styled={'true' | 'false'}
16
- * onerror={(event) => {}}
17
- * onclick={(event) => {}}
18
- * />
19
- * ```
20
- */
21
- declare const XrButton: import("svelte").Component<HTMLButtonAttributes & {
3
+ type SupportState = 'unsupported' | 'insecure' | 'blocked' | 'supported';
4
+ type Props = HTMLButtonAttributes & {
22
5
  /** The type of `XRSession` to create */
23
6
  mode: XRSessionMode;
24
7
  /**
@@ -31,16 +14,36 @@ declare const XrButton: import("svelte").Component<HTMLButtonAttributes & {
31
14
  } | undefined;
32
15
  };
33
16
  /** Whether this button should only enter / exit an `XRSession`. Default is to toggle both ways */
34
- force?: "enter" | "exit";
17
+ force?: 'enter' | 'exit';
35
18
  /** Whether to apply automatic styling to the button. Set false to apply custom styles. Default is true. */
36
19
  styled?: boolean;
37
20
  children?: Snippet<[{
38
- state: "unsupported" | "insecure" | "blocked" | "supported";
21
+ state: SupportState;
39
22
  }]>;
40
23
  onclick?: (event: {
41
- state: "unsupported" | "insecure" | "blocked" | "supported";
24
+ state: SupportState;
42
25
  nativeEvent: MouseEvent;
43
26
  }) => void;
44
27
  onerror?: (error: Error) => void;
45
- }, {}, "">;
28
+ };
29
+ /**
30
+ * `<XRButton />` is an HTML `<button />` that can be used to init and
31
+ * display info about your WebXR session. This is aliased by `ARButton` and
32
+ * `VRButton` with sensible session defaults.
33
+ *
34
+ * ```svelte
35
+ * <XRButton
36
+ * mode={'immersive-ar' | 'immersive-vr' | 'inline'}
37
+ * sessionInit={{
38
+ * optionalFeatures: ['local-floor', 'bounded-floor', 'hand-tracking', 'layers']
39
+ * }}
40
+ * force={'enter' | 'exit' | undefined}
41
+ * styled={'true' | 'false'}
42
+ * onerror={(event) => {}}
43
+ * onclick={(event) => {}}
44
+ * />
45
+ * ```
46
+ */
47
+ declare const XrButton: import("svelte").Component<Props, {}, "">;
48
+ type XrButton = ReturnType<typeof XrButton>;
46
49
  export default XrButton;
@@ -8,7 +8,7 @@
8
8
  thickness?: number
9
9
  }
10
10
 
11
- let { color = new Color('white'), size = 0.03, thickness = 0.035 }: Props = $props()
11
+ const { color = new Color('white'), size = 0.03, thickness = 0.035 }: Props = $props()
12
12
 
13
13
  const vertexShader = `
14
14
  uniform mat4 projectionMatrix;
@@ -52,6 +52,7 @@
52
52
  $effect.pre(() => {
53
53
  uniforms.thickness.value = thickness
54
54
  })
55
+
55
56
  $effect.pre(() => {
56
57
  uniforms.color.value = color
57
58
  })
@@ -1,7 +1,9 @@
1
1
  import { type ColorRepresentation } from 'three';
2
- declare const Cursor: import("svelte").Component<{
2
+ interface Props {
3
3
  color?: ColorRepresentation;
4
4
  size?: number;
5
5
  thickness?: number;
6
- }, {}, "">;
6
+ }
7
+ declare const Cursor: import("svelte").Component<Props, {}, "">;
8
+ type Cursor = ReturnType<typeof Cursor>;
7
9
  export default Cursor;
@@ -1,7 +1,17 @@
1
- <script lang="ts">
1
+ <script
2
+ module
3
+ lang="ts"
4
+ >
2
5
  import { Group, Vector3, Matrix3 } from 'three'
3
- import { T, useTask } from '@threlte/core'
4
- import { pointerIntersection, pointerState } from '../../internal/stores'
6
+
7
+ const vec3 = new Vector3()
8
+ const normalMatrix = new Matrix3()
9
+ const worldNormal = new Vector3()
10
+ </script>
11
+
12
+ <script lang="ts">
13
+ import { T, useTask, useThrelte } from '@threlte/core'
14
+ import { pointerIntersection, pointerState } from '../../internal/state.svelte'
5
15
  import Cursor from './Cursor.svelte'
6
16
  import type { Snippet } from 'svelte'
7
17
 
@@ -10,27 +20,30 @@
10
20
  children?: Snippet
11
21
  }
12
22
 
13
- let { handedness, children }: Props = $props()
23
+ const { handedness, children }: Props = $props()
14
24
 
15
- const ref = new Group()
16
- const vec3 = new Vector3()
17
- const normalMatrix = new Matrix3()
18
- const worldNormal = new Vector3()
25
+ const { scene } = useThrelte()
26
+ const hovering = $derived(pointerState[handedness].hovering)
27
+ const intersection = $derived(pointerIntersection[handedness])
19
28
 
20
- let hovering = $derived($pointerState[handedness].hovering)
21
- let intersection = $derived(pointerIntersection[handedness])
29
+ const ref = new Group()
22
30
 
23
31
  const { start, stop } = useTask(
24
32
  () => {
25
- if (intersection.current === undefined) return
26
- const { point, face, object } = intersection.current
33
+ if (intersection === undefined) {
34
+ return
35
+ }
36
+
37
+ const { point, face, object } = intersection
27
38
  ref.position.lerp(point, 0.4)
28
39
 
29
- if (face) {
30
- normalMatrix.getNormalMatrix(object.matrixWorld)
31
- worldNormal.copy(face.normal).applyMatrix3(normalMatrix).normalize()
32
- ref.lookAt(vec3.addVectors(point, worldNormal))
40
+ if (face === null || face === undefined) {
41
+ return
33
42
  }
43
+
44
+ normalMatrix.getNormalMatrix(object.matrixWorld)
45
+ worldNormal.copy(face.normal).applyMatrix3(normalMatrix).normalize()
46
+ ref.lookAt(vec3.addVectors(point, worldNormal))
34
47
  },
35
48
  {
36
49
  autoStart: false
@@ -38,8 +51,8 @@
38
51
  )
39
52
 
40
53
  $effect.pre(() => {
41
- if (hovering) {
42
- ref.position.copy(intersection.current!.point)
54
+ if (hovering && intersection) {
55
+ ref.position.copy(intersection.point)
43
56
  start()
44
57
  } else {
45
58
  stop()
@@ -49,6 +62,7 @@
49
62
 
50
63
  <T
51
64
  is={ref}
65
+ attach={scene}
52
66
  visible={hovering}
53
67
  >
54
68
  {#if children}
@@ -1,6 +1,8 @@
1
1
  import type { Snippet } from 'svelte';
2
- declare const PointerCursor: import("svelte").Component<{
3
- handedness: "left" | "right";
2
+ interface Props {
3
+ handedness: 'left' | 'right';
4
4
  children?: Snippet;
5
- }, {}, "">;
5
+ }
6
+ declare const PointerCursor: import("svelte").Component<Props, {}, "">;
7
+ type PointerCursor = ReturnType<typeof PointerCursor>;
6
8
  export default PointerCursor;
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { T } from '@threlte/core'
3
- import { pointerState, teleportState, teleportIntersection } from '../../internal/stores'
3
+ import { pointerState, teleportState, teleportIntersection } from '../../internal/state.svelte'
4
4
  import type { Snippet } from 'svelte'
5
5
 
6
6
  interface Props {
@@ -8,12 +8,12 @@
8
8
  children?: Snippet
9
9
  }
10
10
 
11
- let { handedness, children }: Props = $props()
11
+ const { handedness, children }: Props = $props()
12
12
 
13
- let hovering = $derived($teleportState[handedness].hovering)
14
- let intersection = $derived(teleportIntersection[handedness])
15
- let visible = $derived(
16
- $pointerState[handedness].enabled || (hovering && $intersection === undefined)
13
+ const hovering = $derived(teleportState[handedness].hovering)
14
+ const intersection = $derived(teleportIntersection[handedness])
15
+ const visible = $derived(
16
+ pointerState[handedness].enabled || (hovering && intersection === undefined)
17
17
  )
18
18
 
19
19
  const vertexShader = `
@@ -1,6 +1,8 @@
1
1
  import type { Snippet } from 'svelte';
2
- declare const ShortRay: import("svelte").Component<{
3
- handedness: "left" | "right";
2
+ interface Props {
3
+ handedness: 'left' | 'right';
4
4
  children?: Snippet;
5
- }, {}, "">;
5
+ }
6
+ declare const ShortRay: import("svelte").Component<Props, {}, "">;
7
+ type ShortRay = ReturnType<typeof ShortRay>;
6
8
  export default ShortRay;
@@ -1,8 +1,18 @@
1
- <script lang="ts">
2
- import { spring } from 'svelte/motion'
1
+ <script
2
+ module
3
+ lang="ts"
4
+ >
3
5
  import { Group, Matrix3, Vector3 } from 'three'
4
- import { T, useTask } from '@threlte/core'
5
- import { teleportIntersection } from '../../internal/stores'
6
+
7
+ const vec3 = new Vector3()
8
+ const normalMatrix = new Matrix3()
9
+ const worldNormal = new Vector3()
10
+ </script>
11
+
12
+ <script lang="ts">
13
+ import { Spring } from 'svelte/motion'
14
+ import { T, useTask, useThrelte } from '@threlte/core'
15
+ import { teleportIntersection } from '../../internal/state.svelte'
6
16
  import Cursor from './Cursor.svelte'
7
17
  import type { Snippet } from 'svelte'
8
18
 
@@ -11,20 +21,20 @@
11
21
  children?: Snippet
12
22
  }
13
23
 
14
- let { handedness, children }: Props = $props()
24
+ const { handedness, children }: Props = $props()
15
25
 
16
- const ref = new Group()
17
- const vec3 = new Vector3()
18
- const normalMatrix = new Matrix3()
19
- const worldNormal = new Vector3()
26
+ const { scene } = useThrelte()
27
+ const intersection = $derived(teleportIntersection[handedness])
20
28
 
21
- let intersection = $derived(teleportIntersection[handedness])
29
+ const ref = new Group()
22
30
 
23
31
  const { start, stop } = useTask(
24
32
  () => {
25
- if (intersection.current === undefined) return
33
+ if (intersection === undefined) {
34
+ return
35
+ }
26
36
 
27
- const { point, face, object } = intersection.current
37
+ const { point, face, object } = intersection
28
38
  ref.position.lerp(point, 0.4)
29
39
 
30
40
  if (face) {
@@ -38,15 +48,15 @@
38
48
  }
39
49
  )
40
50
 
41
- const size = spring(0.1, { stiffness: 0.2 })
51
+ const size = new Spring(0.1, { stiffness: 0.2 })
42
52
 
43
53
  $effect.pre(() => {
44
- if ($intersection === undefined) {
54
+ if (intersection === undefined) {
45
55
  size.set(0.1)
46
56
  stop()
47
57
  } else {
48
58
  size.set(1)
49
- ref.position.copy($intersection.point)
59
+ ref.position.copy(intersection.point)
50
60
  start()
51
61
  }
52
62
  })
@@ -54,13 +64,14 @@
54
64
 
55
65
  <T
56
66
  is={ref}
57
- visible={$intersection !== undefined}
67
+ attach={scene}
68
+ visible={intersection !== undefined}
58
69
  >
59
70
  {#if children}
60
71
  {@render children()}
61
72
  {:else}
62
73
  <Cursor
63
- size={$size}
74
+ size={size.current}
64
75
  thickness={0.015}
65
76
  />
66
77
  {/if}
@@ -1,6 +1,8 @@
1
1
  import type { Snippet } from 'svelte';
2
- declare const TeleportCursor: import("svelte").Component<{
3
- handedness: "left" | "right";
2
+ interface Props {
3
+ handedness: 'left' | 'right';
4
4
  children?: Snippet;
5
- }, {}, "">;
5
+ }
6
+ declare const TeleportCursor: import("svelte").Component<Props, {}, "">;
7
+ type TeleportCursor = ReturnType<typeof TeleportCursor>;
6
8
  export default TeleportCursor;
@@ -1,11 +1,24 @@
1
- <script lang="ts">
1
+ <script
2
+ module
3
+ lang="ts"
4
+ >
2
5
  import { Vector3, QuadraticBezierCurve3, type XRTargetRaySpace, Vector2 } from 'three'
6
+
7
+ const rayStart = new Vector3()
8
+ const rayMidpoint = new Vector3()
9
+ const curve = new QuadraticBezierCurve3()
10
+ const vec3 = new Vector3()
11
+ const v2_1 = new Vector2()
12
+ const v2_2 = new Vector2()
13
+ </script>
14
+
15
+ <script lang="ts">
16
+ import type { Snippet } from 'svelte'
3
17
  import { Line2 } from 'three/examples/jsm/lines/Line2.js'
4
18
  import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry.js'
5
19
  import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js'
6
- import { T, useTask } from '@threlte/core'
7
- import { teleportIntersection } from '../../internal/stores'
8
- import type { Snippet } from 'svelte'
20
+ import { T, useTask, useThrelte } from '@threlte/core'
21
+ import { teleportIntersection } from '../../internal/state.svelte'
9
22
 
10
23
  interface Props {
11
24
  handedness: 'left' | 'right'
@@ -13,26 +26,18 @@
13
26
  children?: Snippet
14
27
  }
15
28
 
16
- let { handedness, targetRay, children }: Props = $props()
17
-
18
- let lineGeometry = new LineGeometry()
29
+ const { handedness, targetRay, children }: Props = $props()
19
30
 
20
- const rayStart = new Vector3()
21
- const rayMidpoint = new Vector3()
22
- const curve = new QuadraticBezierCurve3()
31
+ const { scene } = useThrelte()
23
32
  const rayDivisions = 40
24
33
  const positions = new Float32Array(rayDivisions * 3)
25
- const vec3 = new Vector3()
26
-
27
- const v2_1 = new Vector2()
28
- const v2_2 = new Vector2()
29
-
30
- let intersection = $derived(teleportIntersection[handedness])
34
+ const lineGeometry = new LineGeometry()
35
+ const intersection = $derived(teleportIntersection[handedness])
31
36
 
32
37
  const setCurvePoints = (alpha = 0.3) => {
33
- if (intersection.current === undefined) return
38
+ if (intersection === undefined) return
34
39
 
35
- const rayEnd = intersection.current.point
40
+ const rayEnd = intersection.point
36
41
  targetRay.getWorldPosition(rayStart)
37
42
 
38
43
  rayMidpoint.x = (rayStart.x + rayEnd.x) / 2
@@ -69,7 +74,7 @@
69
74
  )
70
75
 
71
76
  $effect.pre(() => {
72
- if ($intersection === undefined) {
77
+ if (intersection === undefined) {
73
78
  stop()
74
79
  } else {
75
80
  setCurvePoints(1)
@@ -83,7 +88,8 @@
83
88
  {:else}
84
89
  <T
85
90
  is={Line2}
86
- visible={$intersection !== undefined}
91
+ attach={scene}
92
+ visible={intersection !== undefined}
87
93
  position.z={-0.01}
88
94
  >
89
95
  <T is={lineGeometry} />
@@ -1,8 +1,10 @@
1
1
  import { type XRTargetRaySpace } from 'three';
2
2
  import type { Snippet } from 'svelte';
3
- declare const TeleportRay: import("svelte").Component<{
4
- handedness: "left" | "right";
3
+ interface Props {
4
+ handedness: 'left' | 'right';
5
5
  targetRay: XRTargetRaySpace;
6
6
  children?: Snippet;
7
- }, {}, "">;
7
+ }
8
+ declare const TeleportRay: import("svelte").Component<Props, {}, "">;
9
+ type TeleportRay = ReturnType<typeof TeleportRay>;
8
10
  export default TeleportRay;
@@ -0,0 +1,5 @@
1
+ import { type Readable } from 'svelte/store';
2
+ export type CurrentReadable<T> = Readable<T> & {
3
+ current: T;
4
+ };
5
+ export declare const toCurrentReadable: <T>(getter: () => T) => CurrentReadable<T>;
@@ -0,0 +1,11 @@
1
+ import { toStore } from 'svelte/store';
2
+ export const toCurrentReadable = (getter) => {
3
+ const store = toStore(getter);
4
+ store.current = getter();
5
+ $effect.pre(() => {
6
+ return store.subscribe((value) => {
7
+ store.current = value;
8
+ });
9
+ });
10
+ return store;
11
+ };
@@ -0,0 +1,13 @@
1
+ import type { XRController } from '../types';
2
+ import { type CurrentReadable } from './currentReadable.svelte';
3
+ declare class Controllers {
4
+ left: XRController | undefined;
5
+ right: XRController | undefined;
6
+ none: XRController | undefined;
7
+ }
8
+ export declare const controllers: Controllers;
9
+ /**
10
+ * Provides a reference to a current XRController, filtered by handedness.
11
+ */
12
+ export declare const useController: (handedness: XRHandedness) => CurrentReadable<XRController | undefined>;
13
+ export {};
@@ -0,0 +1,22 @@
1
+ import { toCurrentReadable } from './currentReadable.svelte';
2
+ class Controllers {
3
+ left = $state.raw();
4
+ right = $state.raw();
5
+ none = $state.raw();
6
+ }
7
+ export const controllers = new Controllers();
8
+ /**
9
+ * Provides a reference to a current XRController, filtered by handedness.
10
+ */
11
+ export const useController = (handedness) => {
12
+ switch (handedness) {
13
+ case 'left':
14
+ return toCurrentReadable(() => controllers.left);
15
+ case 'right':
16
+ return toCurrentReadable(() => controllers.right);
17
+ case 'none':
18
+ return toCurrentReadable(() => controllers.none);
19
+ default:
20
+ throw new Error('useController handedness must be left, right, or none.');
21
+ }
22
+ };
@@ -0,0 +1,12 @@
1
+ import type { XRHand } from '../types';
2
+ import { type CurrentReadable } from './currentReadable.svelte';
3
+ declare class Hands {
4
+ left: XRHand | undefined;
5
+ right: XRHand | undefined;
6
+ }
7
+ export declare const hands: Hands;
8
+ /**
9
+ * Provides a reference to a current XRHand, filtered by handedness.
10
+ */
11
+ export declare const useHand: (handedness: "left" | "right") => CurrentReadable<undefined | XRHand>;
12
+ export {};
@@ -1,15 +1,18 @@
1
- import { currentWritable } from '@threlte/core';
2
- export const left = currentWritable(undefined);
3
- export const right = currentWritable(undefined);
1
+ import { toCurrentReadable } from './currentReadable.svelte';
2
+ class Hands {
3
+ left = $state.raw();
4
+ right = $state.raw();
5
+ }
6
+ export const hands = new Hands();
4
7
  /**
5
8
  * Provides a reference to a current XRHand, filtered by handedness.
6
9
  */
7
10
  export const useHand = (handedness) => {
8
11
  switch (handedness) {
9
12
  case 'left':
10
- return left;
13
+ return toCurrentReadable(() => hands.left);
11
14
  case 'right':
12
- return right;
15
+ return toCurrentReadable(() => hands.right);
13
16
  default:
14
17
  throw new Error('useHand handedness must be left or right.');
15
18
  }
@@ -3,4 +3,4 @@ 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<XRJointSpace | undefined>;
6
+ export declare const useHandJoint: (handedness: "left" | "right", joint: HandJoints) => import("./currentReadable.svelte").CurrentReadable<XRJointSpace | undefined>;