@threlte/xr 1.0.0-next.0 → 1.0.0-next.10

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.
Files changed (50) hide show
  1. package/dist/components/ARButton.svelte +9 -7
  2. package/dist/components/ARButton.svelte.d.ts +10 -2
  3. package/dist/components/Controller.svelte +56 -42
  4. package/dist/components/Controller.svelte.d.ts +13 -19
  5. package/dist/components/Hand.svelte +38 -16
  6. package/dist/components/Hand.svelte.d.ts +10 -19
  7. package/dist/components/Headset.svelte +1 -1
  8. package/dist/components/Headset.svelte.d.ts +3 -1
  9. package/dist/components/VRButton.svelte +5 -4
  10. package/dist/components/VRButton.svelte.d.ts +10 -2
  11. package/dist/components/XR.svelte +19 -14
  12. package/dist/components/XR.svelte.d.ts +15 -25
  13. package/dist/components/XRButton.svelte +19 -8
  14. package/dist/components/XRButton.svelte.d.ts +11 -2
  15. package/dist/components/internal/Cursor.svelte +13 -12
  16. package/dist/components/internal/PointerCursor.svelte +10 -9
  17. package/dist/components/internal/PointerCursor.svelte.d.ts +3 -3
  18. package/dist/components/internal/ScenePortal.svelte +15 -5
  19. package/dist/components/internal/ScenePortal.svelte.d.ts +3 -1
  20. package/dist/components/internal/ShortRay.svelte +29 -28
  21. package/dist/components/internal/ShortRay.svelte.d.ts +3 -3
  22. package/dist/components/internal/TeleportCursor.svelte +9 -7
  23. package/dist/components/internal/TeleportCursor.svelte.d.ts +3 -3
  24. package/dist/components/internal/TeleportRay.svelte +9 -8
  25. package/dist/components/internal/TeleportRay.svelte.d.ts +3 -3
  26. package/dist/internal/defaultFeatures.d.ts +4 -0
  27. package/dist/internal/defaultFeatures.js +14 -0
  28. package/dist/internal/setupControllers.js +1 -1
  29. package/dist/internal/setupHands.js +1 -1
  30. package/dist/internal/setupHeadset.js +14 -5
  31. package/dist/internal/setupRaf.js +8 -12
  32. package/dist/internal/stores.d.ts +5 -4
  33. package/dist/plugins/pointerControls/context.d.ts +12 -0
  34. package/dist/plugins/pointerControls/hook.d.ts +1 -1
  35. package/dist/plugins/pointerControls/hook.js +6 -5
  36. package/dist/plugins/pointerControls/plugin.js +9 -11
  37. package/dist/plugins/pointerControls/setup.js +35 -34
  38. package/dist/plugins/pointerControls/types.d.ts +10 -10
  39. package/dist/plugins/pointerControls/types.js +9 -9
  40. package/dist/plugins/teleportControls/context.d.ts +11 -5
  41. package/dist/plugins/teleportControls/context.js +45 -2
  42. package/dist/plugins/teleportControls/index.js +4 -13
  43. package/dist/plugins/teleportControls/plugin.js +11 -11
  44. package/dist/plugins/teleportControls/setup.js +1 -1
  45. package/dist/types.d.ts +20 -2
  46. package/package.json +34 -14
  47. package/dist/plugins/pointerControls/useComponentEventHandlers.d.ts +0 -4
  48. package/dist/plugins/pointerControls/useComponentEventHandlers.js +0 -13
  49. package/dist/plugins/teleportControls/hook.d.ts +0 -7
  50. package/dist/plugins/teleportControls/hook.js +0 -40
@@ -1,6 +1,7 @@
1
1
  /// <reference types="webxr" />
2
2
  import { SvelteComponent } from "svelte";
3
3
  import type { HTMLButtonAttributes } from 'svelte/elements';
