@threlte/xr 1.0.8 → 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.
- package/dist/components/ARButton.svelte.d.ts +11 -24
- package/dist/components/Controller.svelte +31 -27
- package/dist/components/Controller.svelte.d.ts +7 -5
- package/dist/components/Hand.svelte +24 -28
- package/dist/components/Hand.svelte.d.ts +6 -4
- package/dist/components/Headset.svelte.d.ts +4 -2
- package/dist/components/VRButton.svelte.d.ts +5 -18
- package/dist/components/XR.svelte +44 -43
- package/dist/components/XR.svelte.d.ts +24 -22
- package/dist/components/XRButton.svelte +3 -3
- package/dist/components/XRButton.svelte.d.ts +26 -23
- package/dist/components/internal/Cursor.svelte.d.ts +4 -2
- package/dist/components/internal/PointerCursor.svelte +8 -7
- package/dist/components/internal/PointerCursor.svelte.d.ts +5 -3
- package/dist/components/internal/ShortRay.svelte +3 -3
- package/dist/components/internal/ShortRay.svelte.d.ts +5 -3
- package/dist/components/internal/TeleportCursor.svelte +8 -7
- package/dist/components/internal/TeleportCursor.svelte.d.ts +5 -3
- package/dist/components/internal/TeleportRay.svelte +5 -5
- package/dist/components/internal/TeleportRay.svelte.d.ts +5 -3
- package/dist/hooks/currentReadable.svelte.d.ts +5 -0
- package/dist/hooks/currentReadable.svelte.js +11 -0
- package/dist/hooks/useController.svelte.d.ts +13 -0
- package/dist/hooks/useController.svelte.js +22 -0
- package/dist/hooks/useHand.svelte.d.ts +12 -0
- package/dist/hooks/{useHand.js → useHand.svelte.js} +8 -5
- package/dist/hooks/{useHandJoint.d.ts → useHandJoint.svelte.d.ts} +1 -1
- package/dist/hooks/useHandJoint.svelte.js +21 -0
- package/dist/hooks/useHeadset.js +1 -1
- package/dist/hooks/useHitTest.svelte.js +67 -0
- package/dist/hooks/useTeleport.js +1 -2
- package/dist/hooks/useXR.d.ts +6 -7
- package/dist/hooks/useXR.js +10 -12
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -4
- package/dist/internal/raf.d.ts +1 -0
- package/dist/internal/raf.js +2 -0
- package/dist/internal/setupControllers.js +13 -14
- package/dist/internal/setupHands.js +8 -9
- package/dist/internal/{setupHeadset.js → setupHeadset.svelte.js} +6 -6
- package/dist/internal/setupRaf.svelte.js +17 -0
- package/dist/internal/state.svelte.d.ts +51 -0
- package/dist/internal/state.svelte.js +40 -0
- package/dist/internal/useHandTrackingState.js +10 -7
- package/dist/lib/getXRSessionOptions.d.ts +1 -1
- package/dist/lib/toggleXRSession.d.ts +3 -3
- package/dist/lib/toggleXRSession.js +3 -3
- package/dist/plugins/pointerControls/compute.js +2 -6
- package/dist/plugins/pointerControls/index.js +4 -10
- package/dist/plugins/pointerControls/{setup.js → setup.svelte.js} +18 -19
- package/dist/plugins/teleportControls/compute.js +2 -6
- package/dist/plugins/teleportControls/index.js +4 -10
- package/dist/plugins/teleportControls/{setup.js → setup.svelte.js} +9 -10
- package/package.json +8 -8
- package/dist/hooks/useController.d.ts +0 -9
- package/dist/hooks/useController.js +0 -19
- package/dist/hooks/useHand.d.ts +0 -8
- package/dist/hooks/useHandJoint.js +0 -20
- package/dist/hooks/useHitTest.js +0 -79
- package/dist/internal/setupRaf.js +0 -16
- package/dist/internal/stores.d.ts +0 -43
- package/dist/internal/stores.js +0 -42
- /package/dist/hooks/{useHitTest.d.ts → useHitTest.svelte.d.ts} +0 -0
- /package/dist/internal/{setupHeadset.d.ts → setupHeadset.svelte.d.ts} +0 -0
- /package/dist/internal/{setupRaf.d.ts → setupRaf.svelte.d.ts} +0 -0
- /package/dist/plugins/pointerControls/{setup.d.ts → setup.svelte.d.ts} +0 -0
- /package/dist/plugins/teleportControls/{setup.d.ts → setup.svelte.d.ts} +0 -0
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
import type { ComponentProps } from 'svelte';
|
|
2
|
+
import XRButton from './XRButton.svelte';
|
|
3
|
+
type Props = Omit<ComponentProps<typeof XRButton>, 'mode' | 'sessionInit'> & {
|
|
4
|
+
sessionInit?: XRSessionInit & {
|
|
5
|
+
domOverlay?: {
|
|
6
|
+
root: HTMLElement;
|
|
7
|
+
} | undefined;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
1
10
|
/**
|
|
2
11
|
* `<ARButton />` is an HTML `<button />` that can be used to init and display info about your immersive AR session.
|
|
3
12
|
*
|
|
@@ -8,28 +17,6 @@
|
|
|
8
17
|
* />
|
|
9
18
|
* ```
|
|
10
19
|
*/
|
|
11
|
-
declare const ArButton: import("svelte").Component<
|
|
12
|
-
|
|
13
|
-
sessionInit?: XRSessionInit & {
|
|
14
|
-
domOverlay?: {
|
|
15
|
-
root: HTMLElement;
|
|
16
|
-
} | undefined;
|
|
17
|
-
};
|
|
18
|
-
force?: "enter" | "exit";
|
|
19
|
-
styled?: boolean;
|
|
20
|
-
children?: import("svelte").Snippet<[{
|
|
21
|
-
state: "unsupported" | "insecure" | "blocked" | "supported";
|
|
22
|
-
}]>;
|
|
23
|
-
onclick?: (event: {
|
|
24
|
-
state: "unsupported" | "insecure" | "blocked" | "supported";
|
|
25
|
-
nativeEvent: MouseEvent;
|
|
26
|
-
}) => void;
|
|
27
|
-
onerror?: (error: Error) => void;
|
|
28
|
-
}, "mode" | "sessionInit"> & {
|
|
29
|
-
sessionInit?: XRSessionInit & {
|
|
30
|
-
domOverlay?: {
|
|
31
|
-
root: HTMLElement;
|
|
32
|
-
} | undefined;
|
|
33
|
-
};
|
|
34
|
-
}, {}, "">;
|
|
20
|
+
declare const ArButton: import("svelte").Component<Props, {}, "">;
|
|
21
|
+
type ArButton = ReturnType<typeof ArButton>;
|
|
35
22
|
export default ArButton;
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
<!--
|
|
2
2
|
@component `<Controller />` represents a THREE.XRTargetRaySpace, a THREE.XRGripSpace, and a controller model.
|
|
3
3
|
-->
|
|
4
|
-
<script
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
<script lang="ts">
|
|
5
|
+
import { T, useThrelte } from '@threlte/core'
|
|
6
|
+
import { controllers } from '../hooks/useController.svelte'
|
|
7
|
+
import {
|
|
8
|
+
isHandTracking,
|
|
9
|
+
pointerState,
|
|
10
|
+
teleportState,
|
|
11
|
+
controllerEvents
|
|
12
|
+
} from '../internal/state.svelte'
|
|
11
13
|
import type { XRControllerEvents } from '../types'
|
|
12
14
|
import PointerCursor from './internal/PointerCursor.svelte'
|
|
13
15
|
import ShortRay from './internal/ShortRay.svelte'
|
|
@@ -15,13 +17,6 @@
|
|
|
15
17
|
import TeleportRay from './internal/TeleportRay.svelte'
|
|
16
18
|
import type { Snippet } from 'svelte'
|
|
17
19
|
|
|
18
|
-
const stores = {
|
|
19
|
-
left: leftStore,
|
|
20
|
-
right: rightStore
|
|
21
|
-
} as const
|
|
22
|
-
</script>
|
|
23
|
-
|
|
24
|
-
<script lang="ts">
|
|
25
20
|
type Props = {
|
|
26
21
|
children?: Snippet
|
|
27
22
|
grip?: Snippet
|
|
@@ -75,10 +70,12 @@
|
|
|
75
70
|
teleportCursor: teleportCursorSnippet
|
|
76
71
|
}: Props = $props()
|
|
77
72
|
|
|
73
|
+
const { scene } = useThrelte()
|
|
74
|
+
|
|
78
75
|
const handedness = $derived<'left' | 'right'>(left ? 'left' : right ? 'right' : hand ?? 'left')
|
|
79
76
|
|
|
80
|
-
$effect.pre(() =>
|
|
81
|
-
controllerEvents[handedness]
|
|
77
|
+
$effect.pre(() => {
|
|
78
|
+
controllerEvents[handedness] = {
|
|
82
79
|
onconnected,
|
|
83
80
|
ondisconnected,
|
|
84
81
|
onselect,
|
|
@@ -87,20 +84,27 @@
|
|
|
87
84
|
onsqueeze,
|
|
88
85
|
onsqueezeend,
|
|
89
86
|
onsqueezestart
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const
|
|
98
|
-
const
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return () => {
|
|
90
|
+
controllerEvents[handedness] = undefined
|
|
91
|
+
}
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
const xrController = $derived(controllers[handedness])
|
|
95
|
+
const grip = $derived(xrController?.grip)
|
|
96
|
+
const targetRay = $derived(xrController?.targetRay)
|
|
97
|
+
const model = $derived(xrController?.model)
|
|
98
|
+
const hasPointerControls = $derived(pointerState[handedness].enabled)
|
|
99
|
+
const hasTeleportControls = $derived(teleportState[handedness].enabled)
|
|
99
100
|
</script>
|
|
100
101
|
|
|
101
|
-
{#if
|
|
102
|
+
{#if !isHandTracking.current}
|
|
102
103
|
{#if grip}
|
|
103
|
-
<T
|
|
104
|
+
<T
|
|
105
|
+
is={grip}
|
|
106
|
+
attach={scene}
|
|
107
|
+
>
|
|
104
108
|
{#if children}
|
|
105
109
|
{@render children?.()}
|
|
106
110
|
{:else}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { XRControllerEvents } from '../types';
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
|
-
|
|
4
|
-
declare const Controller: import("svelte").Component<{
|
|
3
|
+
type Props = {
|
|
5
4
|
children?: Snippet;
|
|
6
5
|
grip?: Snippet;
|
|
7
6
|
targetRay?: Snippet;
|
|
@@ -9,7 +8,7 @@ declare const Controller: import("svelte").Component<{
|
|
|
9
8
|
pointerCursor?: Snippet;
|
|
10
9
|
teleportRay?: Snippet;
|
|
11
10
|
teleportCursor?: Snippet;
|
|
12
|
-
} &
|
|
11
|
+
} & XRControllerEvents & ({
|
|
13
12
|
/** Whether the controller should be matched with the left hand. */
|
|
14
13
|
left: true;
|
|
15
14
|
right?: undefined;
|
|
@@ -21,8 +20,11 @@ declare const Controller: import("svelte").Component<{
|
|
|
21
20
|
hand?: undefined;
|
|
22
21
|
} | {
|
|
23
22
|
/** Whether the controller should be matched with the left or right hand. */
|
|
24
|
-
hand:
|
|
23
|
+
hand: 'left' | 'right';
|
|
25
24
|
left?: undefined;
|
|
26
25
|
right?: undefined;
|
|
27
|
-
})
|
|
26
|
+
});
|
|
27
|
+
/** `<Controller />` represents a THREE.XRTargetRaySpace, a THREE.XRGripSpace, and a controller model. */
|
|
28
|
+
declare const Controller: import("svelte").Component<Props, {}, "">;
|
|
29
|
+
type Controller = ReturnType<typeof Controller>;
|
|
28
30
|
export default Controller;
|
|
@@ -1,21 +1,11 @@
|
|
|
1
|
-
<script
|
|
2
|
-
lang="ts"
|
|
3
|
-
module
|
|
4
|
-
>
|
|
1
|
+
<script lang="ts">
|
|
5
2
|
import { Group } from 'three'
|
|
6
3
|
import { T, useThrelte, useTask } from '@threlte/core'
|
|
7
4
|
import type { XRHandEvents } from '../types'
|
|
8
|
-
import { isHandTracking, handEvents } from '../internal/
|
|
9
|
-
import {
|
|
5
|
+
import { isHandTracking, handEvents } from '../internal/state.svelte'
|
|
6
|
+
import { hands } from '../hooks/useHand.svelte'
|
|
10
7
|
import type { Snippet } from 'svelte'
|
|
11
8
|
|
|
12
|
-
const stores = {
|
|
13
|
-
left: leftStore,
|
|
14
|
-
right: rightStore
|
|
15
|
-
} as const
|
|
16
|
-
</script>
|
|
17
|
-
|
|
18
|
-
<script lang="ts">
|
|
19
9
|
type Props = {
|
|
20
10
|
children?: Snippet
|
|
21
11
|
targetRay?: Snippet
|
|
@@ -56,19 +46,21 @@
|
|
|
56
46
|
}: Props = $props()
|
|
57
47
|
|
|
58
48
|
const { scene, renderer, scheduler, renderStage } = useThrelte()
|
|
59
|
-
const { xr } = renderer
|
|
60
|
-
const space = xr.getReferenceSpace()
|
|
61
49
|
|
|
62
50
|
const handedness = $derived<'left' | 'right'>(left ? 'left' : right ? 'right' : hand ?? 'left')
|
|
63
51
|
|
|
64
|
-
$effect.pre(() =>
|
|
65
|
-
handEvents[handedness]
|
|
52
|
+
$effect.pre(() => {
|
|
53
|
+
handEvents[handedness] = {
|
|
66
54
|
onconnected,
|
|
67
55
|
ondisconnected,
|
|
68
56
|
onpinchend,
|
|
69
57
|
onpinchstart
|
|
70
|
-
}
|
|
71
|
-
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return () => {
|
|
61
|
+
handEvents[handedness] = undefined
|
|
62
|
+
}
|
|
63
|
+
})
|
|
72
64
|
|
|
73
65
|
const group = new Group()
|
|
74
66
|
|
|
@@ -82,7 +74,8 @@
|
|
|
82
74
|
*/
|
|
83
75
|
const { start, stop } = useTask(
|
|
84
76
|
() => {
|
|
85
|
-
const frame = xr.getFrame()
|
|
77
|
+
const frame = renderer.xr.getFrame()
|
|
78
|
+
const space = renderer.xr.getReferenceSpace()
|
|
86
79
|
const joint = inputSource?.get('wrist')
|
|
87
80
|
|
|
88
81
|
if (joint === undefined || space === null) return
|
|
@@ -103,33 +96,36 @@
|
|
|
103
96
|
)
|
|
104
97
|
|
|
105
98
|
$effect.pre(() => {
|
|
106
|
-
if (
|
|
99
|
+
if (isHandTracking.current && (wrist !== undefined || children !== undefined) && inputSource) {
|
|
107
100
|
start()
|
|
108
101
|
} else {
|
|
109
102
|
stop()
|
|
110
103
|
}
|
|
111
104
|
})
|
|
112
105
|
|
|
113
|
-
const
|
|
114
|
-
const inputSource = $derived(
|
|
115
|
-
const model = $derived(
|
|
106
|
+
const xrHand = $derived(hands[handedness])
|
|
107
|
+
const inputSource = $derived(xrHand?.inputSource)
|
|
108
|
+
const model = $derived(xrHand?.model)
|
|
116
109
|
</script>
|
|
117
110
|
|
|
118
|
-
{#if
|
|
119
|
-
<T
|
|
111
|
+
{#if xrHand?.hand && isHandTracking.current}
|
|
112
|
+
<T
|
|
113
|
+
is={xrHand.hand}
|
|
114
|
+
attach={scene}
|
|
115
|
+
>
|
|
120
116
|
{#if children === undefined}
|
|
121
117
|
<T is={model} />
|
|
122
118
|
{/if}
|
|
123
119
|
</T>
|
|
124
120
|
|
|
125
121
|
{#if targetRay !== undefined}
|
|
126
|
-
<T is={
|
|
122
|
+
<T is={xrHand.targetRay}>
|
|
127
123
|
{@render targetRay()}
|
|
128
124
|
</T>
|
|
129
125
|
{/if}
|
|
130
126
|
{/if}
|
|
131
127
|
|
|
132
|
-
{#if
|
|
128
|
+
{#if isHandTracking.current}
|
|
133
129
|
<T
|
|
134
130
|
is={group}
|
|
135
131
|
attach={scene}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { XRHandEvents } from '../types';
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
|
-
|
|
3
|
+
type Props = {
|
|
4
4
|
children?: Snippet;
|
|
5
5
|
targetRay?: Snippet;
|
|
6
6
|
wrist?: Snippet;
|
|
7
|
-
} &
|
|
7
|
+
} & XRHandEvents & ({
|
|
8
8
|
/** Whether the XRHand should be matched with the left hand. */
|
|
9
9
|
left: true;
|
|
10
10
|
right?: undefined;
|
|
@@ -16,8 +16,10 @@ declare const Hand: import("svelte").Component<{
|
|
|
16
16
|
hand?: undefined;
|
|
17
17
|
} | {
|
|
18
18
|
/** Whether the XRHand should be matched with the left or right hand. */
|
|
19
|
-
hand:
|
|
19
|
+
hand: 'left' | 'right';
|
|
20
20
|
left?: undefined;
|
|
21
21
|
right?: undefined;
|
|
22
|
-
})
|
|
22
|
+
});
|
|
23
|
+
declare const Hand: import("svelte").Component<Props, {}, "">;
|
|
24
|
+
type Hand = ReturnType<typeof Hand>;
|
|
23
25
|
export default Hand;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
2
|
import type { Group } from 'three';
|
|
3
|
-
|
|
3
|
+
interface Props {
|
|
4
4
|
children?: Snippet<[{
|
|
5
5
|
ref: Group;
|
|
6
6
|
}]>;
|
|
7
|
-
}
|
|
7
|
+
}
|
|
8
|
+
declare const Headset: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type Headset = ReturnType<typeof Headset>;
|
|
8
10
|
export default Headset;
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import type { ComponentProps } from 'svelte';
|
|
2
|
+
import XRButton from './XRButton.svelte';
|
|
3
|
+
type Props = Omit<ComponentProps<typeof XRButton>, 'mode' | 'sessionInit'>;
|
|
1
4
|
/**
|
|
2
5
|
* `<VRButton />` is an HTML `<button />` that can be used to init and display info about your immersive VR session.
|
|
3
6
|
*
|
|
@@ -8,22 +11,6 @@
|
|
|
8
11
|
* />
|
|
9
12
|
* ```
|
|
10
13
|
*/
|
|
11
|
-
declare const VrButton: import("svelte").Component<
|
|
12
|
-
|
|
13
|
-
sessionInit?: XRSessionInit & {
|
|
14
|
-
domOverlay?: {
|
|
15
|
-
root: HTMLElement;
|
|
16
|
-
} | undefined;
|
|
17
|
-
};
|
|
18
|
-
force?: "enter" | "exit";
|
|
19
|
-
styled?: boolean;
|
|
20
|
-
children?: import("svelte").Snippet<[{
|
|
21
|
-
state: "unsupported" | "insecure" | "blocked" | "supported";
|
|
22
|
-
}]>;
|
|
23
|
-
onclick?: (event: {
|
|
24
|
-
state: "unsupported" | "insecure" | "blocked" | "supported";
|
|
25
|
-
nativeEvent: MouseEvent;
|
|
26
|
-
}) => void;
|
|
27
|
-
onerror?: (error: Error) => void;
|
|
28
|
-
}, "mode" | "sessionInit">, {}, "">;
|
|
14
|
+
declare const VrButton: import("svelte").Component<Props, {}, "">;
|
|
15
|
+
type VrButton = ReturnType<typeof VrButton>;
|
|
29
16
|
export default VrButton;
|
|
@@ -18,18 +18,18 @@ This should be placed within a Threlte `<Canvas />`.
|
|
|
18
18
|
|
|
19
19
|
-->
|
|
20
20
|
<script lang="ts">
|
|
21
|
-
import {
|
|
22
|
-
import { useThrelte
|
|
21
|
+
import type { Snippet } from 'svelte'
|
|
22
|
+
import { useThrelte } from '@threlte/core'
|
|
23
23
|
import type { XRSessionEvent } from '../types'
|
|
24
24
|
import {
|
|
25
25
|
isHandTracking,
|
|
26
26
|
isPresenting,
|
|
27
27
|
referenceSpaceType,
|
|
28
28
|
session,
|
|
29
|
-
xr
|
|
30
|
-
} from '../internal/
|
|
31
|
-
import { setupRaf } from '../internal/setupRaf'
|
|
32
|
-
import { setupHeadset } from '../internal/setupHeadset'
|
|
29
|
+
xr
|
|
30
|
+
} from '../internal/state.svelte'
|
|
31
|
+
import { setupRaf } from '../internal/setupRaf.svelte'
|
|
32
|
+
import { setupHeadset } from '../internal/setupHeadset.svelte'
|
|
33
33
|
import { setupControllers } from '../internal/setupControllers'
|
|
34
34
|
import { setupHands } from '../internal/setupHands'
|
|
35
35
|
|
|
@@ -85,7 +85,6 @@ This should be placed within a Threlte `<Canvas />`.
|
|
|
85
85
|
}: Props = $props()
|
|
86
86
|
|
|
87
87
|
const { renderer, renderMode } = useThrelte()
|
|
88
|
-
const { xr } = renderer
|
|
89
88
|
|
|
90
89
|
let originalRenderMode = $renderMode
|
|
91
90
|
|
|
@@ -95,41 +94,35 @@ This should be placed within a Threlte `<Canvas />`.
|
|
|
95
94
|
setupHands()
|
|
96
95
|
|
|
97
96
|
const handleSessionStart = () => {
|
|
98
|
-
isPresenting.
|
|
99
|
-
onsessionstart?.({ type: 'sessionstart', target:
|
|
97
|
+
isPresenting.current = true
|
|
98
|
+
onsessionstart?.({ type: 'sessionstart', target: session.current } as any)
|
|
100
99
|
}
|
|
101
100
|
|
|
102
101
|
const handleSessionEnd = () => {
|
|
103
|
-
onsessionend?.({ type: 'sessionend', target:
|
|
104
|
-
isPresenting.
|
|
105
|
-
session.
|
|
102
|
+
onsessionend?.({ type: 'sessionend', target: session.current } as any)
|
|
103
|
+
isPresenting.current = false
|
|
104
|
+
session.current = undefined
|
|
106
105
|
}
|
|
107
106
|
|
|
108
107
|
const handleVisibilityChange = (event: globalThis.XRSessionEvent) => {
|
|
109
|
-
onvisibilitychange?.({ ...event, target:
|
|
108
|
+
onvisibilitychange?.({ ...event, target: session.current! })
|
|
110
109
|
}
|
|
111
110
|
|
|
112
111
|
const handleInputSourcesChange = (event: XRInputSourcesChangeEvent) => {
|
|
113
|
-
|
|
114
|
-
oninputsourceschange?.({ ...event, target:
|
|
112
|
+
isHandTracking.current = Object.values(event.session.inputSources).some((source) => source.hand)
|
|
113
|
+
oninputsourceschange?.({ ...event, target: session.current! })
|
|
115
114
|
}
|
|
116
115
|
|
|
117
116
|
const handleFramerateChange = (event: globalThis.XRSessionEvent) => {
|
|
118
|
-
onvisibilitychange?.({ ...event, target:
|
|
117
|
+
onvisibilitychange?.({ ...event, target: session.current! })
|
|
119
118
|
}
|
|
120
119
|
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
$effect(() => {
|
|
121
|
+
const currentSession = session.current
|
|
123
122
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
} catch {
|
|
127
|
-
// Do nothing
|
|
123
|
+
if (currentSession === undefined) {
|
|
124
|
+
return
|
|
128
125
|
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
watch(session, (currentSession) => {
|
|
132
|
-
if (currentSession === undefined) return
|
|
133
126
|
|
|
134
127
|
currentSession.addEventListener('visibilitychange', handleVisibilityChange)
|
|
135
128
|
currentSession.addEventListener('inputsourceschange', handleInputSourcesChange)
|
|
@@ -142,8 +135,8 @@ This should be placed within a Threlte `<Canvas />`.
|
|
|
142
135
|
}
|
|
143
136
|
})
|
|
144
137
|
|
|
145
|
-
|
|
146
|
-
if (
|
|
138
|
+
$effect.pre(() => {
|
|
139
|
+
if (isPresenting.current) {
|
|
147
140
|
originalRenderMode = renderMode.current
|
|
148
141
|
renderMode.set('always')
|
|
149
142
|
} else {
|
|
@@ -151,38 +144,46 @@ This should be placed within a Threlte `<Canvas />`.
|
|
|
151
144
|
}
|
|
152
145
|
})
|
|
153
146
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
xr.
|
|
158
|
-
xr.
|
|
147
|
+
$effect.pre(() => {
|
|
148
|
+
const currentSession = session.current
|
|
149
|
+
|
|
150
|
+
xr.current = renderer.xr
|
|
151
|
+
renderer.xr.enabled = true
|
|
152
|
+
renderer.xr.addEventListener('sessionstart', handleSessionStart)
|
|
153
|
+
renderer.xr.addEventListener('sessionend', handleSessionEnd)
|
|
159
154
|
|
|
160
155
|
return () => {
|
|
161
|
-
|
|
162
|
-
xr.enabled = false
|
|
163
|
-
xr.removeEventListener('sessionstart', handleSessionStart)
|
|
164
|
-
xr.removeEventListener('sessionend', handleSessionEnd)
|
|
156
|
+
xr.current = undefined
|
|
157
|
+
renderer.xr.enabled = false
|
|
158
|
+
renderer.xr.removeEventListener('sessionstart', handleSessionStart)
|
|
159
|
+
renderer.xr.removeEventListener('sessionend', handleSessionEnd)
|
|
165
160
|
|
|
166
161
|
// if unmounted while presenting (e.g. due to sveltekit navigation), end the session
|
|
167
|
-
|
|
162
|
+
currentSession?.end()
|
|
168
163
|
}
|
|
169
164
|
})
|
|
170
165
|
|
|
171
166
|
$effect.pre(() => {
|
|
172
|
-
|
|
167
|
+
if (frameRate === undefined) return
|
|
168
|
+
|
|
169
|
+
try {
|
|
170
|
+
session.current?.updateTargetFrameRate(frameRate)
|
|
171
|
+
} catch {
|
|
172
|
+
// Do nothing
|
|
173
|
+
}
|
|
173
174
|
})
|
|
174
175
|
|
|
175
176
|
$effect.pre(() => {
|
|
176
|
-
xr.setFoveation(foveation)
|
|
177
|
+
renderer.xr.setFoveation(foveation)
|
|
177
178
|
})
|
|
178
179
|
|
|
179
180
|
$effect.pre(() => {
|
|
180
|
-
xr.setReferenceSpaceType(referenceSpace)
|
|
181
|
-
|
|
181
|
+
renderer.xr.setReferenceSpaceType(referenceSpace)
|
|
182
|
+
referenceSpaceType.current = referenceSpace
|
|
182
183
|
})
|
|
183
184
|
</script>
|
|
184
185
|
|
|
185
|
-
{#if
|
|
186
|
+
{#if isPresenting.current}
|
|
186
187
|
{@render children?.()}
|
|
187
188
|
{:else}
|
|
188
189
|
{@render fallback?.()}
|
|
@@ -1,23 +1,6 @@
|
|
|
1
|
-
import {
|
|
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<
|
|
26
|
+
onsessionstart?: (event: XRSessionEvent<'sessionstart'>) => void;
|
|
44
27
|
/** Called after an XRSession is terminated */
|
|
45
|
-
onsessionend?: (event: XRSessionEvent<
|
|
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 {
|
|
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 (
|
|
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
|
)
|
|
@@ -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
|
-
{
|
|
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
|
-
|
|
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?:
|
|
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:
|
|
21
|
+
state: SupportState;
|
|
39
22
|
}]>;
|
|
40
23
|
onclick?: (event: {
|
|
41
|
-
state:
|
|
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;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { type ColorRepresentation } from 'three';
|
|
2
|
-
|
|
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;
|