@threlte/xr 1.0.0-next.2 → 1.0.0-next.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.
Files changed (41) hide show
  1. package/dist/components/ARButton.svelte +8 -6
  2. package/dist/components/ARButton.svelte.d.ts +12 -2
  3. package/dist/components/Controller.svelte +55 -41
  4. package/dist/components/Controller.svelte.d.ts +26 -19
  5. package/dist/components/Hand.svelte +37 -15
  6. package/dist/components/Hand.svelte.d.ts +15 -19
  7. package/dist/components/VRButton.svelte +4 -3
  8. package/dist/components/VRButton.svelte.d.ts +12 -2
  9. package/dist/components/XR.svelte +17 -12
  10. package/dist/components/XR.svelte.d.ts +18 -25
  11. package/dist/components/XRButton.svelte +9 -7
  12. package/dist/components/XRButton.svelte.d.ts +12 -2
  13. package/dist/components/internal/Cursor.svelte.d.ts +3 -3
  14. package/dist/components/internal/PointerCursor.svelte +5 -3
  15. package/dist/components/internal/PointerCursor.svelte.d.ts +1 -4
  16. package/dist/components/internal/ShortRay.svelte +23 -21
  17. package/dist/components/internal/ShortRay.svelte.d.ts +1 -4
  18. package/dist/components/internal/TeleportCursor.svelte +5 -3
  19. package/dist/components/internal/TeleportCursor.svelte.d.ts +1 -4
  20. package/dist/components/internal/TeleportRay.svelte +5 -3
  21. package/dist/components/internal/TeleportRay.svelte.d.ts +1 -4
  22. package/dist/internal/defaultFeatures.d.ts +4 -0
  23. package/dist/internal/defaultFeatures.js +14 -0
  24. package/dist/internal/setupControllers.js +1 -1
  25. package/dist/internal/setupHands.js +1 -1
  26. package/dist/internal/setupHeadset.js +14 -5
  27. package/dist/internal/setupRaf.js +8 -12
  28. package/dist/internal/stores.d.ts +5 -4
  29. package/dist/plugins/pointerControls/plugin.js +6 -2
  30. package/dist/plugins/pointerControls/setup.js +28 -27
  31. package/dist/plugins/pointerControls/types.d.ts +10 -10
  32. package/dist/plugins/pointerControls/types.js +9 -9
  33. package/dist/plugins/teleportControls/context.d.ts +9 -3
  34. package/dist/plugins/teleportControls/context.js +45 -2
  35. package/dist/plugins/teleportControls/index.js +4 -13
  36. package/dist/plugins/teleportControls/plugin.js +2 -2
  37. package/dist/plugins/teleportControls/setup.js +1 -1
  38. package/dist/types.d.ts +20 -2
  39. package/package.json +3 -3
  40. package/dist/plugins/teleportControls/hook.d.ts +0 -7
  41. package/dist/plugins/teleportControls/hook.js +0 -38
@@ -2,7 +2,7 @@
2
2
  import { T, useTask } from "@threlte/core";
3
3
  import { pointerIntersection, pointerState } from "../../internal/stores";
4
4
  import Cursor from "./Cursor.svelte";
5
- let { handedness } = $props();
5
+ let { handedness, children } = $props();
6
6
  const ref = new Group();
7
7
  const vec3 = new Vector3();
8
8
  const normalMatrix = new Matrix3();
