@terraware/web-components 4.2.21 → 4.2.22

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 (93) hide show
  1. package/components/VirtualWalkthrough/Annotation.d.ts.map +1 -1
  2. package/components/VirtualWalkthrough/Annotation.js +10 -0
  3. package/components/VirtualWalkthrough/BoundaryWall.d.ts +11 -0
  4. package/components/VirtualWalkthrough/BoundaryWall.d.ts.map +1 -0
  5. package/components/VirtualWalkthrough/BoundaryWall.js +35 -0
  6. package/components/VirtualWalkthrough/TfXrNavigation.d.ts +42 -6
  7. package/components/VirtualWalkthrough/TfXrNavigation.d.ts.map +1 -1
  8. package/components/VirtualWalkthrough/TfXrNavigation.js +109 -9
  9. package/components/VirtualWalkthrough/TfXrNavigation.test.js +110 -0
  10. package/components/VirtualWalkthrough/VirtualWalkthroughViewer.d.ts.map +1 -1
  11. package/components/VirtualWalkthrough/VirtualWalkthroughViewer.js +50 -3
  12. package/components/VirtualWalkthrough/VrAnnotationPanel.d.ts +9 -0
  13. package/components/VirtualWalkthrough/VrAnnotationPanel.d.ts.map +1 -0
  14. package/components/VirtualWalkthrough/VrAnnotationPanel.js +22 -0
  15. package/components/VirtualWalkthrough/XrAnnotationInteraction.d.ts +6 -0
  16. package/components/VirtualWalkthrough/XrAnnotationInteraction.d.ts.map +1 -0
  17. package/components/VirtualWalkthrough/XrAnnotationInteraction.js +15 -0
  18. package/components/VirtualWalkthrough/XrGazeDwell.d.ts +6 -0
  19. package/components/VirtualWalkthrough/XrGazeDwell.d.ts.map +1 -0
  20. package/components/VirtualWalkthrough/XrGazeDwell.js +15 -0
  21. package/components/VirtualWalkthrough/XrPointerRay.d.ts +7 -0
  22. package/components/VirtualWalkthrough/XrPointerRay.d.ts.map +1 -0
  23. package/components/VirtualWalkthrough/XrPointerRay.js +15 -0
  24. package/components/VirtualWalkthrough/boundary-wall.d.ts +86 -0
  25. package/components/VirtualWalkthrough/boundary-wall.d.ts.map +1 -0
  26. package/components/VirtualWalkthrough/boundary-wall.js +331 -0
  27. package/components/VirtualWalkthrough/boundary-wall.test.js +203 -0
  28. package/components/VirtualWalkthrough/vr-annotation-panel-layout.d.ts +6 -0
  29. package/components/VirtualWalkthrough/vr-annotation-panel-layout.d.ts.map +1 -0
  30. package/components/VirtualWalkthrough/vr-annotation-panel-layout.js +22 -0
  31. package/components/VirtualWalkthrough/vr-annotation-panel-layout.test.js +26 -0
  32. package/components/VirtualWalkthrough/vr-annotation-panel.d.ts +40 -0
  33. package/components/VirtualWalkthrough/vr-annotation-panel.d.ts.map +1 -0
  34. package/components/VirtualWalkthrough/vr-annotation-panel.js +312 -0
  35. package/components/VirtualWalkthrough/xr-annotation-candidates.d.ts +18 -0
  36. package/components/VirtualWalkthrough/xr-annotation-candidates.d.ts.map +1 -0
  37. package/components/VirtualWalkthrough/xr-annotation-candidates.js +38 -0
  38. package/components/VirtualWalkthrough/xr-annotation-interaction.d.ts +13 -0
  39. package/components/VirtualWalkthrough/xr-annotation-interaction.d.ts.map +1 -0
  40. package/components/VirtualWalkthrough/xr-annotation-interaction.js +59 -0
  41. package/components/VirtualWalkthrough/xr-annotation-targeting.d.ts +10 -0
  42. package/components/VirtualWalkthrough/xr-annotation-targeting.d.ts.map +1 -0
  43. package/components/VirtualWalkthrough/xr-annotation-targeting.js +36 -0
  44. package/components/VirtualWalkthrough/xr-annotation-targeting.test.js +66 -0
  45. package/components/VirtualWalkthrough/xr-button-arming.d.ts +14 -0
  46. package/components/VirtualWalkthrough/xr-button-arming.d.ts.map +1 -0
  47. package/components/VirtualWalkthrough/xr-button-arming.js +19 -0
  48. package/components/VirtualWalkthrough/xr-button-arming.test.js +35 -0
  49. package/components/VirtualWalkthrough/xr-button-hold.d.ts +20 -0
  50. package/components/VirtualWalkthrough/xr-button-hold.d.ts.map +1 -0
  51. package/components/VirtualWalkthrough/xr-button-hold.js +43 -0
  52. package/components/VirtualWalkthrough/xr-button-hold.test.js +57 -0
  53. package/components/VirtualWalkthrough/xr-exit-button.d.ts +21 -4
  54. package/components/VirtualWalkthrough/xr-exit-button.d.ts.map +1 -1
  55. package/components/VirtualWalkthrough/xr-exit-button.js +98 -37
  56. package/components/VirtualWalkthrough/xr-face-buttons.d.ts +15 -0
  57. package/components/VirtualWalkthrough/xr-face-buttons.d.ts.map +1 -0
  58. package/components/VirtualWalkthrough/xr-face-buttons.js +31 -0
  59. package/components/VirtualWalkthrough/xr-face-buttons.test.js +82 -0
  60. package/components/VirtualWalkthrough/xr-gaze-dwell-state.d.ts +17 -0
  61. package/components/VirtualWalkthrough/xr-gaze-dwell-state.d.ts.map +1 -0
  62. package/components/VirtualWalkthrough/xr-gaze-dwell-state.js +39 -0
  63. package/components/VirtualWalkthrough/xr-gaze-dwell-state.test.js +61 -0
  64. package/components/VirtualWalkthrough/xr-gaze-dwell.d.ts +30 -0
  65. package/components/VirtualWalkthrough/xr-gaze-dwell.d.ts.map +1 -0
  66. package/components/VirtualWalkthrough/xr-gaze-dwell.js +173 -0
  67. package/components/VirtualWalkthrough/xr-interactive-ui.d.ts +13 -0
  68. package/components/VirtualWalkthrough/xr-interactive-ui.d.ts.map +1 -0
  69. package/components/VirtualWalkthrough/xr-interactive-ui.js +25 -0
  70. package/components/VirtualWalkthrough/xr-interactive-ui.test.js +165 -0
  71. package/components/VirtualWalkthrough/xr-pointer-ray.d.ts +10 -0
  72. package/components/VirtualWalkthrough/xr-pointer-ray.d.ts.map +1 -0
  73. package/components/VirtualWalkthrough/xr-pointer-ray.js +24 -0
  74. package/components/VirtualWalkthrough/xr-progress-pie.d.ts +10 -0
  75. package/components/VirtualWalkthrough/xr-progress-pie.d.ts.map +1 -0
  76. package/components/VirtualWalkthrough/xr-progress-pie.js +30 -0
  77. package/components/VirtualWalkthrough/xr-progress-pie.test.js +79 -0
  78. package/components/VirtualWalkthrough/xr-ray-quad.d.ts +13 -0
  79. package/components/VirtualWalkthrough/xr-ray-quad.d.ts.map +1 -0
  80. package/components/VirtualWalkthrough/xr-ray-quad.js +29 -0
  81. package/components/VirtualWalkthrough/xr-ray-quad.test.js +37 -0
  82. package/components/VirtualWalkthrough/xr-scene-bounds.d.ts +17 -0
  83. package/components/VirtualWalkthrough/xr-scene-bounds.d.ts.map +1 -0
  84. package/components/VirtualWalkthrough/xr-scene-bounds.js +41 -0
  85. package/components/VirtualWalkthrough/xr-scene-bounds.test.js +70 -0
  86. package/components/VirtualWalkthrough/xr-teleport-gesture.d.ts +30 -0
  87. package/components/VirtualWalkthrough/xr-teleport-gesture.d.ts.map +1 -0
  88. package/components/VirtualWalkthrough/xr-teleport-gesture.js +45 -0
  89. package/components/VirtualWalkthrough/xr-teleport-gesture.test.js +90 -0
  90. package/hooks/useXr.d.ts +2 -0
  91. package/hooks/useXr.d.ts.map +1 -1
  92. package/hooks/useXr.js +10 -6
  93. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"Annotation.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/Annotation.tsx"],"names":[],"mappings":"AAUA,OAAO,yBAAyB,CAAC;AAEjC,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAS5D,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC;IAChB,IAAI,CAAC,EAAE,kBAAkB,CAAC;IAC1B,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,IAAI,CAAC;IAChE,MAAM,CAAC,EAAE,CAAC,UAAU,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACjF,sBAAsB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACnG;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,QAAA,MAAM,UAAU,GAAI,OAAO,eAAe,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,4CAiL7D,CAAC;AAEF,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"Annotation.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/Annotation.tsx"],"names":[],"mappings":"AAUA,OAAO,yBAAyB,CAAC;AAEjC,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAS5D,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC;IAChB,IAAI,CAAC,EAAE,kBAAkB,CAAC;IAC1B,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,IAAI,CAAC;IAChE,MAAM,CAAC,EAAE,CAAC,UAAU,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACjF,sBAAsB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACnG;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,QAAA,MAAM,UAAU,GAAI,OAAO,eAAe,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,4CA2L7D,CAAC;AAEF,eAAe,UAAU,CAAC"}
