@threlte/xr 1.0.8 → 1.1.1
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 +55 -54
- package/dist/components/XR.svelte.d.ts +29 -27
- 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 +5 -5
- 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 +11 -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/dist/types.d.ts +12 -18
- package/package.json +10 -10
- 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 {
|
|
23
|
-
import
|
|
21
|
+
import type { EventListener, WebXRManager, Event as ThreeEvent } from 'three'
|
|
22
|
+
import type { Snippet } from 'svelte'
|
|
23
|
+
import { useThrelte } from '@threlte/core'
|
|
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
|
|
|
@@ -59,17 +59,17 @@ This should be placed within a Threlte `<Canvas />`.
|
|
|
59
59
|
fallback?: Snippet
|
|
60
60
|
children?: Snippet
|
|
61
61
|
|
|
62
|
-
/** Called as an XRSession is
|
|
63
|
-
onsessionstart?: (event:
|
|
62
|
+
/** Called as an XRSession is started */
|
|
63
|
+
onsessionstart?: (event: ThreeEvent<'sessionstart', WebXRManager>) => void
|
|
64
64
|
|
|
65
|
-
/** Called after an XRSession is
|
|
66
|
-
onsessionend?: (event: XRSessionEvent
|
|
65
|
+
/** Called after an XRSession is ended */
|
|
66
|
+
onsessionend?: (event: XRSessionEvent) => void
|
|
67
67
|
|
|
68
68
|
/** Called when an XRSession is hidden or unfocused. */
|
|
69
|
-
onvisibilitychange?: (event:
|
|
69
|
+
onvisibilitychange?: (event: XRSessionEvent) => void
|
|
70
70
|
|
|
71
71
|
/** Called when available inputsources change */
|
|
72
|
-
oninputsourceschange?: (event:
|
|
72
|
+
oninputsourceschange?: (event: XRSessionEvent) => void
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
let {
|
|
@@ -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
|
|
|
@@ -94,56 +93,52 @@ This should be placed within a Threlte `<Canvas />`.
|
|
|
94
93
|
setupControllers()
|
|
95
94
|
setupHands()
|
|
96
95
|
|
|
97
|
-
const handleSessionStart = () => {
|
|
98
|
-
isPresenting.
|
|
99
|
-
onsessionstart?.(
|
|
96
|
+
const handleSessionStart: EventListener<object, 'sessionstart', WebXRManager> = (event) => {
|
|
97
|
+
isPresenting.current = true
|
|
98
|
+
onsessionstart?.(event)
|
|
100
99
|
}
|
|
101
100
|
|
|
102
|
-
const handleSessionEnd = () => {
|
|
103
|
-
onsessionend?.(
|
|
104
|
-
isPresenting.
|
|
105
|
-
session.
|
|
101
|
+
const handleSessionEnd = (event: XRSessionEvent) => {
|
|
102
|
+
onsessionend?.(event)
|
|
103
|
+
isPresenting.current = false
|
|
104
|
+
session.current = undefined
|
|
106
105
|
}
|
|
107
106
|
|
|
108
|
-
const handleVisibilityChange = (event:
|
|
109
|
-
onvisibilitychange?.(
|
|
107
|
+
const handleVisibilityChange = (event: XRSessionEvent) => {
|
|
108
|
+
onvisibilitychange?.(event)
|
|
110
109
|
}
|
|
111
110
|
|
|
112
111
|
const handleInputSourcesChange = (event: XRInputSourcesChangeEvent) => {
|
|
113
|
-
|
|
114
|
-
oninputsourceschange?.(
|
|
112
|
+
isHandTracking.current = Object.values(event.session.inputSources).some((source) => source.hand)
|
|
113
|
+
oninputsourceschange?.(event)
|
|
115
114
|
}
|
|
116
115
|
|
|
117
|
-
const handleFramerateChange = (event:
|
|
118
|
-
onvisibilitychange?.(
|
|
116
|
+
const handleFramerateChange = (event: XRSessionEvent) => {
|
|
117
|
+
onvisibilitychange?.(event)
|
|
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)
|
|
136
129
|
currentSession.addEventListener('frameratechange', handleFramerateChange)
|
|
130
|
+
currentSession.addEventListener('end', handleSessionEnd)
|
|
137
131
|
|
|
138
132
|
return () => {
|
|
139
133
|
currentSession.removeEventListener('visibilitychange', handleVisibilityChange)
|
|
140
134
|
currentSession.removeEventListener('inputsourceschange', handleInputSourcesChange)
|
|
141
135
|
currentSession.removeEventListener('frameratechange', handleFramerateChange)
|
|
136
|
+
currentSession.removeEventListener('end', handleSessionEnd)
|
|
142
137
|
}
|
|
143
138
|
})
|
|
144
139
|
|
|
145
|
-
|
|
146
|
-
if (
|
|
140
|
+
$effect.pre(() => {
|
|
141
|
+
if (isPresenting.current) {
|
|
147
142
|
originalRenderMode = renderMode.current
|
|
148
143
|
renderMode.set('always')
|
|
149
144
|
} else {
|
|
@@ -151,38 +146,44 @@ This should be placed within a Threlte `<Canvas />`.
|
|
|
151
146
|
}
|
|
152
147
|
})
|
|
153
148
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
xr.
|
|
158
|
-
xr.
|
|
149
|
+
$effect.pre(() => {
|
|
150
|
+
const currentSession = session.current
|
|
151
|
+
|
|
152
|
+
xr.current = renderer.xr
|
|
153
|
+
renderer.xr.enabled = true
|
|
154
|
+
renderer.xr.addEventListener('sessionstart', handleSessionStart)
|
|
159
155
|
|
|
160
156
|
return () => {
|
|
161
|
-
|
|
162
|
-
xr.enabled = false
|
|
163
|
-
xr.removeEventListener('sessionstart', handleSessionStart)
|
|
164
|
-
xr.removeEventListener('sessionend', handleSessionEnd)
|
|
157
|
+
xr.current = undefined
|
|
158
|
+
renderer.xr.enabled = false
|
|
159
|
+
renderer.xr.removeEventListener('sessionstart', handleSessionStart)
|
|
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 {
|
|
2
|
-
import type {
|
|
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<{
|
|
1
|
+
import type { WebXRManager, Event as ThreeEvent } from 'three';
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
interface Props {
|
|
21
4
|
/**
|
|
22
5
|
* Enables foveated rendering. Default is `1`, the three.js default.
|
|
23
6
|
*
|
|
@@ -39,13 +22,32 @@ declare const Xr: import("svelte").Component<{
|
|
|
39
22
|
referenceSpace?: XRReferenceSpaceType;
|
|
40
23
|
fallback?: Snippet;
|
|
41
24
|
children?: Snippet;
|
|
42
|
-
/** Called as an XRSession is
|
|
43
|
-
onsessionstart?: (event:
|
|
44
|
-
/** Called after an XRSession is
|
|
45
|
-
onsessionend?: (event: XRSessionEvent
|
|
25
|
+
/** Called as an XRSession is started */
|
|
26
|
+
onsessionstart?: (event: ThreeEvent<'sessionstart', WebXRManager>) => void;
|
|
27
|
+
/** Called after an XRSession is ended */
|
|
28
|
+
onsessionend?: (event: XRSessionEvent) => void;
|
|
46
29
|
/** Called when an XRSession is hidden or unfocused. */
|
|
47
|
-
onvisibilitychange?: (event:
|
|
30
|
+
onvisibilitychange?: (event: XRSessionEvent) => void;
|
|
48
31
|
/** Called when available inputsources change */
|
|
49
|
-
oninputsourceschange?: (event:
|
|
50
|
-
}
|
|
32
|
+
oninputsourceschange?: (event: XRSessionEvent) => void;
|
|
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}
|