@@ -39,7 +39,9 @@ $effect.pre(() => {
39
39
  is={ref}
40
40
  visible={hovering}
41
41
  >
42
- <slot>
42
+ {#if children}
43
+ {@render children()}
44
+ {:else}
43
45
  <Cursor />
44
- </slot>
46
+ {/if}
45
47
  </T>
@@ -2,7 +2,6 @@ import { SvelteComponent } from "svelte";
2
2
  declare const __propDef: {
3
3
  props: {
4
4
  handedness: 'left' | 'right';
5
- } & {
6
5
  children?: ((this: void) => typeof import("svelte").SnippetReturn & {
7
6
  _: "functions passed to {@render ...} tags must use the `Snippet` type imported from \"svelte\"";
8
7
  }) | undefined;
@@ -10,9 +9,7 @@ declare const __propDef: {
10
9
  events: {
11
10
  [evt: string]: CustomEvent<any>;
12
11
  };
13
- slots: {
14
- default: {};
15
- };
12
+ slots: {};
16
13
  };
17
14
  export type PointerCursorProps = typeof __propDef.props;
18
15
  export type PointerCursorEvents = typeof __propDef.events;
@@ -1,15 +1,33 @@
1
1
  <script lang="ts">import { T } from "@threlte/core";
2
2
  import { pointerState, teleportState, teleportIntersection } from "../../internal/stores";
3
- let { handedness } = $props();
3
+ let { handedness, children } = $props();
4
4
  let hovering = $derived($teleportState[handedness].hovering);
5
5
  let intersection = $derived(teleportIntersection[handedness]);
6
6
  let visible = $derived(
7
7
  $pointerState[handedness].enabled || hovering && $intersection === void 0
8
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
+ }`;
9
25
  </script>
10
26
 
11
27
  <T.Group {visible}>
12
- <slot>
28
+ {#if children}
29
+ {@render children()}
30
+ {:else}
13
31
  <T.Mesh
14
32
  rotation.x={-Math.PI / 2}
15
33
  position.z={-0.1}
@@ -17,25 +35,9 @@ let visible = $derived(
17
35
  <T.CylinderGeometry args={[0.002, 0.002, 0.2, 16, 1, false]} />
18
36
  <T.RawShaderMaterial
19
37
  transparent
20
- vertexShader={`
21
- uniform mat4 modelViewMatrix;
22
- uniform mat4 projectionMatrix;
23
- attribute vec2 uv;
24
- attribute vec3 position;
25
- varying vec2 vUv;
26
- void main() {
27
- vUv = uv;
28
- gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
29
- }
30
- `}
31
- fragmentShader={`
32
- precision mediump float;
33
- varying vec2 vUv;
34
- void main() {
35
- gl_FragColor = vec4(1.0, 1.0, 1.0, pow(vUv.y - 1.0, 2.0));
36
- }
37
- `}
38
+ {vertexShader}
39
+ {fragmentShader}
38
40
  />
39
41
  </T.Mesh>
40
- </slot>
42
+ {/if}
41
43
  </T.Group>
@@ -2,7 +2,6 @@ import { SvelteComponent } from "svelte";
2
2
  declare const __propDef: {
3
3
  props: {
4
4
  handedness: 'left' | 'right';
5
- } & {
6
5
  children?: ((this: void) => typeof import("svelte").SnippetReturn & {
7
6
  _: "functions passed to {@render ...} tags must use the `Snippet` type imported from \"svelte\"";
8
7
  }) | undefined;
@@ -10,9 +9,7 @@ declare const __propDef: {
10
9
  events: {
11
10
  [evt: string]: CustomEvent<any>;
12
11
  };
13
- slots: {
14
- default: {};
15
- };
12
+ slots: {};
16
13
  };
17
14
  export type ShortRayProps = typeof __propDef.props;
18
15
  export type ShortRayEvents = typeof __propDef.events;
@@ -3,7 +3,7 @@ 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
- let { handedness } = $props();
6
+ let { handedness, children } = $props();
7
7
  const ref = new Group();
8
8
  const vec3 = new Vector3();
9
9
  const normalMatrix = new Matrix3();
@@ -42,10 +42,12 @@ $effect.pre(() => {
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>
@@ -2,7 +2,6 @@ import { SvelteComponent } from "svelte";
2
2
  declare const __propDef: {
3
3
  props: {
4
4
  handedness: 'left' | 'right';
5
- } & {
6
5
  children?: ((this: void) => typeof import("svelte").SnippetReturn & {
7
6
  _: "functions passed to {@render ...} tags must use the `Snippet` type imported from \"svelte\"";
8
7
  }) | undefined;
@@ -10,9 +9,7 @@ declare const __propDef: {
10
9
  events: {
11
10
  [evt: string]: CustomEvent<any>;
12
11
  };
13
- slots: {
14
- default: {};
15
- };
12
+ slots: {};
16
13
  };
17
14
  export type TeleportCursorProps = typeof __propDef.props;
18
15
  export type TeleportCursorEvents = typeof __propDef.events;
@@ -4,7 +4,7 @@ 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
- let { handedness, targetRay } = $props();
7
+ let { handedness, targetRay, children } = $props();
8
8
  let lineGeometry = new LineGeometry();
9
9
  const rayStart = new Vector3();
10
10
  const rayMidpoint = new Vector3();
@@ -55,7 +55,9 @@ $effect.pre(() => {
55
55
  });
56
56
  </script>
57
57
 
58
- <slot>
58
+ {#if children}
59
+ {@render children()}
60
+ {:else}
59
61
  <T
60
62
  is={Line2}
61
63
  visible={$intersection !== undefined}
@@ -67,4 +69,4 @@ $effect.pre(() => {
67
69
  linewidth={0.004}
68
70
  />
69
71
  </T>
70
- </slot>
72
+ {/if}
@@ -4,7 +4,6 @@ declare const __propDef: {
4
4
  props: {
5
5
  handedness: 'left' | 'right';
6
6
  targetRay: XRTargetRaySpace;
7
- } & {
8
7
  children?: ((this: void) => typeof import("svelte").SnippetReturn & {
9
8
  _: "functions passed to {@render ...} tags must use the `Snippet` type imported from \"svelte\"";
10
9
  }) | undefined;
@@ -12,9 +11,7 @@ declare const __propDef: {
12
11
  events: {
13
12
  [evt: string]: CustomEvent<any>;
14
13
  };
15
- slots: {
16
- default: {};
17
- };
14
+ slots: {};
18
15
  };
19
16
  export type TeleportRayProps = typeof __propDef.props;
20
17
  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: {
@@ -1,6 +1,7 @@
1
1
  import { injectPlugin, watch } from '@threlte/core';
2
2
  import { writable } from 'svelte/store';
3
3
  import { usePointerControls } from './hook';
4
+ import { events } from './types';
4
5
  export const injectPointerControlsPlugin = () => {
5
6
  injectPlugin('threlte-pointer-controls', ({ ref, props }) => {
6
7
  if (!ref.isObject3D)
@@ -8,9 +9,12 @@ export const injectPointerControlsPlugin = () => {
8
9
  const { addInteractiveObject, removeInteractiveObject } = usePointerControls();
9
10
  const refStore = writable(ref);
10
11
  watch(refStore, ($refStore) => {
11
- if (props.$$events === undefined)
12
+ const hasEventHandlers = Object.entries(props).some(([key, value]) => {
13
+ return value !== undefined && events.includes(key);
14
+ });
15
+ if (!hasEventHandlers)
12
16
  return;
13
- addInteractiveObject($refStore, props.$$events);
17
+ addInteractiveObject($refStore, props);
14
18
  return () => removeInteractiveObject($refStore);
15
19
  });
16
20
  return {
@@ -24,10 +24,10 @@ export const setupPointerControls = (context, handContext, fixedStep = 1 / 40) =
24
24
  return;
25
25
  handContext.initialClick = [hit.point.x, hit.point.y, hit.point.z];
26
26
  handContext.initialHits = hits.map((hit) => hit.eventObject);
27
- handleEvent('pointerdown', event);
27
+ handleEvent('onpointerdown', event);
28
28
  };
29
29
  const handlePointerUp = (event) => {
30
- handleEvent('pointerup', event);
30
+ handleEvent('onpointerup', event);
31
31
  };
32
32
  const handleClick = (event) => {
33
33
  // If a click yields no results, pass it back to the user as a miss
@@ -35,7 +35,7 @@ export const setupPointerControls = (context, handContext, fixedStep = 1 / 40) =
35
35
  if (hits.length === 0) {
36
36
  pointerMissed(context.interactiveObjects, event);
37
37
  }
38
- handleEvent('click', event);
38
+ handleEvent('onclick', event);
39
39
  };
40
40
  function cancelPointer(intersections) {
41
41
  for (const [, hoveredObj] of handContext.hovered) {
@@ -51,8 +51,8 @@ export const setupPointerControls = (context, handContext, fixedStep = 1 / 40) =
51
51
  if (events !== undefined) {
52
52
  // Clear out intersects, they are outdated by now
53
53
  const data = { ...hoveredObj, intersections };
54
- events.pointerout?.(data);
55
- events.pointerleave?.(data);
54
+ events.onpointerout?.(data);
55
+ events.onpointerleave?.(data);
56
56
  // Deal with cancelation
57
57
  handContext.pointerOverTarget.set(false);
58
58
  cancelPointer([]);
@@ -88,8 +88,8 @@ export const setupPointerControls = (context, handContext, fixedStep = 1 / 40) =
88
88
  return getHits();
89
89
  }
90
90
  const handleEvent = (name, event) => {
91
- const isPointerMove = name === 'pointermove';
92
- const isClickEvent = name === 'click' || name === 'contextmenu';
91
+ const isPointerMove = name === 'onpointermove';
92
+ const isClickEvent = name === 'onclick' || name === 'oncontextmenu';
93
93
  // Take care of unhover
94
94
  if (isPointerMove)
95
95
  cancelPointer(hits);
@@ -121,17 +121,17 @@ export const setupPointerControls = (context, handContext, fixedStep = 1 / 40) =
121
121
  if (isPointerMove) {
122
122
  // Move event ...
123
123
  handContext.pointer.update((value) => value.copy(intersectionEvent.point));
124
- if (events.pointerover ||
125
- events.pointerenter ||
126
- events.pointerout ||
127
- events.pointerleave) {
124
+ if (events.onpointerover ||
125
+ events.onpointerenter ||
126
+ events.onpointerout ||
127
+ events.onpointerleave) {
128
128
  const id = getIntersectionId(intersectionEvent);
129
129
  const hoveredItem = handContext.hovered.get(id);
130
130
  if (hoveredItem === undefined) {
131
131
  // If the object wasn't previously hovered, book it and call its handler
132
132
  handContext.hovered.set(id, intersectionEvent);
133
- events.pointerover?.(intersectionEvent);
134
- events.pointerenter?.(intersectionEvent);
133
+ events.onpointerover?.(intersectionEvent);
134
+ events.onpointerenter?.(intersectionEvent);
135
135
  handContext.pointerOverTarget.set(true);
136
136
  }
137
137
  else if (hoveredItem.stopped) {
@@ -140,10 +140,10 @@ export const setupPointerControls = (context, handContext, fixedStep = 1 / 40) =
140
140
  }
141
141
  }
142
142
  // Call pointer move
143
- events.pointermove?.(intersectionEvent);
143
+ events.onpointermove?.(intersectionEvent);
144
144
  }
145
145
  else if ((!isClickEvent || handContext.initialHits.includes(hit.eventObject)) &&
146
- events[name]) {
146
+ events[name] !== undefined) {
147
147
  // Missed events have to come first
148
148
  pointerMissed(context.interactiveObjects.filter((object) => !handContext.initialHits.includes(object)), event);
149
149
  // Call the event
@@ -162,7 +162,7 @@ export const setupPointerControls = (context, handContext, fixedStep = 1 / 40) =
162
162
  if (targetRay === undefined)
163
163
  return;
164
164
  if (targetRay.position.distanceTo(lastPosition) > EPSILON) {
165
- handleEvent('pointermove');
165
+ handleEvent('onpointermove');
166
166
  }
167
167
  lastPosition.copy(targetRay.position);
168
168
  }, {
@@ -181,17 +181,18 @@ export const setupPointerControls = (context, handContext, fixedStep = 1 / 40) =
181
181
  input.targetRay.removeEventListener('select', handleClick);
182
182
  };
183
183
  });
184
- // watch(hand, (input) => {
185
- // if (input === undefined) return
186
- // input.hand.addEventListener('pinchstart', handlePointerDown)
187
- // input.hand.addEventListener('pinchend', handlePointerUp)
188
- // input.hand.addEventListener('pinchend', handleClick)
189
- // return () => {
190
- // input.hand.removeEventListener('pinchstart', handlePointerDown)
191
- // input.hand.removeEventListener('pinchend', handlePointerUp)
192
- // input.hand.removeEventListener('pinchend', handleClick)
193
- // }
194
- // })
184
+ watch(hand, (input) => {
185
+ if (input === undefined)
186
+ return;
187
+ input.hand.addEventListener('pinchstart', handlePointerDown);
188
+ input.hand.addEventListener('pinchend', handlePointerUp);
189
+ input.hand.addEventListener('pinchend', handleClick);
190
+ return () => {
191
+ input.hand.removeEventListener('pinchstart', handlePointerDown);
192
+ input.hand.removeEventListener('pinchend', handlePointerUp);
193
+ input.hand.removeEventListener('pinchend', handleClick);
194
+ };
195
+ });
195
196
  watch([useXR().isPresenting, handContext.enabled], ([isPresenting, enabled]) => {
196
197
  if (isPresenting && enabled) {
197
198
  start();
@@ -48,15 +48,15 @@ export interface PointerCaptureTarget {
48
48
  target: Element;
49
49
  }
50
50
  export type ThrelteXREvents = {
51
- click: IntersectionEvent;
52
- contextmenu: IntersectionEvent;
53
- pointerup: IntersectionEvent;
54
- pointerdown: IntersectionEvent;
55
- pointerover: IntersectionEvent;
56
- pointerout: IntersectionEvent;
57
- pointerenter: IntersectionEvent;
58
- pointerleave: IntersectionEvent;
59
- pointermove: IntersectionEvent;
60
- pointermissed: IntersectionEvent;
51
+ onclick: IntersectionEvent;
52
+ oncontextmenu: IntersectionEvent;
53
+ onpointerup: IntersectionEvent;
54
+ onpointerdown: IntersectionEvent;
55
+ onpointerover: IntersectionEvent;
56
+ onpointerout: IntersectionEvent;
57
+ onpointerenter: IntersectionEvent;
58
+ onpointerleave: IntersectionEvent;
59
+ onpointermove: IntersectionEvent;
60
+ onpointermissed: IntersectionEvent;
61
61
  };
62
62
  export declare const events: (keyof ThrelteXREvents)[];
@@ -1,11 +1,11 @@
1
1
  export const events = [
2
- 'click',
3
- 'contextmenu',
4
- 'pointerup',
5
- 'pointerdown',
6
- 'pointerover',
7
- 'pointerout',
8
- 'pointerenter',
9
- 'pointerleave',
10
- 'pointermove'
2
+ 'onclick',
3
+ 'oncontextmenu',
4
+ 'onpointerup',
5
+ 'onpointerdown',
6
+ 'onpointerover',
7
+ 'onpointerout',
8
+ 'onpointerenter',
9
+ 'onpointerleave',
10
+ 'onpointermove'
11
11
  ];
@@ -1,6 +1,8 @@
1
- import type { Mesh, Raycaster, Intersection } from 'three';
1
+ import { type Mesh, Raycaster, type Intersection } from 'three';
2
2
  import type { CurrentWritable } from '@threlte/core';
3
+ import type { TeleportControlsOptions } from '.';
3
4
  export type ComputeFunction = (context: Context, handContext: HandContext) => void;
5
+ export type TeleportEvents = {};
4
6
  export interface Context {
5
7
  interactiveObjects: Mesh[];
6
8
  surfaces: Map<string, Mesh>;
@@ -8,6 +10,10 @@ export interface Context {
8
10
  dispatchers: WeakMap<Mesh, Record<string, (arg: unknown) => void>>;
9
11
  raycaster: Raycaster;
10
12
  compute: ComputeFunction;
13
+ addBlocker: (mesh: Mesh) => void;
14
+ removeBlocker: (mesh: Mesh) => void;
15
+ addSurface: (mesh: Mesh, events: TeleportEvents) => void;
16
+ removeSurface: (mesh: Mesh) => void;
11
17
  }
12
18
  export interface HandContext {
13
19
  hand: 'left' | 'right';
@@ -17,5 +23,5 @@ export interface HandContext {
17
23
  }
18
24
  export declare const getHandContext: (hand: 'left' | 'right') => HandContext;
19
25
  export declare const setHandContext: (hand: 'left' | 'right', context: HandContext) => void;
20
- export declare const getTeleportContext: () => Context;
21
- export declare const setTeleportContext: (context: Context) => void;
26
+ export declare const useTeleportControls: () => Context;
27
+ export declare const createTeleportContext: (compute: TeleportControlsOptions['compute']) => Context;
@@ -1,4 +1,6 @@
1
+ import { Raycaster } from 'three';
1
2
  import { getContext, setContext } from 'svelte';
3
+ import { defaultComputeFunction } from './compute';
2
4
  const handContextKeys = {
3
5
  left: Symbol('teleport-controls-context-left-hand'),
4
6
  right: Symbol('teleport-controls-context-right-hand')
@@ -10,9 +12,50 @@ export const getHandContext = (hand) => {
10
12
  export const setHandContext = (hand, context) => {
11
13
  setContext(handContextKeys[hand], context);
12
14
  };
13
- export const getTeleportContext = () => {
15
+ export const useTeleportControls = () => {
14
16
  return getContext(contextKey);
15
17
  };
16
- export const setTeleportContext = (context) => {
18
+ export const createTeleportContext = (compute) => {
19
+ const addSurface = (mesh, events) => {
20
+ // check if the object is already in the list
21
+ if (context.interactiveObjects.indexOf(mesh) > -1) {
22
+ return;
23
+ }
24
+ context.interactiveObjects.push(mesh);
25
+ context.surfaces.set(mesh.uuid, mesh);
26
+ context.dispatchers.set(mesh, events);
27
+ };
28
+ const removeSurface = (mesh) => {
29
+ const index = context.interactiveObjects.indexOf(mesh);
30
+ context.interactiveObjects.splice(index, 1);
31
+ context.surfaces.delete(mesh.uuid);
32
+ context.dispatchers.delete(mesh);
33
+ };
34
+ const addBlocker = (mesh) => {
35
+ // check if the object is already in the list
36
+ if (context.interactiveObjects.indexOf(mesh) > -1) {
37
+ return;
38
+ }
39
+ context.interactiveObjects.push(mesh);
40
+ context.blockers.set(mesh.uuid, mesh);
41
+ };
42
+ const removeBlocker = (mesh) => {
43
+ const index = context.interactiveObjects.indexOf(mesh);
44
+ context.interactiveObjects.splice(index, 1);
45
+ context.blockers.delete(mesh.uuid);
46
+ };
47
+ const context = {
48
+ interactiveObjects: [],
49
+ surfaces: new Map(),
50
+ blockers: new Map(),
51
+ dispatchers: new WeakMap(),
52
+ raycaster: new Raycaster(),
53
+ compute: compute ?? defaultComputeFunction,
54
+ addBlocker,
55
+ removeBlocker,
56
+ addSurface,
57
+ removeSurface
58
+ };
17
59
  setContext(contextKey, context);
60
+ return context;
18
61
  };