@@ -101,6 +101,15 @@ const Annotation = props => {
101
101
  }
102
102
  }, [setCamera]);
103
103
 
104
+ // VR open path: shows the annotation without moving the camera (roomscale head pose
105
+ // comes from the XR views, so a camera move would be disorienting or a no-op).
106
+ const handleVrOpen = useCallback((screenX, screenY) => {
107
+ if (isEditRef.current) {
108
+ return;
109
+ }
110
+ onViewRef.current?.(annotationForViewRef.current, screenX, screenY);
111
+ }, []);
112
+
104
113
  // Stable callback so its identity doesn't churn the underlying script prop each frame.
105
114
  const handleScreenPositionUpdate = useCallback((screenX, screenY, size) => {
106
115
  onScreenPositionUpdateRef.current?.(index, screenX, screenY, size);
@@ -190,6 +199,7 @@ const Annotation = props => {
190
199
  text: bodyText,
191
200
  enabled: visible,
192
201
  onClickCallback: handleClick,
202
+ onVrOpenCallback: handleVrOpen,
193
203
  onScreenPositionUpdateCallback: isViewed ? handleScreenPositionUpdate : undefined
194
204
  })
195
205
  });
@@ -0,0 +1,11 @@
1
+ import { Vec3 } from 'playcanvas';
2
+ export type BoundaryWallProps = {
3
+ /** World-space, i.e. already multiplied by the scene scaleFactor. */
4
+ center: Vec3;
5
+ radius: number;
6
+ groundPlane: Vec3[];
7
+ baseY: number;
8
+ };
9
+ declare const BoundaryWall: ({ center, radius, groundPlane, baseY }: BoundaryWallProps) => import("react/jsx-runtime").JSX.Element;
10
+ export default BoundaryWall;
11
+ //# sourceMappingURL=BoundaryWall.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BoundaryWall.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/BoundaryWall.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAIlC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,qEAAqE;IACrE,MAAM,EAAE,IAAI,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,IAAI,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,QAAA,MAAM,YAAY,GAAI,wCAAwC,iBAAiB,4CAuB9E,CAAC;AAEF,eAAe,YAAY,CAAC"}
@@ -0,0 +1,35 @@
1
+ import React, { useEffect } from 'react';
2
+ import { Entity } from '@playcanvas/react';
3
+ import { Script } from '@playcanvas/react/components';
4
+ import { useApp } from '@playcanvas/react/hooks';
5
+ import { BoundaryWallScript } from './boundary-wall';
6
+ import { jsx as _jsx } from "react/jsx-runtime";
7
+ const BoundaryWall = ({
8
+ center,
9
+ radius,
10
+ groundPlane,
11
+ baseY
12
+ }) => {
13
+ const app = useApp();
14
+ useEffect(() => {
15
+ // Set imperatively rather than via reactive Script props. @playcanvas/react wraps Script
16
+ // in memo() whose shallowEquals returns early on the first prop with a .equals() method
17
+ // (Vec3), so any prop after a Vec3 is never compared and would not propagate.
18
+ // @ts-expect-error - scripts are added dynamically to the entity
19
+ const script = app.root.findByName('boundary-wall')?.script?.boundaryWall;
20
+ if (script) {
21
+ script.center = center;
22
+ script.radius = radius;
23
+ script.groundPlane = groundPlane;
24
+ script.baseY = baseY;
25
+ script.rebuild();
26
+ }
27
+ }, [app, center, radius, groundPlane, baseY]);
28
+ return /*#__PURE__*/_jsx(Entity, {
29
+ name: "boundary-wall",
30
+ children: /*#__PURE__*/_jsx(Script, {
31
+ script: BoundaryWallScript
32
+ })
33
+ });
34
+ };
35
+ export default BoundaryWall;
@@ -1,15 +1,51 @@
1
+ import { Vec3, XrInputSource } from 'playcanvas';
1
2
  import { XrNavigation as PcXrNavigation } from 'playcanvas/scripts/esm/xr/xr-navigation.mjs';
