@threlte/xr 1.0.0-next.3 → 1.0.0-next.5

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 (30) hide show
  1. package/dist/components/ARButton.svelte +6 -4
  2. package/dist/components/ARButton.svelte.d.ts +2 -4
  3. package/dist/components/Controller.svelte +31 -39
  4. package/dist/components/Controller.svelte.d.ts +12 -15
  5. package/dist/components/Hand.svelte +29 -14
  6. package/dist/components/Hand.svelte.d.ts +8 -11
  7. package/dist/components/Headset.svelte.d.ts +1 -3
  8. package/dist/components/VRButton.svelte +2 -1
  9. package/dist/components/VRButton.svelte.d.ts +2 -4
  10. package/dist/components/XR.svelte.d.ts +3 -6
  11. package/dist/components/XRButton.svelte +10 -1
  12. package/dist/components/XRButton.svelte.d.ts +3 -4
  13. package/dist/components/internal/Cursor.svelte.d.ts +3 -3
  14. package/dist/components/internal/PointerCursor.svelte +5 -3
  15. package/dist/components/internal/PointerCursor.svelte.d.ts +3 -7
  16. package/dist/components/internal/ScenePortal.svelte.d.ts +1 -3
  17. package/dist/components/internal/ShortRay.svelte +23 -21
  18. package/dist/components/internal/ShortRay.svelte.d.ts +3 -7
  19. package/dist/components/internal/TeleportCursor.svelte +5 -3
  20. package/dist/components/internal/TeleportCursor.svelte.d.ts +3 -7
  21. package/dist/components/internal/TeleportRay.svelte +5 -3
  22. package/dist/components/internal/TeleportRay.svelte.d.ts +3 -7
  23. package/dist/internal/defaultFeatures.d.ts +4 -0
  24. package/dist/internal/defaultFeatures.js +14 -0
  25. package/dist/internal/setupControllers.js +1 -1
  26. package/dist/internal/setupHands.js +1 -1
  27. package/dist/internal/setupHeadset.js +14 -5
  28. package/dist/internal/setupRaf.js +8 -12
  29. package/dist/types.d.ts +21 -19
  30. package/package.json +27 -7
