@threlte/xr 0.0.2 → 0.0.4

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/README.md CHANGED
@@ -1,13 +1,64 @@
1
- <h1>threlte-xr</h1>
1
+ <div align="right">
2
+ <a href="https://www.npmjs.com/package/@threlte/xr">
3
+ <img alt="npm" src="https://img.shields.io/npm/v/@threlte/xr?color=fe4100&labelColor=171d27&logo=npm&logoColor=white"/>
4
+ </a>
5
+ <a href="https://github.com/threlte/threlte/blob/main/LICENSE.md">
6
+ <img alt="license" src="https://img.shields.io/npm/l/@threlte/core?color=fe4100&labelColor=171d27&logo=git&logoColor=white"/>
7
+ </a>
8
+ <a href="https://discord.com/channels/985983540804091964">
9
+ <img alt="discord" src="https://img.shields.io/discord/985983540804091964?label=discord&color=fe4100&labelColor=171d27&logo=discord&logoColor=white"/>
10
+ </a>
11
+ <a href="https://threlte.xyz">
12
+ <img alt="docs" src="https://img.shields.io/website?down_color=red&down_message=offline&label=docs&color=fe4100&labelColor=171d27&up_message=online&url=https%3A%2F%2Fthrelte.xyz&logo=svelte&logoColor=white"/>
13
+ </a>
14
+ </div>
2
15
 