4
+ import type { Snippet } from 'svelte';
4
5
  declare const __propDef: {
5
6
  props: HTMLButtonAttributes & {
6
7
  /** The type of `XRSession` to create */
@@ -18,6 +19,14 @@ declare const __propDef: {
18
19
  force?: "enter" | "exit" | undefined;
19
20
  /** Whether to apply automatic styling to the button. Set false to apply custom styles. Default is true. */
20
21
  styled?: boolean | undefined;
22
+ children?: Snippet<[{
23
+ state: "blocked" | "unsupported" | "insecure" | "supported";
24
+ }]> | undefined;
25
+ onclick?: ((event: {
26
+ state: "blocked" | "unsupported" | "insecure" | "supported";
27
+ nativeEvent: MouseEvent;
28
+ }) => void) | undefined;
29
+ onerror?: ((error: Error) => void) | undefined;
21
30
  };
22
31
  events: {
23
32
  [evt: string]: CustomEvent<any>;
@@ -40,8 +49,8 @@ export type XrButtonSlots = typeof __propDef.slots;
40
49
  * }}
41
50
  * force={'enter' | 'exit' | undefined}
42
51
  * styled={'true' | 'false'}
43
- * on:error={(event) => {}}
44
- * on:click={(event) => {}}
52
+ * onerror={(event) => {}}
53
+ * onclick={(event) => {}}
45
54
  * />
46
55
  * ```
47
56
  */
@@ -1,8 +1,6 @@
1
- <script>import { Color, DoubleSide, RawShaderMaterial } from "three";
1
+ <script lang="ts">import { Color, DoubleSide, RawShaderMaterial } from "three";
2
2
  import { T } from "@threlte/core";
3
- export let color = new Color("white");
4
- export let size = 0.03;
5
- export let thickness = 0.035;
3
+ let { color = new Color("white"), size = 0.03, thickness = 0.035 } = $props();
6
4
  const vertexShader = `
7
5
  uniform mat4 projectionMatrix;
8
6
  uniform mat4 modelViewMatrix;
@@ -26,21 +24,24 @@ const fragmentShader = `
26
24
  gl_FragColor = vec4(color, alpha);
27
25
  }
28
26
  `;
27
+ const uniforms = {
28
+ thickness: { value: thickness },
29
+ color: { value: color }
30
+ };
29
31
  const shaderMaterial = new RawShaderMaterial({
30
32
  vertexShader,
31
33
  fragmentShader,
32
- uniforms: {
33
- thickness: { value: thickness },
34
- color: { value: color }
35
- },
34
+ uniforms,
36
35
  side: DoubleSide,
37
36
  transparent: true,
38
37
  depthTest: false
39
38
  });
40
- $:
41
- shaderMaterial.uniforms.thickness.value = thickness;
42
- $:
43
- shaderMaterial.uniforms.color.value = color;
39
+ $effect.pre(() => {
40
+ uniforms.thickness.value = thickness;
41
+ });
42
+ $effect.pre(() => {
43
+ uniforms.color.value = color;
44
+ });
44
45
  </script>
45
46
 
46
47
  <T.Mesh scale={size}>
@@ -1,16 +1,14 @@
1
- <script>import { Group, Vector3, Matrix3 } from "three";
1
+ <script lang="ts">import { Group, Vector3, Matrix3 } from "three";
2
2
  import { T, useTask } from "@threlte/core";
3
3
  import { pointerIntersection, pointerState } from "../../internal/stores";
4
4
  import Cursor from "./Cursor.svelte";
5
- export let handedness;
5
+ let { handedness, children } = $props();
6
6
  const ref = new Group();
7
7
  const vec3 = new Vector3();
8
8
  const normalMatrix = new Matrix3();
9
9
  const worldNormal = new Vector3();
10
- $:
11
- hovering = $pointerState[handedness].hovering;
12
- $:
13
- intersection = pointerIntersection[handedness];
10
+ let hovering = $derived($pointerState[handedness].hovering);
11
+ let intersection = $derived(pointerIntersection[handedness]);
14
12
  const { start, stop } = useTask(
15
13
  () => {
16
14
  if (intersection.current === void 0)
@@ -27,20 +25,23 @@ const { start, stop } = useTask(
27
25
  autoStart: false
28
26
  }
29
27
  );
30
- $:
28
+ $effect.pre(() => {
31
29
  if (hovering) {
32
30
  ref.position.copy(intersection.current.point);
33
31
  start();
34
32
  } else {
35
33
  stop();
36
34
  }
35
+ });
37
36
  </script>
38
37
 
39
38
  <T
40
39
  is={ref}
41
40
  visible={hovering}
42
41
  >
43
- <slot>
42
+ {#if children}
43
+ {@render children()}
44
+ {:else}
44
45
  <Cursor />
45
- </slot>
46
+ {/if}
46
47
  </T>
@@ -1,14 +1,14 @@
1
1
  import { SvelteComponent } from "svelte";
2
+ import type { Snippet } from 'svelte';
2
3
  declare const __propDef: {
3
4
  props: {
4
5
  handedness: 'left' | 'right';
6
+ children?: Snippet<[]> | undefined;
5
7
  };
6
8
  events: {
7
9
  [evt: string]: CustomEvent<any>;
8
10
  };
9
- slots: {
10
- default: {};
11
- };
11
+ slots: {};
12
12
  };
13
13
  export type PointerCursorProps = typeof __propDef.props;
14
14
  export type PointerCursorEvents = typeof __propDef.events;
@@ -1,10 +1,20 @@
1
- <script>import { useThrelte, HierarchicalObject } from "@threlte/core";
1
+ <script lang="ts">import { T, useThrelte } from "@threlte/core";
2
+ import { Object3D } from "three";
2
3
  const { scene } = useThrelte();
4
+ const proxy = new Object3D();
5
+ proxy.add = (child) => {
6
+ scene.add(child);
7
+ return child;
8
+ };
9
+ proxy.remove = (child) => {
10
+ scene.remove(child);
11
+ return child;
12
+ };
3
13
  </script>
4
14
 
5
- <HierarchicalObject
6
- onChildMount={(child) => scene.add(child)}
7
- onChildDestroy={(child) => scene.remove(child)}
15
+ <T
16
+ is={proxy}
17
+ attach={false}
8
18
  >
9
19
  <slot />
10
- </HierarchicalObject>
20
+ </T>
@@ -1,6 +1,8 @@
1
1
  import { SvelteComponent } from "svelte";
2
2
  declare const __propDef: {
3
- props: Record<string, never>;
3
+ props: {
4
+ children?: import("svelte").Snippet<[]> | undefined;
5
+ };
4
6
  events: {
5
7
  [evt: string]: CustomEvent<any>;
6
8
  };
@@ -1,16 +1,33 @@
1
- <script>import { T } from "@threlte/core";
1
+ <script lang="ts">import { T } from "@threlte/core";
2
2
  import { pointerState, teleportState, teleportIntersection } from "../../internal/stores";
3
- export let handedness;
4
- $:
5
- hovering = $teleportState[handedness].hovering;
6
- $:
7
- intersection = teleportIntersection[handedness];
8
- $:
9
- visible = $pointerState[handedness].enabled || hovering && $intersection === void 0;
3
+ let { handedness, children } = $props();
4
+ let hovering = $derived($teleportState[handedness].hovering);
5
+ let intersection = $derived(teleportIntersection[handedness]);
6
+ let visible = $derived(
7
+ $pointerState[handedness].enabled || hovering && $intersection === void 0
8
+ );
9
+ const vertexShader = `
10
+ uniform mat4 modelViewMatrix;
11
+ uniform mat4 projectionMatrix;
12
+ attribute vec2 uv;
13
+ attribute vec3 position;
14
+ varying vec2 vUv;
15
+ void main() {
16
+ vUv = uv;
17
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
18
+ }`;
19
+ const fragmentShader = `
20
+ precision mediump float;
21
+ varying vec2 vUv;
22
+ void main() {
23
+ gl_FragColor = vec4(1.0, 1.0, 1.0, pow(vUv.y - 1.0, 2.0));
24
+ }`;
10
25
  </script>
11
26
 
12
27
  <T.Group {visible}>
13
- <slot>
28
+ {#if children}
29
+ {@render children()}
30
+ {:else}
14
31
  <T.Mesh
15
32
  rotation.x={-Math.PI / 2}
16
33
  position.z={-0.1}
@@ -18,25 +35,9 @@ $:
18
35
  <T.CylinderGeometry args={[0.002, 0.002, 0.2, 16, 1, false]} />
19
36
  <T.RawShaderMaterial
20
37
  transparent
21
- vertexShader={`
22
- uniform mat4 modelViewMatrix;
23
- uniform mat4 projectionMatrix;
24
- attribute vec2 uv;
25
- attribute vec3 position;
26
- varying vec2 vUv;
27
- void main() {
28
- vUv = uv;
29
- gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
30
- }
31
- `}
32
- fragmentShader={`
33
- precision mediump float;
34
- varying vec2 vUv;
35
- void main() {
36
- gl_FragColor = vec4(1.0, 1.0, 1.0, pow(vUv.y - 1.0, 2.0));
37
- }
38
- `}
38
+ {vertexShader}
39
+ {fragmentShader}
39
40
  />
40
41
  </T.Mesh>
41
- </slot>
42
+ {/if}
42
43
  </T.Group>
@@ -1,14 +1,14 @@
1
1
  import { SvelteComponent } from "svelte";
2
+ import type { Snippet } from 'svelte';
2
3
  declare const __propDef: {
3
4
  props: {
4
5
  handedness: 'left' | 'right';
6
+ children?: Snippet<[]> | undefined;
5
7
  };
6
8
  events: {
7
9
  [evt: string]: CustomEvent<any>;
8
10
  };
9
- slots: {
10
- default: {};
11
- };
11
+ slots: {};
12
12
  };
13
13
  export type ShortRayProps = typeof __propDef.props;
14
14
  export type ShortRayEvents = typeof __propDef.events;
@@ -1,15 +1,14 @@
1
- <script>import { spring } from "svelte/motion";
1
+ <script lang="ts">import { spring } from "svelte/motion";
2
2
  import { Group, Matrix3, Vector3 } from "three";
3
3
  import { T, useTask } from "@threlte/core";
4
4
  import { teleportIntersection } from "../../internal/stores";
5
5
  import Cursor from "./Cursor.svelte";
6
- export let handedness;
6
+ let { handedness, children } = $props();
7
7
  const ref = new Group();
8
8
  const vec3 = new Vector3();
9
9
  const normalMatrix = new Matrix3();
10
10
  const worldNormal = new Vector3();
11
- $:
12
- intersection = teleportIntersection[handedness];
11
+ let intersection = $derived(teleportIntersection[handedness]);
13
12
  const { start, stop } = useTask(
14
13
  () => {
15
14
  if (intersection.current === void 0)
@@ -27,7 +26,7 @@ const { start, stop } = useTask(
27
26
  }
28
27
  );
29
28
  const size = spring(0.1, { stiffness: 0.2 });
30
- $:
29
+ $effect.pre(() => {
31
30
  if ($intersection === void 0) {
32
31
  size.set(0.1);
33
32
  stop();
@@ -36,16 +35,19 @@ $:
36
35
  ref.position.copy($intersection.point);
37
36
  start();
38
37
  }
38
+ });
39
39
  </script>
40
40
 
41
41
  <T
42
42
  is={ref}
43
43
  visible={$intersection !== undefined}
44
44
  >
45
- <slot>
45
+ {#if children}
46
+ {@render children()}
47
+ {:else}
46
48
  <Cursor
47
49
  size={$size}
48
50
  thickness={0.015}
49
51
  />
50
- </slot>
52
+ {/if}
51
53
  </T>
@@ -1,14 +1,14 @@
1
1
  import { SvelteComponent } from "svelte";
2
+ import type { Snippet } from 'svelte';
2
3
  declare const __propDef: {
3
4
  props: {
4
5
  handedness: 'left' | 'right';
6
+ children?: Snippet<[]> | undefined;
5
7
  };
6
8
  events: {
7
9
  [evt: string]: CustomEvent<any>;
8
10
  };
9
- slots: {
10
- default: {};
11
- };
11
+ slots: {};
12
12
  };
13
13
  export type TeleportCursorProps = typeof __propDef.props;
14
14
  export type TeleportCursorEvents = typeof __propDef.events;
@@ -1,11 +1,10 @@
1
- <script>import { Vector3, QuadraticBezierCurve3, Vector2 } from "three";
1
+ <script lang="ts">import { Vector3, QuadraticBezierCurve3, Vector2 } from "three";
2
2
  import { Line2 } from "three/examples/jsm/lines/Line2.js";
3
3
  import { LineGeometry } from "three/examples/jsm/lines/LineGeometry.js";
4
4
  import { LineMaterial } from "three/examples/jsm/lines/LineMaterial.js";
5
5
  import { T, useTask } from "@threlte/core";
6
6
  import { teleportIntersection } from "../../internal/stores";
7
- export let handedness;
8
- export let targetRay;
7
+ let { handedness, targetRay, children } = $props();
9
8
  let lineGeometry = new LineGeometry();
10
9
  const rayStart = new Vector3();
11
10
  const rayMidpoint = new Vector3();
@@ -15,8 +14,7 @@ const positions = new Float32Array(rayDivisions * 3);
15
14
  const vec3 = new Vector3();
16
15
  const v2_1 = new Vector2();
17
16
  const v2_2 = new Vector2();
18
- $:
19
- intersection = teleportIntersection[handedness];
17
+ let intersection = $derived(teleportIntersection[handedness]);
20
18
  const setCurvePoints = (alpha = 0.3) => {
21
19
  if (intersection.current === void 0)
22
20
  return;
@@ -47,16 +45,19 @@ const { start, stop } = useTask(
47
45
  },
48
46
  { autoStart: false }
49
47
  );
50
- $:
48
+ $effect.pre(() => {
51
49
  if ($intersection === void 0) {
52
50
  stop();
53
51
  } else {
54
52
  setCurvePoints(1);
55
53
  start();
56
54
  }
55
+ });
57
56
  </script>
58
57
 
59
- <slot>
58
+ {#if children}
59
+ {@render children()}
60
+ {:else}
60
61
  <T
61
62
  is={Line2}
62
63
  visible={$intersection !== undefined}
@@ -68,4 +69,4 @@ $:
68
69
  linewidth={0.004}
69
70
  />
70
71
  </T>
71
- </slot>
72
+ {/if}
@@ -1,16 +1,16 @@
1
1
  import { SvelteComponent } from "svelte";
2
2
  import { type XRTargetRaySpace } from 'three';
3
+ import type { Snippet } from 'svelte';
3
4
  declare const __propDef: {
4
5
  props: {
5
6
  handedness: 'left' | 'right';
6
7
  targetRay: XRTargetRaySpace;
8
+ children?: Snippet<[]> | undefined;
7
9
  };
8
10
  events: {
9
11
  [evt: string]: CustomEvent<any>;
10
12
  };
11
- slots: {
12
- default: {};
13
- };
13
+ slots: {};
14
14
  };
15
15
  export type TeleportRayProps = typeof __propDef.props;
16
16
  export type TeleportRayEvents = typeof __propDef.events;
@@ -0,0 +1,4 @@
1
+ export declare const defaultFeatures: {
2
+ requiredFeatures: never[];
3
+ optionalFeatures: string[];
4
+ };
@@ -0,0 +1,14 @@
1
+ export const defaultFeatures = {
2
+ requiredFeatures: [],
3
+ optionalFeatures: [
4
+ 'local-floor',
5
+ 'bounded-floor',
6
+ 'anchors',
7
+ 'hand-tracking',
8
+ 'plane-detection',
9
+ 'layers',
10
+ 'depth-sorted-layers',
11
+ 'hit-test',
12
+ 'mesh-detection'
13
+ ]
14
+ };
@@ -23,7 +23,7 @@ export const setupControllers = () => {
23
23
  if (hasHands())
24
24
  return;
25
25
  const { data } = event;
26
- controllerEvents[data.handedness]?.[event.type]?.(event);
26
+ controllerEvents[data.handedness]?.current?.[`on${event.type}`]?.(event);
27
27
  };
28
28
  function handleConnected(event) {
29
29
  const { model, targetRay, grip } = indexMap.get(this);
@@ -24,7 +24,7 @@ export const setupHands = () => {
24
24
  return;
25
25
  const handEvent = event;
26
26
  const handedness = 'handedness' in handEvent ? handEvent.handedness : handEvent.data.handedness;
27
- handEvents[handedness]?.[event.type]?.(event);
27
+ handEvents[handedness]?.current?.[`on${event.type}`]?.(event);
28
28
  };
29
29
  function handleConnected(event) {
30
30
  const hand = this;
@@ -3,8 +3,9 @@ import { useThrelte, useTask, watch } from '@threlte/core';
3
3
  import { useXR } from '../hooks/useXR';
4
4
  export const headset = new Group();
5
5
  export const setupHeadset = () => {
6
- const { renderer, camera } = useThrelte();
6
+ const { renderer, camera, scheduler, renderStage } = useThrelte();
7
7
  const { xr } = renderer;
8
+ const stage = scheduler.createStage(Symbol('xr-headset-stage'), { before: renderStage });
8
9
  const immersiveFrame = useTask(() => {
9
10
  const space = xr.getReferenceSpace();
10
11
  if (space === null)
@@ -17,13 +18,21 @@ export const setupHeadset = () => {
17
18
  const { position, orientation } = pose.transform;
18
19
  headset.position.set(position.x, position.y, position.z);
19
20
  headset.quaternion.set(orientation.x, orientation.y, orientation.z, orientation.w);
20
- }, { autoStart: false, autoInvalidate: false });
21
+ }, {
22
+ autoStart: false,
23
+ autoInvalidate: false,
24
+ stage
25
+ });
21
26
  const nonImmersiveFrame = useTask(() => {
22
27
  headset.position.copy(camera.current.position);
23
28
  headset.quaternion.copy(camera.current.quaternion);
24
- }, { autoStart: false, autoInvalidate: false });
25
- watch(useXR().isPresenting, (isPresenting) => {
26
- if (isPresenting) {
29
+ }, {
30
+ autoStart: false,
31
+ autoInvalidate: false,
32
+ stage
33
+ });
34
+ watch(useXR().isPresenting, ($isPresenting) => {
35
+ if ($isPresenting) {
27
36
  immersiveFrame.start();
28
37
  nonImmersiveFrame.stop();
29
38
  }
@@ -1,21 +1,17 @@
1
- // @ts-expect-error svelte/internal is untyped.
2
- // import { set_raf } from 'svelte/internal'
3
1
  import { onDestroy } from 'svelte';
2
+ // @ts-expect-error untyped internal import, when have you ever done me wrong?
3
+ import { raf } from 'svelte/internal/client';
4
4
  import { watch } from '@threlte/core';
5
5
  import { session } from './stores';
6
6
  export const setupRaf = () => {
7
7
  if (typeof window === 'undefined')
8
8
  return;
9
- const browserRaf = (fn) => requestAnimationFrame(fn);
10
- const currentRaf = { fn: browserRaf };
11
- // set_raf((fn: FrameRequestCallback) => currentRaf.fn(fn))
9
+ const originalTick = raf.tick;
12
10
  watch(session, (session) => {
13
- if (session) {
14
- currentRaf.fn = (fn) => session.requestAnimationFrame(fn);
15
- }
16
- else {
17
- currentRaf.fn = browserRaf;
18
- }
11
+ raf.tick =
12
+ session === undefined
13
+ ? originalTick
14
+ : (fn) => session.requestAnimationFrame(fn);
19
15
  });
20
- onDestroy(() => (currentRaf.fn = browserRaf));
16
+ onDestroy(() => (raf.tick = originalTick));
21
17
  };
@@ -1,17 +1,18 @@
1
1
  /// <reference types="webxr" />
2
2
  import type { WebXRManager, Intersection } from 'three';
3
+ import type { XRControllerEvents, XRHandEvents } from '../types';
3
4
  export declare const isPresenting: import("@threlte/core").CurrentWritable<boolean>;
4
5
  export declare const isHandTracking: import("@threlte/core").CurrentWritable<boolean>;
5
6
  export declare const session: import("@threlte/core").CurrentWritable<XRSession | undefined>;
6
7
  export declare const referenceSpaceType: import("@threlte/core").CurrentWritable<XRReferenceSpaceType | undefined>;
7
8
  export declare const xr: import("@threlte/core").CurrentWritable<WebXRManager | undefined>;
8
9
  export declare const controllerEvents: {
9
- left: import("@threlte/core").CurrentWritable<Record<string, (arg: unknown) => void> | undefined>;
10
- right: import("@threlte/core").CurrentWritable<Record<string, (arg: unknown) => void> | undefined>;
10
+ left: import("@threlte/core").CurrentWritable<XRControllerEvents | undefined>;
11
+ right: import("@threlte/core").CurrentWritable<XRControllerEvents | undefined>;
11
12
  };
12
13
  export declare const handEvents: {
13
- left: import("@threlte/core").CurrentWritable<Record<string, (arg: unknown) => void> | undefined>;
14
- right: import("@threlte/core").CurrentWritable<Record<string, (arg: unknown) => void> | undefined>;
14
+ left: import("@threlte/core").CurrentWritable<XRHandEvents | undefined>;
15
+ right: import("@threlte/core").CurrentWritable<XRHandEvents | undefined>;
15
16
  };
16
17
  export declare const teleportState: import("@threlte/core").CurrentWritable<{
17
18
  left: {
@@ -0,0 +1,12 @@
1
+ import type { Object3D } from 'three';
2
+ import type { ControlsContext, HandContext } from './types';
3
+ export declare const getHandContext: (hand: 'left' | 'right') => HandContext;
4
+ export declare const setHandContext: (hand: 'left' | 'right', context: HandContext) => void;
5
+ export declare const getControlsContext: () => ControlsContext;
6
+ export declare const setControlsContext: (context: ControlsContext) => void;
7
+ interface InternalContext {
8
+ dispatchers: WeakMap<Object3D, Record<string, (arg: unknown) => void>>;
9
+ }
10
+ export declare const getInternalContext: () => InternalContext;
11
+ export declare const setInternalContext: () => void;
12
+ export {};
@@ -1,5 +1,5 @@
1
1
  import type { Object3D } from 'three';
2
2
  export declare const usePointerControls: () => {
3
- addInteractiveObject: (object: Object3D) => void;
3
+ addInteractiveObject: (object: Object3D, events: Record<string, (arg: unknown) => void>) => void;
4
4
  removeInteractiveObject: (object: Object3D) => void;
5
5
  };
@@ -1,15 +1,16 @@
1
- import { createRawEventDispatcher } from '@threlte/core';
2
1
  import { getControlsContext, getInternalContext } from './context';
3
2
  export const usePointerControls = () => {
4
- const { dispatchers } = getInternalContext();
5
3
  const context = getControlsContext();
6
- const eventDispatcher = createRawEventDispatcher();
7
- const addInteractiveObject = (object) => {
4
+ const { dispatchers } = getInternalContext();
5
+ if (!context) {
6
+ throw new Error('No pointer controls context found. Did you forget to implement pointerControls()?');
7
+ }
8
+ const addInteractiveObject = (object, events) => {
8
9
  // check if the object is already in the list
9
10
  if (context.interactiveObjects.indexOf(object) > -1) {
10
11
  return;
11
12
  }
12
- dispatchers.set(object, eventDispatcher);
13
+ dispatchers.set(object, events);
13
14
  context.interactiveObjects.push(object);
14
15
  };
15
16
  const removeInteractiveObject = (object) => {
@@ -1,23 +1,21 @@
1
1
  import { injectPlugin, watch } from '@threlte/core';
2
2
  import { writable } from 'svelte/store';
3
3
  import { usePointerControls } from './hook';
4
- import { useComponentHasEventHandlers } from './useComponentEventHandlers';
4
+ import { events } from './types';
5
5
  export const injectPointerControlsPlugin = () => {
6
- injectPlugin('threlte-pointer-controls', ({ ref }) => {
7
- if (ref.isMesh !== true)
6
+ injectPlugin('threlte-pointer-controls', ({ ref, props }) => {
7
+ if (!ref.isObject3D)
8
8
  return;
9
9
  const { addInteractiveObject, removeInteractiveObject } = usePointerControls();
10
10
  const refStore = writable(ref);
11
- const { hasEventHandlers } = useComponentHasEventHandlers();
12
- watch([hasEventHandlers, refStore], ([hasEventHandlers, ref]) => {
13
- // Because hasEventHandlers will only be set from false to true in the
14
- // lifecycle of the component, we can safely assume that we do not need to
15
- // remove the object from the list of interactive objects when
16
- // hasEventHandlers is false.
11
+ watch(refStore, ($refStore) => {
12
+ const hasEventHandlers = Object.entries(props).some(([key, value]) => {
13
+ return value !== undefined && events.includes(key);
14
+ });
17
15
  if (!hasEventHandlers)
18
16
  return;
19
- addInteractiveObject(ref);
20
- return () => removeInteractiveObject(ref);
17
+ addInteractiveObject($refStore, props);
18
+ return () => removeInteractiveObject($refStore);
21
19
  });
22
20
  return {
23
21
  onRefChange(ref) {