@@ -10,15 +10,17 @@
10
10
  ```
11
11
  -->
12
12
  <script lang="ts">import XRButton from "./XRButton.svelte";
13
- let { ...props } = $props();
13
+ import { defaultFeatures } from "../internal/defaultFeatures";
14
+ let { children, ...props } = $props();
14
15
  </script>
15
16
 
16
17
  <XRButton
17
18
  sessionInit={{
18
19
  domOverlay: typeof document !== 'undefined' ? { root: document.body } : undefined,
19
- requiredFeatures: ['plane-detection'],
20
- optionalFeatures: ['local-floor', 'bounded-floor', 'hand-tracking', 'layers', 'hit-test']
20
+ ...defaultFeatures
21
21
  }}
22
22
  {...props}
23
23
  mode="immersive-ar"
24
- />
24
+ >
25
+ {@render children?.()}
26
+ </XRButton>
@@ -10,11 +10,9 @@ declare const __propDef: {
10
10
  }) | undefined;
11
11
  force?: "enter" | "exit" | undefined;
12
12
  styled?: boolean | undefined;
13
- children?: ((this: void, args_0: {
13
+ children?: import("svelte").Snippet<[{
14
14
  state: "blocked" | "unsupported" | "insecure" | "supported";
15
- }) => typeof import("svelte").SnippetReturn & {
16
- _: "functions passed to {@render ...} tags must use the `Snippet` type imported from \"svelte\"";
17
- }) | undefined;
15
+ }]> | undefined;
18
16
  onclick?: ((event: {
19
17
  state: "blocked" | "unsupported" | "insecure" | "supported";
20
18
  nativeEvent: MouseEvent;
@@ -30,7 +30,14 @@ const stores = {
30
30
  onselectstart,
31
31
  onsqueeze,
32
32
  onsqueezeend,
33
- onsqueezestart
33
+ onsqueezestart,
34
+ children,
35
+ grip: gripSnippet,
36
+ targetRay: targetRaySnippet,
37
+ pointerRay: pointerRaySnippet,
38
+ pointerCursor: pointerCursorSnippet,
39
+ teleportRay: teleportRaySnippet,
40
+ teleportCursor: teleportCursorSnippet
34
41
  } = $props();
35
42
  const handedness = writable(left ? "left" : right ? "right" : hand);
36
43
  $effect.pre(() => handedness.set(left ? "left" : right ? "right" : hand));
@@ -57,26 +64,25 @@ let hasTeleportControls = $derived($teleportState[$handedness].enabled);
57
64
  {#if !$isHandTracking}
58
65
  {#if grip}
59
66
  <T is={grip}>
60
- <slot>
67
+ {#if children}
68
+ {@render children?.()}
69
+ {:else}
61
70
  <T is={model} />
62
- </slot>
71
+ {/if}
63
72
 
64
- <slot name="grip" />
73
+ {@render gripSnippet?.()}
65
74
  </T>
66
75
  {/if}
67
76
 
68
77
  {#if targetRay}
69
78
  <T is={targetRay}>
70
- <slot name="target-ray" />
79
+ {@render targetRaySnippet?.()}
71
80
 
72
81
  {#if hasPointerControls || hasTeleportControls}
73
- {#if $$slots['pointer-ray']}
74
- <ShortRay handedness={$handedness}>
75
- <slot name="pointer-ray" />
76
- </ShortRay>
77
- {:else}
78
- <ShortRay handedness={$handedness} />
79
- {/if}
82
+ <ShortRay
83
+ handedness={$handedness}
84
+ children={pointerRaySnippet}
85
+ />
80
86
  {/if}
81
87
  </T>
82
88
  {/if}
@@ -84,36 +90,22 @@ let hasTeleportControls = $derived($teleportState[$handedness].enabled);
84
90
 
85
91
  <ScenePortal>
86
92
  {#if hasPointerControls}
87
- {#if $$slots['pointer-cursor']}
88
- <PointerCursor handedness={$handedness}>
89
- <slot name="pointer-cursor" />
90
- </PointerCursor>
91
- {:else}
92
- <PointerCursor handedness={$handedness} />
93
- {/if}
93
+ <PointerCursor
94
+ handedness={$handedness}
95
+ children={pointerCursorSnippet}
96
+ />
94
97
  {/if}
95
98
 
96
99
  {#if hasTeleportControls && targetRay !== undefined}
97
- {#if $$slots['teleport-ray']}
98
- <TeleportRay
99
- {targetRay}
100
- handedness={$handedness}
101
- >
102
- <slot name="teleport-ray" />
103
- </TeleportRay>
104
- {:else}
105
- <TeleportRay
106
- {targetRay}
107
- handedness={$handedness}
108
- />
109
- {/if}
100
+ <TeleportRay
101
+ {targetRay}
102
+ handedness={$handedness}
103
+ children={teleportRaySnippet}
104
+ />
110
105
 
111
- {#if $$slots['teleport-ray']}
112
- <TeleportCursor handedness={$handedness}>
113
- <slot name="teleport-cursor" />
114
- </TeleportCursor>
115
- {:else}
116
- <TeleportCursor handedness={$handedness} />
117
- {/if}
106
+ <TeleportCursor
107
+ handedness={$handedness}
108
+ children={teleportCursorSnippet}
109
+ />
118
110
  {/if}
119
111
  </ScenePortal>
@@ -1,7 +1,16 @@
1
1
  import { SvelteComponent } from "svelte";
2
2
  import type { XRControllerEvents } from '../types';
3
+ import type { Snippet } from 'svelte';
3
4
  declare const __propDef: {
4
- props: (XRControllerEvents & ({
5
+ props: {
6
+ children?: Snippet<[]> | undefined;
7
+ grip?: Snippet<[]> | undefined;
8
+ targetRay?: Snippet<[]> | undefined;
9
+ pointerRay?: Snippet<[]> | undefined;
10
+ pointerCursor?: Snippet<[]> | undefined;
11
+ teleportRay?: Snippet<[]> | undefined;
12
+ teleportCursor?: Snippet<[]> | undefined;
13
+ } & XRControllerEvents & ({
5
14
  /** Whether the controller should be matched with the left hand. */
6
15
  left: true;
7
16
  right?: undefined;
@@ -16,23 +25,11 @@ declare const __propDef: {
16
25
  hand: 'left' | 'right';
17
26
  left?: undefined;
18
27
  right?: undefined;
19
- })) & {
20
- children?: ((this: void) => typeof import("svelte").SnippetReturn & {
21
- _: "functions passed to {@render ...} tags must use the `Snippet` type imported from \"svelte\"";
22
- }) | undefined;
23
- };
28
+ });
24
29
  events: {
25
30
  [evt: string]: CustomEvent<any>;
26
31
  };
27
- slots: {
28
- default: {};
29
- grip: {};
30
- 'target-ray': {};
31
- 'pointer-ray': {};
32
- 'pointer-cursor': {};
33
- 'teleport-ray': {};
34
- 'teleport-cursor': {};
35
- };
32
+ slots: {};
36
33
  };
37
34
  export type ControllerProps = typeof __propDef.props;
38
35
  export type ControllerEvents = typeof __propDef.events;
@@ -13,8 +13,20 @@ const stores = {
13
13
  };
14
14
  </script>
15
15
 
16
- <script lang="ts">let { left, right, hand, onconnected, ondisconnected, onpinchend, onpinchstart } = $props();
17
- const { xr } = useThrelte().renderer;
16
+ <script lang="ts">let {
17
+ left,
18
+ right,
19
+ hand,
20
+ onconnected,
21
+ ondisconnected,
22
+ onpinchend,
23
+ onpinchstart,
24
+ children,
25
+ targetRay,
26
+ wrist
27
+ } = $props();
28
+ const { renderer, scheduler, renderStage } = useThrelte();
29
+ const { xr } = renderer;
18
30
  const space = xr.getReferenceSpace();
19
31
  const handedness = writable(left ? "left" : right ? "right" : hand);
20
32
  $effect.pre(() => handedness.set(left ? "left" : right ? "right" : hand));
@@ -26,24 +38,27 @@ $effect.pre(
26
38
  onpinchstart
27
39
  })
28
40
  );
29
- let children = new Group();
41
+ let group = new Group();
30
42
  const { start, stop } = useTask(
31
43
  () => {
32
44
  const frame = xr.getFrame();
33
- const joint = inputSource.get("wrist");
45
+ const joint = inputSource?.get("wrist");
34
46
  if (joint === void 0 || space === null)
35
47
  return;
36
48
  const pose = frame.getJointPose?.(joint, space);
37
49
  if (pose === void 0 || pose === null)
38
50
  return;
39
51
  const { position, orientation } = pose.transform;
40
- children.position.set(position.x, position.y, position.z);
41
- children.quaternion.set(orientation.x, orientation.y, orientation.z, orientation.w);
52
+ group.position.set(position.x, position.y, position.z);
53
+ group.quaternion.set(orientation.x, orientation.y, orientation.z, orientation.w);
42
54
  },
43
- { autoStart: false }
55
+ {
56
+ autoStart: false,
57
+ stage: scheduler.createStage(Symbol("xr-hand-stage"), { before: renderStage })
58
+ }
44
59
  );
45
60
  $effect.pre(() => {
46
- if ($isHandTracking && ($$slots.wrist || $$slots.default) && inputSource) {
61
+ if ($isHandTracking && (wrist !== void 0 || children !== void 0) && inputSource) {
47
62
  start();
48
63
  } else {
49
64
  stop();
@@ -56,23 +71,23 @@ let model = $derived($store?.model);
56
71
 
57
72
  {#if $store?.hand && $isHandTracking}
58
73
  <T is={$store.hand}>
59
- {#if $$slots.default === undefined}
74
+ {#if children === undefined}
60
75
  <T is={model} />
61
76
  {/if}
62
77
  </T>
63
78
 
64
- {#if $$slots['target-ray'] !== undefined}
79
+ {#if targetRay !== undefined}
65
80
  <T is={$store.targetRay}>
66
- <slot name="target-ray" />
81
+ {@render targetRay()}
67
82
  </T>
68
83
  {/if}
69
84
  {/if}
70
85
 
71
86
  {#if $isHandTracking}
72
87
  <ScenePortal>
73
- <T is={children}>
74
- <slot name="wrist" />
75
- <slot />
88
+ <T is={group}>
89
+ {@render wrist?.()}
90
+ {@render children?.()}
76
91
  </T>
77
92
  </ScenePortal>
78
93
  {/if}
@@ -1,7 +1,12 @@
1
1
  import { SvelteComponent } from "svelte";
2
2
  import type { XRHandEvents } from '../types';
3
+ import type { Snippet } from 'svelte';
3
4
  declare const __propDef: {
4
- props: (XRHandEvents & ({
5
+ props: {
6
+ children?: Snippet<[]> | undefined;
7
+ targetRay?: Snippet<[]> | undefined;
8
+ wrist?: Snippet<[]> | undefined;
9
+ } & XRHandEvents & ({
5
10
  /** Whether the XRHand should be matched with the left hand. */
6
11
  left: true;
7
12
  right?: undefined;
@@ -16,19 +21,11 @@ declare const __propDef: {
16
21
  hand: 'left' | 'right';
17
22
  left?: undefined;
18
23
  right?: undefined;
19
- })) & {
20
- children?: ((this: void) => typeof import("svelte").SnippetReturn & {
21
- _: "functions passed to {@render ...} tags must use the `Snippet` type imported from \"svelte\"";
22
- }) | undefined;
23
- };
24
+ });
24
25
  events: {
25
26
  [evt: string]: CustomEvent<any>;
26
27
  };
27
- slots: {
28
- 'target-ray': {};
29
- wrist: {};
30
- default: {};
31
- };
28
+ slots: {};
32
29
  };
33
30
  export type HandProps = typeof __propDef.props;
34
31
  export type HandEvents = typeof __propDef.events;
@@ -1,9 +1,7 @@
1
1
  import { SvelteComponent } from "svelte";
2
2
  declare const __propDef: {
3
3
  props: {
4
- children?: ((this: void) => typeof import("svelte").SnippetReturn & {
5
- _: "functions passed to {@render ...} tags must use the `Snippet` type imported from \"svelte\"";
6
- }) | undefined;
4
+ children?: import("svelte").Snippet<[]> | undefined;
7
5
  };
8
6
  events: {
9
7
  [evt: string]: CustomEvent<any>;
@@ -10,12 +10,13 @@
10
10
  ```