2
3
  /**
3
- * Extended XrNavigation that fixes the bug where tryTeleport doesn't respect
4
- * the enableTeleport flag setting.
4
+ * XrNavigation that never teleports on a select meant for something else: it honours the
5
+ * enableTeleport flag (which the base ignores in tryTeleport) and skips sources whose ray is over
6
+ * interactive UI.
5
7
  */
6
8
  export declare class TfXrNavigation extends PcXrNavigation {
7
9
  static scriptName: string;
10
+ /** Assigned by the React wrapper. True when this source's ray is over interactive UI. */
11
+ isTeleportBlocked?: (inputSource: XrInputSource) => boolean;
8
12
  /**
9
- * Override tryTeleport to respect the enableTeleport flag.
10
- * The base implementation is always called from handleSelectEnd,
11
- * but we only want to teleport when enableTeleport is true.
13
+ * World-space bounds circle. Assigned by the React wrapper rather than passed as a Script prop:
14
+ * boundsCenter is a Vec3, and @playcanvas/react's memo() comparator stops at the first prop with
15
+ * an .equals() method, so any prop after it would never propagate.
16
+ * A boundsRadius of 0 disables clamping entirely.
12
17
  */
13
- tryTeleport(inputSource: any): void;
18
+ boundsCenter: Vec3;
19
+ boundsRadius: number;
20
+ /** How far the rig was pulled back in the last postUpdate to keep the head inside the bounds. */
21
+ clampDistance: number;
22
+ private _gestures;
23
+ private _isBlocked;
24
+ tryTeleport(inputSource: XrInputSource): void;
25
+ update(dt: number): void;
26
+ /**
27
+ * Holds the head inside the bounds circle. Runs in postUpdate, after PlayCanvas has written this
28
+ * frame's head pose into the camera's local transform (xr.update runs before app.update) and after
29
+ * the base script's locomotion, so one clamp covers every way the head can move: thumbstick
30
+ * translation of the rig, room-scale walking within the rig, and snap turns pivoting the rig.
31
+ *
32
+ * This script must stay a no-op outside XR regardless of how it's mounted.
33
+ */
34
+ postUpdate(): void;
35
+ /**
36
+ * Clamps the teleport landing point to the bounds circle. Overriding here rather than in
37
+ * tryTeleport covers both paths that produce a landing point — the per-frame aim in
38
+ * _handleTeleportation and tryTeleport's own recompute when no hit is cached — and because
39
+ * _handleTeleportation positions the landing ring from this same record immediately afterwards,
40
+ * the ring previews the clamped destination without any extra work.
41
+ *
42
+ * Only valid hits are clamped: when the base finds no hit it leaves rec.point holding a stale
43
+ * value from an earlier frame, which must not be projected onto the circle and shown as a target.
44
+ * Clamping only ever shortens the throw, so a hit that passed the base's distance check still does.
45
+ */
46
+ _computeArcHit(origin: Vec3, direction: Vec3, rec: {
47
+ point: Vec3;
48
+ valid: boolean;
49
+ }): void;
14
50
  }
