@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.
- package/components/VirtualWalkthrough/Annotation.d.ts.map +1 -1
- package/components/VirtualWalkthrough/Annotation.js +10 -0
- package/components/VirtualWalkthrough/BoundaryWall.d.ts +11 -0
- package/components/VirtualWalkthrough/BoundaryWall.d.ts.map +1 -0
- package/components/VirtualWalkthrough/BoundaryWall.js +35 -0
- package/components/VirtualWalkthrough/TfXrNavigation.d.ts +42 -6
- package/components/VirtualWalkthrough/TfXrNavigation.d.ts.map +1 -1
- package/components/VirtualWalkthrough/TfXrNavigation.js +109 -9
- package/components/VirtualWalkthrough/TfXrNavigation.test.js +110 -0
- package/components/VirtualWalkthrough/VirtualWalkthroughViewer.d.ts.map +1 -1
- package/components/VirtualWalkthrough/VirtualWalkthroughViewer.js +50 -3
- package/components/VirtualWalkthrough/VrAnnotationPanel.d.ts +9 -0
- package/components/VirtualWalkthrough/VrAnnotationPanel.d.ts.map +1 -0
- package/components/VirtualWalkthrough/VrAnnotationPanel.js +22 -0
- package/components/VirtualWalkthrough/XrAnnotationInteraction.d.ts +6 -0
- package/components/VirtualWalkthrough/XrAnnotationInteraction.d.ts.map +1 -0
- package/components/VirtualWalkthrough/XrAnnotationInteraction.js +15 -0
- package/components/VirtualWalkthrough/XrGazeDwell.d.ts +6 -0
- package/components/VirtualWalkthrough/XrGazeDwell.d.ts.map +1 -0
- package/components/VirtualWalkthrough/XrGazeDwell.js +15 -0
- package/components/VirtualWalkthrough/XrPointerRay.d.ts +7 -0
- package/components/VirtualWalkthrough/XrPointerRay.d.ts.map +1 -0
- package/components/VirtualWalkthrough/XrPointerRay.js +15 -0
- package/components/VirtualWalkthrough/boundary-wall.d.ts +86 -0
- package/components/VirtualWalkthrough/boundary-wall.d.ts.map +1 -0
- package/components/VirtualWalkthrough/boundary-wall.js +331 -0
- package/components/VirtualWalkthrough/boundary-wall.test.js +203 -0
- package/components/VirtualWalkthrough/vr-annotation-panel-layout.d.ts +6 -0
- package/components/VirtualWalkthrough/vr-annotation-panel-layout.d.ts.map +1 -0
- package/components/VirtualWalkthrough/vr-annotation-panel-layout.js +22 -0
- package/components/VirtualWalkthrough/vr-annotation-panel-layout.test.js +26 -0
- package/components/VirtualWalkthrough/vr-annotation-panel.d.ts +40 -0
- package/components/VirtualWalkthrough/vr-annotation-panel.d.ts.map +1 -0
- package/components/VirtualWalkthrough/vr-annotation-panel.js +312 -0
- package/components/VirtualWalkthrough/xr-annotation-candidates.d.ts +18 -0
- package/components/VirtualWalkthrough/xr-annotation-candidates.d.ts.map +1 -0
- package/components/VirtualWalkthrough/xr-annotation-candidates.js +38 -0
- package/components/VirtualWalkthrough/xr-annotation-interaction.d.ts +13 -0
- package/components/VirtualWalkthrough/xr-annotation-interaction.d.ts.map +1 -0
- package/components/VirtualWalkthrough/xr-annotation-interaction.js +59 -0
- package/components/VirtualWalkthrough/xr-annotation-targeting.d.ts +10 -0
- package/components/VirtualWalkthrough/xr-annotation-targeting.d.ts.map +1 -0
- package/components/VirtualWalkthrough/xr-annotation-targeting.js +36 -0
- package/components/VirtualWalkthrough/xr-annotation-targeting.test.js +66 -0
- package/components/VirtualWalkthrough/xr-button-arming.d.ts +14 -0
- package/components/VirtualWalkthrough/xr-button-arming.d.ts.map +1 -0
- package/components/VirtualWalkthrough/xr-button-arming.js +19 -0
- package/components/VirtualWalkthrough/xr-button-arming.test.js +35 -0
- package/components/VirtualWalkthrough/xr-button-hold.d.ts +20 -0
- package/components/VirtualWalkthrough/xr-button-hold.d.ts.map +1 -0
- package/components/VirtualWalkthrough/xr-button-hold.js +43 -0
- package/components/VirtualWalkthrough/xr-button-hold.test.js +57 -0
- package/components/VirtualWalkthrough/xr-exit-button.d.ts +21 -4
- package/components/VirtualWalkthrough/xr-exit-button.d.ts.map +1 -1
- package/components/VirtualWalkthrough/xr-exit-button.js +98 -37
- package/components/VirtualWalkthrough/xr-face-buttons.d.ts +15 -0
- package/components/VirtualWalkthrough/xr-face-buttons.d.ts.map +1 -0
- package/components/VirtualWalkthrough/xr-face-buttons.js +31 -0
- package/components/VirtualWalkthrough/xr-face-buttons.test.js +82 -0
- package/components/VirtualWalkthrough/xr-gaze-dwell-state.d.ts +17 -0
- package/components/VirtualWalkthrough/xr-gaze-dwell-state.d.ts.map +1 -0
- package/components/VirtualWalkthrough/xr-gaze-dwell-state.js +39 -0
- package/components/VirtualWalkthrough/xr-gaze-dwell-state.test.js +61 -0
- package/components/VirtualWalkthrough/xr-gaze-dwell.d.ts +30 -0
- package/components/VirtualWalkthrough/xr-gaze-dwell.d.ts.map +1 -0
- package/components/VirtualWalkthrough/xr-gaze-dwell.js +173 -0
- package/components/VirtualWalkthrough/xr-interactive-ui.d.ts +13 -0
- package/components/VirtualWalkthrough/xr-interactive-ui.d.ts.map +1 -0
- package/components/VirtualWalkthrough/xr-interactive-ui.js +25 -0
- package/components/VirtualWalkthrough/xr-interactive-ui.test.js +165 -0
- package/components/VirtualWalkthrough/xr-pointer-ray.d.ts +10 -0
- package/components/VirtualWalkthrough/xr-pointer-ray.d.ts.map +1 -0
- package/components/VirtualWalkthrough/xr-pointer-ray.js +24 -0
- package/components/VirtualWalkthrough/xr-progress-pie.d.ts +10 -0
- package/components/VirtualWalkthrough/xr-progress-pie.d.ts.map +1 -0
- package/components/VirtualWalkthrough/xr-progress-pie.js +30 -0
- package/components/VirtualWalkthrough/xr-progress-pie.test.js +79 -0
- package/components/VirtualWalkthrough/xr-ray-quad.d.ts +13 -0
- package/components/VirtualWalkthrough/xr-ray-quad.d.ts.map +1 -0
- package/components/VirtualWalkthrough/xr-ray-quad.js +29 -0
- package/components/VirtualWalkthrough/xr-ray-quad.test.js +37 -0
- package/components/VirtualWalkthrough/xr-scene-bounds.d.ts +17 -0
- package/components/VirtualWalkthrough/xr-scene-bounds.d.ts.map +1 -0
- package/components/VirtualWalkthrough/xr-scene-bounds.js +41 -0
- package/components/VirtualWalkthrough/xr-scene-bounds.test.js +70 -0
- package/components/VirtualWalkthrough/xr-teleport-gesture.d.ts +30 -0
- package/components/VirtualWalkthrough/xr-teleport-gesture.d.ts.map +1 -0
- package/components/VirtualWalkthrough/xr-teleport-gesture.js +45 -0
- package/components/VirtualWalkthrough/xr-teleport-gesture.test.js +90 -0
- package/hooks/useXr.d.ts +2 -0
- package/hooks/useXr.d.ts.map +1 -1
- package/hooks/useXr.js +10 -6
- 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,
|
|
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
|
-
*
|
|
4
|
-
*
|
|
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
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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
|
-
|
|
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;
|
|
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
|
-
*
|
|
5
|
-
*
|
|
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
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
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
|
-
//
|
|
17
|
-
|
|
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;
|
|
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:
|
|
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 @@
|
|
|
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;
|