@threlte/xr 0.0.12 → 0.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 +1 -1
- package/dist/components/ARButton.svelte.d.ts +38 -37
- package/dist/components/Controller.svelte +24 -83
- package/dist/components/Hand.svelte +27 -64
- package/dist/components/Hand.svelte.d.ts +1 -0
- package/dist/components/Headset.svelte.d.ts +1 -0
- package/dist/components/VRButton.svelte.d.ts +38 -37
- package/dist/components/XR.svelte +47 -53
- package/dist/components/XRButton.svelte +2 -2
- package/dist/components/XRButton.svelte.d.ts +38 -37
- package/dist/components/internal/Cursor.svelte.d.ts +1 -0
- package/dist/components/internal/PointerCursor.svelte +7 -3
- package/dist/components/internal/PointerCursor.svelte.d.ts +1 -0
- package/dist/components/internal/ScenePortal.svelte.d.ts +1 -0
- package/dist/components/internal/ShortRay.svelte.d.ts +1 -0
- package/dist/components/internal/TeleportCursor.svelte +7 -3
- package/dist/components/internal/TeleportCursor.svelte.d.ts +1 -0
- package/dist/components/internal/TeleportRay.svelte.d.ts +1 -0
- package/dist/hooks/useController.js +2 -4
- package/dist/hooks/useHand.js +2 -0
- package/dist/hooks/useHandJoint.d.ts +2 -2
- package/dist/hooks/useHeadset.d.ts +2 -1
- package/dist/hooks/useHeadset.js +1 -1
- package/dist/hooks/useHitTest.d.ts +2 -2
- package/dist/hooks/useHitTest.js +16 -6
- package/dist/hooks/useTeleport.d.ts +2 -2
- package/dist/hooks/useTeleport.js +2 -2
- package/dist/internal/setupControllers.d.ts +1 -0
- package/dist/internal/setupControllers.js +69 -0
- package/dist/internal/setupHands.d.ts +1 -0
- package/dist/internal/setupHands.js +63 -0
- package/dist/internal/setupHeadset.d.ts +3 -0
- package/dist/internal/{headset.js → setupHeadset.js} +2 -3
- package/dist/internal/setupRaf.d.ts +1 -0
- package/dist/internal/{updateRaf.js → setupRaf.js} +2 -2
- package/dist/internal/stores.d.ts +22 -7
- package/dist/internal/stores.js +15 -9
- package/dist/internal/useFixed.d.ts +1 -1
- package/dist/internal/useHandTrackingState.js +1 -1
- package/dist/lib/toggleXRSession.js +3 -3
- package/dist/plugins/teleportControls/context.d.ts +7 -6
- package/dist/plugins/teleportControls/hook.d.ts +5 -4
- package/dist/plugins/teleportControls/index.d.ts +1 -1
- package/dist/types.d.ts +10 -8
- package/package.json +4 -4
- package/dist/internal/headset.d.ts +0 -3
- package/dist/internal/updateRaf.d.ts +0 -1
- package/dist/plugins/pointerControls/types.d.ts +0 -61
|
@@ -17,33 +17,38 @@ This should be placed within a Threlte `<Canvas />`.
|
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
-->
|
|
20
|
-
<script>import {
|
|
21
|
-
import { createRawEventDispatcher, useThrelte } from "@threlte/core";
|
|
20
|
+
<script>import { onMount } from "svelte";
|
|
21
|
+
import { createRawEventDispatcher, useThrelte, watch } from "@threlte/core";
|
|
22
22
|
import {
|
|
23
|
-
initialized,
|
|
24
23
|
isHandTracking,
|
|
25
24
|
isPresenting,
|
|
26
25
|
referenceSpaceType,
|
|
27
26
|
session,
|
|
28
27
|
xr as xrStore
|
|
29
28
|
} from "../internal/stores";
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
29
|
+
import { setupRaf } from "../internal/setupRaf";
|
|
30
|
+
import { setupHeadset } from "../internal/setupHeadset";
|
|
31
|
+
import { setupControllers } from "../internal/setupControllers";
|
|
32
|
+
import { setupHands } from "../internal/setupHands";
|
|
32
33
|
export let foveation = 1;
|
|
33
34
|
export let frameRate = void 0;
|
|
34
35
|
export let referenceSpace = "local-floor";
|
|
35
36
|
const dispatch = createRawEventDispatcher();
|
|
36
|
-
|
|
37
|
-
const { renderer, frameloop } = useThrelte();
|
|
37
|
+
const { renderer, renderMode } = useThrelte();
|
|
38
38
|
const { xr } = renderer;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
let originalRenderMode = $renderMode;
|
|
40
|
+
setupRaf();
|
|
41
|
+
setupHeadset();
|
|
42
|
+
setupControllers();
|
|
43
|
+
setupHands();
|
|
44
|
+
const handleSessionStart = () => {
|
|
45
|
+
isPresenting.set(true);
|
|
46
|
+
dispatch("sessionstart", { type: "sessionstart", target: $session });
|
|
42
47
|
};
|
|
43
|
-
const handleSessionEnd = (
|
|
44
|
-
dispatch("sessionend", {
|
|
45
|
-
|
|
46
|
-
|
|
48
|
+
const handleSessionEnd = () => {
|
|
49
|
+
dispatch("sessionend", { type: "sessionend", target: $session });
|
|
50
|
+
isPresenting.set(false);
|
|
51
|
+
session.set(void 0);
|
|
47
52
|
};
|
|
48
53
|
const handleVisibilityChange = (event) => {
|
|
49
54
|
dispatch("visibilitychange", { ...event, target: $session });
|
|
@@ -63,14 +68,7 @@ const updateTargetFrameRate = (frameRate2) => {
|
|
|
63
68
|
} catch {
|
|
64
69
|
}
|
|
65
70
|
};
|
|
66
|
-
|
|
67
|
-
if (currentSession === void 0)
|
|
68
|
-
return;
|
|
69
|
-
currentSession.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
70
|
-
currentSession.removeEventListener("inputsourceschange", handleInputSourcesChange);
|
|
71
|
-
currentSession.removeEventListener("frameratechange", handleFramerateChange);
|
|
72
|
-
};
|
|
73
|
-
const updateSession = async (currentSession) => {
|
|
71
|
+
watch(session, (currentSession) => {
|
|
74
72
|
if (currentSession === void 0)
|
|
75
73
|
return;
|
|
76
74
|
currentSession.addEventListener("visibilitychange", handleVisibilityChange);
|
|
@@ -78,44 +76,40 @@ const updateSession = async (currentSession) => {
|
|
|
78
76
|
currentSession.addEventListener("frameratechange", handleFramerateChange);
|
|
79
77
|
xr.setFoveation(foveation);
|
|
80
78
|
updateTargetFrameRate(frameRate);
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
xr.addEventListener("sessionstart", handleSessionStart);
|
|
87
|
-
xr.addEventListener("sessionend", handleSessionEnd);
|
|
88
|
-
useUpdateHeadset();
|
|
89
|
-
onDestroy(() => {
|
|
90
|
-
$initialized = false;
|
|
91
|
-
$xrStore = void 0;
|
|
92
|
-
xr.enabled = false;
|
|
93
|
-
xr.removeEventListener("sessionstart", handleSessionStart);
|
|
94
|
-
xr.removeEventListener("sessionend", handleSessionEnd);
|
|
79
|
+
return () => {
|
|
80
|
+
currentSession.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
81
|
+
currentSession.removeEventListener("inputsourceschange", handleInputSourcesChange);
|
|
82
|
+
currentSession.removeEventListener("frameratechange", handleFramerateChange);
|
|
83
|
+
};
|
|
95
84
|
});
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
$:
|
|
101
|
-
if (lastSession !== $session) {
|
|
102
|
-
cleanupSession(lastSession);
|
|
103
|
-
updateSession($session);
|
|
104
|
-
lastSession = $session;
|
|
105
|
-
}
|
|
106
|
-
let originalFrameloop = $frameloop;
|
|
107
|
-
$frameloop = "always";
|
|
108
|
-
$:
|
|
109
|
-
if ($isPresenting) {
|
|
110
|
-
originalFrameloop = $frameloop;
|
|
111
|
-
$frameloop = "always";
|
|
85
|
+
watch(isPresenting, (presenting) => {
|
|
86
|
+
if (presenting) {
|
|
87
|
+
originalRenderMode = renderMode.current;
|
|
88
|
+
renderMode.set("always");
|
|
112
89
|
} else {
|
|
113
|
-
|
|
90
|
+
renderMode.set(originalRenderMode);
|
|
114
91
|
}
|
|
92
|
+
});
|
|
93
|
+
onMount(() => {
|
|
94
|
+
$xrStore = xr;
|
|
95
|
+
xr.enabled = true;
|
|
96
|
+
xr.addEventListener("sessionstart", handleSessionStart);
|
|
97
|
+
xr.addEventListener("sessionend", handleSessionEnd);
|
|
98
|
+
return () => {
|
|
99
|
+
$xrStore = void 0;
|
|
100
|
+
xr.enabled = false;
|
|
101
|
+
xr.removeEventListener("sessionstart", handleSessionStart);
|
|
102
|
+
xr.removeEventListener("sessionend", handleSessionEnd);
|
|
103
|
+
};
|
|
104
|
+
});
|
|
115
105
|
$:
|
|
116
106
|
updateTargetFrameRate(frameRate);
|
|
117
107
|
$:
|
|
118
108
|
xr.setFoveation(foveation);
|
|
109
|
+
$: {
|
|
110
|
+
xr.setReferenceSpaceType(referenceSpace);
|
|
111
|
+
$referenceSpaceType = referenceSpace;
|
|
112
|
+
}
|
|
119
113
|
</script>
|
|
120
114
|
|
|
121
115
|
{#if $isPresenting}
|
|
@@ -21,14 +21,14 @@ display info about your WebXR session. This is aliased by `ARButton` and
|
|
|
21
21
|
<script>import { createEventDispatcher } from "svelte";
|
|
22
22
|
import { getXRSupportState } from "../lib/getXRSupportState";
|
|
23
23
|
import { toggleXRSession } from "../lib/toggleXRSession";
|
|
24
|
-
import { session,
|
|
24
|
+
import { session, xr } from "../internal/stores";
|
|
25
25
|
export let mode;
|
|
26
26
|
export let sessionInit = void 0;
|
|
27
27
|
export let force = void 0;
|
|
28
28
|
export let styled = true;
|
|
29
29
|
const dispatch = createEventDispatcher();
|
|
30
30
|
const handleButtonClick = async (nativeEvent, state) => {
|
|
31
|
-
if (!$
|
|
31
|
+
if (!$xr) {
|
|
32
32
|
throw new Error("The <XR> component was not created. This is required to start an XR session.");
|
|
33
33
|
}
|
|
34
34
|
dispatch("click", { state, nativeEvent });
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference types="bun-types" />
|
|
1
2
|
/// <reference types="webxr" />
|
|
2
3
|
import { SvelteComponent } from "svelte";
|
|
3
4
|
declare const __propDef: {
|
|
@@ -54,12 +55,12 @@ declare const __propDef: {
|
|
|
54
55
|
results?: number | null | undefined;
|
|
55
56
|
security?: string | null | undefined;
|
|
56
57
|
unselectable?: "on" | "off" | null | undefined;
|
|
57
|
-
inputmode?: "
|
|
58
|
+
inputmode?: "url" | "search" | "none" | "text" | "tel" | "email" | "numeric" | "decimal" | null | undefined;
|
|
58
59
|
is?: string | null | undefined;
|
|
59
60
|
'bind:innerHTML'?: string | null | undefined;
|
|
60
61
|
'bind:textContent'?: string | null | undefined;
|
|
61
62
|
'bind:innerText'?: string | null | undefined;
|
|
62
|
-
'bind:contentRect'?:
|
|
63
|
+
'bind:contentRect'?: any;
|
|
63
64
|
'bind:contentBoxSize'?: ResizeObserverSize[] | null | undefined;
|
|
64
65
|
'bind:borderBoxSize'?: ResizeObserverSize[] | null | undefined;
|
|
65
66
|
'bind:devicePixelContentBoxSize'?: ResizeObserverSize[] | null | undefined;
|
|
@@ -132,37 +133,37 @@ declare const __propDef: {
|
|
|
132
133
|
'on:input'?: import("svelte/elements").FormEventHandler<HTMLButtonElement> | null | undefined;
|
|
133
134
|
'on:reset'?: import("svelte/elements").FormEventHandler<HTMLButtonElement> | null | undefined;
|
|
134
135
|
'on:submit'?: import("svelte/elements").EventHandler<SubmitEvent, HTMLButtonElement> | null | undefined;
|
|
135
|
-
'on:invalid'?: import("svelte/elements").EventHandler<Event
|
|
136
|
+
'on:invalid'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
136
137
|
'on:formdata'?: import("svelte/elements").EventHandler<FormDataEvent, HTMLButtonElement> | null | undefined;
|
|
137
|
-
'on:load'?: import("svelte/elements").EventHandler<Event
|
|
138
|
-
'on:error'?: import("svelte/elements").EventHandler<Event
|
|
139
|
-
'on:toggle'?: import("svelte/elements").EventHandler<Event
|
|
138
|
+
'on:load'?: import("svelte/elements").EventHandler<Event<EventTarget>, Element> | null | undefined;
|
|
139
|
+
'on:error'?: import("svelte/elements").EventHandler<Event<EventTarget>, Element> | null | undefined;
|
|
140
|
+
'on:toggle'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
140
141
|
'on:keydown'?: import("svelte/elements").KeyboardEventHandler<HTMLButtonElement> | null | undefined;
|
|
141
142
|
'on:keypress'?: import("svelte/elements").KeyboardEventHandler<HTMLButtonElement> | null | undefined;
|
|
142
143
|
'on:keyup'?: import("svelte/elements").KeyboardEventHandler<HTMLButtonElement> | null | undefined;
|
|
143
|
-
'on:abort'?: import("svelte/elements").EventHandler<Event
|
|
144
|
-
'on:canplay'?: import("svelte/elements").EventHandler<Event
|
|
145
|
-
'on:canplaythrough'?: import("svelte/elements").EventHandler<Event
|
|
146
|
-
'on:cuechange'?: import("svelte/elements").EventHandler<Event
|
|
147
|
-
'on:durationchange'?: import("svelte/elements").EventHandler<Event
|
|
148
|
-
'on:emptied'?: import("svelte/elements").EventHandler<Event
|
|
149
|
-
'on:encrypted'?: import("svelte/elements").EventHandler<Event
|
|
150
|
-
'on:ended'?: import("svelte/elements").EventHandler<Event
|
|
151
|
-
'on:loadeddata'?: import("svelte/elements").EventHandler<Event
|
|
152
|
-
'on:loadedmetadata'?: import("svelte/elements").EventHandler<Event
|
|
153
|
-
'on:loadstart'?: import("svelte/elements").EventHandler<Event
|
|
154
|
-
'on:pause'?: import("svelte/elements").EventHandler<Event
|
|
155
|
-
'on:play'?: import("svelte/elements").EventHandler<Event
|
|
156
|
-
'on:playing'?: import("svelte/elements").EventHandler<Event
|
|
157
|
-
'on:progress'?: import("svelte/elements").EventHandler<Event
|
|
158
|
-
'on:ratechange'?: import("svelte/elements").EventHandler<Event
|
|
159
|
-
'on:seeked'?: import("svelte/elements").EventHandler<Event
|
|
160
|
-
'on:seeking'?: import("svelte/elements").EventHandler<Event
|
|
161
|
-
'on:stalled'?: import("svelte/elements").EventHandler<Event
|
|
162
|
-
'on:suspend'?: import("svelte/elements").EventHandler<Event
|
|
163
|
-
'on:timeupdate'?: import("svelte/elements").EventHandler<Event
|
|
164
|
-
'on:volumechange'?: import("svelte/elements").EventHandler<Event
|
|
165
|
-
'on:waiting'?: import("svelte/elements").EventHandler<Event
|
|
144
|
+
'on:abort'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
145
|
+
'on:canplay'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
146
|
+
'on:canplaythrough'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
147
|
+
'on:cuechange'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
148
|
+
'on:durationchange'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
149
|
+
'on:emptied'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
150
|
+
'on:encrypted'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
151
|
+
'on:ended'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
152
|
+
'on:loadeddata'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
153
|
+
'on:loadedmetadata'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
154
|
+
'on:loadstart'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
155
|
+
'on:pause'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
156
|
+
'on:play'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
157
|
+
'on:playing'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
158
|
+
'on:progress'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
159
|
+
'on:ratechange'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
160
|
+
'on:seeked'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
161
|
+
'on:seeking'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
162
|
+
'on:stalled'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
163
|
+
'on:suspend'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
164
|
+
'on:timeupdate'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
165
|
+
'on:volumechange'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
166
|
+
'on:waiting'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
166
167
|
'on:auxclick'?: import("svelte/elements").MouseEventHandler<HTMLButtonElement> | null | undefined;
|
|
167
168
|
'on:click'?: import("svelte/elements").MouseEventHandler<HTMLButtonElement> | null | undefined;
|
|
168
169
|
'on:contextmenu'?: import("svelte/elements").MouseEventHandler<HTMLButtonElement> | null | undefined;
|
|
@@ -182,9 +183,9 @@ declare const __propDef: {
|
|
|
182
183
|
'on:mouseout'?: import("svelte/elements").MouseEventHandler<HTMLButtonElement> | null | undefined;
|
|
183
184
|
'on:mouseover'?: import("svelte/elements").MouseEventHandler<HTMLButtonElement> | null | undefined;
|
|
184
185
|
'on:mouseup'?: import("svelte/elements").MouseEventHandler<HTMLButtonElement> | null | undefined;
|
|
185
|
-
'on:select'?: import("svelte/elements").EventHandler<Event
|
|
186
|
-
'on:selectionchange'?: import("svelte/elements").EventHandler<Event
|
|
187
|
-
'on:selectstart'?: import("svelte/elements").EventHandler<Event
|
|
186
|
+
'on:select'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
187
|
+
'on:selectionchange'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
188
|
+
'on:selectstart'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
188
189
|
'on:touchcancel'?: import("svelte/elements").TouchEventHandler<HTMLButtonElement> | null | undefined;
|
|
189
190
|
'on:touchend'?: import("svelte/elements").TouchEventHandler<HTMLButtonElement> | null | undefined;
|
|
190
191
|
'on:touchmove'?: import("svelte/elements").TouchEventHandler<HTMLButtonElement> | null | undefined;
|
|
@@ -215,11 +216,11 @@ declare const __propDef: {
|
|
|
215
216
|
'on:introend'?: import("svelte/elements").EventHandler<CustomEvent<null>, HTMLButtonElement> | null | undefined;
|
|
216
217
|
'on:message'?: import("svelte/elements").MessageEventHandler<HTMLButtonElement> | null | undefined;
|
|
217
218
|
'on:messageerror'?: import("svelte/elements").MessageEventHandler<HTMLButtonElement> | null | undefined;
|
|
218
|
-
'on:visibilitychange'?: import("svelte/elements").EventHandler<Event
|
|
219
|
-
'on:cancel'?: import("svelte/elements").EventHandler<Event
|
|
220
|
-
'on:close'?: import("svelte/elements").EventHandler<Event
|
|
221
|
-
'on:fullscreenchange'?: import("svelte/elements").EventHandler<Event
|
|
222
|
-
'on:fullscreenerror'?: import("svelte/elements").EventHandler<Event
|
|
219
|
+
'on:visibilitychange'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
220
|
+
'on:cancel'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
221
|
+
'on:close'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
222
|
+
'on:fullscreenchange'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
223
|
+
'on:fullscreenerror'?: import("svelte/elements").EventHandler<Event<EventTarget>, HTMLButtonElement> | null | undefined;
|
|
223
224
|
mode: XRSessionMode;
|
|
224
225
|
sessionInit?: XRSessionInit & {
|
|
225
226
|
domOverlay?: {
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
<script>import { Group, Vector3 } from "three";
|
|
1
|
+
<script>import { Group, Vector3, Matrix3 } from "three";
|
|
2
2
|
import { T, useFrame } from "@threlte/core";
|
|
3
3
|
import { pointerIntersection, pointerState } from "../../internal/stores";
|
|
4
4
|
import Cursor from "./Cursor.svelte";
|
|
5
5
|
export let handedness;
|
|
6
6
|
const ref = new Group();
|
|
7
7
|
const vec3 = new Vector3();
|
|
8
|
+
const normalMatrix = new Matrix3();
|
|
9
|
+
const worldNormal = new Vector3();
|
|
8
10
|
$:
|
|
9
11
|
hovering = $pointerState[handedness].hovering;
|
|
10
12
|
$:
|
|
@@ -13,10 +15,12 @@ const { start, stop } = useFrame(
|
|
|
13
15
|
() => {
|
|
14
16
|
if (intersection.current === void 0)
|
|
15
17
|
return;
|
|
16
|
-
const { point, face } = intersection.current;
|
|
18
|
+
const { point, face, object } = intersection.current;
|
|
17
19
|
ref.position.lerp(point, 0.4);
|
|
18
20
|
if (face) {
|
|
19
|
-
|
|
21
|
+
normalMatrix.getNormalMatrix(object.matrixWorld);
|
|
22
|
+
worldNormal.copy(face.normal).applyMatrix3(normalMatrix).normalize();
|
|
23
|
+
ref.lookAt(vec3.addVectors(point, worldNormal));
|
|
20
24
|
}
|
|
21
25
|
},
|
|
22
26
|
{
|
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
<script>import { spring } from "svelte/motion";
|
|
2
|
-
import { Group, Vector3 } from "three";
|
|
2
|
+
import { Group, Matrix3, Vector3 } from "three";
|
|
3
3
|
import { T, useFrame } from "@threlte/core";
|
|
4
4
|
import { teleportIntersection } from "../../internal/stores";
|
|
5
5
|
import Cursor from "./Cursor.svelte";
|
|
6
6
|
export let handedness;
|
|
7
7
|
const ref = new Group();
|
|
8
8
|
const vec3 = new Vector3();
|
|
9
|
+
const normalMatrix = new Matrix3();
|
|
10
|
+
const worldNormal = new Vector3();
|
|
9
11
|
$:
|
|
10
12
|
intersection = teleportIntersection[handedness];
|
|
11
13
|
const { start, stop } = useFrame(
|
|
12
14
|
() => {
|
|
13
15
|
if (intersection.current === void 0)
|
|
14
16
|
return;
|
|
15
|
-
const { point, face } = intersection.current;
|
|
17
|
+
const { point, face, object } = intersection.current;
|
|
16
18
|
ref.position.lerp(point, 0.4);
|
|
17
19
|
if (face) {
|
|
18
|
-
|
|
20
|
+
normalMatrix.getNormalMatrix(object.matrixWorld);
|
|
21
|
+
worldNormal.copy(face.normal).applyMatrix3(normalMatrix).normalize();
|
|
22
|
+
ref.lookAt(vec3.addVectors(point, worldNormal));
|
|
19
23
|
}
|
|
20
24
|
},
|
|
21
25
|
{
|
|
@@ -2,10 +2,6 @@ import { currentWritable } from '@threlte/core';
|
|
|
2
2
|
export const left = currentWritable(undefined);
|
|
3
3
|
export const right = currentWritable(undefined);
|
|
4
4
|
export const gaze = currentWritable(undefined);
|
|
5
|
-
const gamepadLeft = currentWritable(undefined);
|
|
6
|
-
const gamepadRight = currentWritable(undefined);
|
|
7
|
-
left.subscribe((value) => gamepadLeft.set(value?.inputSource.gamepad));
|
|
8
|
-
right.subscribe((value) => gamepadRight.set(value?.inputSource.gamepad));
|
|
9
5
|
/**
|
|
10
6
|
* Provides a reference to a current XRController, filtered by handedness.
|
|
11
7
|
*/
|
|
@@ -17,5 +13,7 @@ export const useController = (handedness) => {
|
|
|
17
13
|
return right;
|
|
18
14
|
case 'none':
|
|
19
15
|
return gaze;
|
|
16
|
+
default:
|
|
17
|
+
throw new Error('useController handedness must be left, right, or none.');
|
|
20
18
|
}
|
|
21
19
|
};
|
package/dist/hooks/useHand.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import type { XRJointSpace } from 'three';
|
|
2
2
|
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<
|
|
6
|
+
export declare const useHandJoint: (handedness: 'left' | 'right', joint: HandJoints) => import("@threlte/core").CurrentWritable<XRJointSpace | undefined>;
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import type { Group } from 'three';
|
|
2
|
+
export declare const useHeadset: () => Readonly<Group>;
|
package/dist/hooks/useHeadset.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="webxr" />
|
|
2
|
-
import
|
|
3
|
-
export type HitTestCallback = (hitMatrix:
|
|
2
|
+
import { Matrix4 } from 'three';
|
|
3
|
+
export type HitTestCallback = (hitMatrix: Matrix4, hit: XRHitTestResult | undefined) => void;
|
|
4
4
|
export type UseHitTestOptions = {
|
|
5
5
|
/**
|
|
6
6
|
* The ray source when performing hit testing.
|
package/dist/hooks/useHitTest.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Matrix4 } from 'three';
|
|
2
2
|
import { useThrelte, useFrame, watch, currentWritable } from '@threlte/core';
|
|
3
3
|
import { useXR } from './useXR';
|
|
4
4
|
import { useController } from './useController';
|
|
@@ -17,12 +17,14 @@ export const useHitTest = (hitTestCallback, options = {}) => {
|
|
|
17
17
|
const source = options.source ?? 'viewer';
|
|
18
18
|
const { xr } = useThrelte().renderer;
|
|
19
19
|
const xrState = useXR();
|
|
20
|
-
const hitMatrix = new
|
|
20
|
+
const hitMatrix = new Matrix4();
|
|
21
21
|
let hitTestSource = currentWritable(undefined);
|
|
22
22
|
if (source === 'viewer') {
|
|
23
23
|
watch(xrState.session, async (session) => {
|
|
24
|
-
if (session === undefined)
|
|
24
|
+
if (session === undefined) {
|
|
25
|
+
hitTestSource.set(undefined);
|
|
25
26
|
return;
|
|
27
|
+
}
|
|
26
28
|
const space = await session.requestReferenceSpace('viewer');
|
|
27
29
|
hitTestSource.set(await session.requestHitTestSource?.({ space }));
|
|
28
30
|
});
|
|
@@ -31,14 +33,18 @@ export const useHitTest = (hitTestCallback, options = {}) => {
|
|
|
31
33
|
const controller = useController(source === 'leftInput' ? 'left' : 'right');
|
|
32
34
|
const hand = useController(source === 'leftInput' ? 'left' : 'right');
|
|
33
35
|
watch([xrState.session, controller], async ([session, input]) => {
|
|
34
|
-
if (input === undefined || session === undefined)
|
|
36
|
+
if (input === undefined || session === undefined) {
|
|
37
|
+
hitTestSource.set(undefined);
|
|
35
38
|
return;
|
|
39
|
+
}
|
|
36
40
|
const space = input.inputSource.targetRaySpace;
|
|
37
41
|
hitTestSource.set(await session.requestHitTestSource?.({ space }));
|
|
38
42
|
});
|
|
39
43
|
watch([xrState.session, hand], async ([session, input]) => {
|
|
40
|
-
if (input === undefined || session === undefined)
|
|
44
|
+
if (input === undefined || session === undefined) {
|
|
45
|
+
hitTestSource.set(undefined);
|
|
41
46
|
return;
|
|
47
|
+
}
|
|
42
48
|
const space = input.inputSource.targetRaySpace;
|
|
43
49
|
hitTestSource.set(await session.requestHitTestSource?.({ space }));
|
|
44
50
|
});
|
|
@@ -56,7 +62,11 @@ export const useHitTest = (hitTestCallback, options = {}) => {
|
|
|
56
62
|
hitMatrix.fromArray(pose.transform.matrix);
|
|
57
63
|
hitTestCallback(hitMatrix, hit);
|
|
58
64
|
}, { autostart: false });
|
|
59
|
-
watch(hitTestSource, (testSource) => {
|
|
65
|
+
watch([xrState.isPresenting, hitTestSource], ([isPresenting, testSource]) => {
|
|
66
|
+
if (!isPresenting) {
|
|
67
|
+
stop();
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
60
70
|
if (testSource === undefined) {
|
|
61
71
|
stop();
|
|
62
72
|
// Execute callback one last time to inform consumers of no hits.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Quaternion, type Vector3, type Vector3Tuple } from 'three';
|
|
2
2
|
/**
|
|
3
3
|
* Returns a callback to teleport the player from the world origin to a position and optional orientation.
|
|
4
4
|
*
|
|
@@ -14,4 +14,4 @@ import * as THREE from 'three';
|
|
|
14
14
|
*
|
|
15
15
|
* teleport(vec3, quat)
|
|
16
16
|
*/
|
|
17
|
-
export declare const useTeleport: () => (position:
|
|
17
|
+
export declare const useTeleport: () => (position: Vector3 | Vector3Tuple, orientation?: Quaternion) => void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Quaternion } from 'three';
|
|
2
2
|
import { useThrelte } from '@threlte/core';
|
|
3
|
-
const quaternion = new
|
|
3
|
+
const quaternion = new Quaternion();
|
|
4
4
|
const offset = { x: 0, y: 0, z: 0 };
|
|
5
5
|
/**
|
|
6
6
|
* Returns a callback to teleport the player from the world origin to a position and optional orientation.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const setupControllers: () => void;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { XRControllerModelFactory } from 'three/examples/jsm/webxr/XRControllerModelFactory';
|
|
2
|
+
import { useThrelte } from '@threlte/core';
|
|
3
|
+
import { onMount } from 'svelte';
|
|
4
|
+
import { useHandTrackingState } from './useHandTrackingState';
|
|
5
|
+
import { gaze, left, right } from '../hooks/useController';
|
|
6
|
+
import { controllerDispatchers } from './stores';
|
|
7
|
+
export const setupControllers = () => {
|
|
8
|
+
const factory = new XRControllerModelFactory();
|
|
9
|
+
const stores = { left, right, none: gaze };
|
|
10
|
+
const { xr } = useThrelte().renderer;
|
|
11
|
+
const hasHands = useHandTrackingState();
|
|
12
|
+
const controllers = [xr.getController(0), xr.getController(1)];
|
|
13
|
+
const indexMap = new Map();
|
|
14
|
+
controllers.forEach((targetRay, index) => {
|
|
15
|
+
indexMap.set(targetRay, {
|
|
16
|
+
targetRay,
|
|
17
|
+
grip: xr.getControllerGrip(index),
|
|
18
|
+
model: factory.createControllerModel(targetRay)
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
onMount(() => {
|
|
22
|
+
const dispatch = (event) => {
|
|
23
|
+
if (hasHands())
|
|
24
|
+
return;
|
|
25
|
+
const { data } = event;
|
|
26
|
+
controllerDispatchers[data.handedness]?.current?.(event.type, event);
|
|
27
|
+
};
|
|
28
|
+
function handleConnected(event) {
|
|
29
|
+
const { model, targetRay, grip } = indexMap.get(this);
|
|
30
|
+
const { data: inputSource } = event;
|
|
31
|
+
stores[event.data.handedness].set({
|
|
32
|
+
inputSource,
|
|
33
|
+
targetRay,
|
|
34
|
+
grip,
|
|
35
|
+
model
|
|
36
|
+
});
|
|
37
|
+
dispatch(event);
|
|
38
|
+
}
|
|
39
|
+
const handleDisconnected = (event) => {
|
|
40
|
+
dispatch(event);
|
|
41
|
+
stores[event.data.handedness].set(undefined);
|
|
42
|
+
};
|
|
43
|
+
for (const targetRay of controllers) {
|
|
44
|
+
targetRay.addEventListener('connected', handleConnected);
|
|
45
|
+
targetRay.addEventListener('disconnected', handleDisconnected);
|
|
46
|
+
targetRay.addEventListener('select', dispatch);
|
|
47
|
+
targetRay.addEventListener('selectstart', dispatch);
|
|
48
|
+
targetRay.addEventListener('selectend', dispatch);
|
|
49
|
+
targetRay.addEventListener('squeeze', dispatch);
|
|
50
|
+
targetRay.addEventListener('squeezestart', dispatch);
|
|
51
|
+
targetRay.addEventListener('squeezeend', dispatch);
|
|
52
|
+
}
|
|
53
|
+
return () => {
|
|
54
|
+
for (const targetRay of controllers) {
|
|
55
|
+
targetRay.removeEventListener('connected', handleConnected);
|
|
56
|
+
targetRay.removeEventListener('disconnected', handleDisconnected);
|
|
57
|
+
targetRay.removeEventListener('select', dispatch);
|
|
58
|
+
targetRay.removeEventListener('selectstart', dispatch);
|
|
59
|
+
targetRay.removeEventListener('selectend', dispatch);
|
|
60
|
+
targetRay.removeEventListener('squeeze', dispatch);
|
|
61
|
+
targetRay.removeEventListener('squeezestart', dispatch);
|
|
62
|
+
targetRay.removeEventListener('squeezeend', dispatch);
|
|
63
|
+
}
|
|
64
|
+
stores.left.set(undefined);
|
|
65
|
+
stores.right.set(undefined);
|
|
66
|
+
stores.none.set(undefined);
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const setupHands: () => void;
|