15
51
  //# sourceMappingURL=TfXrNavigation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TfXrNavigation.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/TfXrNavigation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,IAAI,cAAc,EAAE,MAAM,6CAA6C,CAAC;AAE7F;;;GAGG;AACH,qBAAa,cAAe,SAAQ,cAAc;IAChD,MAAM,CAAC,UAAU,SAAoB;IAErC;;;;OAIG;IACH,WAAW,CAAC,WAAW,EAAE,GAAG;CAS7B"}
1
+ {"version":3,"file":"TfXrNavigation.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/TfXrNavigation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,IAAI,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACzD,OAAO,EAAE,YAAY,IAAI,cAAc,EAAE,MAAM,6CAA6C,CAAC;AAkB7F;;;;GAIG;AACH,qBAAa,cAAe,SAAQ,cAAc;IAChD,MAAM,CAAC,UAAU,SAAoB;IAErC,yFAAyF;IACzF,iBAAiB,CAAC,EAAE,CAAC,WAAW,EAAE,aAAa,KAAK,OAAO,CAAC;IAE5D;;;;;OAKG;IACH,YAAY,OAAc;IAC1B,YAAY,SAAK;IAEjB,iGAAiG;IACjG,aAAa,SAAK;IAElB,OAAO,CAAC,SAAS,CAA8B;IAE/C,OAAO,CAAC,UAAU;IAIlB,WAAW,CAAC,WAAW,EAAE,aAAa;IAYtC,MAAM,CAAC,EAAE,EAAE,MAAM;IAmCjB;;;;;;;OAOG;IACH,UAAU;IAoBV;;;;;;;;;;OAUG;IACH,cAAc,CAAC,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE;QAAE,KAAK,EAAE,IAAI,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE;CAkBnF"}
@@ -1,24 +1,124 @@
1
+ import { Vec3 } from 'playcanvas';
1
2
  import { XrNavigation as PcXrNavigation } from 'playcanvas/scripts/esm/xr/xr-navigation.mjs';
3
+ import { clampToCircle } from './xr-scene-bounds';
4
+ import { TeleportGestureLatch } from './xr-teleport-gesture';
5
+
6
+ /** The base script's per-source aim state, which it exposes only as underscore-private fields. */
2
7
 
3
8
  /**
4
- * Extended XrNavigation that fixes the bug where tryTeleport doesn't respect
5
- * the enableTeleport flag setting.
9
+ * XrNavigation that never teleports on a select meant for something else: it honours the
10
+ * enableTeleport flag (which the base ignores in tryTeleport) and skips sources whose ray is over
11
+ * interactive UI.
6
12
  */