11
11
  -->
12
12
  <script lang="ts">import XRButton from "./XRButton.svelte";
13
+ import { defaultFeatures } from "../internal/defaultFeatures";
13
14
  let { ...props } = $props();
14
15
  </script>
15
16
 
16
17
  <XRButton
17
18
  sessionInit={{
18
- optionalFeatures: ['local-floor', 'bounded-floor', 'hand-tracking', 'layers']
19
+ ...defaultFeatures
19
20
  }}
20
21
  {...props}
21
22
  mode="immersive-vr"
@@ -10,11 +10,9 @@ declare const __propDef: {
10
10
  }) | undefined;
11
11
  force?: "enter" | "exit" | undefined;
12
12
  styled?: boolean | undefined;
13
- children?: ((this: void, args_0: {
13
+ children?: import("svelte").Snippet<[{
14
14
  state: "blocked" | "unsupported" | "insecure" | "supported";
15
- }) => typeof import("svelte").SnippetReturn & {
16
- _: "functions passed to {@render ...} tags must use the `Snippet` type imported from \"svelte\"";
17
- }) | undefined;
15
+ }]> | undefined;
18
16
  onclick?: ((event: {
19
17
  state: "blocked" | "unsupported" | "insecure" | "supported";
20
18
  nativeEvent: MouseEvent;
@@ -1,5 +1,6 @@
1
1
  /// <reference types="webxr" />
2
2
  import { SvelteComponent } from "svelte";
3
+ import { type Snippet } from 'svelte';
3
4
  import type { XRSessionEvent } from '../types';
4
5
  declare const __propDef: {
5
6
  props: {
@@ -22,12 +23,8 @@ declare const __propDef: {
22
23
  frameRate?: number | undefined;
23
24
  /** Type of WebXR reference space to use. Default is `local-floor` */
24
25
  referenceSpace?: XRReferenceSpaceType | undefined;
25
- fallback?: ((this: void) => typeof import("svelte").SnippetReturn & {
26
- _: "functions passed to {@render ...} tags must use the `Snippet` type imported from \"svelte\"";
27
- }) | undefined;
28
- children?: ((this: void) => typeof import("svelte").SnippetReturn & {
29
- _: "functions passed to {@render ...} tags must use the `Snippet` type imported from \"svelte\"";
30
- }) | undefined;
26
+ fallback?: Snippet<[]> | undefined;
27
+ children?: Snippet<[]> | undefined;
31
28
  /** Called as an XRSession is requested */
32
29
  onsessionstart?: ((event: XRSessionEvent<'sessionstart'>) => void) | undefined;
33
30
  /** Called after an XRSession is terminated */
@@ -20,7 +20,16 @@ display info about your WebXR session. This is aliased by `ARButton` and
20
20
  <script lang="ts">import { getXRSupportState } from "../lib/getXRSupportState";
21
21
  import { toggleXRSession } from "../lib/toggleXRSession";
22
22
  import { session, xr } from "../internal/stores";
23
- let { mode, sessionInit, force, styled = true, onclick, onerror, children, ...props } = $props();
23
+ let {
24
+ mode,
25
+ sessionInit,
26
+ force,
27
+ styled = true,
28
+ onclick,
29
+ onerror,
30
+ children,
31
+ ...props
32
+ } = $props();
24
33
  const handleButtonClick = async (nativeEvent, state) => {
25
34
  if (!$xr) {
26
35
  throw new Error(
@@ -1,6 +1,7 @@
1
1
  /// <reference types="webxr" />
2
2
  import { SvelteComponent } from "svelte";
3
3
  import type { HTMLButtonAttributes } from 'svelte/elements';
4
+ import type { Snippet } from 'svelte';
4
5
  declare const __propDef: {
5
6
  props: HTMLButtonAttributes & {
6
7
  /** The type of `XRSession` to create */
@@ -18,11 +19,9 @@ declare const __propDef: {
18
19
  force?: "enter" | "exit" | undefined;
19
20
  /** Whether to apply automatic styling to the button. Set false to apply custom styles. Default is true. */
20
21
  styled?: boolean | undefined;
21
- children?: ((this: void, args_0: {
22
+ children?: Snippet<[{
22
23
  state: "blocked" | "unsupported" | "insecure" | "supported";
23
- }) => typeof import("svelte").SnippetReturn & {
24
- _: "functions passed to {@render ...} tags must use the `Snippet` type imported from \"svelte\"";
25
- }) | undefined;
24
+ }]> | undefined;
26
25
  onclick?: ((event: {
27
26
  state: "blocked" | "unsupported" | "insecure" | "supported";
28
27
  nativeEvent: MouseEvent;
@@ -2,9 +2,9 @@ import { SvelteComponent } from "svelte";
2
2
  import { type ColorRepresentation } from 'three';
3
3
  declare const __propDef: {
4
4
  props: {
5
- color: ColorRepresentation;
6
- size: number;
7
- thickness: number;
5
+ color?: ColorRepresentation | undefined;
6
+ size?: number | undefined;
7
+ thickness?: number | undefined;
8
8
  };
9
9
  events: {
10
10
  [evt: string]: CustomEvent<any>;
@@ -2,7 +2,7 @@
2
2
  import { T, useTask } from "@threlte/core";
3
3
  import { pointerIntersection, pointerState } from "../../internal/stores";
4
4
  import Cursor from "./Cursor.svelte";
5
- let { handedness } = $props();
5
+ let { handedness, children } = $props();
6
6
  const ref = new Group();
7
7
  const vec3 = new Vector3();
8
8
  const normalMatrix = new Matrix3();
@@ -39,7 +39,9 @@ $effect.pre(() => {
39
39
  is={ref}
40
40
  visible={hovering}
41
41
  >
42
- <slot>
42
+ {#if children}
43
+ {@render children()}
44
+ {:else}
43
45
  <Cursor />
44
- </slot>
46
+ {/if}
45
47
  </T>
@@ -1,18 +1,14 @@
1
1
  import { SvelteComponent } from "svelte";
2
+ import type { Snippet } from 'svelte';
2
3
  declare const __propDef: {
3
4
  props: {
4
5
  handedness: 'left' | 'right';
5
- } & {
6
- children?: ((this: void) => typeof import("svelte").SnippetReturn & {
7
- _: "functions passed to {@render ...} tags must use the `Snippet` type imported from \"svelte\"";
8
- }) | undefined;
6
+ children?: Snippet<[]> | undefined;
9
7
  };
10
8
  events: {
11
9
  [evt: string]: CustomEvent<any>;
12
10
  };
13
- slots: {
14
- default: {};
15
- };
11
+ slots: {};
16
12
  };
17
13
  export type PointerCursorProps = typeof __propDef.props;
18
14
  export type PointerCursorEvents = typeof __propDef.events;
@@ -1,9 +1,7 @@
1
1
  import { SvelteComponent } from "svelte";
2
2
  declare const __propDef: {
3
3
  props: {
4
- children?: ((this: void) => typeof import("svelte").SnippetReturn & {
5
- _: "functions passed to {@render ...} tags must use the `Snippet` type imported from \"svelte\"";
6
- }) | undefined;
4
+ children?: import("svelte").Snippet<[]> | undefined;
7
5
  };
8
6
  events: {
9
7
  [evt: string]: CustomEvent<any>;
@@ -1,15 +1,33 @@
1
1
  <script lang="ts">import { T } from "@threlte/core";
2
2
  import { pointerState, teleportState, teleportIntersection } from "../../internal/stores";
3
- let { handedness } = $props();
3
+ let { handedness, children } = $props();
4
4
  let hovering = $derived($teleportState[handedness].hovering);
5
5
  let intersection = $derived(teleportIntersection[handedness]);
6
6
  let visible = $derived(
7
7
  $pointerState[handedness].enabled || hovering && $intersection === void 0
8
8
  );
9
+ const vertexShader = `
10
+ uniform mat4 modelViewMatrix;
11
+ uniform mat4 projectionMatrix;
12
+ attribute vec2 uv;
13
+ attribute vec3 position;
14
+ varying vec2 vUv;
15
+ void main() {
16
+ vUv = uv;
17
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
18
+ }`;
19
+ const fragmentShader = `
20
+ precision mediump float;
21
+ varying vec2 vUv;
22
+ void main() {
23
+ gl_FragColor = vec4(1.0, 1.0, 1.0, pow(vUv.y - 1.0, 2.0));
24
+ }`;
9
25
  </script>
10
26
 
11
27
  <T.Group {visible}>
12
- <slot>
28
+ {#if children}
29
+ {@render children()}
30
+ {:else}
13
31
  <T.Mesh
14
32
  rotation.x={-Math.PI / 2}
15
33
  position.z={-0.1}
@@ -17,25 +35,9 @@ let visible = $derived(
17
35
  <T.CylinderGeometry args={[0.002, 0.002, 0.2, 16, 1, false]} />
18
36
  <T.RawShaderMaterial
19
37
  transparent
20
- vertexShader={`
21
- uniform mat4 modelViewMatrix;
22
- uniform mat4 projectionMatrix;
23
- attribute vec2 uv;
24
- attribute vec3 position;
25
- varying vec2 vUv;
26
- void main() {
27
- vUv = uv;
28
- gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
29
- }
30
- `}
31
- fragmentShader={`
32
- precision mediump float;
33
- varying vec2 vUv;
34
- void main() {
35
- gl_FragColor = vec4(1.0, 1.0, 1.0, pow(vUv.y - 1.0, 2.0));
36
- }
37
- `}
38
+ {vertexShader}
39
+ {fragmentShader}
38
40
  />
39
41
  </T.Mesh>
40
- </slot>
42
+ {/if}
41
43
  </T.Group>
@@ -1,18 +1,14 @@
1
1
  import { SvelteComponent } from "svelte";
2
+ import type { Snippet } from 'svelte';
2
3
  declare const __propDef: {
3
4
  props: {
4
5
  handedness: 'left' | 'right';
5
- } & {
6
- children?: ((this: void) => typeof import("svelte").SnippetReturn & {
7
- _: "functions passed to {@render ...} tags must use the `Snippet` type imported from \"svelte\"";
8
- }) | undefined;
6
+ children?: Snippet<[]> | undefined;
9
7
  };
10
8
  events: {
11
9
  [evt: string]: CustomEvent<any>;
12
10
  };
13
- slots: {
14
- default: {};
15
- };
11
+ slots: {};
16
12
  };
17
13
  export type ShortRayProps = typeof __propDef.props;
18
14
  export type ShortRayEvents = typeof __propDef.events;
@@ -3,7 +3,7 @@ import { Group, Matrix3, Vector3 } from "three";
3
3
  import { T, useTask } from "@threlte/core";
4
4
  import { teleportIntersection } from "../../internal/stores";
5
5
  import Cursor from "./Cursor.svelte";
6
- let { handedness } = $props();
6
+ let { handedness, children } = $props();
7
7
  const ref = new Group();
8
8
  const vec3 = new Vector3();
9
9
  const normalMatrix = new Matrix3();
@@ -42,10 +42,12 @@ $effect.pre(() => {
42
42
  is={ref}
43
43
  visible={$intersection !== undefined}
44
44
  >
45
- <slot>
45
+ {#if children}
46
+ {@render children()}
47
+ {:else}
46
48
  <Cursor
47
49
  size={$size}
48
50
  thickness={0.015}
49
51
  />
50
- </slot>
52
+ {/if}
51
53
  </T>
@@ -1,18 +1,14 @@
1
1
  import { SvelteComponent } from "svelte";
2
+ import type { Snippet } from 'svelte';
2
3
  declare const __propDef: {
3
4
  props: {
4
5
  handedness: 'left' | 'right';
5
- } & {
6
- children?: ((this: void) => typeof import("svelte").SnippetReturn & {
7
- _: "functions passed to {@render ...} tags must use the `Snippet` type imported from \"svelte\"";
8
- }) | undefined;
6
+ children?: Snippet<[]> | undefined;
9
7
  };
10
8
  events: {
11
9
  [evt: string]: CustomEvent<any>;
12
10
  };
13
- slots: {
14
- default: {};
15
- };
11
+ slots: {};
16
12
  };
17
13
  export type TeleportCursorProps = typeof __propDef.props;
18
14
  export type TeleportCursorEvents = typeof __propDef.events;
@@ -4,7 +4,7 @@ import { LineGeometry } from "three/examples/jsm/lines/LineGeometry.js";
4
4
  import { LineMaterial } from "three/examples/jsm/lines/LineMaterial.js";
5
5
  import { T, useTask } from "@threlte/core";
6
6
  import { teleportIntersection } from "../../internal/stores";
7
- let { handedness, targetRay } = $props();
7
+ let { handedness, targetRay, children } = $props();
8
8
  let lineGeometry = new LineGeometry();
9
9
  const rayStart = new Vector3();
10
10
  const rayMidpoint = new Vector3();
@@ -55,7 +55,9 @@ $effect.pre(() => {
55
55
  });
56
56
  </script>
57
57
 
58
- <slot>
58
+ {#if children}
59
+ {@render children()}
60
+ {:else}
59
61
  <T
60
62
  is={Line2}
61
63
  visible={$intersection !== undefined}
@@ -67,4 +69,4 @@ $effect.pre(() => {
67
69
  linewidth={0.004}
68
70
  />
69
71
  </T>
70
- </slot>
72
+ {/if}
@@ -1,20 +1,16 @@
1
1
  import { SvelteComponent } from "svelte";
2
2
  import { type XRTargetRaySpace } from 'three';
3
+ import type { Snippet } from 'svelte';
3
4
  declare const __propDef: {
4
5
  props: {
5
6
  handedness: 'left' | 'right';
6
7
  targetRay: XRTargetRaySpace;
7
- } & {
8
- children?: ((this: void) => typeof import("svelte").SnippetReturn & {
9
- _: "functions passed to {@render ...} tags must use the `Snippet` type imported from \"svelte\"";
10
- }) | undefined;
8
+ children?: Snippet<[]> | undefined;
11
9
  };
12
10
  events: {
13
11
  [evt: string]: CustomEvent<any>;
14
12
  };
15
- slots: {
16
- default: {};
17
- };
13
+ slots: {};
18
14
  };
19
15
  export type TeleportRayProps = typeof __propDef.props;
20
16
  export type TeleportRayEvents = typeof __propDef.events;
@@ -0,0 +1,4 @@
1
+ export declare const defaultFeatures: {
2
+ requiredFeatures: never[];
3
+ optionalFeatures: string[];
4
+ };
@@ -0,0 +1,14 @@
1
+ export const defaultFeatures = {
2
+ requiredFeatures: [],
3
+ optionalFeatures: [
4
+ 'local-floor',
5
+ 'bounded-floor',
6
+ 'anchors',
7
+ 'hand-tracking',
8
+ 'plane-detection',
9
+ 'layers',
10
+ 'depth-sorted-layers',
11
+ 'hit-test',
12
+ 'mesh-detection'
13
+ ]
14
+ };
@@ -23,7 +23,7 @@ export const setupControllers = () => {
23
23
  if (hasHands())
24
24
  return;
25
25
  const { data } = event;
26
- controllerEvents[data.handedness]?.[event.type]?.(event);
26
+ controllerEvents[data.handedness]?.current?.[`on${event.type}`]?.(event);
27
27
  };
28
28
  function handleConnected(event) {
29
29
  const { model, targetRay, grip } = indexMap.get(this);
@@ -24,7 +24,7 @@ export const setupHands = () => {
24
24
  return;
25
25
  const handEvent = event;
26
26
  const handedness = 'handedness' in handEvent ? handEvent.handedness : handEvent.data.handedness;
27
- handEvents[handedness]?.[event.type]?.(event);
27
+ handEvents[handedness]?.current?.[`on${event.type}`]?.(event);
28
28
  };
29
29
  function handleConnected(event) {
30
30
  const hand = this;
@@ -3,8 +3,9 @@ import { useThrelte, useTask, watch } from '@threlte/core';
3
3
  import { useXR } from '../hooks/useXR';
4
4
  export const headset = new Group();
5
5
  export const setupHeadset = () => {
6
- const { renderer, camera } = useThrelte();
6
+ const { renderer, camera, scheduler, renderStage } = useThrelte();
7
7
  const { xr } = renderer;
8
+ const stage = scheduler.createStage(Symbol('xr-headset-stage'), { before: renderStage });
8
9
  const immersiveFrame = useTask(() => {
9
10
  const space = xr.getReferenceSpace();
10
11
  if (space === null)
@@ -17,13 +18,21 @@ export const setupHeadset = () => {
17
18
  const { position, orientation } = pose.transform;
18
19
  headset.position.set(position.x, position.y, position.z);
19
20
  headset.quaternion.set(orientation.x, orientation.y, orientation.z, orientation.w);
20
- }, { autoStart: false, autoInvalidate: false });
21
+ }, {
22
+ autoStart: false,
23
+ autoInvalidate: false,
24
+ stage
25
+ });
21
26
  const nonImmersiveFrame = useTask(() => {
22
27
  headset.position.copy(camera.current.position);
23
28
  headset.quaternion.copy(camera.current.quaternion);
24
- }, { autoStart: false, autoInvalidate: false });
25
- watch(useXR().isPresenting, (isPresenting) => {
26
- if (isPresenting) {
29
+ }, {
30
+ autoStart: false,
31
+ autoInvalidate: false,
32
+ stage
33
+ });
34
+ watch(useXR().isPresenting, ($isPresenting) => {
35
+ if ($isPresenting) {
27
36
  immersiveFrame.start();
28
37
  nonImmersiveFrame.stop();
29
38
  }
@@ -1,21 +1,17 @@
1
- // @ts-expect-error svelte/internal is untyped.
2
- // import { set_raf } from 'svelte/internal'
3
1
  import { onDestroy } from 'svelte';
2
+ // @ts-expect-error untyped internal import, when have you ever done me wrong?
3
+ import { raf } from 'svelte/internal/client';
4
4
  import { watch } from '@threlte/core';
5
5
  import { session } from './stores';
6
6
  export const setupRaf = () => {
7
7
  if (typeof window === 'undefined')
8
8
  return;
9
- const browserRaf = (fn) => requestAnimationFrame(fn);
10
- const currentRaf = { fn: browserRaf };
11
- // set_raf((fn: FrameRequestCallback) => currentRaf.fn(fn))
9
+ const originalTick = raf.tick;
12
10
  watch(session, (session) => {
13
- if (session) {
14
- currentRaf.fn = (fn) => session.requestAnimationFrame(fn);
15
- }
16
- else {
17
- currentRaf.fn = browserRaf;
18
- }
11
+ raf.tick =
12
+ session === undefined
13
+ ? originalTick
14
+ : (fn) => session.requestAnimationFrame(fn);
19
15
  });
20
- onDestroy(() => (currentRaf.fn = browserRaf));
16
+ onDestroy(() => (raf.tick = originalTick));
21
17
  };
package/dist/types.d.ts CHANGED
@@ -5,25 +5,26 @@ import type { XRHandModel } from 'three/examples/jsm/webxr/XRHandModelFactory.js
5
5
  export type XRSessionEventType = 'sessionstart' | 'sessionend' | 'visibilitychange' | 'frameratechange';
6
6
  export type XRControllerEventType = 'select' | 'selectstart' | 'selectend' | 'squeeze' | 'squeezeend' | 'squeezestart' | 'disconnected' | 'connected';
7
7
  export type XRControllerEvents = {
8
- onconnected?: XRControllerEvent<'connected'>;
9
- ondisconnected?: XRControllerEvent<'disconnected'>;
10
- onselect?: XRControllerEvent<'select'>;
11
- onselectstart?: XRControllerEvent<'selectstart'>;
12
- onselectend?: XRControllerEvent<'selectend'>;
13
- onsqueeze?: XRControllerEvent<'squeeze'>;
14
- onsqueezeend?: XRControllerEvent<'squeezeend'>;
15
- onsqueezestart?: XRControllerEvent<'squeezestart'>;
8
+ onconnected?: XRControllerEventCallback<'connected'>;
9
+ ondisconnected?: XRControllerEventCallback<'disconnected'>;
10
+ onselect?: XRControllerEventCallback<'select'>;
11
+ onselectstart?: XRControllerEventCallback<'selectstart'>;
12
+ onselectend?: XRControllerEventCallback<'selectend'>;
13
+ onsqueeze?: XRControllerEventCallback<'squeeze'>;
14
+ onsqueezeend?: XRControllerEventCallback<'squeezeend'>;
15
+ onsqueezestart?: XRControllerEventCallback<'squeezestart'>;
16
16
  };
17
17
  export type XRHandEventType = 'pinchstart' | 'pinchend' | 'connected' | 'disconnected';
18
18
  export type XRSessionEvent<Type = XRSessionEventType> = (event: Event & {
19
19
  type: Type;
20
20
  target: XRSession;
21
21
  }) => void;
22
- export type XRControllerEvent<Type = XRControllerEventType> = (event: Event & {
22
+ export type XRControllerEvent<Type = XRControllerEventType> = Event & {
23
23
  type: Type;
24
24
  target: Group;
25
25
  data: XRInputSource;
26
- }) => void;
26
+ };
27
+ export type XRControllerEventCallback<Type = XRControllerEventType> = (event: XRControllerEvent<Type>) => void;
27
28
  export type XRController = {
28
29
  targetRay: XRTargetRaySpace;
29
30
  grip: XRGripSpace;
@@ -36,18 +37,19 @@ export type XRHand = {
36
37
  model?: XRHandModel;
37
38
  inputSource: globalThis.XRHand;
38
39
  };
39
- export type XRHandEvents = {
40
- onconnected?: XRHandEvent<'connected'>;
41
- ondisconnected?: XRHandEvent<'disconnected'>;
42
- onpinchstart?: XRHandEvent<'pinchstart'>;
43
- onpinchend?: XRHandEvent<'pinchend'>;
44
- };
45
- export type XRHandEvent<Type = XRHandEventType> = Type extends 'connected' | 'disconnected' ? (event: {
40
+ export type XRHandEvent<Type = XRHandEventType> = Type extends 'connected' | 'disconnected' ? {
46
41
  type: Type;
47
42
  target: XRHandSpace;
48
43
  data: XRInputSource;
49
- }) => void : Type extends 'pinchstart' | 'pinchend' ? (event: {
44
+ } : Type extends 'pinchstart' | 'pinchend' ? {
50
45
  type: Type;
51
46
  handedness: 'left' | 'right';
52
47
  target: null;
53
- }) => void : never;
48
+ } : never;
49
+ export type XRHandEventCallback<Type> = (event: XRHandEvent<Type>) => void;
50
+ export type XRHandEvents = {
51
+ onconnected?: XRHandEventCallback<'connected'>;
52
+ ondisconnected?: XRHandEventCallback<'disconnected'>;
53
+ onpinchstart?: XRHandEventCallback<'pinchstart'>;
54
+ onpinchend?: XRHandEventCallback<'pinchend'>;
55
+ };
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@threlte/xr",
3
- "version": "1.0.0-next.3",
3
+ "version": "1.0.0-next.5",
4
4
  "author": "Micheal Parks <michealparks1989@gmail.com> (https://parks.lol)",
5
5
  "license": "MIT",
6
+ "description": "Tools to more easily create VR and AR experiences with Threlte",
6
7
  "devDependencies": {
7
8
  "@sveltejs/adapter-auto": "^3.2.0",
8
9
  "@sveltejs/kit": "^2.5.5",
@@ -14,22 +15,41 @@
14
15
  "eslint-plugin-svelte": "^2.36.0",
15
16
  "svelte-check": "^3.6.9",
16
17
  "typescript": "^5.4.5",
17
- "@types/three": "^0.163.0",
18
+ "@types/three": "^0.165.0",
18
19
  "autoprefixer": "^10.4.19",
19
20
  "postcss": "^8.4.38",
20
21
  "publint": "^0.2.7",
21
- "svelte": "^5.0.0-next.107",
22
- "three": "^0.163.0",
22
+ "svelte": "^5.0.0-next.181",
23
+ "three": "^0.165.0",
23
24
  "tslib": "^2.6.2",
24
25
  "vite": "^5.2.8",
25
26
  "vite-plugin-mkcert": "^1.17.5",
26
- "@threlte/core": "8.0.0-next.6"
27
+ "@threlte/core": "8.0.0-next.10"
27
28
  },
28
29
  "peerDependencies": {
29
30
  "svelte": ">=5",
30
31
  "three": ">=0.155"
31
32
  },
32
33
  "type": "module",
34
+ "keywords": [
35
+ "webxr",
36
+ "vr",
37
+ "ar",
38
+ "threlte",
39
+ "svelte",
40
+ "three",
41
+ "three.js",
42
+ "3d"
43
+ ],
44
+ "homepage": "https://threlte.xyz",
45
+ "repository": {
46
+ "type": "git",
47
+ "url": "https://github.com/threlte/threlte.git",
48
+ "directory": "packages/xr"
49
+ },
50
+ "bugs": {
51
+ "url": "https://github.com/threlte/threlte/issues"
52
+ },
33
53
  "exports": {
34
54
  ".": {
35
55
  "types": "./dist/index.d.ts",
@@ -48,8 +68,8 @@
48
68
  "package": "svelte-kit sync && svelte-package && node ./scripts/cleanupPackage.js && publint",
49
69
  "check": "svelte-check --tsconfig ./tsconfig.json",
50
70
  "check:watch": "svelte-check --tsconfig ./tsconfig.json --watch",
51
- "lint": "prettier --check --plugin-search-dir=. . && eslint .",
52
- "format": "prettier --write --plugin-search-dir=. .",
71
+ "lint": "prettier --check .",
72
+ "format": "prettier --write .",
53
73
  "cleanup": "rimraf node_modules .svelte-kit dist"
54
74
  }
55
75
  }