3
- [![Version](https://img.shields.io/npm/v/threlte-xr?style=flat&colorA=000000&colorB=000000)](https://www.npmjs.com/package/threlte-xr)
4
- [![Downloads](https://img.shields.io/npm/dt/threlte-xr.svg?style=flat&colorA=000000&colorB=000000)](https://www.npmjs.com/package/threlte-xr)
16
+ <a href="https://threlte.xyz">
17
+ <img src="https://threlte.xyz/logo/threlte-banner.jpg"/>
18
+ </a>
5
19
 
6
- Svelte components and hooks for creating VR/AR applications with [threlte (v6)](https://next.threlte.xyz/), inspired by the design of [react-xr](https://github.com/pmndrs/react-xr).
20
+ ## Rapidly Build Interactive 3D Apps for the Web
7
21
 
8
- > **Warning**
9
- > `threlte-xr` is early in development. There will likely be frequent breaking changes until it reaches 0.1.0. It will only work with Threlte v6 onward.
22
+ Threlte is a [Svelte](https://svelte.dev/) library that simplifies creating 3D apps for the web. It provides a **declarative**, **type-safe**, **reactive** and **interactive** API out-of-the-box.
10
23
 
11
- ```bash
12
- npm install threlte-xr
24
+ Threlte's **3D rendering** is powered by [Three.js](https://threejs.org/), and it also provides a **physics engine** through [Rapier](https://rapier.rs/) and an **animation studio** via [Theatre.js](https://www.theatrejs.com/); see [packages](#packages) for details.
25
+
26
+ Check out our **[documentation](https://threlte.xyz)** and our **[Discord community](https://discord.gg/EqUBCfCaGm)**.
27
+
28
+ ## @threlte/xr
29
+
30
+ [@threlte/xr](https://threlte.xyz/docs/reference/xr/getting-started) provides a set of components and hooks that make it easy to create XR experiences.
31
+
32
+ ## Quickstart
33
+
34
+ ### Installation
35
+
36
+ For a quick interactive setup of a fresh Threlte project, run:
37
+
38
+ ```sh
39
+ npm create threlte my-project
13
40
  ```
41
+ and select the `@threlte/xr` option.
42
+
43
+ Alternatively you can check out the full [installation instructions](https://threlte.xyz/docs/learn/getting-started/installation).
44
+
45
+ ### Support
46
+
47
+ Have questions? Feel free to ask in our [Discord support forum](https://discord.com/channels/985983540804091964/1031843197963477002).
48
+
49
+ ## Contributing
50
+
51
+ Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.
52
+
53
+ - **Filing Issues** - if you have feature requestions or you think you spotted a bug, [submit an issue](https://github.com/threlte/threlte/issues/new).
54
+ - **Contributing Code** - if you would like to drop us a PR, read the [contribution guide](https://github.com/threlte/threlte/blob/main/CONTRIBUTING.md) first.
55
+
56
+ ## Sponsors
57
+
58
+ [![Powered by Vercel](./assets/vercel/powered-by-vercel.svg)](https://vercel.com/?utm_source=threlte&utm_campaign=oss)
59
+
60
+ ---
61
+
62
+ ### License
63
+
64
+ The MIT License (MIT). Please see the [License File](LICENSE.md) for more information.
@@ -1,6 +1,5 @@
1
1
  <!--
2
- @component
3
- `<Controller />` represents a THREE.XRTargetRaySpace, a THREE.XRGripSpace, and a controller model.
2
+ @component `<Controller />` represents a THREE.XRTargetRaySpace, a THREE.XRGripSpace, and a controller model.
4
3
  -->
5
4
  <script
6
5
 
@@ -8,7 +7,7 @@
8
7
  >import { T, createRawEventDispatcher, useThrelte } from "@threlte/core";
9
8
  import { onDestroy } from "svelte";
10
9
  import { XRControllerModelFactory } from "three/examples/jsm/webxr/XRControllerModelFactory";
11
- import ShortRay from "../components/ShortRay.svelte";
10
+ import ShortRay from "./ShortRay.svelte";
12
11
  import { gaze, left as leftStore, right as rightStore } from "../hooks/useController";
13
12
  import { useControllerEvent } from "../hooks/useEvent";
14
13
  import { fire } from "../internal/events";
@@ -50,9 +49,10 @@ const dispatch = createRawEventDispatcher();
50
49
  const { xr } = useThrelte().renderer;
51
50
  const handleConnected = (event) => {
52
51
  const data = event.data;
53
- if (data.handedness !== handedness)
52
+ const targetData = eventMap.get(event.target);
53
+ if (data.handedness !== handedness || !targetData)
54
54
  return;
55
- stores[data.handedness].set({ ...eventMap.get(event.target), inputSource: data });
55
+ stores[data.handedness].set({ ...targetData, inputSource: data });
56
56
  fire("connected", event, { input: "controller" });
57
57
  };
58
58
  const handleDisconnected = (event) => {
@@ -63,20 +63,20 @@ const handleDisconnected = (event) => {
63
63
  };
64
64
  const handleEvent = (event) => fire(event.type, event);
65
65
  for (const index of [0, 1]) {
66
- const controller2 = xr.getController(index);
66
+ const controller = xr.getController(index);
67
67
  const grip2 = xr.getControllerGrip(index);
68
68
  const model2 = factory.createControllerModel(grip2);
69
- eventMap.set(controller2, { controller: controller2, model: model2, grip: grip2 });
70
- controller2.addEventListener("connected", handleConnected);
71
- controller2.addEventListener("disconnected", handleDisconnected);
72
- events.forEach((name) => controller2.addEventListener(name, handleEvent));
69
+ eventMap.set(controller, { targetRay: controller, model: model2, grip: grip2 });
70
+ controller.addEventListener("connected", handleConnected);
71
+ controller.addEventListener("disconnected", handleDisconnected);
72
+ events.forEach((name) => controller.addEventListener(name, handleEvent));
73
73
  }
74
74
  $:
75
75
  store = left ? stores.left : stores.right;
76
76
  $:
77
77
  grip = $store?.grip;
78
78
  $:
79
- controller = $store?.controller;
79
+ targetRay = $store?.targetRay;
80
80
  $:
81
81
  model = $store?.model;
82
82
  for (const type of ["connected", "disconnected", ...events]) {
@@ -86,10 +86,10 @@ for (const type of ["connected", "disconnected", ...events]) {
86
86
  }
87
87
  onDestroy(() => {
88
88
  for (const index of [0, 1]) {
89
- const controller2 = xr.getController(index);
90
- controller2.removeEventListener("connected", handleConnected);
91
- controller2.removeEventListener("disconnected", handleDisconnected);
92
- events.forEach((name) => controller2.removeEventListener(name, handleEvent));
89
+ const controller = xr.getController(index);
90
+ controller.removeEventListener("connected", handleConnected);
91
+ controller.removeEventListener("disconnected", handleDisconnected);
92
+ events.forEach((name) => controller.removeEventListener(name, handleEvent));
93
93
  }
94
94
  });
95
95
  </script>
@@ -108,15 +108,15 @@ onDestroy(() => {
108
108
  </T>
109
109
  {/if}
110
110
 
111
- {#if controller}
111
+ {#if targetRay}
112
112
  <T
113
- is={controller}
113
+ is={targetRay}
114
114
  name="XR controller {handedness}"
115
115
  visible={!$isHandTracking}
116
116
  >
117
117
  <slot name="target-ray" />
118
118
  <ShortRay
119
- visible={$activeTeleportController === controller &&
119
+ visible={$activeTeleportController === targetRay &&
120
120
  $pendingTeleportDestination === undefined}
121
121
  />
122
122
  </T>
@@ -2,8 +2,9 @@ import { SvelteComponent } from "svelte";
2
2
  import type { XRControllerEvent } from '../types';
3
3
  declare const __propDef: {
4
4
  props: {
5
- /** Whether the controller should be matched with the left hand. */ left?: boolean;
6
- /** Whether the controller should be matched with the right hand. */ right?: boolean;
5
+ left: true;
6
+ } | {
7
+ right: true;
7
8
  };
8
9
  slots: {
9
10
  default: {};
@@ -0,0 +1,28 @@
1
+ <script>import { T, HierarchicalObject, useFrame, useThrelte } from "@threlte/core";
2
+ import { Group } from "three";
3
+ import { useBaseReferenceSpace } from "../internal/useBaseReferenceSpace";
4
+ const { renderer, scene } = useThrelte();
5
+ const group = new Group();
6
+ const baseReferenceSpace = useBaseReferenceSpace();
7
+ useFrame(() => {
8
+ const space = baseReferenceSpace.current;
9
+ if (space === null)
10
+ return;
11
+ const frame = renderer.xr.getFrame();
12
+ const pose = frame.getViewerPose(space);
13
+ if (pose === void 0)
14
+ return;
15
+ const { position, orientation } = pose.transform;
16
+ group.position.set(position.x, position.y, position.z);
17
+ group.quaternion.set(orientation.x, orientation.y, orientation.z, orientation.w);
18
+ });
19
+ </script>
20
+
21
+ <HierarchicalObject
22
+ onChildMount={(child) => { scene.add(child) }}
23
+ onChildDestroy={(child) => { scene.remove(child) }}
24
+ >
25
+ <T is={group}>
26
+ <slot />
27
+ </T>
28
+ </HierarchicalObject>
@@ -0,0 +1,16 @@
1
+ import { SvelteComponent } from "svelte";
2
+ declare const __propDef: {
3
+ props: Record<string, never>;
4
+ events: {
5
+ [evt: string]: CustomEvent<any>;
6
+ };
7
+ slots: {
8
+ default: {};
9
+ };
10
+ };
11
+ export type HeadsetProps = typeof __propDef.props;
12
+ export type HeadsetEvents = typeof __propDef.events;
13
+ export type HeadsetSlots = typeof __propDef.slots;
14
+ export default class Headset extends SvelteComponent<HeadsetProps, HeadsetEvents, HeadsetSlots> {
15
+ }
16
+ export {};
@@ -1,6 +1,6 @@
1
1
  <script>import { T } from "@threlte/core";
2
2
  const vertexShader = `
3
- uniform mat4 modelViewMatrix;
3
+ uniform mat4 modelViewMatrix;
4
4
  uniform mat4 projectionMatrix;
5
5
  attribute vec2 uv;
6
6
  attribute vec3 position;
@@ -24,5 +24,9 @@ const fragmentShader = `
24
24
  {@const heightSegments = 1}
25
25
  {@const openEnded = false}
26
26
  <T.CylinderGeometry args={[radius, radius, height, radialSegments, heightSegments, openEnded]} />
27
- <T.RawShaderMaterial transparent {vertexShader} {fragmentShader} />
27
+ <T.RawShaderMaterial
28
+ transparent
29
+ {vertexShader}
30
+ {fragmentShader}
31
+ />
28
32
  </T.Mesh>
@@ -97,7 +97,7 @@ const handleSelectEnd = () => {
97
97
  useFrame(() => {
98
98
  const selecting = (teleportGamepad.current?.axes[3] ?? 0) < -0.9;
99
99
  if (selecting && activeController === void 0) {
100
- handleSelectStart(teleportController.current.controller);
100
+ handleSelectStart(teleportController.current.targetRay);
101
101
  } else if (!selecting && activeController !== void 0) {
102
102
  handleSelectEnd();
103
103
  }
@@ -133,4 +133,4 @@ teleportPlugin(navMeshes);
133
133
  polygonOffsetFactor={-1}
134
134
  />
135
135
  </T.Mesh>
136
- </slot>
136
+ </slot>
@@ -1,7 +1,6 @@
1
1
  <!--
2
2
 
3
- @component
4
- `<XR />` is a WebXR manager that configures your scene for XR rendering and interaction.
3
+ @component `<XR />` is a WebXR manager that configures your scene for XR rendering and interaction.
5
4
 
6
5
  This should be placed within a Threlte `<Canvas />`.
7
6
 
@@ -18,10 +17,16 @@ This should be placed within a Threlte `<Canvas />`.
18
17
  ```
19
18
 
20
19
  -->
21
-
22
20
  <script>import { onDestroy } from "svelte";
23
- import { useThrelte, createRawEventDispatcher } from "@threlte/core";
24
- import { session, referenceSpaceType, isPresenting, isHandTracking, initialized, xr as xrStore } from "../internal/stores";
21
+ import { createRawEventDispatcher, useThrelte } from "@threlte/core";
22
+ import {
23
+ initialized,
24
+ isHandTracking,
25
+ isPresenting,
26
+ referenceSpaceType,
27
+ session,
28
+ xr as xrStore
29
+ } from "../internal/stores";
25
30
  export let foveation = 1;
26
31
  export let frameRate = void 0;
27
32
  export let referenceSpace = "local-floor";
@@ -111,4 +116,6 @@ $:
111
116
 
112
117
  {#if $isPresenting}
113
118
  <slot />
114
- {/if}
119
+ {:else}
120
+ <slot name="fallback" />
121
+ {/if}
@@ -22,6 +22,7 @@ declare const __propDef: {
22
22
  };
23
23
  slots: {
24
24
  default: {};
25
+ fallback: {};
25
26
  };
26
27
  events: {
27
28
  /** Called as an XRSession is requested */
@@ -1,5 +1,5 @@
1
1
  /// <reference types="webxr" />
2
- import type { XRControllerEventType, XRHandEventType, XRControllerEvent, XRHandEvent } from '../types';
2
+ import type { XRControllerEvent, XRControllerEventType, XRHandEvent, XRHandEventType } from '../types';
3
3
  /**
4
4
  * Adds listeners for controller events.
5
5
  */
@@ -1,6 +1,5 @@
1
1
  import { onDestroy } from 'svelte';
2
- import { isHandTracking } from '../internal/stores';
3
- import { on, off } from '../internal/events';
2
+ import { off, on } from '../internal/events';
4
3
  /**
5
4
  * Adds listeners for controller events.
6
5
  */
@@ -1,7 +1,6 @@
1
1
  import * as THREE from 'three';
2
2
  import { useThrelte } from '@threlte/core';
3
- import { session } from '../internal/stores';
4
- import { onDestroy } from 'svelte';
3
+ import { useBaseReferenceSpace } from '../internal/useBaseReferenceSpace';
5
4
  const quaternion = new THREE.Quaternion();
6
5
  const offset = { x: 0, y: 0, z: 0 };
7
6
  /**
@@ -17,18 +16,13 @@ const offset = { x: 0, y: 0, z: 0 };
17
16
  */
18
17
  export const useTeleport = () => {
19
18
  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);
19
+ const baseReferenceSpace = useBaseReferenceSpace();
27
20
  /**
28
21
  * Teleports a player to a position - and optionally a direction.
29
22
  */
30
23
  return (position, direction = quaternion) => {
31
- if (baseReferenceSpace === null || baseReferenceSpace === undefined)
24
+ const space = baseReferenceSpace.current;
25
+ if (space === null || space === undefined)
32
26
  return;
33
27
  let x = 0, y = 0, z = 0;
34
28
  if (Array.isArray(position)) {
@@ -43,12 +37,12 @@ export const useTeleport = () => {
43
37
  offset.x = -x;
44
38
  offset.y = -y;
45
39
  offset.z = -z;
46
- const pose = xr.getFrame().getViewerPose(baseReferenceSpace);
40
+ const pose = xr.getFrame().getViewerPose(space);
47
41
  if (pose !== undefined) {
48
42
  offset.x += pose.transform.position.x;
49
43
  offset.z += pose.transform.position.z;
50
44
  }
51
45
  const teleportOffset = new XRRigidTransform(offset, direction);
52
- xr.setReferenceSpace(baseReferenceSpace.getOffsetReferenceSpace(teleportOffset));
46
+ xr.setReferenceSpace(space.getOffsetReferenceSpace(teleportOffset));
53
47
  };
54
48
  };
package/dist/index.d.ts CHANGED
@@ -3,6 +3,7 @@ export { default as VRButton } from './components/VRButton.svelte';
3
3
  export { default as XRButton } from './components/XRButton.svelte';
4
4
  export { default as Controller } from './components/Controller.svelte';
5
5
  export { default as Hand } from './components/Hand.svelte';
6
+ export { default as Headset } from './components/Headset.svelte';
6
7
  export { default as TeleportControls } from './components/TeleportControls.svelte';
7
8
  export { default as XR } from './components/XR.svelte';
8
9
  export { getXRSupportState } from './lib/getXRSupportState';
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ export { default as VRButton } from './components/VRButton.svelte';
3
3
  export { default as XRButton } from './components/XRButton.svelte';
4
4
  export { default as Controller } from './components/Controller.svelte';
5
5
  export { default as Hand } from './components/Hand.svelte';
6
+ export { default as Headset } from './components/Headset.svelte';
6
7
  export { default as TeleportControls } from './components/TeleportControls.svelte';
7
8
  export { default as XR } from './components/XR.svelte';
8
9
  export { getXRSupportState } from './lib/getXRSupportState';
@@ -0,0 +1,6 @@
1
+ /// <reference types="webxr" />
2
+ /**
3
+ * Provides a reference space of the world origin,
4
+ * useful for determining offsets using XRReferenceSpace
5
+ */
6
+ export declare const useBaseReferenceSpace: () => import("@threlte/core").CurrentWritable<XRReferenceSpace | null>;
@@ -0,0 +1,19 @@
1
+ import { onDestroy } from 'svelte';
2
+ import { useThrelte, currentWritable } from '@threlte/core';
3
+ import { session } from './stores';
4
+ /**
5
+ * Provides a reference space of the world origin,
6
+ * useful for determining offsets using XRReferenceSpace
7
+ */
8
+ export const useBaseReferenceSpace = () => {
9
+ const { xr } = useThrelte().renderer;
10
+ const baseReferenceSpace = currentWritable(null);
11
+ onDestroy(session.subscribe((value) => {
12
+ if (value === undefined) {
13
+ baseReferenceSpace.set(null);
14
+ return;
15
+ }
16
+ baseReferenceSpace.set(xr.getReferenceSpace());
17
+ }));
18
+ return baseReferenceSpace;
19
+ };
package/dist/types.d.ts CHANGED
@@ -14,7 +14,7 @@ export type XRControllerEvent<Type = XRControllerEventType> = THREE.Event & {
14
14
  data?: XRInputSource;
15
15
  };
16
16
  export type XRController = {
17
- controller: THREE.XRTargetRaySpace;
17
+ targetRay: THREE.XRTargetRaySpace;
18
18
  grip: THREE.XRGripSpace;
19
19
  model?: XRControllerModel;
20
20
  inputSource: XRInputSource;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@threlte/xr",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "author": "Micheal Parks <michealparks1989@gmail.com> (https://parks.lol)",
5
5
  "license": "MIT",
6
6
  "devDependencies": {
@@ -18,7 +18,7 @@
18
18
  "typescript": "^5.1.6",
19
19
  "vite": "^4.4.6",
20
20
  "vite-plugin-mkcert": "^1.16.0",
21
- "@threlte/core": "6.0.7"
21
+ "@threlte/core": "6.0.8"
22
22
  },
23
23
  "peerDependencies": {
24
24
  "svelte": ">=4",