7
13
  export class TfXrNavigation extends PcXrNavigation {
8
14
  static scriptName = 'tfXrNavigation';
9
15
 
16
+ /** Assigned by the React wrapper. True when this source's ray is over interactive UI. */
17
+
10
18
  /**
11
- * Override tryTeleport to respect the enableTeleport flag.
12
- * The base implementation is always called from handleSelectEnd,
13
- * but we only want to teleport when enableTeleport is true.
19
+ * World-space bounds circle. Assigned by the React wrapper rather than passed as a Script prop:
20
+ * boundsCenter is a Vec3, and @playcanvas/react's memo() comparator stops at the first prop with
21
+ * an .equals() method, so any prop after it would never propagate.
22
+ * A boundsRadius of 0 disables clamping entirely.
14
23
  */
24
+ boundsCenter = new Vec3();
25
+ boundsRadius = 0;
26
+
27
+ /** How far the rig was pulled back in the last postUpdate to keep the head inside the bounds. */
28
+ clampDistance = 0;
29
+ _gestures = new TeleportGestureLatch();
30
+ _isBlocked(inputSource) {
31
+ return !this.enableTeleport || this.isTeleportBlocked?.(inputSource) === true;
32
+ }
15
33
  tryTeleport(inputSource) {
16
- // Only proceed with teleportation if enableTeleport is true
17
- if (!this.enableTeleport) {
34
+ // The latch decides on the frames the press actually aimed through, because super.tryTeleport
35
+ // commits the arc hit cached back then rather than re-tracing the release-time ray. The live
36
+ // check stays too: the same release also fires 'select', so a ray that lands on UI at release
37
+ // is operating that UI and must not move the rig as well.
38
+ if (this._gestures.consumeBlocked(inputSource) || this._isBlocked(inputSource)) {
18
39
  return;
19
40
  }
20
-
21
- // Call the parent implementation
22
41
  super.tryTeleport(inputSource);
23
42
  }
43
+ update(dt) {
44
+ super.update(dt);
45
+ const internals = this;
46
+ for (const inputSource of internals._inputSources) {
47
+ // Sampled once per source per frame: isTeleportBlocked ray-tests every hotspot.
48
+ const uiBlocked = this.isTeleportBlocked?.(inputSource) === true;
49
+
50
+ // Tracked unconditionally, and before the arc-hiding work below. super.update has just cached
51
+ // this frame's arc hit, which is what a release would commit, so the latch has to see the same
52
+ // frame. A source that isn't pressed carries no gesture, so any stale entry from a press that
53
+ // ended while teleport was disabled (skipping tryTeleport entirely) is dropped here rather
54
+ // than leaking into the source's next press.
55
+ this._gestures.track(inputSource, {
56
+ pressed: internals._activePointers.get(inputSource) === true,
57
+ teleportDisabled: !this.enableTeleport,
58
+ uiBlocked
59
+ });
60
+
61
+ // The base hides the arc only from inside its own teleport handling, which it skips entirely
62
+ // once teleport is off - so an arc raised before the block (gaze dwell can open a panel while
63
+ // the trigger is held) would otherwise stay frozen in the world. These writes are idempotent,
64
+ // and the base re-enables the visuals itself on the first frame a source is unblocked.
65
+ const visual = internals._arcVisuals.get(inputSource);
66
+ if (!visual || !visual.entity.enabled && !visual.ringEntity.enabled) {
67
+ continue;
68
+ }
69
+ if (!this.enableTeleport || uiBlocked) {
70
+ visual.entity.enabled = false;
71
+ visual.ringEntity.enabled = false;
72
+ }
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Holds the head inside the bounds circle. Runs in postUpdate, after PlayCanvas has written this
78
+ * frame's head pose into the camera's local transform (xr.update runs before app.update) and after
79
+ * the base script's locomotion, so one clamp covers every way the head can move: thumbstick
80
+ * translation of the rig, room-scale walking within the rig, and snap turns pivoting the rig.
81
+ *
82
+ * This script must stay a no-op outside XR regardless of how it's mounted.
83
+ */
84
+ postUpdate() {
85
+ this.clampDistance = 0;
86
+ const camera = this._cameraEntity;
87
+ if (!this.app.xr?.active || !camera || this.boundsRadius <= 0) {
88
+ return;
89
+ }
90
+ const head = camera.getPosition();
91
+ const clamped = clampToCircle(head.x, head.z, this.boundsCenter.x, this.boundsCenter.z, this.boundsRadius);
92
+ if (clamped.distance === 0) {
93
+ return;
94
+ }
95
+
96
+ // World-space translate, not translateLocal: a snap turn has yawed the rig, so a local-space
97
+ // correction would be rotated away from the direction the head actually overshot.
98
+ this.entity.translate(clamped.x - head.x, 0, clamped.z - head.z);
99
+ this.clampDistance = clamped.distance;
100
+ }
101
+
102
+ /**
103
+ * Clamps the teleport landing point to the bounds circle. Overriding here rather than in
104
+ * tryTeleport covers both paths that produce a landing point — the per-frame aim in
105
+ * _handleTeleportation and tryTeleport's own recompute when no hit is cached — and because
106
+ * _handleTeleportation positions the landing ring from this same record immediately afterwards,
107
+ * the ring previews the clamped destination without any extra work.
108
+ *
109
+ * Only valid hits are clamped: when the base finds no hit it leaves rec.point holding a stale
110
+ * value from an earlier frame, which must not be projected onto the circle and shown as a target.
111
+ * Clamping only ever shortens the throw, so a hit that passed the base's distance check still does.
112
+ */
113
+ _computeArcHit(origin, direction, rec) {
114
+ super._computeArcHit(origin, direction, rec);
115
+ if (!rec.valid || this.boundsRadius <= 0) {
116
+ return;
117
+ }
118
+
119
+ // Y is left alone: it is the navigation plane height, and bounds are XZ-only.
120
+ const clamped = clampToCircle(rec.point.x, rec.point.z, this.boundsCenter.x, this.boundsCenter.z, this.boundsRadius);
121
+ rec.point.x = clamped.x;
122
+ rec.point.z = clamped.z;
123
+ }
24
124
  }
@@ -0,0 +1,110 @@
1
+ import { Vec3 } from 'playcanvas';
2
+
3
+ // The real base script is an .mjs file that Jest's default CRA transform config never transforms
4
+ // (node_modules is excluded), so importing it directly fails to parse. TfXrNavigation.postUpdate
5
+ // never calls into the base class, so a stub with no behaviour is enough to satisfy `extends`.
6
+ jest.mock('playcanvas/scripts/esm/xr/xr-navigation.mjs', () => ({
7
+ XrNavigation: class {}
8
+ }));
9
+ import { TfXrNavigation } from './TfXrNavigation';
10
+ const BOUNDS_CENTER = new Vec3(2, 0, 3);
11
+ const BOUNDS_RADIUS = 5;
12
+ const makeNavigation = options => {
13
+ const translate = jest.fn();
14
+ const navigation = Object.create(TfXrNavigation.prototype);
15
+ navigation.boundsCenter = BOUNDS_CENTER.clone();
16
+ navigation.boundsRadius = options.boundsRadius;
17
+ navigation.clampDistance = 0;
18
+ navigation.app = {
19
+ xr: {
20
+ active: options.xrActive
21
+ }
22
+ };
23
+ navigation.entity = {
24
+ translate
25
+ };
26
+ Object.assign(navigation, {
27
+ _cameraEntity: {
28
+ getPosition: () => options.head
29
+ }
30
+ });
31
+ return {
32
+ navigation,
33
+ translate
34
+ };
35
+ };
36
+ describe('TfXrNavigation.postUpdate', () => {
37
+ it('does not clamp when there is no active XR session, even with the head far outside the circle', () => {
38
+ const {
39
+ navigation,
40
+ translate
41
+ } = makeNavigation({
42
+ xrActive: false,
43
+ boundsRadius: BOUNDS_RADIUS,
44
+ head: new Vec3(1000, 1.5, 1000)
45
+ });
46
+ navigation.postUpdate();
47
+ expect(translate).not.toHaveBeenCalled();
48
+ expect(navigation.clampDistance).toBe(0);
49
+ });
50
+ it('does not clamp when boundsRadius is 0', () => {
51
+ const {
52
+ navigation,
53
+ translate
54
+ } = makeNavigation({
55
+ xrActive: true,
56
+ boundsRadius: 0,
57
+ head: new Vec3(1000, 1.5, 1000)
58
+ });
59
+ navigation.postUpdate();
60
+ expect(translate).not.toHaveBeenCalled();
61
+ expect(navigation.clampDistance).toBe(0);
62
+ });
63
+ it('does not clamp when the head is inside the circle', () => {
64
+ const {
65
+ navigation,
66
+ translate
67
+ } = makeNavigation({
68
+ xrActive: true,
69
+ boundsRadius: BOUNDS_RADIUS,
70
+ head: new Vec3(3, 1.5, 4)
71
+ });
72
+ navigation.postUpdate();
73
+ expect(translate).not.toHaveBeenCalled();
74
+ expect(navigation.clampDistance).toBe(0);
75
+ });
76
+ it('pulls the rig back and records the overshoot when the head is outside the circle in an active session', () => {
77
+ const {
78
+ navigation,
79
+ translate
80
+ } = makeNavigation({
81
+ xrActive: true,
82
+ boundsRadius: BOUNDS_RADIUS,
83
+ head: new Vec3(10, 1.5, 3)
84
+ });
85
+ navigation.postUpdate();
86
+ expect(translate).toHaveBeenCalledTimes(1);
87
+ expect(translate).toHaveBeenCalledWith(-3, 0, 0);
88
+ expect(navigation.clampDistance).toBe(3);
89
+ });
90
+ it('does not latch clampDistance across frames', () => {
91
+ const {
92
+ navigation,
93
+ translate
94
+ } = makeNavigation({
95
+ xrActive: true,
96
+ boundsRadius: BOUNDS_RADIUS,
97
+ head: new Vec3(10, 1.5, 3)
98
+ });
99
+ navigation.postUpdate();
100
+ expect(navigation.clampDistance).toBe(3);
101
+ Object.assign(navigation, {
102
+ _cameraEntity: {
103
+ getPosition: () => new Vec3(3, 1.5, 4)
104
+ }
105
+ });
106
+ navigation.postUpdate();
107
+ expect(translate).toHaveBeenCalledTimes(1);
108
+ expect(navigation.clampDistance).toBe(0);
109
+ });
110
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"VirtualWalkthroughViewer.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/VirtualWalkthroughViewer.tsx"],"names":[],"mappings":"AAYA,OAAmB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAK3D,OAAsB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAUtE,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,WAAW,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7D,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,iBAAiB,EAAE,CAAC,WAAW,EAAE,eAAe,EAAE,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5E,OAAO,EAAE,oBAAoB,CAAC;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,QAAA,MAAM,wBAAwB,GAAI,qQAkB/B,6BAA6B,4CA2T/B,CAAC;AAEF,eAAe,wBAAwB,CAAC"}
1
+ {"version":3,"file":"VirtualWalkthroughViewer.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/VirtualWalkthroughViewer.tsx"],"names":[],"mappings":"AAYA,OAAmB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAM3D,OAAsB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAqBtE,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,WAAW,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7D,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,iBAAiB,EAAE,CAAC,WAAW,EAAE,eAAe,EAAE,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5E,OAAO,EAAE,oBAAoB,CAAC;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,QAAA,MAAM,wBAAwB,GAAI,qQAkB/B,6BAA6B,4CA+W/B,CAAC;AAEF,eAAe,wBAAwB,CAAC"}
@@ -12,16 +12,28 @@ import Annotation from './Annotation';
12
12
  import AnnotationPanel from './AnnotationPanel';
13
13
  import { AutoRotator } from './AutoRotator';
14
14
  import BoundaryRing from './BoundaryRing';
15
+ import BoundaryWall from './BoundaryWall';
15
16
  import GradientSky from './GradientSky';
16
17
  import SplatControls from './SplatControls';
17
18
  import SplatModel from './SplatModel';
18
19
  import { TfAnnotationManager } from './TfAnnotationManager';
19
20
  import { TfXrNavigation } from './TfXrNavigation';
21
+ import VrAnnotationPanel from './VrAnnotationPanel';
22
+ import XrAnnotationInteraction from './XrAnnotationInteraction';
20
23
  import XrExitButton from './XrExitButton';
24
+ import XrGazeDwell from './XrGazeDwell';
25
+ import XrPointerRay from './XrPointerRay';
21
26
  import { WalkthroughCamera } from './walkthrough-camera';
27
+ import { rayHitsInteractiveUi } from './xr-interactive-ui';
22
28
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
23
29
  const DEFAULT_FOCUS_POINT = [0, 0.1, 0];
24
30
  const DEFAULT_POSITION = [1, 0.1, 0];
31
+
32
+ // How far outside the clamp radius the boundary wall stands. Without it the wall would be coplanar
33
+ // with the surface the head is clamped to, so a pinned user would have it in their face and through
34
+ // their near clip plane. Deliberately an absolute world-space distance rather than one scaled by
35
+ // scaleFactor: it is about the size of the user's body, not about the size of the scene.
36
+ const WALL_INSET = 0.25;
25
37
  const VirtualWalkthroughViewer = ({
26
38
  splatSrc,
27
39
  origin = DEFAULT_FOCUS_POINT,
@@ -60,7 +72,9 @@ const VirtualWalkthroughViewer = ({
60
72
  const [viewingAnnotationIndex, setViewingAnnotationIndex] = useState(-1);
61
73
  const [viewedScreenPos, setViewedScreenPos] = useState(null);
62
74
  const {
63
- isCurrentlyInXr
75
+ isCurrentlyInXr,
76
+ isCurrentlyInVr,
77
+ isCurrentlyInAr
64
78
  } = useXr();
65
79
  const sceneBoundsRadius = useMemo(() => {
66
80
  if (sceneBounds?.m !== undefined) {
@@ -89,6 +103,16 @@ const VirtualWalkthroughViewer = ({
89
103
  walkthroughCam.groundPlane = cameraGroundPlane;
90
104
  }
91
105
  }, [cameraGroundPlane, app]);
106
+ useEffect(() => {
107
+ // Set imperatively for the same reason as BoundaryRing: boundsCenter is a Vec3, and the
108
+ // @playcanvas/react memo() comparator stops at the first prop with an .equals() method.
109
+ // @ts-expect-error - scripts are added dynamically to the entity
110
+ const navigation = app.root.findByName('camera-root')?.script?.tfXrNavigation;
111
+ if (navigation) {
112
+ navigation.boundsCenter = cameraBoundsCenter;
113
+ navigation.boundsRadius = sceneBoundsRadius * scaleFactor;
114
+ }
115
+ }, [app, cameraBoundsCenter, sceneBoundsRadius, scaleFactor]);
92
116
  const handleToggleFreeFly = useCallback(() => {
93
117
  const newFreeFly = !isFreeFly;
94
118
  // @ts-expect-error - scripts are added dynamically to the camera entity
@@ -163,6 +187,9 @@ const VirtualWalkthroughViewer = ({
163
187
  setViewedScreenPos(null);
164
188
  }, []);
165
189
  const handleAnnotationScreenPositionUpdate = useCallback((_index, screenX, screenY, size) => {
190
+ if (isCurrentlyInXr) {
191
+ return;
192
+ }
166
193
  // The scene is frozen while the panel is open, so guard against redundant
167
194
  // updates from the per-frame callback to avoid needless re-renders.
168
195
  setViewedScreenPos(prev => prev && prev.x === screenX && prev.y === screenY && prev.size === size ? prev : {
@@ -170,12 +197,18 @@ const VirtualWalkthroughViewer = ({
170
197
  y: screenY,
171
198
  size
172
199
  });
173
- }, []);
200
+ }, [isCurrentlyInXr]);
174
201
  const handleCloseAnnotation = useCallback(() => {
175
202
  setViewingAnnotation(null);
176
203
  setViewingAnnotationIndex(-1);
177
204
  setViewedScreenPos(null);
178
205
  }, []);
206
+ const isTeleportBlocked = useCallback(inputSource => rayHitsInteractiveUi(app, inputSource.getOrigin(), inputSource.getDirection()), [app]);
207
+ useEffect(() => {
208
+ if (!isCurrentlyInXr) {
209
+ handleCloseAnnotation();
210
+ }
211
+ }, [isCurrentlyInXr, handleCloseAnnotation]);
179
212
  const handleAnnotationUpdate = useCallback(updates => {
180
213
  if (selectedAnnotationIndex === -1) {
181
214
  return;
@@ -223,10 +256,19 @@ const VirtualWalkthroughViewer = ({
223
256
  }), /*#__PURE__*/_jsx(XrExitButton, {}), /*#__PURE__*/_jsx(Script, {
224
257
  script: XrControllers,
225
258
  enabled: !isEdit
259
+ }), /*#__PURE__*/_jsx(XrAnnotationInteraction, {
260
+ onDismiss: handleCloseAnnotation
261
+ }), /*#__PURE__*/_jsx(XrPointerRay, {}), isCurrentlyInVr && viewingAnnotation && /*#__PURE__*/_jsx(VrAnnotationPanel, {
262
+ annotation: viewingAnnotation,
263
+ annotationIndex: viewingAnnotationIndex,
264
+ scaleFactor: scaleFactor
265
+ }, viewingAnnotationIndex), isCurrentlyInXr && /*#__PURE__*/_jsx(XrGazeDwell, {
266
+ activeIndex: viewingAnnotationIndex
226
267
  }), /*#__PURE__*/_jsx(Script, {
227
268
  script: TfXrNavigation,
228
269
  enabled: !isEdit,
229
- enableTeleport: false
270
+ enableTeleport: !isCurrentlyInAr && !viewingAnnotation,
271
+ isTeleportBlocked: isTeleportBlocked
230
272
  }), !isCurrentlyInXr && /*#__PURE__*/_jsx(Script, {
231
273
  script: AutoRotator,
232
274
  enabled: !isEdit && autoRotate && !viewingAnnotation,
@@ -265,6 +307,11 @@ const VirtualWalkthroughViewer = ({
265
307
  onScreenPositionUpdate: handleAnnotationScreenPositionUpdate
266
308
  }, `annotation-${index}`))]
267
309
  })]
310
+ }), isCurrentlyInXr && sceneBoundsRadius > 0 && /*#__PURE__*/_jsx(BoundaryWall, {
311
+ center: cameraBoundsCenter,
312
+ radius: sceneBoundsRadius * scaleFactor + WALL_INSET,
313
+ groundPlane: cameraGroundPlane,
314
+ baseY: 0
268
315
  }), /*#__PURE__*/_jsx(SplatControls, {
269
316
  defaultCameraFocus: origin,
270
317
  defaultCameraPosition: cameraPosition,
@@ -0,0 +1,9 @@
1
+ import type { AnnotationProps } from './Annotation';
2
+ interface VrAnnotationPanelProps {
3
+ annotation: AnnotationProps;
4
+ annotationIndex: number;
5
+ scaleFactor: number;
6
+ }
7
+ declare const VrAnnotationPanel: ({ annotation, annotationIndex, scaleFactor }: VrAnnotationPanelProps) => import("react/jsx-runtime").JSX.Element;
8
+ export default VrAnnotationPanel;
9
+ //# sourceMappingURL=VrAnnotationPanel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VrAnnotationPanel.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/VrAnnotationPanel.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAGpD,UAAU,sBAAsB;IAC9B,UAAU,EAAE,eAAe,CAAC;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,QAAA,MAAM,iBAAiB,GAAI,8CAA8C,sBAAsB,4CAY9F,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
@@ -0,0 +1,22 @@
1
+ import React from 'react';
2
+ import { Entity } from '@playcanvas/react';
3
+ import { Script } from '@playcanvas/react/components';
4
+ import { VrAnnotationPanel as VrAnnotationPanelScript } from './vr-annotation-panel';
5
+ import { jsx as _jsx } from "react/jsx-runtime";
6
+ const VrAnnotationPanel = ({
7
+ annotation,
8
+ annotationIndex,
9
+ scaleFactor
10
+ }) => /*#__PURE__*/_jsx(Entity, {
11
+ name: "vr-annotation-panel",
12
+ children: /*#__PURE__*/_jsx(Script, {
13
+ script: VrAnnotationPanelScript,
14
+ title: annotation.title,
15
+ label: annotation.label,
16
+ bodyText: annotation.bodyText,
17
+ imageUrls: annotation.imageUrls,
18
+ annotationIndex: annotationIndex,
19
+ scaleFactor: scaleFactor
20
+ })
21
+ });
22
+ export default VrAnnotationPanel;
@@ -0,0 +1,6 @@
1
+ interface XrAnnotationInteractionProps {
2
+ onDismiss?: () => void;
3
+ }
4
+ declare const XrAnnotationInteraction: ({ onDismiss }: XrAnnotationInteractionProps) => import("react/jsx-runtime").JSX.Element;
5
+ export default XrAnnotationInteraction;
6
+ //# sourceMappingURL=XrAnnotationInteraction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"XrAnnotationInteraction.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/XrAnnotationInteraction.tsx"],"names":[],"mappings":"AAOA,UAAU,4BAA4B;IACpC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,QAAA,MAAM,uBAAuB,GAAI,eAAe,4BAA4B,4CAI3E,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+ import { Entity } from '@playcanvas/react';
3
+ import { Script } from '@playcanvas/react/components';
4
+ import { XrAnnotationInteraction as XrAnnotationInteractionScript } from './xr-annotation-interaction';
5
+ import { jsx as _jsx } from "react/jsx-runtime";
6
+ const XrAnnotationInteraction = ({
7
+ onDismiss
8
+ }) => /*#__PURE__*/_jsx(Entity, {
9
+ name: "xr-annotation-interaction",
10
+ children: /*#__PURE__*/_jsx(Script, {
11
+ script: XrAnnotationInteractionScript,
12
+ onDismissCallback: onDismiss
13
+ })
14
+ });
15
+ export default XrAnnotationInteraction;
@@ -0,0 +1,6 @@
1
+ interface XrGazeDwellProps {
2
+ activeIndex: number;
3
+ }
4
+ declare const XrGazeDwell: ({ activeIndex }: XrGazeDwellProps) => import("react/jsx-runtime").JSX.Element;
5
+ export default XrGazeDwell;
6
+ //# sourceMappingURL=XrGazeDwell.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"XrGazeDwell.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/XrGazeDwell.tsx"],"names":[],"mappings":"AAOA,UAAU,gBAAgB;IACxB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,QAAA,MAAM,WAAW,GAAI,iBAAiB,gBAAgB,4CAIrD,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+ import { Entity } from '@playcanvas/react';
3
+ import { Script } from '@playcanvas/react/components';
4
+ import { XrGazeDwell as XrGazeDwellScript } from './xr-gaze-dwell';
5
+ import { jsx as _jsx } from "react/jsx-runtime";
6
+ const XrGazeDwell = ({
7
+ activeIndex
8
+ }) => /*#__PURE__*/_jsx(Entity, {
9
+ name: "xr-gaze-dwell",
10
+ children: /*#__PURE__*/_jsx(Script, {
11
+ script: XrGazeDwellScript,
12
+ activeIndex: activeIndex
13
+ })
14
+ });
15
+ export default XrGazeDwell;
@@ -0,0 +1,7 @@
1
+ import { Color } from 'playcanvas';
2
+ export type XrPointerRayProps = {
3
+ color?: Color;
4
+ };
5
+ declare const XrPointerRay: ({ color }: XrPointerRayProps) => import("react/jsx-runtime").JSX.Element;
6
+ export default XrPointerRay;
7
+ //# sourceMappingURL=XrPointerRay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"XrPointerRay.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/XrPointerRay.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAInC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,CAAC;AAEF,QAAA,MAAM,YAAY,GAAI,WAA+B,iBAAiB,4CAIrE,CAAC;AAEF,eAAe,YAAY,CAAC"}
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+ import { Entity } from '@playcanvas/react';
3
+ import { Script } from '@playcanvas/react/components';
4
+ import { DEFAULT_RAY_COLOR, XrPointerRay as XrPointerRayScript } from './xr-pointer-ray';
5
+ import { jsx as _jsx } from "react/jsx-runtime";
6
+ const XrPointerRay = ({
7
+ color = DEFAULT_RAY_COLOR
8
+ }) => /*#__PURE__*/_jsx(Entity, {
9
+ name: "xr-pointer-ray",
10
+ children: /*#__PURE__*/_jsx(Script, {
11
+ script: XrPointerRayScript,
12
+ color: color
13
+ })
14
+ });
15
+ export default XrPointerRay;