@threlte/xr 0.0.2
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/LICENSE +9 -0
- package/README.md +13 -0
- package/dist/components/ARButton.svelte +25 -0
- package/dist/components/ARButton.svelte.d.ts +250 -0
- package/dist/components/Controller.svelte +124 -0
- package/dist/components/Controller.svelte.d.ts +30 -0
- package/dist/components/Hand.svelte +119 -0
- package/dist/components/Hand.svelte.d.ts +24 -0
- package/dist/components/Hands.svelte +35 -0
- package/dist/components/Hands.svelte.d.ts +24 -0
- package/dist/components/Ray.svelte +23 -0
- package/dist/components/Ray.svelte.d.ts +18 -0
- package/dist/components/ShortRay.svelte +28 -0
- package/dist/components/ShortRay.svelte.d.ts +16 -0
- package/dist/components/TeleportControls.svelte +136 -0
- package/dist/components/TeleportControls.svelte.d.ts +54 -0
- package/dist/components/VRButton.svelte +24 -0
- package/dist/components/VRButton.svelte.d.ts +250 -0
- package/dist/components/XR.svelte +114 -0
- package/dist/components/XR.svelte.d.ts +59 -0
- package/dist/components/XRButton.svelte +79 -0
- package/dist/components/XRButton.svelte.d.ts +265 -0
- package/dist/hooks/index.d.ts +7 -0
- package/dist/hooks/index.js +7 -0
- package/dist/hooks/useController.d.ts +14 -0
- package/dist/hooks/useController.js +32 -0
- package/dist/hooks/useEvent.d.ts +14 -0
- package/dist/hooks/useEvent.js +34 -0
- package/dist/hooks/useHand.d.ts +8 -0
- package/dist/hooks/useHand.js +14 -0
- package/dist/hooks/useHandJoint.d.ts +6 -0
- package/dist/hooks/useHandJoint.js +19 -0
- package/dist/hooks/useHitTest.d.ts +5 -0
- package/dist/hooks/useHitTest.js +44 -0
- package/dist/hooks/useTeleport.d.ts +13 -0
- package/dist/hooks/useTeleport.js +54 -0
- package/dist/hooks/useXR.d.ts +14 -0
- package/dist/hooks/useXR.js +15 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +11 -0
- package/dist/internal/events.d.ts +5 -0
- package/dist/internal/events.js +26 -0
- package/dist/internal/stores.d.ts +10 -0
- package/dist/internal/stores.js +10 -0
- package/dist/lib/getXRSessionOptions.d.ts +2 -0
- package/dist/lib/getXRSessionOptions.js +15 -0
- package/dist/lib/getXRSupportState.d.ts +7 -0
- package/dist/lib/getXRSupportState.js +20 -0
- package/dist/lib/handJoints.d.ts +2 -0
- package/dist/lib/handJoints.js +27 -0
- package/dist/lib/toggleXRSession.d.ts +14 -0
- package/dist/lib/toggleXRSession.js +33 -0
- package/dist/plugins/teleportPlugin.d.ts +1 -0
- package/dist/plugins/teleportPlugin.js +41 -0
- package/dist/types.d.ts +31 -0
- package/dist/types.js +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { currentWritable } from '@threlte/core';
|
|
2
|
+
export const left = currentWritable(undefined);
|
|
3
|
+
export const right = currentWritable(undefined);
|
|
4
|
+
/**
|
|
5
|
+
* Provides a reference to a current XRHand, filtered by handedness.
|
|
6
|
+
*/
|
|
7
|
+
export const useHand = (handedness) => {
|
|
8
|
+
switch (handedness) {
|
|
9
|
+
case 'left':
|
|
10
|
+
return left;
|
|
11
|
+
case 'right':
|
|
12
|
+
return right;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type * as THREE from 'three';
|
|
2
|
+
import type { HandJoints } from '../lib/handJoints';
|
|
3
|
+
/**
|
|
4
|
+
* Provides a reference to a requested hand joint, once available.
|
|
5
|
+
*/
|
|
6
|
+
export declare const useHandJoint: (handedness: 'left' | 'right', joint: HandJoints) => import("@threlte/core").CurrentWritable<THREE.XRJointSpace | undefined>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { currentWritable, useFrame } from '@threlte/core';
|
|
2
|
+
import { useHand } from './useHand';
|
|
3
|
+
/**
|
|
4
|
+
* Provides a reference to a requested hand joint, once available.
|
|
5
|
+
*/
|
|
6
|
+
export const useHandJoint = (handedness, joint) => {
|
|
7
|
+
const jointSpaceStore = currentWritable(undefined);
|
|
8
|
+
const xrhand = useHand(handedness);
|
|
9
|
+
const { stop } = useFrame(({ invalidate }) => {
|
|
10
|
+
const jointSpace = xrhand.current?.hand.joints[joint];
|
|
11
|
+
// The joint radius is a good indicator that the joint is ready
|
|
12
|
+
if (jointSpace?.jointRadius !== undefined) {
|
|
13
|
+
jointSpaceStore.set(jointSpace);
|
|
14
|
+
invalidate();
|
|
15
|
+
stop();
|
|
16
|
+
}
|
|
17
|
+
}, { invalidate: false });
|
|
18
|
+
return jointSpaceStore;
|
|
19
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import { onDestroy } from 'svelte';
|
|
3
|
+
import { session } from '../internal/stores';
|
|
4
|
+
import { useThrelte, useFrame } from '@threlte/core';
|
|
5
|
+
/**
|
|
6
|
+
* Use this hook to perform a hit test for an AR environment.
|
|
7
|
+
*/
|
|
8
|
+
export const useHitTest = (hitTestCallback) => {
|
|
9
|
+
const { xr } = useThrelte().renderer;
|
|
10
|
+
const hitMatrix = new THREE.Matrix4();
|
|
11
|
+
let started = false;
|
|
12
|
+
let hitTestSource;
|
|
13
|
+
onDestroy(session.subscribe(async (value) => {
|
|
14
|
+
if (value === undefined) {
|
|
15
|
+
hitTestSource = undefined;
|
|
16
|
+
if (started) {
|
|
17
|
+
stop();
|
|
18
|
+
started = false;
|
|
19
|
+
}
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const space = await value.requestReferenceSpace('viewer');
|
|
23
|
+
hitTestSource = await value.requestHitTestSource?.({ space });
|
|
24
|
+
if (!started) {
|
|
25
|
+
start();
|
|
26
|
+
started = true;
|
|
27
|
+
}
|
|
28
|
+
}));
|
|
29
|
+
const { start, stop } = useFrame(() => {
|
|
30
|
+
if (hitTestSource === undefined)
|
|
31
|
+
return;
|
|
32
|
+
const [hit] = xr.getFrame().getHitTestResults(hitTestSource);
|
|
33
|
+
if (hit === undefined)
|
|
34
|
+
return;
|
|
35
|
+
const referenceSpace = xr.getReferenceSpace();
|
|
36
|
+
if (referenceSpace === null)
|
|
37
|
+
return;
|
|
38
|
+
const pose = hit.getPose(referenceSpace);
|
|
39
|
+
if (pose === undefined)
|
|
40
|
+
return;
|
|
41
|
+
hitMatrix.fromArray(pose.transform.matrix);
|
|
42
|
+
hitTestCallback(hitMatrix, hit);
|
|
43
|
+
}, { autostart: false });
|
|
44
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
/**
|
|
3
|
+
* Returns a callback to teleport the player to a position.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* const teleport = useTeleport()
|
|
7
|
+
* const vec3 = new THREE.Vector3()
|
|
8
|
+
*
|
|
9
|
+
* vec3.set(5, 0, 5)
|
|
10
|
+
*
|
|
11
|
+
* teleport(vec3)
|
|
12
|
+
*/
|
|
13
|
+
export declare const useTeleport: () => (position: THREE.Vector3 | THREE.Vector3Tuple, direction?: THREE.Quaternion) => void;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import { useThrelte } from '@threlte/core';
|
|
3
|
+
import { session } from '../internal/stores';
|
|
4
|
+
import { onDestroy } from 'svelte';
|
|
5
|
+
const quaternion = new THREE.Quaternion();
|
|
6
|
+
const offset = { x: 0, y: 0, z: 0 };
|
|
7
|
+
/**
|
|
8
|
+
* Returns a callback to teleport the player to a position.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* const teleport = useTeleport()
|
|
12
|
+
* const vec3 = new THREE.Vector3()
|
|
13
|
+
*
|
|
14
|
+
* vec3.set(5, 0, 5)
|
|
15
|
+
*
|
|
16
|
+
* teleport(vec3)
|
|
17
|
+
*/
|
|
18
|
+
export const useTeleport = () => {
|
|
19
|
+
const { xr } = useThrelte().renderer;
|
|
20
|
+
let baseReferenceSpace;
|
|
21
|
+
const unsub = session.subscribe((value) => {
|
|
22
|
+
if (value === undefined)
|
|
23
|
+
return;
|
|
24
|
+
baseReferenceSpace = xr.getReferenceSpace();
|
|
25
|
+
});
|
|
26
|
+
onDestroy(unsub);
|
|
27
|
+
/**
|
|
28
|
+
* Teleports a player to a position - and optionally a direction.
|
|
29
|
+
*/
|
|
30
|
+
return (position, direction = quaternion) => {
|
|
31
|
+
if (baseReferenceSpace === null || baseReferenceSpace === undefined)
|
|
32
|
+
return;
|
|
33
|
+
let x = 0, y = 0, z = 0;
|
|
34
|
+
if (Array.isArray(position)) {
|
|
35
|
+
;
|
|
36
|
+
[x, y, z] = position;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
x = position.x;
|
|
40
|
+
y = position.y;
|
|
41
|
+
z = position.z;
|
|
42
|
+
}
|
|
43
|
+
offset.x = -x;
|
|
44
|
+
offset.y = -y;
|
|
45
|
+
offset.z = -z;
|
|
46
|
+
const pose = xr.getFrame().getViewerPose(baseReferenceSpace);
|
|
47
|
+
if (pose !== undefined) {
|
|
48
|
+
offset.x += pose.transform.position.x;
|
|
49
|
+
offset.z += pose.transform.position.z;
|
|
50
|
+
}
|
|
51
|
+
const teleportOffset = new XRRigidTransform(offset, direction);
|
|
52
|
+
xr.setReferenceSpace(baseReferenceSpace.getOffsetReferenceSpace(teleportOffset));
|
|
53
|
+
};
|
|
54
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/// <reference types="webxr" />
|
|
2
|
+
import type { CurrentWritable } from '@threlte/core';
|
|
3
|
+
import { isPresenting, isHandTracking, session } from '../internal/stores';
|
|
4
|
+
/**
|
|
5
|
+
* Provides access to context related to `<XR />`.
|
|
6
|
+
*/
|
|
7
|
+
export declare const useXR: () => {
|
|
8
|
+
isPresenting: CurrentWritable<boolean>;
|
|
9
|
+
isHandTracking: CurrentWritable<boolean>;
|
|
10
|
+
session: CurrentWritable<XRSession | undefined>;
|
|
11
|
+
xrFrame: {
|
|
12
|
+
current: XRFrame;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { isPresenting, isHandTracking, session, xr } from '../internal/stores';
|
|
2
|
+
const stores = {
|
|
3
|
+
isPresenting,
|
|
4
|
+
isHandTracking,
|
|
5
|
+
session,
|
|
6
|
+
xrFrame: {
|
|
7
|
+
get current() {
|
|
8
|
+
return xr.current.getFrame();
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Provides access to context related to `<XR />`.
|
|
14
|
+
*/
|
|
15
|
+
export const useXR = () => stores;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { default as ARButton } from './components/ARButton.svelte';
|
|
2
|
+
export { default as VRButton } from './components/VRButton.svelte';
|
|
3
|
+
export { default as XRButton } from './components/XRButton.svelte';
|
|
4
|
+
export { default as Controller } from './components/Controller.svelte';
|
|
5
|
+
export { default as Hand } from './components/Hand.svelte';
|
|
6
|
+
export { default as TeleportControls } from './components/TeleportControls.svelte';
|
|
7
|
+
export { default as XR } from './components/XR.svelte';
|
|
8
|
+
export { getXRSupportState } from './lib/getXRSupportState';
|
|
9
|
+
export { toggleXRSession } from './lib/toggleXRSession';
|
|
10
|
+
export { handJoints } from './lib/handJoints';
|
|
11
|
+
export * from './hooks';
|
|
12
|
+
export type * from './types';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { default as ARButton } from './components/ARButton.svelte';
|
|
2
|
+
export { default as VRButton } from './components/VRButton.svelte';
|
|
3
|
+
export { default as XRButton } from './components/XRButton.svelte';
|
|
4
|
+
export { default as Controller } from './components/Controller.svelte';
|
|
5
|
+
export { default as Hand } from './components/Hand.svelte';
|
|
6
|
+
export { default as TeleportControls } from './components/TeleportControls.svelte';
|
|
7
|
+
export { default as XR } from './components/XR.svelte';
|
|
8
|
+
export { getXRSupportState } from './lib/getXRSupportState';
|
|
9
|
+
export { toggleXRSession } from './lib/toggleXRSession';
|
|
10
|
+
export { handJoints } from './lib/handJoints';
|
|
11
|
+
export * from './hooks';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
type Callback<T = any, O = any> = (event: T, metadata?: O) => void;
|
|
2
|
+
export declare const on: <T, O = any>(name: string, cb: Callback<T, O>) => (() => void);
|
|
3
|
+
export declare const off: <T>(name: string, cb: Callback<T, any>) => void;
|
|
4
|
+
export declare const fire: <T, O = any>(name: string, payload: T, metadata?: O | undefined) => void;
|
|
5
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const events = {};
|
|
2
|
+
export const on = (name, cb) => {
|
|
3
|
+
const fns = events[name];
|
|
4
|
+
if (fns === undefined) {
|
|
5
|
+
events[name] = [cb];
|
|
6
|
+
}
|
|
7
|
+
else {
|
|
8
|
+
fns.push(cb);
|
|
9
|
+
}
|
|
10
|
+
return () => off(name, cb);
|
|
11
|
+
};
|
|
12
|
+
export const off = (name, cb) => {
|
|
13
|
+
const arr = events[name];
|
|
14
|
+
if (arr === undefined)
|
|
15
|
+
return;
|
|
16
|
+
arr.splice(arr.indexOf(cb), 1);
|
|
17
|
+
if (arr.length === 0)
|
|
18
|
+
delete events[name];
|
|
19
|
+
};
|
|
20
|
+
export const fire = (name, payload, metadata) => {
|
|
21
|
+
const fns = events[name];
|
|
22
|
+
if (fns === undefined)
|
|
23
|
+
return;
|
|
24
|
+
for (const fn of fns)
|
|
25
|
+
fn(payload, metadata);
|
|
26
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/// <reference types="webxr" />
|
|
2
|
+
/// <reference types="svelte" />
|
|
3
|
+
export declare const initialized: import("svelte/store").Writable<boolean>;
|
|
4
|
+
export declare const isPresenting: import("@threlte/core").CurrentWritable<boolean>;
|
|
5
|
+
export declare const isHandTracking: import("@threlte/core").CurrentWritable<boolean>;
|
|
6
|
+
export declare const session: import("@threlte/core").CurrentWritable<XRSession | undefined>;
|
|
7
|
+
export declare const referenceSpaceType: import("@threlte/core").CurrentWritable<XRReferenceSpaceType | undefined>;
|
|
8
|
+
export declare const activeTeleportController: import("@threlte/core").CurrentWritable<import("three").XRTargetRaySpace | undefined>;
|
|
9
|
+
export declare const pendingTeleportDestination: import("@threlte/core").CurrentWritable<import("three").Vector3 | undefined>;
|
|
10
|
+
export declare const xr: import("@threlte/core").CurrentWritable<import("three").WebXRManager | undefined>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { writable } from 'svelte/store';
|
|
2
|
+
import { currentWritable } from '@threlte/core';
|
|
3
|
+
export const initialized = writable(false);
|
|
4
|
+
export const isPresenting = currentWritable(false);
|
|
5
|
+
export const isHandTracking = currentWritable(false);
|
|
6
|
+
export const session = currentWritable(undefined);
|
|
7
|
+
export const referenceSpaceType = currentWritable(undefined);
|
|
8
|
+
export const activeTeleportController = currentWritable(undefined);
|
|
9
|
+
export const pendingTeleportDestination = currentWritable(undefined);
|
|
10
|
+
export const xr = currentWritable(undefined);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const getXRSessionOptions = (referenceSpaceType, sessionInit) => {
|
|
2
|
+
if (referenceSpaceType === undefined && sessionInit === undefined) {
|
|
3
|
+
return undefined;
|
|
4
|
+
}
|
|
5
|
+
if (referenceSpaceType && sessionInit === undefined) {
|
|
6
|
+
return { optionalFeatures: [referenceSpaceType] };
|
|
7
|
+
}
|
|
8
|
+
if (referenceSpaceType && sessionInit) {
|
|
9
|
+
return {
|
|
10
|
+
...sessionInit,
|
|
11
|
+
optionalFeatures: [...new Set([...(sessionInit.optionalFeatures ?? []), referenceSpaceType])]
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
return sessionInit;
|
|
15
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/// <reference types="webxr" />
|
|
2
|
+
/**
|
|
3
|
+
* Gets the support state of requested session mode.
|
|
4
|
+
* @param mode Session mode: 'inline' | 'immersive-vr' | 'immersive-ar'
|
|
5
|
+
* @returns The current support state
|
|
6
|
+
*/
|
|
7
|
+
export declare const getXRSupportState: (mode: XRSessionMode) => Promise<'unsupported' | 'insecure' | 'blocked' | 'supported'>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gets the support state of requested session mode.
|
|
3
|
+
* @param mode Session mode: 'inline' | 'immersive-vr' | 'immersive-ar'
|
|
4
|
+
* @returns The current support state
|
|
5
|
+
*/
|
|
6
|
+
export const getXRSupportState = async (mode) => {
|
|
7
|
+
if (navigator?.xr === undefined) {
|
|
8
|
+
return 'unsupported';
|
|
9
|
+
}
|
|
10
|
+
if (location.protocol !== 'https:') {
|
|
11
|
+
return 'insecure';
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
const supported = await navigator.xr.isSessionSupported(mode);
|
|
15
|
+
return supported ? 'supported' : 'unsupported';
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
return error.name === 'SecurityError' ? 'blocked' : 'unsupported';
|
|
19
|
+
}
|
|
20
|
+
};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const handJoints: readonly ["wrist", "thumb-metacarpal", "thumb-phalanx-proximal", "thumb-phalanx-distal", "thumb-tip", "index-finger-metacarpal", "index-finger-phalanx-proximal", "index-finger-phalanx-intermediate", "index-finger-phalanx-distal", "index-finger-tip", "middle-finger-metacarpal", "middle-finger-phalanx-proximal", "middle-finger-phalanx-intermediate", "middle-finger-phalanx-distal", "middle-finger-tip", "ring-finger-metacarpal", "ring-finger-phalanx-proximal", "ring-finger-phalanx-intermediate", "ring-finger-phalanx-distal", "ring-finger-tip", "pinky-finger-metacarpal", "pinky-finger-phalanx-proximal", "pinky-finger-phalanx-intermediate", "pinky-finger-phalanx-distal", "pinky-finger-tip"];
|
|
2
|
+
export type HandJoints = (typeof handJoints)[number];
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export const handJoints = [
|
|
2
|
+
'wrist',
|
|
3
|
+
'thumb-metacarpal',
|
|
4
|
+
'thumb-phalanx-proximal',
|
|
5
|
+
'thumb-phalanx-distal',
|
|
6
|
+
'thumb-tip',
|
|
7
|
+
'index-finger-metacarpal',
|
|
8
|
+
'index-finger-phalanx-proximal',
|
|
9
|
+
'index-finger-phalanx-intermediate',
|
|
10
|
+
'index-finger-phalanx-distal',
|
|
11
|
+
'index-finger-tip',
|
|
12
|
+
'middle-finger-metacarpal',
|
|
13
|
+
'middle-finger-phalanx-proximal',
|
|
14
|
+
'middle-finger-phalanx-intermediate',
|
|
15
|
+
'middle-finger-phalanx-distal',
|
|
16
|
+
'middle-finger-tip',
|
|
17
|
+
'ring-finger-metacarpal',
|
|
18
|
+
'ring-finger-phalanx-proximal',
|
|
19
|
+
'ring-finger-phalanx-intermediate',
|
|
20
|
+
'ring-finger-phalanx-distal',
|
|
21
|
+
'ring-finger-tip',
|
|
22
|
+
'pinky-finger-metacarpal',
|
|
23
|
+
'pinky-finger-phalanx-proximal',
|
|
24
|
+
'pinky-finger-phalanx-intermediate',
|
|
25
|
+
'pinky-finger-phalanx-distal',
|
|
26
|
+
'pinky-finger-tip'
|
|
27
|
+
];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/// <reference types="webxr" />
|
|
2
|
+
/**
|
|
3
|
+
* Starts / ends an XR session.
|
|
4
|
+
*
|
|
5
|
+
* @param sessionMode an XR session mode: 'inline' | 'immersive-vr' | 'immersive-ar'
|
|
6
|
+
* @param sessionInit an XRSessionInit object
|
|
7
|
+
* @param force Whether this button should only enter / exit an `XRSession`. Default is to toggle both ways
|
|
8
|
+
* @returns
|
|
9
|
+
*/
|
|
10
|
+
export declare const toggleXRSession: (sessionMode: XRSessionMode, sessionInit?: (XRSessionInit & {
|
|
11
|
+
domOverlay?: {
|
|
12
|
+
root: HTMLElement;
|
|
13
|
+
} | undefined;
|
|
14
|
+
}) | undefined, force?: 'enter' | 'exit') => Promise<XRSession | undefined>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { session, referenceSpaceType, xr } from '../internal/stores';
|
|
2
|
+
import { getXRSessionOptions } from './getXRSessionOptions';
|
|
3
|
+
/**
|
|
4
|
+
* Starts / ends an XR session.
|
|
5
|
+
*
|
|
6
|
+
* @param sessionMode an XR session mode: 'inline' | 'immersive-vr' | 'immersive-ar'
|
|
7
|
+
* @param sessionInit an XRSessionInit object
|
|
8
|
+
* @param force Whether this button should only enter / exit an `XRSession`. Default is to toggle both ways
|
|
9
|
+
* @returns
|
|
10
|
+
*/
|
|
11
|
+
export const toggleXRSession = async (sessionMode, sessionInit, force) => {
|
|
12
|
+
const currentSession = session.current;
|
|
13
|
+
const hasSession = currentSession !== undefined;
|
|
14
|
+
if (force === 'enter' && hasSession)
|
|
15
|
+
return currentSession;
|
|
16
|
+
if (force === 'exit' && !hasSession)
|
|
17
|
+
return;
|
|
18
|
+
// Exit a session if entered
|
|
19
|
+
if (hasSession) {
|
|
20
|
+
await currentSession.end();
|
|
21
|
+
session.set(undefined);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
// Otherwise enter a session
|
|
25
|
+
const options = getXRSessionOptions(referenceSpaceType.current, sessionInit);
|
|
26
|
+
const nextSession = await navigator.xr.requestSession(sessionMode, options);
|
|
27
|
+
if (xr.current === undefined) {
|
|
28
|
+
throw new Error('An <XR> component was not created when attempting to toggle a session.');
|
|
29
|
+
}
|
|
30
|
+
await xr.current.setSession(nextSession);
|
|
31
|
+
session.set(nextSession);
|
|
32
|
+
return nextSession;
|
|
33
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const teleportPlugin: (navMeshes: THREE.Mesh[]) => void;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { injectPlugin } from '@threlte/core';
|
|
2
|
+
export const teleportPlugin = (navMeshes) => injectPlugin('teleport-controls', ({ ref, props }) => {
|
|
3
|
+
let currentRef = ref;
|
|
4
|
+
let currentProps = props;
|
|
5
|
+
if (!currentRef.isMesh)
|
|
6
|
+
return;
|
|
7
|
+
if (!('teleportSurface' in props))
|
|
8
|
+
return;
|
|
9
|
+
const addSurface = (mesh) => {
|
|
10
|
+
const registered = navMeshes.some((navMesh) => navMesh.uuid === mesh.uuid);
|
|
11
|
+
if (!registered) {
|
|
12
|
+
navMeshes.push(mesh);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
const removeSurface = (mesh) => {
|
|
16
|
+
const index = navMeshes.indexOf(mesh);
|
|
17
|
+
if (index > -1) {
|
|
18
|
+
navMeshes.splice(index, 1);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
return {
|
|
22
|
+
pluginProps: ['teleportSurface'],
|
|
23
|
+
onRefChange(ref) {
|
|
24
|
+
removeSurface(currentRef);
|
|
25
|
+
currentRef = ref;
|
|
26
|
+
if (currentProps.teleportSurface) {
|
|
27
|
+
addSurface(currentRef);
|
|
28
|
+
}
|
|
29
|
+
return () => removeSurface(currentRef);
|
|
30
|
+
},
|
|
31
|
+
onPropsChange(props) {
|
|
32
|
+
currentProps = props;
|
|
33
|
+
if (currentProps.teleportSurface) {
|
|
34
|
+
addSurface(currentRef);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
removeSurface(currentRef);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
});
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/// <reference types="webxr" />
|
|
2
|
+
import type { XRControllerModel } from 'three/examples/jsm/webxr/XRControllerModelFactory';
|
|
3
|
+
import type { XRHandModel } from 'three/examples/jsm/webxr/XRHandModelFactory';
|
|
4
|
+
export type XRSessionEventType = 'sessionstart' | 'sessionend' | 'visibilitychange' | 'frameratechange';
|
|
5
|
+
export type XRControllerEventType = 'select' | 'selectstart' | 'selectend' | 'squeeze' | 'squeezeend' | 'squeezestart' | 'disconnected' | 'connected';
|
|
6
|
+
export type XRHandEventType = 'pinchstart' | 'pinchend' | 'connected' | 'disconnected';
|
|
7
|
+
export type XRSessionEvent<Type = XRSessionEventType> = THREE.Event & {
|
|
8
|
+
type: Type;
|
|
9
|
+
target: XRSession;
|
|
10
|
+
};
|
|
11
|
+
export type XRControllerEvent<Type = XRControllerEventType> = THREE.Event & {
|
|
12
|
+
type: Type;
|
|
13
|
+
target: any;
|
|
14
|
+
data?: XRInputSource;
|
|
15
|
+
};
|
|
16
|
+
export type XRController = {
|
|
17
|
+
controller: THREE.XRTargetRaySpace;
|
|
18
|
+
grip: THREE.XRGripSpace;
|
|
19
|
+
model?: XRControllerModel;
|
|
20
|
+
inputSource: XRInputSource;
|
|
21
|
+
};
|
|
22
|
+
export type XRHand = {
|
|
23
|
+
hand: THREE.XRHandSpace;
|
|
24
|
+
model?: XRHandModel;
|
|
25
|
+
inputSource: globalThis.XRHand;
|
|
26
|
+
};
|
|
27
|
+
export type XRHandEvent<Type = XRHandEventType, Target = null | THREE.XRHandSpace> = THREE.Event & {
|
|
28
|
+
type: Type;
|
|
29
|
+
target: Target;
|
|
30
|
+
};
|
|
31
|
+
export type HitTestCallback = (hitMatrix: THREE.Matrix4, hit: XRHitTestResult) => void;
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@threlte/xr",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"author": "Micheal Parks <michealparks1989@gmail.com> (https://parks.lol)",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"devDependencies": {
|
|
7
|
+
"@sveltejs/adapter-auto": "^2.1.0",
|
|
8
|
+
"@sveltejs/kit": "^1.22.3",
|
|
9
|
+
"@sveltejs/package": "^2.2.0",
|
|
10
|
+
"@types/three": "^0.155.1",
|
|
11
|
+
"autoprefixer": "^10.4.14",
|
|
12
|
+
"postcss": "^8.4.27",
|
|
13
|
+
"publint": "^0.1.16",
|
|
14
|
+
"svelte": "^4.1.1",
|
|
15
|
+
"svelte-check": "^3.4.6",
|
|
16
|
+
"three": "^0.154.0",
|
|
17
|
+
"tslib": "^2.6.0",
|
|
18
|
+
"typescript": "^5.1.6",
|
|
19
|
+
"vite": "^4.4.6",
|
|
20
|
+
"vite-plugin-mkcert": "^1.16.0",
|
|
21
|
+
"@threlte/core": "6.0.7"
|
|
22
|
+
},
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"svelte": ">=4",
|
|
25
|
+
"three": ">=0.133"
|
|
26
|
+
},
|
|
27
|
+
"type": "module",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"svelte": "./dist/index.js"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"svelte": "./dist/index.js",
|
|
36
|
+
"files": [
|
|
37
|
+
"dist",
|
|
38
|
+
"!dist/**/*.test.*",
|
|
39
|
+
"!dist/**/*.spec.*"
|
|
40
|
+
],
|
|
41
|
+
"scripts": {
|
|
42
|
+
"dev": "vite dev --host",
|
|
43
|
+
"package": "svelte-kit sync && svelte-package && node ./scripts/cleanupPackage.js && publint",
|
|
44
|
+
"check": "svelte-check --tsconfig ./tsconfig.json",
|
|
45
|
+
"check:watch": "svelte-check --tsconfig ./tsconfig.json --watch",
|
|
46
|
+
"lint": "prettier --check --plugin-search-dir=. . && eslint .",
|
|
47
|
+
"format": "prettier --write --plugin-search-dir=. .",
|
|
48
|
+
"cleanup": "rimraf node_modules .svelte-kit dist"
|
|
49
|
+
}
|
|
50
|
+
}
|