@pascal-app/viewer 1.0.0-beta.5 → 1.0.0
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/LICENSE +21 -0
- package/README.md +55 -0
- package/dist/capture/asset-url.d.ts +2 -0
- package/dist/capture/asset-url.d.ts.map +1 -0
- package/dist/capture/asset-url.js +13 -0
- package/dist/capture/capture-runtime.d.ts +43 -0
- package/dist/capture/capture-runtime.d.ts.map +1 -0
- package/dist/capture/capture-runtime.js +228 -0
- package/dist/capture/frame.d.ts +4 -0
- package/dist/capture/frame.d.ts.map +1 -0
- package/dist/capture/frame.js +21 -0
- package/dist/capture/index.d.ts +12 -0
- package/dist/capture/index.d.ts.map +1 -0
- package/dist/capture/index.js +11 -0
- package/dist/capture/layer-visibility.d.ts +5 -0
- package/dist/capture/layer-visibility.d.ts.map +1 -0
- package/dist/capture/layer-visibility.js +11 -0
- package/dist/capture/layers/clay-matcap.d.ts +3 -0
- package/dist/capture/layers/clay-matcap.d.ts.map +1 -0
- package/dist/capture/layers/clay-matcap.js +26 -0
- package/dist/capture/layers/device-motion-layer.d.ts +7 -0
- package/dist/capture/layers/device-motion-layer.d.ts.map +1 -0
- package/dist/capture/layers/device-motion-layer.js +78 -0
- package/dist/capture/layers/point-cloud-layer.d.ts +15 -0
- package/dist/capture/layers/point-cloud-layer.d.ts.map +1 -0
- package/dist/capture/layers/point-cloud-layer.js +106 -0
- package/dist/capture/layers/room-model-layer.d.ts +9 -0
- package/dist/capture/layers/room-model-layer.d.ts.map +1 -0
- package/dist/capture/layers/room-model-layer.js +71 -0
- package/dist/capture/layers/surface-mesh-data.d.ts +9 -0
- package/dist/capture/layers/surface-mesh-data.d.ts.map +1 -0
- package/dist/capture/layers/surface-mesh-data.js +74 -0
- package/dist/capture/layers/surface-mesh-layer.d.ts +7 -0
- package/dist/capture/layers/surface-mesh-layer.d.ts.map +1 -0
- package/dist/capture/layers/surface-mesh-layer.js +16 -0
- package/dist/capture/preview.d.ts +3 -0
- package/dist/capture/preview.d.ts.map +1 -0
- package/dist/capture/preview.js +2 -0
- package/dist/capture/source-state.d.ts +22 -0
- package/dist/capture/source-state.d.ts.map +1 -0
- package/dist/capture/source-state.js +182 -0
- package/dist/capture/stream-rendering.d.ts +14 -0
- package/dist/capture/stream-rendering.d.ts.map +1 -0
- package/dist/capture/stream-rendering.js +69 -0
- package/dist/capture/trajectory.d.ts +20 -0
- package/dist/capture/trajectory.d.ts.map +1 -0
- package/dist/capture/trajectory.js +77 -0
- package/dist/components/viewer/batched-mesh-spike.d.ts +14 -0
- package/dist/components/viewer/batched-mesh-spike.d.ts.map +1 -0
- package/dist/components/viewer/batched-mesh-spike.js +64 -0
- package/dist/components/viewer/frame-limiter.d.ts.map +1 -1
- package/dist/components/viewer/frame-limiter.js +4 -3
- package/dist/components/viewer/index.d.ts.map +1 -1
- package/dist/components/viewer/index.js +70 -62
- package/dist/components/viewer/lights.d.ts.map +1 -1
- package/dist/components/viewer/lights.js +123 -89
- package/dist/components/viewer/perf-monitor.d.ts +6 -1
- package/dist/components/viewer/perf-monitor.d.ts.map +1 -1
- package/dist/components/viewer/perf-monitor.js +307 -116
- package/dist/components/viewer/perf-panel.d.ts +3 -0
- package/dist/components/viewer/perf-panel.d.ts.map +1 -0
- package/dist/components/viewer/perf-panel.js +178 -0
- package/dist/components/viewer/pointer-raycast-layers.d.ts +15 -0
- package/dist/components/viewer/pointer-raycast-layers.d.ts.map +1 -0
- package/dist/components/viewer/pointer-raycast-layers.js +28 -0
- package/dist/components/viewer/post-processing.d.ts.map +1 -1
- package/dist/components/viewer/post-processing.js +124 -83
- package/dist/components/viewer/registered-systems.d.ts +14 -5
- package/dist/components/viewer/registered-systems.d.ts.map +1 -1
- package/dist/components/viewer/registered-systems.js +22 -10
- package/dist/components/viewer/scene-atmosphere.d.ts +30 -0
- package/dist/components/viewer/scene-atmosphere.d.ts.map +1 -0
- package/dist/components/viewer/scene-atmosphere.js +89 -0
- package/dist/components/viewer/scene-environment.d.ts.map +1 -1
- package/dist/components/viewer/scene-environment.js +19 -5
- package/dist/components/viewer/scene-ground-replacement.d.ts +5 -0
- package/dist/components/viewer/scene-ground-replacement.d.ts.map +1 -0
- package/dist/components/viewer/scene-ground-replacement.js +35 -0
- package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
- package/dist/components/viewer/viewer-camera.js +4 -1
- package/dist/components/viewer/viewer-presentations.d.ts +67 -0
- package/dist/components/viewer/viewer-presentations.d.ts.map +1 -0
- package/dist/components/viewer/viewer-presentations.js +120 -0
- package/dist/hooks/use-library-materials-version.d.ts +7 -0
- package/dist/hooks/use-library-materials-version.d.ts.map +1 -0
- package/dist/hooks/use-library-materials-version.js +10 -0
- package/dist/hooks/use-node-events.d.ts.map +1 -1
- package/dist/hooks/use-node-events.js +1 -0
- package/dist/index.d.ts +15 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -6
- package/dist/lib/csg-utils.d.ts +1 -0
- package/dist/lib/csg-utils.d.ts.map +1 -1
- package/dist/lib/csg-utils.js +6 -1
- package/dist/lib/gpu-perf.d.ts +0 -2
- package/dist/lib/gpu-perf.d.ts.map +1 -1
- package/dist/lib/gpu-perf.js +7 -21
- package/dist/lib/layer-pass.d.ts +28 -0
- package/dist/lib/layer-pass.d.ts.map +1 -0
- package/dist/lib/layer-pass.js +254 -0
- package/dist/lib/materials.d.ts +12 -0
- package/dist/lib/materials.d.ts.map +1 -1
- package/dist/lib/materials.js +51 -17
- package/dist/lib/merged-outline-node.d.ts +17 -8
- package/dist/lib/merged-outline-node.d.ts.map +1 -1
- package/dist/lib/merged-outline-node.js +194 -52
- package/dist/lib/perf-actions.d.ts +55 -0
- package/dist/lib/perf-actions.d.ts.map +1 -0
- package/dist/lib/perf-actions.js +211 -0
- package/dist/lib/perf-observers.d.ts +2 -0
- package/dist/lib/perf-observers.d.ts.map +1 -0
- package/dist/lib/perf-observers.js +17 -0
- package/dist/lib/perf-panel-store.d.ts +71 -0
- package/dist/lib/perf-panel-store.d.ts.map +1 -0
- package/dist/lib/perf-panel-store.js +29 -0
- package/dist/lib/perf-tracks.d.ts +43 -0
- package/dist/lib/perf-tracks.d.ts.map +1 -0
- package/dist/lib/perf-tracks.js +113 -0
- package/dist/lib/pointer-events.d.ts +7 -0
- package/dist/lib/pointer-events.d.ts.map +1 -0
- package/dist/lib/pointer-events.js +680 -0
- package/dist/lib/post-processing-resources.d.ts +12 -0
- package/dist/lib/post-processing-resources.d.ts.map +1 -0
- package/dist/lib/post-processing-resources.js +19 -0
- package/dist/lib/renderer-capability.d.ts.map +1 -1
- package/dist/lib/renderer-capability.js +4 -2
- package/dist/lib/scene-visibility.d.ts +2 -1
- package/dist/lib/scene-visibility.d.ts.map +1 -1
- package/dist/lib/scene-visibility.js +22 -1
- package/dist/store/use-viewer.d.ts +2 -2
- package/dist/store/use-viewer.d.ts.map +1 -1
- package/dist/systems/ceiling/ceiling-system.js +1 -1
- package/dist/systems/door/door-animation-system.d.ts.map +1 -1
- package/dist/systems/door/door-animation-system.js +6 -9
- package/dist/systems/door/door-system.d.ts.map +1 -1
- package/dist/systems/door/door-system.js +13 -2
- package/dist/systems/geometry/geometry-system.d.ts.map +1 -1
- package/dist/systems/geometry/geometry-system.js +3 -2
- package/dist/systems/level/level-system.d.ts.map +1 -1
- package/dist/systems/level/level-system.js +1 -1
- package/dist/systems/level/level-utils.d.ts +9 -0
- package/dist/systems/level/level-utils.d.ts.map +1 -1
- package/dist/systems/level/level-utils.js +14 -0
- package/dist/systems/perf-action-settle/perf-action-settle-system.d.ts +8 -0
- package/dist/systems/perf-action-settle/perf-action-settle-system.d.ts.map +1 -0
- package/dist/systems/perf-action-settle/perf-action-settle-system.js +32 -0
- package/dist/systems/roof/roof-system.d.ts.map +1 -1
- package/dist/systems/roof/roof-system.js +1196 -58
- package/dist/systems/scan/scan-system.js +6 -5
- package/dist/systems/slab/slab-system.d.ts.map +1 -1
- package/dist/systems/slab/slab-system.js +1 -2
- package/dist/systems/stair/stair-system.d.ts.map +1 -1
- package/dist/systems/stair/stair-system.js +9 -8
- package/dist/systems/surface-hole-geometry.d.ts +1 -1
- package/dist/systems/surface-hole-geometry.d.ts.map +1 -1
- package/dist/systems/surface-hole-geometry.js +1 -1
- package/dist/systems/wall/level-miter-cache.d.ts.map +1 -1
- package/dist/systems/wall/wall-build-lifecycle.d.ts +9 -0
- package/dist/systems/wall/wall-build-lifecycle.d.ts.map +1 -0
- package/dist/systems/wall/wall-build-lifecycle.js +74 -0
- package/dist/systems/wall/wall-cutout-cache.d.ts +57 -0
- package/dist/systems/wall/wall-cutout-cache.d.ts.map +1 -0
- package/dist/systems/wall/wall-cutout-cache.js +304 -0
- package/dist/systems/wall/wall-cutout.d.ts +12 -1
- package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
- package/dist/systems/wall/wall-cutout.js +24 -161
- package/dist/systems/wall/wall-material-variant.d.ts +31 -0
- package/dist/systems/wall/wall-material-variant.d.ts.map +1 -0
- package/dist/systems/wall/wall-material-variant.js +10 -0
- package/dist/systems/wall/wall-materials.d.ts +6 -0
- package/dist/systems/wall/wall-materials.d.ts.map +1 -1
- package/dist/systems/wall/wall-materials.js +29 -13
- package/dist/systems/wall/wall-placeholder-sweep.d.ts +50 -0
- package/dist/systems/wall/wall-placeholder-sweep.d.ts.map +1 -0
- package/dist/systems/wall/wall-placeholder-sweep.js +51 -0
- package/dist/systems/wall/wall-rebuild-notifications.d.ts +5 -0
- package/dist/systems/wall/wall-rebuild-notifications.d.ts.map +1 -0
- package/dist/systems/wall/wall-rebuild-notifications.js +23 -0
- package/dist/systems/wall/wall-system.d.ts +11 -4
- package/dist/systems/wall/wall-system.d.ts.map +1 -1
- package/dist/systems/wall/wall-system.js +405 -153
- package/dist/systems/window/window-animation-system.d.ts.map +1 -1
- package/dist/systems/window/window-animation-system.js +9 -8
- package/dist/systems/window/window-system.d.ts +1 -0
- package/dist/systems/window/window-system.d.ts.map +1 -1
- package/dist/systems/window/window-system.js +16 -2
- package/package.json +17 -4
- package/dist/lib/polygon-union.d.ts +0 -4
- package/dist/lib/polygon-union.d.ts.map +0 -1
- package/dist/lib/polygon-union.js +0 -289
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Pascal Group Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -120,6 +120,61 @@ floor-plan pan/zoom/rotation, and the compass synchronize through transient subs
|
|
|
120
120
|
navigation does not require a React render per frame. Set `showCompass={false}` or
|
|
121
121
|
`showSwitcher={false}` when the host supplies its own controls.
|
|
122
122
|
|
|
123
|
+
## Capture Sessions
|
|
124
|
+
|
|
125
|
+
`@pascal-app/viewer/capture` holds the optional capture runtime and its reference layers. Mount
|
|
126
|
+
`CaptureRuntime` as a child of `Viewer` and provide a source resolver. The host owns access control
|
|
127
|
+
and transport; the runtime owns source lifecycle, scan-node placement, layer visibility, and
|
|
128
|
+
reference renderers for RoomPlan models, device trajectories, and PLY/live point clouds. The
|
|
129
|
+
session contracts it consumes live in `@pascal-app/core/capture`.
|
|
130
|
+
|
|
131
|
+
```tsx
|
|
132
|
+
import { createHttpCaptureSource } from '@pascal-app/core/capture'
|
|
133
|
+
import { Viewer } from '@pascal-app/viewer'
|
|
134
|
+
import { CaptureRuntime } from '@pascal-app/viewer/capture'
|
|
135
|
+
|
|
136
|
+
function CaptureViewer() {
|
|
137
|
+
return (
|
|
138
|
+
<Viewer>
|
|
139
|
+
<CaptureRuntime
|
|
140
|
+
onError={(error, context) => reportCaptureError(error, context)}
|
|
141
|
+
resolveSource={(locator) => createHttpCaptureSource(locator, { credentials: 'include' })}
|
|
142
|
+
retryKey={retryVersion}
|
|
143
|
+
/>
|
|
144
|
+
</Viewer>
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Unknown streams remain in the descriptor and can be rendered by passing a custom renderer keyed by
|
|
150
|
+
stream role or kind. A live transport implements `CaptureSource.subscribe()`; no particular
|
|
151
|
+
WebSocket, WebRTC, or collaboration backend is required.
|
|
152
|
+
|
|
153
|
+
`CaptureRuntime` keeps telemetry host-neutral: pass `onError` to report source or per-stream
|
|
154
|
+
failures in the host, then increment `retryKey` to reload every affected session. Direct
|
|
155
|
+
`useCaptureSource()` consumers can call its `retry()` function instead.
|
|
156
|
+
|
|
157
|
+
Hosts can pass `defaultLayerVisibility` to keep expensive optional layers disabled until a user
|
|
158
|
+
enables them. Persisted values in the scan node's `layers` map always override those host defaults;
|
|
159
|
+
without host defaults, every available layer remains visible for backwards compatibility. Hidden
|
|
160
|
+
sessions and layers are unmounted rather than only made visually transparent, so they stop
|
|
161
|
+
raycasting, artifact work, animation, and live packet subscriptions while disabled.
|
|
162
|
+
|
|
163
|
+
### Local surface previews
|
|
164
|
+
|
|
165
|
+
`@pascal-app/viewer/capture/preview` exports `createSurfaceMeshGeometry` and `createClayMatcap`
|
|
166
|
+
without importing the React viewer runtime, so a capture client can render a locally saved surface
|
|
167
|
+
immediately, before its archive is uploaded. The geometry decoder uses the shared
|
|
168
|
+
`@pascal-app/core/capture` validator, including the native 20,000-face budget, byte lengths, and
|
|
169
|
+
index bounds. It returns `null` for invalid input. The host owns the returned geometry and matcap
|
|
170
|
+
texture and must dispose them on teardown.
|
|
171
|
+
|
|
172
|
+
Direct `CaptureStreamLayer` consumers can pass
|
|
173
|
+
`meshPresentation={{ previewMaterial: 'clay', dollhouse: true }}`. Clay replaces preliminary vertex
|
|
174
|
+
colors; dollhouse enables front-face rendering for surface previews and room models, revealing
|
|
175
|
+
inward-facing room surfaces from outside. It changes per-instance materials, not geometry or
|
|
176
|
+
loader-cached materials. Omitting these options preserves the existing presentation.
|
|
177
|
+
|
|
123
178
|
## Viewer State
|
|
124
179
|
|
|
125
180
|
```typescript
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"asset-url.d.ts","sourceRoot":"","sources":["../../src/capture/asset-url.ts"],"names":[],"mappings":"AAAA,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAa7D"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function rewriteLoopbackAssetUrl(value) {
|
|
2
|
+
try {
|
|
3
|
+
const url = new URL(value);
|
|
4
|
+
if (typeof window !== 'undefined' &&
|
|
5
|
+
(url.hostname === '127.0.0.1' || url.hostname === 'localhost')) {
|
|
6
|
+
url.hostname = window.location.hostname;
|
|
7
|
+
}
|
|
8
|
+
return url.toString();
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type ScanNode } from '@pascal-app/core';
|
|
2
|
+
import { type CaptureSessionDescriptor, type CaptureSource, type CaptureSourceResolver, type CaptureStreamDescriptor, type CaptureStreamPacket } from '@pascal-app/core/capture';
|
|
3
|
+
import { type ComponentType } from 'react';
|
|
4
|
+
export type CaptureMeshPresentation = {
|
|
5
|
+
dollhouse?: boolean;
|
|
6
|
+
previewMaterial?: 'clay' | 'recorded';
|
|
7
|
+
};
|
|
8
|
+
export type CaptureStreamRendererProps = {
|
|
9
|
+
artifactUrl: string | null;
|
|
10
|
+
descriptor: CaptureSessionDescriptor;
|
|
11
|
+
meshPresentation?: CaptureMeshPresentation;
|
|
12
|
+
packets: readonly CaptureStreamPacket[];
|
|
13
|
+
scan: ScanNode;
|
|
14
|
+
source: CaptureSource;
|
|
15
|
+
stream: CaptureStreamDescriptor;
|
|
16
|
+
streamEpoch: string;
|
|
17
|
+
};
|
|
18
|
+
export type CaptureStreamRenderer = ComponentType<CaptureStreamRendererProps>;
|
|
19
|
+
export type CaptureRuntimeErrorContext = {
|
|
20
|
+
phase: 'source';
|
|
21
|
+
scanId: ScanNode['id'];
|
|
22
|
+
sessionId: string;
|
|
23
|
+
} | {
|
|
24
|
+
layerKey: string;
|
|
25
|
+
phase: 'stream';
|
|
26
|
+
scanId: ScanNode['id'];
|
|
27
|
+
sessionId: string;
|
|
28
|
+
streamId: string;
|
|
29
|
+
streamKind: string;
|
|
30
|
+
};
|
|
31
|
+
export type CaptureRuntimeProps = {
|
|
32
|
+
defaultLayerVisibility?: Readonly<Record<string, boolean>>;
|
|
33
|
+
maxPacketsPerStream?: number;
|
|
34
|
+
onError?: (error: Error, context: CaptureRuntimeErrorContext) => void;
|
|
35
|
+
renderers?: Readonly<Record<string, CaptureStreamRenderer>>;
|
|
36
|
+
resolveSource: CaptureSourceResolver;
|
|
37
|
+
retryKey?: number | string;
|
|
38
|
+
};
|
|
39
|
+
export declare function CaptureRuntime({ defaultLayerVisibility, maxPacketsPerStream, onError, renderers, resolveSource, retryKey, }: CaptureRuntimeProps): import("react").JSX.Element;
|
|
40
|
+
export declare function CaptureStreamLayer({ descriptor, meshPresentation, packets, renderers, scan, source, stream, streamEpoch, }: Omit<CaptureStreamRendererProps, 'artifactUrl'> & {
|
|
41
|
+
renderers: Readonly<Record<string, CaptureStreamRenderer>>;
|
|
42
|
+
}): import("react").JSX.Element | null;
|
|
43
|
+
//# sourceMappingURL=capture-runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture-runtime.d.ts","sourceRoot":"","sources":["../../src/capture/capture-runtime.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,QAAQ,EAA2B,MAAM,kBAAkB,CAAA;AACzE,OAAO,EAIL,KAAK,wBAAwB,EAE7B,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAC5B,KAAK,mBAAmB,EAGzB,MAAM,0BAA0B,CAAA;AAEjC,OAAO,EACL,KAAK,aAAa,EAQnB,MAAM,OAAO,CAAA;AAsBd,MAAM,MAAM,uBAAuB,GAAG;IACpC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,eAAe,CAAC,EAAE,MAAM,GAAG,UAAU,CAAA;CACtC,CAAA;AAED,MAAM,MAAM,0BAA0B,GAAG;IACvC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,UAAU,EAAE,wBAAwB,CAAA;IACpC,gBAAgB,CAAC,EAAE,uBAAuB,CAAA;IAC1C,OAAO,EAAE,SAAS,mBAAmB,EAAE,CAAA;IACvC,IAAI,EAAE,QAAQ,CAAA;IACd,MAAM,EAAE,aAAa,CAAA;IACrB,MAAM,EAAE,uBAAuB,CAAA;IAC/B,WAAW,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG,aAAa,CAAC,0BAA0B,CAAC,CAAA;AAE7E,MAAM,MAAM,0BAA0B,GAClC;IACE,KAAK,EAAE,QAAQ,CAAA;IACf,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;CAClB,GACD;IACE,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,QAAQ,CAAA;IACf,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA;AAEL,MAAM,MAAM,mBAAmB,GAAG;IAChC,sBAAsB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IAC1D,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,0BAA0B,KAAK,IAAI,CAAA;IACrE,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC,CAAA;IAC3D,aAAa,EAAE,qBAAqB,CAAA;IACpC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAC3B,CAAA;AAMD,wBAAgB,cAAc,CAAC,EAC7B,sBAA+C,EAC/C,mBAAwB,EACxB,OAAO,EACP,SAA2B,EAC3B,aAAa,EACb,QAAY,GACb,EAAE,mBAAmB,+BA6BrB;AAoHD,wBAAgB,kBAAkB,CAAC,EACjC,UAAU,EACV,gBAAgB,EAChB,OAAO,EACP,SAAS,EACT,IAAI,EACJ,MAAM,EACN,MAAM,EACN,WAAW,GACZ,EAAE,IAAI,CAAC,0BAA0B,EAAE,aAAa,CAAC,GAAG;IACnD,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC,CAAA;CAC3D,sCAqGA"}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { sceneRegistry, useScene } from '@pascal-app/core';
|
|
4
|
+
import { CaptureArtifactReferenceSchema, captureLayerKey, DeviceMotionTrajectorySchema, } from '@pascal-app/core/capture';
|
|
5
|
+
import { createPortal, useFrame } from '@react-three/fiber';
|
|
6
|
+
import { Suspense, useCallback, useEffect, useMemo, useRef, useState, } from 'react';
|
|
7
|
+
import { ErrorBoundary } from '../components/error-boundary';
|
|
8
|
+
import { useNodeEvents } from '../hooks/use-node-events';
|
|
9
|
+
import useViewer from '../store/use-viewer';
|
|
10
|
+
import { rewriteLoopbackAssetUrl } from './asset-url';
|
|
11
|
+
import { resolveCaptureFrameMatrix } from './frame';
|
|
12
|
+
import { isCaptureSessionVisible, isCaptureStreamVisible } from './layer-visibility';
|
|
13
|
+
import { CaptureDeviceMotionLayer } from './layers/device-motion-layer';
|
|
14
|
+
import { CapturePointCloudLayer } from './layers/point-cloud-layer';
|
|
15
|
+
import { CaptureRoomModel } from './layers/room-model-layer';
|
|
16
|
+
import { CaptureSurfaceMeshLayer } from './layers/surface-mesh-layer';
|
|
17
|
+
import { useCaptureSource } from './source-state';
|
|
18
|
+
import { captureModelFormat, isCaptureModelArtifact, isCapturePointCloudArtifact, isCaptureStreamRenderable, streamHydratesJsonPayload, } from './stream-rendering';
|
|
19
|
+
import { parseDeviceTrajectoryPackets, parseDeviceTrajectoryPayload } from './trajectory';
|
|
20
|
+
const EMPTY_RENDERERS = {};
|
|
21
|
+
const EMPTY_LAYER_VISIBILITY = {};
|
|
22
|
+
export function CaptureRuntime({ defaultLayerVisibility = EMPTY_LAYER_VISIBILITY, maxPacketsPerStream = 32, onError, renderers = EMPTY_RENDERERS, resolveSource, retryKey = 0, }) {
|
|
23
|
+
const nodes = useScene((state) => state.nodes);
|
|
24
|
+
const showScans = useViewer((state) => state.showScans);
|
|
25
|
+
const scans = useMemo(() => Object.values(nodes).filter((node) => node.type === 'scan' &&
|
|
26
|
+
node.captureSession !== null &&
|
|
27
|
+
isCaptureSessionVisible(showScans, node.visible)), [nodes, showScans]);
|
|
28
|
+
return (_jsx(_Fragment, { children: scans.map((scan) => (_jsx(CaptureSessionPortal, { defaultLayerVisibility: defaultLayerVisibility, maxPacketsPerStream: maxPacketsPerStream, onError: onError, renderers: renderers, resolveSource: resolveSource, scan: scan }, `${scan.id}:${retryKey}`))) }));
|
|
29
|
+
}
|
|
30
|
+
function CaptureSessionPortal({ defaultLayerVisibility, maxPacketsPerStream, onError, renderers, resolveSource, scan, }) {
|
|
31
|
+
const [target, setTarget] = useState(null);
|
|
32
|
+
const onErrorRef = useRef(onError);
|
|
33
|
+
const handlers = useNodeEvents(scan, 'scan');
|
|
34
|
+
const customRendererKeys = useMemo(() => new Set(Object.keys(renderers)), [renderers]);
|
|
35
|
+
const streamFilter = useCallback((stream) => isCaptureStreamVisible(stream, scan.layers, defaultLayerVisibility) &&
|
|
36
|
+
isCaptureStreamRenderable(stream, customRendererKeys), [customRendererKeys, defaultLayerVisibility, scan.layers]);
|
|
37
|
+
const sourceState = useCaptureSource(scan.captureSession, resolveSource, {
|
|
38
|
+
maxPacketsPerStream,
|
|
39
|
+
streamFilter,
|
|
40
|
+
});
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
onErrorRef.current = onError;
|
|
43
|
+
}, [onError]);
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (!sourceState.error)
|
|
46
|
+
return;
|
|
47
|
+
onErrorRef.current?.(sourceState.error, {
|
|
48
|
+
phase: 'source',
|
|
49
|
+
scanId: scan.id,
|
|
50
|
+
sessionId: scan.captureSession.sessionId,
|
|
51
|
+
});
|
|
52
|
+
}, [scan.captureSession.sessionId, scan.id, sourceState.error]);
|
|
53
|
+
useFrame(() => {
|
|
54
|
+
const nextTarget = sceneRegistry.nodes.get(scan.id) ?? null;
|
|
55
|
+
if (nextTarget !== target)
|
|
56
|
+
setTarget(nextTarget);
|
|
57
|
+
});
|
|
58
|
+
const descriptor = sourceState.descriptor;
|
|
59
|
+
const source = sourceState.source;
|
|
60
|
+
if (!(target && descriptor && source))
|
|
61
|
+
return null;
|
|
62
|
+
const visibleStreams = descriptor.streams.filter(streamFilter);
|
|
63
|
+
return createPortal(_jsx("group", { ...handlers, children: visibleStreams.map((stream) => {
|
|
64
|
+
const layerKey = captureLayerKey(stream);
|
|
65
|
+
const renderKey = captureStreamRenderKey(stream);
|
|
66
|
+
const packets = sourceState.packets[stream.id] ?? [];
|
|
67
|
+
const streamEpoch = sourceState.streamEpochs[stream.id] ??
|
|
68
|
+
`descriptor:${descriptor.revisionId ?? ''}:${stream.id}:${stream.frameId ?? ''}`;
|
|
69
|
+
const latestPacket = packets.at(-1);
|
|
70
|
+
const packetRevision = latestPacket
|
|
71
|
+
? `${latestPacket.generation}:${latestPacket.sequence}:${latestPacket.frameId ?? ''}`
|
|
72
|
+
: 'static';
|
|
73
|
+
return (_jsx(ErrorBoundary, { fallback: _jsx("group", {}), onError: (error) => onErrorRef.current?.(error, {
|
|
74
|
+
layerKey,
|
|
75
|
+
phase: 'stream',
|
|
76
|
+
scanId: scan.id,
|
|
77
|
+
sessionId: scan.captureSession.sessionId,
|
|
78
|
+
streamId: stream.id,
|
|
79
|
+
streamKind: stream.kind,
|
|
80
|
+
}), resetKey: `${renderKey}:${sourceState.descriptorVersion}:${streamEpoch}:${packetRevision}`, scope: `capture:${layerKey}`, children: _jsx(Suspense, { fallback: null, children: _jsx(CaptureStreamLayer, { descriptor: descriptor, packets: packets, renderers: renderers, scan: scan, source: source, stream: stream, streamEpoch: streamEpoch }) }) }, renderKey));
|
|
81
|
+
}) }), target);
|
|
82
|
+
}
|
|
83
|
+
function captureStreamRenderKey(stream) {
|
|
84
|
+
const artifact = stream.artifact;
|
|
85
|
+
return [
|
|
86
|
+
stream.id,
|
|
87
|
+
stream.availability,
|
|
88
|
+
artifact?.id ?? '',
|
|
89
|
+
artifact?.sha256 ?? '',
|
|
90
|
+
artifact?.uri ?? '',
|
|
91
|
+
].join(':');
|
|
92
|
+
}
|
|
93
|
+
export function CaptureStreamLayer({ descriptor, meshPresentation, packets, renderers, scan, source, stream, streamEpoch, }) {
|
|
94
|
+
const artifactUrl = useResolvedArtifact(source, stream.artifact);
|
|
95
|
+
const layerKey = captureLayerKey(stream);
|
|
96
|
+
const Renderer = renderers[layerKey] ?? renderers[stream.kind];
|
|
97
|
+
// Extracted previews archive the inline payload shape as a JSON artifact.
|
|
98
|
+
const payloadArtifactUrl = streamHydratesJsonPayload(stream) ? artifactUrl : null;
|
|
99
|
+
const fetchedPayload = useJsonArtifactPayload(payloadArtifactUrl);
|
|
100
|
+
const payload = stream.inline ?? fetchedPayload;
|
|
101
|
+
const frameId = packets.at(-1)?.frameId ?? stream.frameId ?? stream.artifact?.frameId;
|
|
102
|
+
const frameMatrix = useMemo(() => resolveCaptureFrameMatrix(descriptor, frameId), [descriptor, frameId]);
|
|
103
|
+
const trajectory = useMemo(() => {
|
|
104
|
+
if (layerKey !== 'deviceMotion')
|
|
105
|
+
return null;
|
|
106
|
+
const inline = DeviceMotionTrajectorySchema.safeParse(payload);
|
|
107
|
+
return inline.success
|
|
108
|
+
? parseDeviceTrajectoryPayload(inline.data)
|
|
109
|
+
: parseDeviceTrajectoryPackets(packets.map((packet) => packet.payload));
|
|
110
|
+
}, [layerKey, packets, payload]);
|
|
111
|
+
const motionPlaybackKey = useMemo(() => {
|
|
112
|
+
if (layerKey !== 'deviceMotion')
|
|
113
|
+
return '';
|
|
114
|
+
// Fetched payloads can be megabytes — key them by artifact identity and
|
|
115
|
+
// load state instead of stringifying their content.
|
|
116
|
+
const inlineVersion = packets.length === 0
|
|
117
|
+
? stream.inline != null
|
|
118
|
+
? JSON.stringify(stream.inline)
|
|
119
|
+
: `${payloadArtifactUrl ?? ''}:${fetchedPayload ? 'loaded' : 'pending'}`
|
|
120
|
+
: '';
|
|
121
|
+
return [descriptor.revisionId ?? '', streamEpoch, inlineVersion].join(':');
|
|
122
|
+
}, [
|
|
123
|
+
descriptor.revisionId,
|
|
124
|
+
fetchedPayload,
|
|
125
|
+
layerKey,
|
|
126
|
+
packets.length,
|
|
127
|
+
payloadArtifactUrl,
|
|
128
|
+
stream.inline,
|
|
129
|
+
streamEpoch,
|
|
130
|
+
]);
|
|
131
|
+
if (frameId && !frameMatrix) {
|
|
132
|
+
throw new Error(`Capture stream ${stream.id} references an invalid frame: ${frameId}.`);
|
|
133
|
+
}
|
|
134
|
+
let content = null;
|
|
135
|
+
if (Renderer) {
|
|
136
|
+
content = (_jsx(Renderer, { artifactUrl: artifactUrl, descriptor: descriptor, meshPresentation: meshPresentation, packets: packets, scan: scan, source: source, stream: stream, streamEpoch: streamEpoch }));
|
|
137
|
+
}
|
|
138
|
+
else if (layerKey === 'model' &&
|
|
139
|
+
isCaptureModelArtifact(stream.artifact) &&
|
|
140
|
+
stream.artifact &&
|
|
141
|
+
artifactUrl) {
|
|
142
|
+
content = (_jsx(CaptureRoomModel, { dollhouse: meshPresentation?.dollhouse, format: captureModelFormat(stream.artifact) ?? undefined, mediaType: stream.artifact.mediaType, opacity: scan.opacity, url: artifactUrl }));
|
|
143
|
+
}
|
|
144
|
+
else if (layerKey === 'deviceMotion') {
|
|
145
|
+
content = trajectory ? (_jsx(CaptureDeviceMotionLayer, { trajectory: trajectory }, motionPlaybackKey)) : null;
|
|
146
|
+
}
|
|
147
|
+
else if (layerKey === 'pointCloud') {
|
|
148
|
+
content = (_jsx(CapturePointCloudLayer, { artifactUrl: isCapturePointCloudArtifact(stream.artifact) ? (artifactUrl ?? undefined) : undefined, inline: payload, packets: stream.availability === 'live' ? packets : [] }));
|
|
149
|
+
}
|
|
150
|
+
else if (layerKey === 'surfaceMesh') {
|
|
151
|
+
content = (_jsx(CaptureSurfaceMeshLayer, { appearance: meshPresentation?.previewMaterial, dollhouse: meshPresentation?.dollhouse, inline: payload }));
|
|
152
|
+
}
|
|
153
|
+
if (!(content && frameMatrix))
|
|
154
|
+
return content;
|
|
155
|
+
return (_jsx("group", { matrix: frameMatrix, matrixAutoUpdate: false, children: content }));
|
|
156
|
+
}
|
|
157
|
+
function useJsonArtifactPayload(url) {
|
|
158
|
+
const [error, setError] = useState(null);
|
|
159
|
+
const [payload, setPayload] = useState(null);
|
|
160
|
+
useEffect(() => {
|
|
161
|
+
setError(null);
|
|
162
|
+
setPayload(null);
|
|
163
|
+
if (!url)
|
|
164
|
+
return;
|
|
165
|
+
const abort = new AbortController();
|
|
166
|
+
void fetch(rewriteLoopbackAssetUrl(url), { signal: abort.signal })
|
|
167
|
+
.then(async (response) => {
|
|
168
|
+
if (!response.ok)
|
|
169
|
+
throw new Error(`Could not load ${url}: ${response.status}`);
|
|
170
|
+
return (await response.json());
|
|
171
|
+
})
|
|
172
|
+
.then((data) => {
|
|
173
|
+
if (!abort.signal.aborted)
|
|
174
|
+
setPayload(data);
|
|
175
|
+
})
|
|
176
|
+
.catch((cause) => {
|
|
177
|
+
if (abort.signal.aborted)
|
|
178
|
+
return;
|
|
179
|
+
setError(cause instanceof Error ? cause : new Error(`Could not load ${url}.`));
|
|
180
|
+
});
|
|
181
|
+
return () => abort.abort();
|
|
182
|
+
}, [url]);
|
|
183
|
+
if (error)
|
|
184
|
+
throw error;
|
|
185
|
+
return payload;
|
|
186
|
+
}
|
|
187
|
+
function useResolvedArtifact(source, artifact) {
|
|
188
|
+
const [error, setError] = useState(null);
|
|
189
|
+
const [url, setUrl] = useState(null);
|
|
190
|
+
const artifactKey = artifact ? JSON.stringify(artifact) : null;
|
|
191
|
+
const artifactSnapshot = useMemo(() => artifactKey
|
|
192
|
+
? CaptureArtifactReferenceSchema.parse(JSON.parse(artifactKey))
|
|
193
|
+
: undefined, [artifactKey]);
|
|
194
|
+
useEffect(() => {
|
|
195
|
+
const abort = new AbortController();
|
|
196
|
+
let dispose;
|
|
197
|
+
setError(null);
|
|
198
|
+
setUrl(null);
|
|
199
|
+
if (!artifactSnapshot)
|
|
200
|
+
return () => abort.abort();
|
|
201
|
+
const resolve = source.resolveArtifact
|
|
202
|
+
? source.resolveArtifact(artifactSnapshot, abort.signal)
|
|
203
|
+
: artifactSnapshot.uri
|
|
204
|
+
? Promise.resolve({ url: artifactSnapshot.uri })
|
|
205
|
+
: Promise.reject(new Error(`Capture artifact ${artifactSnapshot.id} has no URI.`));
|
|
206
|
+
void resolve
|
|
207
|
+
.then((result) => {
|
|
208
|
+
if (abort.signal.aborted) {
|
|
209
|
+
result.dispose?.();
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
dispose = result.dispose;
|
|
213
|
+
setUrl(result.url);
|
|
214
|
+
})
|
|
215
|
+
.catch((cause) => {
|
|
216
|
+
if (!abort.signal.aborted) {
|
|
217
|
+
setError(cause instanceof Error ? cause : new Error('Could not resolve capture artifact.'));
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
return () => {
|
|
221
|
+
abort.abort();
|
|
222
|
+
dispose?.();
|
|
223
|
+
};
|
|
224
|
+
}, [artifactSnapshot, source]);
|
|
225
|
+
if (error)
|
|
226
|
+
throw error;
|
|
227
|
+
return url;
|
|
228
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { CaptureSessionDescriptor } from '@pascal-app/core/capture';
|
|
2
|
+
import { Matrix4 } from 'three';
|
|
3
|
+
export declare function resolveCaptureFrameMatrix(descriptor: CaptureSessionDescriptor, frameId: string | undefined): Matrix4 | null;
|
|
4
|
+
//# sourceMappingURL=frame.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame.d.ts","sourceRoot":"","sources":["../../src/capture/frame.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAA;AACxE,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AAE/B,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,wBAAwB,EACpC,OAAO,EAAE,MAAM,GAAG,SAAS,GAC1B,OAAO,GAAG,IAAI,CAiBhB"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Matrix4 } from 'three';
|
|
2
|
+
export function resolveCaptureFrameMatrix(descriptor, frameId) {
|
|
3
|
+
if (!frameId)
|
|
4
|
+
return null;
|
|
5
|
+
const frames = new Map(descriptor.coordinateFrames.map((frame) => [frame.id, frame]));
|
|
6
|
+
const visited = new Set();
|
|
7
|
+
const matrix = new Matrix4();
|
|
8
|
+
let currentId = frameId;
|
|
9
|
+
while (currentId) {
|
|
10
|
+
if (visited.has(currentId))
|
|
11
|
+
return null;
|
|
12
|
+
visited.add(currentId);
|
|
13
|
+
const frame = frames.get(currentId);
|
|
14
|
+
if (!frame)
|
|
15
|
+
return null;
|
|
16
|
+
if (frame.transform)
|
|
17
|
+
matrix.premultiply(new Matrix4().fromArray(frame.transform));
|
|
18
|
+
currentId = frame.parentId;
|
|
19
|
+
}
|
|
20
|
+
return matrix;
|
|
21
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { rewriteLoopbackAssetUrl } from './asset-url';
|
|
2
|
+
export { type CaptureMeshPresentation, CaptureRuntime, type CaptureRuntimeErrorContext, type CaptureRuntimeProps, CaptureStreamLayer, type CaptureStreamRenderer, type CaptureStreamRendererProps, } from './capture-runtime';
|
|
3
|
+
export { resolveCaptureFrameMatrix } from './frame';
|
|
4
|
+
export { isCaptureLayerVisible, isCaptureSessionVisible, isCaptureStreamVisible, } from './layer-visibility';
|
|
5
|
+
export { CaptureDeviceMotionLayer, DEVICE_MOTION_PLAYBACK_SPEED, } from './layers/device-motion-layer';
|
|
6
|
+
export { buildPointCloudData, CapturePointCloudLayer, type PointCloudData, } from './layers/point-cloud-layer';
|
|
7
|
+
export { CaptureRoomModel } from './layers/room-model-layer';
|
|
8
|
+
export { buildSurfaceMeshData, CaptureSurfaceMeshLayer, type SurfaceMeshData, } from './layers/surface-mesh-layer';
|
|
9
|
+
export { appendCapturePacket, type CaptureSourceState, captureSubscriptionStreamIds, type UseCaptureSourceOptions, useCaptureSource, } from './source-state';
|
|
10
|
+
export { type CaptureModelFormat, captureModelFormat, isCaptureModelArtifact, isCapturePointCloudArtifact, isCaptureStreamRenderable, } from './stream-rendering';
|
|
11
|
+
export { type DeviceTrajectory, type DeviceTrajectoryFrame, type DeviceTrajectoryPose, parseDeviceTrajectoryPackets, parseDeviceTrajectoryPayload, sampleDeviceTrajectory, } from './trajectory';
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/capture/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AACrD,OAAO,EACL,KAAK,uBAAuB,EAC5B,cAAc,EACd,KAAK,0BAA0B,EAC/B,KAAK,mBAAmB,EACxB,kBAAkB,EAClB,KAAK,qBAAqB,EAC1B,KAAK,0BAA0B,GAChC,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAA;AACnD,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,8BAA8B,CAAA;AACrC,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,KAAK,cAAc,GACpB,MAAM,4BAA4B,CAAA;AACnC,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAA;AAC5D,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,KAAK,eAAe,GACrB,MAAM,6BAA6B,CAAA;AACpC,OAAO,EACL,mBAAmB,EACnB,KAAK,kBAAkB,EACvB,4BAA4B,EAC5B,KAAK,uBAAuB,EAC5B,gBAAgB,GACjB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACL,KAAK,kBAAkB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,2BAA2B,EAC3B,yBAAyB,GAC1B,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,4BAA4B,EAC5B,4BAA4B,EAC5B,sBAAsB,GACvB,MAAM,cAAc,CAAA"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { rewriteLoopbackAssetUrl } from './asset-url';
|
|
2
|
+
export { CaptureRuntime, CaptureStreamLayer, } from './capture-runtime';
|
|
3
|
+
export { resolveCaptureFrameMatrix } from './frame';
|
|
4
|
+
export { isCaptureLayerVisible, isCaptureSessionVisible, isCaptureStreamVisible, } from './layer-visibility';
|
|
5
|
+
export { CaptureDeviceMotionLayer, DEVICE_MOTION_PLAYBACK_SPEED, } from './layers/device-motion-layer';
|
|
6
|
+
export { buildPointCloudData, CapturePointCloudLayer, } from './layers/point-cloud-layer';
|
|
7
|
+
export { CaptureRoomModel } from './layers/room-model-layer';
|
|
8
|
+
export { buildSurfaceMeshData, CaptureSurfaceMeshLayer, } from './layers/surface-mesh-layer';
|
|
9
|
+
export { appendCapturePacket, captureSubscriptionStreamIds, useCaptureSource, } from './source-state';
|
|
10
|
+
export { captureModelFormat, isCaptureModelArtifact, isCapturePointCloudArtifact, isCaptureStreamRenderable, } from './stream-rendering';
|
|
11
|
+
export { parseDeviceTrajectoryPackets, parseDeviceTrajectoryPayload, sampleDeviceTrajectory, } from './trajectory';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CaptureStreamDescriptor } from '@pascal-app/core/capture';
|
|
2
|
+
export declare function isCaptureLayerVisible(layers: Readonly<Record<string, boolean>>, layerKey: string, defaultLayerVisibility?: Readonly<Record<string, boolean>>): boolean;
|
|
3
|
+
export declare function isCaptureStreamVisible(stream: CaptureStreamDescriptor, layers: Readonly<Record<string, boolean>>, defaultLayerVisibility?: Readonly<Record<string, boolean>>): boolean;
|
|
4
|
+
export declare function isCaptureSessionVisible(showScans: boolean, scanVisible: boolean): boolean;
|
|
5
|
+
//# sourceMappingURL=layer-visibility.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layer-visibility.d.ts","sourceRoot":"","sources":["../../src/capture/layer-visibility.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAA;AAKvE,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,QAAQ,EAAE,MAAM,EAChB,sBAAsB,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAA0B,GACjF,OAAO,CAET;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,uBAAuB,EAC/B,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,sBAAsB,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAA0B,GACjF,OAAO,CAET;AAED,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,GAAG,OAAO,CAEzF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { captureLayerKey } from '@pascal-app/core/capture';
|
|
2
|
+
const EMPTY_LAYER_VISIBILITY = {};
|
|
3
|
+
export function isCaptureLayerVisible(layers, layerKey, defaultLayerVisibility = EMPTY_LAYER_VISIBILITY) {
|
|
4
|
+
return layers[layerKey] ?? defaultLayerVisibility[layerKey] ?? true;
|
|
5
|
+
}
|
|
6
|
+
export function isCaptureStreamVisible(stream, layers, defaultLayerVisibility = EMPTY_LAYER_VISIBILITY) {
|
|
7
|
+
return isCaptureLayerVisible(layers, captureLayerKey(stream), defaultLayerVisibility);
|
|
8
|
+
}
|
|
9
|
+
export function isCaptureSessionVisible(showScans, scanVisible) {
|
|
10
|
+
return showScans && scanVisible;
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clay-matcap.d.ts","sourceRoot":"","sources":["../../../src/capture/layers/clay-matcap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAA4C,MAAM,OAAO,CAAA;AAE7E,wBAAgB,gBAAgB,IAAI,WAAW,CA0B9C"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { DataTexture, LinearFilter, RGBAFormat, SRGBColorSpace } from 'three';
|
|
2
|
+
export function createClayMatcap() {
|
|
3
|
+
const size = 128;
|
|
4
|
+
const pixels = new Uint8Array(size * size * 4);
|
|
5
|
+
for (let row = 0; row < size; row += 1) {
|
|
6
|
+
for (let column = 0; column < size; column += 1) {
|
|
7
|
+
const x = (column / (size - 1)) * 2 - 1;
|
|
8
|
+
const y = (row / (size - 1)) * 2 - 1;
|
|
9
|
+
const z = Math.sqrt(Math.max(0, 1 - x * x - y * y));
|
|
10
|
+
const gloss = Math.exp(-((x + 0.34) ** 2 / 0.045 + (y - 0.42) ** 2 / 0.075));
|
|
11
|
+
const rim = Math.exp(-((x - 0.65) ** 2 / 0.025 + (y + 0.15) ** 2 / 0.5)) * 0.3;
|
|
12
|
+
const base = [0.3 + (1 - z) * 0.22, 0.35 + (x + 1) * 0.16 + z * 0.12, 0.64 + z * 0.2];
|
|
13
|
+
const offset = (row * size + column) * 4;
|
|
14
|
+
for (let channel = 0; channel < 3; channel += 1) {
|
|
15
|
+
pixels[offset + channel] = Math.round(Math.min(1, base[channel] + gloss * 0.58 + rim) * 255);
|
|
16
|
+
}
|
|
17
|
+
pixels[offset + 3] = 255;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const texture = new DataTexture(pixels, size, size, RGBAFormat);
|
|
21
|
+
texture.colorSpace = SRGBColorSpace;
|
|
22
|
+
texture.minFilter = LinearFilter;
|
|
23
|
+
texture.magFilter = LinearFilter;
|
|
24
|
+
texture.needsUpdate = true;
|
|
25
|
+
return texture;
|
|
26
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type DeviceTrajectory } from '../trajectory';
|
|
2
|
+
export declare const DEVICE_MOTION_PLAYBACK_SPEED = 3;
|
|
3
|
+
export declare function CaptureDeviceMotionLayer({ lineWidth, trajectory, }: {
|
|
4
|
+
lineWidth?: number;
|
|
5
|
+
trajectory: DeviceTrajectory;
|
|
6
|
+
}): import("react").JSX.Element;
|
|
7
|
+
//# sourceMappingURL=device-motion-layer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-motion-layer.d.ts","sourceRoot":"","sources":["../../../src/capture/layers/device-motion-layer.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAE,KAAK,gBAAgB,EAA0B,MAAM,eAAe,CAAA;AAE7E,eAAO,MAAM,4BAA4B,IAAI,CAAA;AAE7C,wBAAgB,wBAAwB,CAAC,EACvC,SAAe,EACf,UAAU,GACX,EAAE;IACD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,gBAAgB,CAAA;CAC7B,+BAiDA"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useFrame } from '@react-three/fiber';
|
|
4
|
+
import { useEffect, useMemo, useRef } from 'react';
|
|
5
|
+
import { BufferGeometry, LineBasicMaterial, LineSegments, Quaternion, Line as ThreeLine, Vector3, } from 'three';
|
|
6
|
+
import { sampleDeviceTrajectory } from '../trajectory';
|
|
7
|
+
export const DEVICE_MOTION_PLAYBACK_SPEED = 3;
|
|
8
|
+
export function CaptureDeviceMotionLayer({ lineWidth = 2.5, trajectory, }) {
|
|
9
|
+
const deviceRef = useRef(null);
|
|
10
|
+
const elapsedRef = useRef(0);
|
|
11
|
+
const position = useMemo(() => new Vector3(), []);
|
|
12
|
+
const toPosition = useMemo(() => new Vector3(), []);
|
|
13
|
+
const fromQuaternion = useMemo(() => new Quaternion(), []);
|
|
14
|
+
const toQuaternion = useMemo(() => new Quaternion(), []);
|
|
15
|
+
const trajectorySegments = useMemo(() => {
|
|
16
|
+
const segments = new Map();
|
|
17
|
+
for (const pose of trajectory.poses) {
|
|
18
|
+
const points = segments.get(pose.segment) ?? [];
|
|
19
|
+
points.push(pose.position);
|
|
20
|
+
segments.set(pose.segment, points);
|
|
21
|
+
}
|
|
22
|
+
return [...segments.entries()]
|
|
23
|
+
.filter(([, points]) => points.length > 1)
|
|
24
|
+
.map(([segment, points]) => ({ points, segment }));
|
|
25
|
+
}, [trajectory]);
|
|
26
|
+
useFrame((_, delta) => {
|
|
27
|
+
if (!deviceRef.current)
|
|
28
|
+
return;
|
|
29
|
+
elapsedRef.current += delta * DEVICE_MOTION_PLAYBACK_SPEED;
|
|
30
|
+
const frame = sampleDeviceTrajectory(trajectory, elapsedRef.current);
|
|
31
|
+
position
|
|
32
|
+
.fromArray(frame.from.position)
|
|
33
|
+
.lerp(toPosition.fromArray(frame.to.position), frame.alpha);
|
|
34
|
+
fromQuaternion.fromArray(frame.from.quaternion);
|
|
35
|
+
toQuaternion.fromArray(frame.to.quaternion);
|
|
36
|
+
deviceRef.current.position.copy(position);
|
|
37
|
+
deviceRef.current.quaternion.slerpQuaternions(fromQuaternion, toQuaternion, frame.alpha);
|
|
38
|
+
});
|
|
39
|
+
return (_jsxs("group", { children: [trajectorySegments.map(({ points, segment }) => (_jsx(CaptureLine, { color: "#222326", lineWidth: lineWidth, opacity: 0.78, points: points }, segment))), _jsx("group", { ref: deviceRef, children: _jsx(CameraFrustum, { lineWidth: lineWidth }) })] }));
|
|
40
|
+
}
|
|
41
|
+
function CameraFrustum({ lineWidth }) {
|
|
42
|
+
const apex = [0, 0, 0];
|
|
43
|
+
const topRight = [0.14, 0.1, -0.28];
|
|
44
|
+
const topLeft = [-0.14, 0.1, -0.28];
|
|
45
|
+
const bottomLeft = [-0.14, -0.1, -0.28];
|
|
46
|
+
const bottomRight = [0.14, -0.1, -0.28];
|
|
47
|
+
const corners = [topRight, topLeft, bottomLeft, bottomRight];
|
|
48
|
+
const points = [
|
|
49
|
+
...corners.map((corner) => [apex, corner]),
|
|
50
|
+
[topRight, topLeft],
|
|
51
|
+
[topLeft, bottomLeft],
|
|
52
|
+
[bottomLeft, bottomRight],
|
|
53
|
+
[bottomRight, topRight],
|
|
54
|
+
].flat();
|
|
55
|
+
return (_jsxs("group", { children: [_jsx(CaptureLine, { color: "#f5b900", lineWidth: lineWidth, points: points, segments: true }), _jsxs("mesh", { children: [_jsx("sphereGeometry", { args: [0.025, 12, 12] }), _jsx("meshBasicMaterial", { color: "#ffd84d" })] })] }));
|
|
56
|
+
}
|
|
57
|
+
function CaptureLine({ color, lineWidth, opacity = 1, points, segments = false, }) {
|
|
58
|
+
const line = useMemo(() => {
|
|
59
|
+
const geometry = new BufferGeometry().setFromPoints(points.map(([x, y, z]) => new Vector3(x, y, z)));
|
|
60
|
+
const material = new LineBasicMaterial({
|
|
61
|
+
color,
|
|
62
|
+
depthWrite: opacity >= 1,
|
|
63
|
+
linewidth: lineWidth,
|
|
64
|
+
opacity,
|
|
65
|
+
transparent: opacity < 1,
|
|
66
|
+
});
|
|
67
|
+
const object = segments
|
|
68
|
+
? new LineSegments(geometry, material)
|
|
69
|
+
: new ThreeLine(geometry, material);
|
|
70
|
+
object.frustumCulled = false;
|
|
71
|
+
return object;
|
|
72
|
+
}, [color, lineWidth, opacity, points, segments]);
|
|
73
|
+
useEffect(() => () => {
|
|
74
|
+
line.geometry.dispose();
|
|
75
|
+
line.material.dispose();
|
|
76
|
+
}, [line]);
|
|
77
|
+
return _jsx("primitive", { object: line });
|
|
78
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CaptureStreamPacket } from '@pascal-app/core/capture';
|
|
2
|
+
export type PointCloudData = {
|
|
3
|
+
colors: Float32Array | null;
|
|
4
|
+
positions: Float32Array;
|
|
5
|
+
};
|
|
6
|
+
export declare function CapturePointCloudLayer({ artifactUrl, inline, maxPoints, packets, pointSize, }: {
|
|
7
|
+
artifactUrl?: string;
|
|
8
|
+
inline?: unknown;
|
|
9
|
+
maxPoints?: number;
|
|
10
|
+
packets?: readonly CaptureStreamPacket[];
|
|
11
|
+
pointSize?: number;
|
|
12
|
+
}): import("react").JSX.Element | null;
|
|
13
|
+
export declare function buildPointCloudData(packets: readonly CaptureStreamPacket[], maxPoints: number): PointCloudData;
|
|
14
|
+
export declare function buildPointCloudPayloadData(value: unknown, maxPoints: number): PointCloudData;
|
|
15
|
+
//# sourceMappingURL=point-cloud-layer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"point-cloud-layer.d.ts","sourceRoot":"","sources":["../../../src/capture/layers/point-cloud-layer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAOnE,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,YAAY,GAAG,IAAI,CAAA;IAC3B,SAAS,EAAE,YAAY,CAAA;CACxB,CAAA;AAED,wBAAgB,sBAAsB,CAAC,EACrC,WAAW,EACX,MAAM,EACN,SAAmB,EACnB,OAAY,EACZ,SAAiB,GAClB,EAAE;IACD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,SAAS,mBAAmB,EAAE,CAAA;IACxC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,sCAaA;AAyCD,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,SAAS,mBAAmB,EAAE,EACvC,SAAS,EAAE,MAAM,GAChB,cAAc,CA6BhB;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,cAAc,CAY5F"}
|