@terraware/web-components 5.0.1-rc.0 → 5.0.1
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/SplatModel.d.ts +11 -1
- package/components/VirtualWalkthrough/SplatModel.d.ts.map +1 -1
- package/components/VirtualWalkthrough/SplatModel.js +16 -1
- package/components/VirtualWalkthrough/VirtualWalkthroughViewer.d.ts +8 -1
- package/components/VirtualWalkthrough/VirtualWalkthroughViewer.d.ts.map +1 -1
- package/components/VirtualWalkthrough/VirtualWalkthroughViewer.js +6 -1
- package/components/VirtualWalkthrough/useXrRenderTuning.d.ts +10 -0
- package/components/VirtualWalkthrough/useXrRenderTuning.d.ts.map +1 -0
- package/components/VirtualWalkthrough/useXrRenderTuning.js +46 -0
- package/components/VirtualWalkthrough/xr-render-tuning.d.ts +36 -0
- package/components/VirtualWalkthrough/xr-render-tuning.d.ts.map +1 -0
- package/components/VirtualWalkthrough/xr-render-tuning.js +54 -0
- package/hooks/useXr.d.ts.map +1 -1
- package/hooks/useXr.js +10 -0
- package/package.json +1 -1
|
@@ -20,8 +20,18 @@ export interface SplatModelProps {
|
|
|
20
20
|
cropFade?: boolean;
|
|
21
21
|
cropFadeDistance?: number;
|
|
22
22
|
revealRain?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Maximum allowable budget for splat rendering operations. Only applies to streamed sog models.
|
|
25
|
+
* A value of 0 means no limit. Left at the engine's default when omitted.
|
|
26
|
+
*/
|
|
27
|
+
splatBudget?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Caps the device pixel ratio the scene renders at, trading sharpness for fill rate on high-DPI
|
|
30
|
+
* displays. Left at the engine's default when omitted.
|
|
31
|
+
*/
|
|
32
|
+
maxPixelRatio?: number;
|
|
23
33
|
onError?: (error: Error) => void;
|
|
24
34
|
}
|
|
25
|
-
declare const _default: React.MemoExoticComponent<({ splatSrc, splatFormat, rotation, scaleFactor, cropAabbMin, cropAabbMax, cropEdgeScaleFactor, cropFade, cropFadeDistance, revealRain, onError, }: SplatModelProps) => import("react/jsx-runtime").JSX.Element | null>;
|
|
35
|
+
declare const _default: React.MemoExoticComponent<({ splatSrc, splatFormat, rotation, scaleFactor, cropAabbMin, cropAabbMax, cropEdgeScaleFactor, cropFade, cropFadeDistance, revealRain, splatBudget, maxPixelRatio, onError, }: SplatModelProps) => import("react/jsx-runtime").JSX.Element | null>;
|
|
26
36
|
export default _default;
|
|
27
37
|
//# sourceMappingURL=SplatModel.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SplatModel.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/SplatModel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA0B,MAAM,OAAO,CAAC;AAU/C,OAAO,EAAE,WAAW,EAA0C,MAAM,eAAe,CAAC;AAEpF,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;
|
|
1
|
+
{"version":3,"file":"SplatModel.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/SplatModel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA0B,MAAM,OAAO,CAAC;AAU/C,OAAO,EAAE,WAAW,EAA0C,MAAM,eAAe,CAAC;AAEpF,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;kOAgBE,eAAe;AAgDlB,wBAAgC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { memo, useEffect } from 'react';
|
|
2
2
|
import { Entity } from '@playcanvas/react';
|
|
3
3
|
import { GSplat } from '@playcanvas/react/components';
|
|
4
|
-
import { useSplat } from '@playcanvas/react/hooks';
|
|
4
|
+
import { useApp, useSplat } from '@playcanvas/react/hooks';
|
|
5
5
|
import BlockingSpinner from './BlockingSpinner.js';
|
|
6
6
|
import SplatCrop from './SplatCrop.js';
|
|
7
7
|
import SplatFadeCrop from './SplatFadeCrop.js';
|
|
@@ -20,8 +20,12 @@ const SplatModel = ({
|
|
|
20
20
|
cropFade = false,
|
|
21
21
|
cropFadeDistance = 0.5,
|
|
22
22
|
revealRain = false,
|
|
23
|
+
splatBudget,
|
|
24
|
+
maxPixelRatio,
|
|
23
25
|
onError
|
|
24
26
|
}) => {
|
|
27
|
+
const app = useApp();
|
|
28
|
+
|
|
25
29
|
// The loader selects its parser from this filename rather than from splatSrc, so it has to name the
|
|
26
30
|
// format we are actually loading. See splatFormat.ts.
|
|
27
31
|
const filename = splatLoaderFilename(splatFormat ?? detectSplatFormat(splatSrc));
|
|
@@ -34,6 +38,17 @@ const SplatModel = ({
|
|
|
34
38
|
filename
|
|
35
39
|
}
|
|
36
40
|
});
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (!app) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (splatBudget !== undefined) {
|
|
46
|
+
app.scene.gsplat.splatBudget = splatBudget;
|
|
47
|
+
}
|
|
48
|
+
if (maxPixelRatio !== undefined) {
|
|
49
|
+
app.graphicsDevice.maxPixelRatio = maxPixelRatio;
|
|
50
|
+
}
|
|
51
|
+
}, [app, splatBudget, maxPixelRatio]);
|
|
37
52
|
useEffect(() => {
|
|
38
53
|
if (error) {
|
|
39
54
|
onError?.(new Error(error));
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AnnotationProps } from './Annotation';
|
|
2
|
+
import { SplatModelProps } from './SplatModel';
|
|
2
3
|
import { SplatFormat } from './splatFormat';
|
|
3
4
|
/**
|
|
4
5
|
* Every coordinate, distance and size in this component is world-space, in the metres an XR
|
|
@@ -30,6 +31,12 @@ export interface VirtualWalkthroughViewerProps {
|
|
|
30
31
|
* entity alone — every other coordinate this component takes is already world-space.
|
|
31
32
|
*/
|
|
32
33
|
scaleFactor?: number;
|
|
34
|
+
/**
|
|
35
|
+
* Overrides passed through to the splat model, for tuning things like `splatBudget` and
|
|
36
|
+
* `maxPixelRatio`. The props this component drives itself — `splatSrc`, `splatFormat`,
|
|
37
|
+
* `rotation`, `scaleFactor` and `revealRain` — take precedence over anything set here.
|
|
38
|
+
*/
|
|
39
|
+
splatModelProps?: Partial<SplatModelProps>;
|
|
33
40
|
annotations: AnnotationProps[];
|
|
34
41
|
onSaveAnnotations: (annotations: AnnotationProps[]) => void | Promise<void>;
|
|
35
42
|
editable?: boolean;
|
|
@@ -38,6 +45,6 @@ export interface VirtualWalkthroughViewerProps {
|
|
|
38
45
|
onToggleFullScreen?: () => void;
|
|
39
46
|
maxImagesPerAnnotation?: number;
|
|
40
47
|
}
|
|
41
|
-
declare const VirtualWalkthroughViewer: ({ splatSrc, splatFormat, origin, cameraPosition, sceneBounds, groundPlane: groundPlaneProp, skyColor, groundColor, averageCameraHeight, scaleFactor, annotations, onSaveAnnotations, editable, showFreeFly, isFullScreen, onToggleFullScreen, maxImagesPerAnnotation, }: VirtualWalkthroughViewerProps) => import("react/jsx-runtime").JSX.Element;
|
|
48
|
+
declare const VirtualWalkthroughViewer: ({ splatSrc, splatFormat, origin, cameraPosition, sceneBounds, groundPlane: groundPlaneProp, skyColor, groundColor, averageCameraHeight, scaleFactor, splatModelProps, annotations, onSaveAnnotations, editable, showFreeFly, isFullScreen, onToggleFullScreen, maxImagesPerAnnotation, }: VirtualWalkthroughViewerProps) => import("react/jsx-runtime").JSX.Element;
|
|
42
49
|
export default VirtualWalkthroughViewer;
|
|
43
50
|
//# sourceMappingURL=VirtualWalkthroughViewer.d.ts.map
|
|
@@ -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;AAO3D,OAAmB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAS3D,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAmB5C;;;;GAIG;AACH,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,6CAA6C;IAC7C,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,8CAA8C;IAC9C,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,2FAA2F;IAC3F,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,uEAAuE;IACvE,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,6CAA6C;IAC7C,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IAC3C,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,iBAAiB,EAAE,CAAC,WAAW,EAAE,eAAe,EAAE,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5E,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,0RAmB/B,6BAA6B,4CAgY/B,CAAC;AAEF,eAAe,wBAAwB,CAAC"}
|
|
@@ -24,6 +24,7 @@ import XrExitButton from './XrExitButton.js';
|
|
|
24
24
|
import XrGazeDwell from './XrGazeDwell.js';
|
|
25
25
|
import XrPointerRay from './XrPointerRay.js';
|
|
26
26
|
import { XrStartPose } from './XrStartPose.js';
|
|
27
|
+
import useXrRenderTuning from './useXrRenderTuning.js';
|
|
27
28
|
import { WalkthroughCamera } from './walkthrough-camera.js';
|
|
28
29
|
import { rayHitsInteractiveUi } from './xr-interactive-ui.js';
|
|
29
30
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
@@ -59,6 +60,7 @@ const VirtualWalkthroughViewer = ({
|
|
|
59
60
|
groundColor,
|
|
60
61
|
averageCameraHeight = 0,
|
|
61
62
|
scaleFactor = 1,
|
|
63
|
+
splatModelProps,
|
|
62
64
|
annotations,
|
|
63
65
|
onSaveAnnotations,
|
|
64
66
|
editable = false,
|
|
@@ -90,6 +92,7 @@ const VirtualWalkthroughViewer = ({
|
|
|
90
92
|
isCurrentlyInVr,
|
|
91
93
|
isCurrentlyInAr
|
|
92
94
|
} = useXr();
|
|
95
|
+
useXrRenderTuning();
|
|
93
96
|
const sceneBoundsRadius = useMemo(() => {
|
|
94
97
|
if (sceneBounds?.m !== undefined) {
|
|
95
98
|
return sceneBounds.m;
|
|
@@ -250,12 +253,13 @@ const VirtualWalkthroughViewer = ({
|
|
|
250
253
|
}), [handleAnnotationUpdate]);
|
|
251
254
|
const canSave = useMemo(() => localAnnotations.every(annotation => annotation.title && annotation.title.trim() !== ''), [localAnnotations]);
|
|
252
255
|
const splatModel = useMemo(() => /*#__PURE__*/jsx(SplatModel, {
|
|
256
|
+
...splatModelProps,
|
|
253
257
|
splatSrc: splatSrc,
|
|
254
258
|
splatFormat: splatFormat,
|
|
255
259
|
rotation: [-180, 0, 0],
|
|
256
260
|
scaleFactor: scaleFactor,
|
|
257
261
|
revealRain: isHighPerformance
|
|
258
|
-
}, 'splat'), [isHighPerformance, splatSrc, splatFormat, scaleFactor]);
|
|
262
|
+
}, 'splat'), [isHighPerformance, splatSrc, splatFormat, scaleFactor, splatModelProps]);
|
|
259
263
|
return /*#__PURE__*/jsxs(Fragment, {
|
|
260
264
|
children: [/*#__PURE__*/jsx(GradientSky, {
|
|
261
265
|
topColor: skyColor || '#FFFFFF',
|
|
@@ -295,6 +299,7 @@ const VirtualWalkthroughViewer = ({
|
|
|
295
299
|
script: TfXrNavigation,
|
|
296
300
|
enabled: !isEdit,
|
|
297
301
|
enableTeleport: !isCurrentlyInAr && !viewingAnnotation,
|
|
302
|
+
enableSnapVertical: false,
|
|
298
303
|
isTeleportBlocked: isTeleportBlocked
|
|
299
304
|
}), !isCurrentlyInXr && /*#__PURE__*/jsx(Script, {
|
|
300
305
|
script: AutoRotator,
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Holds the XR render settings in place for the duration of a session.
|
|
3
|
+
*
|
|
4
|
+
* Call this once per application. The gsplat settings it writes are scene-global, so a second
|
|
5
|
+
* caller would capture the first caller's tuned values as the state to restore, and the scene would
|
|
6
|
+
* come out of the session still tuned.
|
|
7
|
+
*/
|
|
8
|
+
export declare const useXrRenderTuning: () => void;
|
|
9
|
+
export default useXrRenderTuning;
|
|
10
|
+
//# sourceMappingURL=useXrRenderTuning.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useXrRenderTuning.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/useXrRenderTuning.ts"],"names":[],"mappings":"AAMA;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,YAsC7B,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { useApp } from '@playcanvas/react/hooks';
|
|
3
|
+
import { applyGsplatTuning, XR_FIXED_FOVEATION, restoreGsplatTuning } from './xr-render-tuning.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Holds the XR render settings in place for the duration of a session.
|
|
7
|
+
*
|
|
8
|
+
* Call this once per application. The gsplat settings it writes are scene-global, so a second
|
|
9
|
+
* caller would capture the first caller's tuned values as the state to restore, and the scene would
|
|
10
|
+
* come out of the session still tuned.
|
|
11
|
+
*/
|
|
12
|
+
const useXrRenderTuning = () => {
|
|
13
|
+
const app = useApp();
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
const xr = app.xr;
|
|
16
|
+
if (!xr) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
let previous = null;
|
|
20
|
+
const restore = () => {
|
|
21
|
+
if (previous) {
|
|
22
|
+
restoreGsplatTuning(app.scene.gsplat, previous);
|
|
23
|
+
previous = null;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const handleStart = () => {
|
|
27
|
+
// Guards against capturing already-tuned values as the restore target if `start` ever
|
|
28
|
+
// fires again before a matching `end`.
|
|
29
|
+
if (!previous) {
|
|
30
|
+
previous = applyGsplatTuning(app.scene.gsplat);
|
|
31
|
+
}
|
|
32
|
+
// Only settable on an active session, so it cannot go in the start options alongside
|
|
33
|
+
// framebufferScaleFactor. A no-op when the presentation layer doesn't support it.
|
|
34
|
+
xr.fixedFoveation = XR_FIXED_FOVEATION;
|
|
35
|
+
};
|
|
36
|
+
xr.on('start', handleStart);
|
|
37
|
+
xr.on('end', restore);
|
|
38
|
+
return () => {
|
|
39
|
+
xr.off('start', handleStart);
|
|
40
|
+
xr.off('end', restore);
|
|
41
|
+
restore();
|
|
42
|
+
};
|
|
43
|
+
}, [app]);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export { useXrRenderTuning as default, useXrRenderTuning };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render settings applied for the duration of an XR session. Splats rasterize as alpha-blended
|
|
3
|
+
* quads with no early-z rejection, which makes a headset's stereo pixel throughput the binding
|
|
4
|
+
* constraint on framerate long before splat count is.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Peripheral back-buffer resolution reduction, 0 (off) to 1 (strongest). Ineffective while MSAA is
|
|
8
|
+
* on, which is one reason the application requests `antialias: false`.
|
|
9
|
+
*/
|
|
10
|
+
export declare const XR_FIXED_FOVEATION = 1;
|
|
11
|
+
/**
|
|
12
|
+
* The subset of `GSplatParams` this module writes. Structural rather than the PlayCanvas type so
|
|
13
|
+
* the logic can be exercised against a plain object.
|
|
14
|
+
*/
|
|
15
|
+
export interface GsplatTuningParams {
|
|
16
|
+
radialSorting: boolean;
|
|
17
|
+
alphaClipForward: number;
|
|
18
|
+
minPixelSize: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* `radialSorting` orders splats by distance from the camera rather than by depth along its forward
|
|
22
|
+
* axis, which is the more accurate ordering when the camera rotates rather than translates. Sorting
|
|
23
|
+
* runs on a worker and so lags the view by at least a frame; head rotation dominates in a headset,
|
|
24
|
+
* where that lag is what reads as swimming.
|
|
25
|
+
*
|
|
26
|
+
* The other two trade fidelity for fill rate: `alphaClipForward` drops near-transparent splats from
|
|
27
|
+
* the forward pass, and `minPixelSize` drops splats that project to less than a few pixels.
|
|
28
|
+
*/
|
|
29
|
+
export declare const XR_GSPLAT_TUNING: GsplatTuningParams;
|
|
30
|
+
/**
|
|
31
|
+
* Applies the XR settings, returning the values that were replaced so they can be restored when the
|
|
32
|
+
* session ends. These live on the scene, so leaving them tuned would degrade desktop rendering.
|
|
33
|
+
*/
|
|
34
|
+
export declare const applyGsplatTuning: (params: GsplatTuningParams) => GsplatTuningParams;
|
|
35
|
+
export declare const restoreGsplatTuning: (params: GsplatTuningParams, previous: GsplatTuningParams) => void;
|
|
36
|
+
//# sourceMappingURL=xr-render-tuning.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xr-render-tuning.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/xr-render-tuning.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;GAGG;AACH,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,OAAO,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,EAAE,kBAI9B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,QAAQ,kBAAkB,KAAG,kBAY9D,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,QAAQ,kBAAkB,EAAE,UAAU,kBAAkB,KAAG,IAI9F,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render settings applied for the duration of an XR session. Splats rasterize as alpha-blended
|
|
3
|
+
* quads with no early-z rejection, which makes a headset's stereo pixel throughput the binding
|
|
4
|
+
* constraint on framerate long before splat count is.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Peripheral back-buffer resolution reduction, 0 (off) to 1 (strongest). Ineffective while MSAA is
|
|
9
|
+
* on, which is one reason the application requests `antialias: false`.
|
|
10
|
+
*/
|
|
11
|
+
const XR_FIXED_FOVEATION = 1;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The subset of `GSplatParams` this module writes. Structural rather than the PlayCanvas type so
|
|
15
|
+
* the logic can be exercised against a plain object.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* `radialSorting` orders splats by distance from the camera rather than by depth along its forward
|
|
20
|
+
* axis, which is the more accurate ordering when the camera rotates rather than translates. Sorting
|
|
21
|
+
* runs on a worker and so lags the view by at least a frame; head rotation dominates in a headset,
|
|
22
|
+
* where that lag is what reads as swimming.
|
|
23
|
+
*
|
|
24
|
+
* The other two trade fidelity for fill rate: `alphaClipForward` drops near-transparent splats from
|
|
25
|
+
* the forward pass, and `minPixelSize` drops splats that project to less than a few pixels.
|
|
26
|
+
*/
|
|
27
|
+
const XR_GSPLAT_TUNING = {
|
|
28
|
+
radialSorting: true,
|
|
29
|
+
alphaClipForward: 0.1,
|
|
30
|
+
minPixelSize: 1
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Applies the XR settings, returning the values that were replaced so they can be restored when the
|
|
35
|
+
* session ends. These live on the scene, so leaving them tuned would degrade desktop rendering.
|
|
36
|
+
*/
|
|
37
|
+
const applyGsplatTuning = params => {
|
|
38
|
+
const previous = {
|
|
39
|
+
radialSorting: params.radialSorting,
|
|
40
|
+
alphaClipForward: params.alphaClipForward,
|
|
41
|
+
minPixelSize: params.minPixelSize
|
|
42
|
+
};
|
|
43
|
+
params.radialSorting = XR_GSPLAT_TUNING.radialSorting;
|
|
44
|
+
params.alphaClipForward = XR_GSPLAT_TUNING.alphaClipForward;
|
|
45
|
+
params.minPixelSize = XR_GSPLAT_TUNING.minPixelSize;
|
|
46
|
+
return previous;
|
|
47
|
+
};
|
|
48
|
+
const restoreGsplatTuning = (params, previous) => {
|
|
49
|
+
params.radialSorting = previous.radialSorting;
|
|
50
|
+
params.alphaClipForward = previous.alphaClipForward;
|
|
51
|
+
params.minPixelSize = previous.minPixelSize;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export { XR_FIXED_FOVEATION, XR_GSPLAT_TUNING, applyGsplatTuning, restoreGsplatTuning };
|
package/hooks/useXr.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useXr.d.ts","sourceRoot":"","sources":["../../src/hooks/useXr.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;AAEjC,UAAU,YAAY;IACpB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;
|
|
1
|
+
{"version":3,"file":"useXr.d.ts","sourceRoot":"","sources":["../../src/hooks/useXr.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;AAEjC,UAAU,YAAY;IACpB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAgBD,eAAO,MAAM,KAAK,GAAI,cAAa,YAAiB;0BA4CT,MAAM;oBAGtC,MAAM;;;;;CA8BhB,CAAC;AAEF,eAAe,KAAK,CAAC"}
|
package/hooks/useXr.js
CHANGED
|
@@ -6,6 +6,15 @@ const XR_TYPES = {
|
|
|
6
6
|
VR: XRTYPE_VR,
|
|
7
7
|
AR: XRTYPE_AR
|
|
8
8
|
};
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Factor PlayCanvas applies on top of the device pixel ratio to compute the headset's
|
|
12
|
+
* framebuffer resolution: `app.graphicsDevice.maxPixelRatio / window.devicePixelRatio *
|
|
13
|
+
* XR_FRAMEBUFFER_SCALE_FACTOR`. Splat rendering in stereo is fill-rate bound, so this (and
|
|
14
|
+
* `maxPixelRatio`, which also multiplies XR resolution) is the largest lever on XR framerate.
|
|
15
|
+
* Read once when the session starts and fixed for its lifetime.
|
|
16
|
+
*/
|
|
17
|
+
const XR_FRAMEBUFFER_SCALE_FACTOR = 1;
|
|
9
18
|
const useXr = ({
|
|
10
19
|
onError
|
|
11
20
|
} = {}) => {
|
|
@@ -56,6 +65,7 @@ const useXr = ({
|
|
|
56
65
|
const startXr = useCallback(type => {
|
|
57
66
|
const camera = app.root.findComponent('camera');
|
|
58
67
|
app.xr?.start(camera, XR_TYPES[type], XRSPACE_LOCALFLOOR, {
|
|
68
|
+
framebufferScaleFactor: XR_FRAMEBUFFER_SCALE_FACTOR,
|
|
59
69
|
callback: err => {
|
|
60
70
|
if (err) {
|
|
61
71
|
onError?.(err);
|