@pascal-app/viewer 1.0.0-beta.4 → 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 +107 -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 +11 -0
- package/dist/components/viewer/frame-limiter.d.ts.map +1 -1
- package/dist/components/viewer/frame-limiter.js +59 -18
- package/dist/components/viewer/index.d.ts +15 -0
- package/dist/components/viewer/index.d.ts.map +1 -1
- package/dist/components/viewer/index.js +74 -82
- 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 +306 -115
- 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/selection-manager.d.ts.map +1 -1
- package/dist/components/viewer/selection-manager.js +7 -2
- package/dist/components/viewer/unsupported-gpu-fallback.d.ts +2 -0
- package/dist/components/viewer/unsupported-gpu-fallback.d.ts.map +1 -0
- package/dist/components/viewer/unsupported-gpu-fallback.js +4 -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 +20 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -7
- 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/dispose-object3d.d.ts +4 -0
- package/dist/lib/dispose-object3d.d.ts.map +1 -0
- package/dist/lib/dispose-object3d.js +27 -0
- package/dist/lib/geometry-groups.d.ts +17 -0
- package/dist/lib/geometry-groups.d.ts.map +1 -0
- package/dist/lib/geometry-groups.js +53 -0
- 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/isolation.d.ts +8 -8
- package/dist/lib/isolation.d.ts.map +1 -1
- package/dist/lib/isolation.js +18 -30
- 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/layers.d.ts +16 -0
- package/dist/lib/layers.d.ts.map +1 -1
- package/dist/lib/layers.js +18 -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 +56 -0
- package/dist/lib/renderer-capability.d.ts.map +1 -0
- package/dist/lib/renderer-capability.js +116 -0
- package/dist/lib/scene-visibility.d.ts +15 -0
- package/dist/lib/scene-visibility.d.ts.map +1 -0
- package/dist/lib/scene-visibility.js +69 -0
- package/dist/lib/shadow-only.d.ts +16 -0
- package/dist/lib/shadow-only.d.ts.map +1 -1
- package/dist/lib/shadow-only.js +8 -17
- package/dist/lib/snapshot-pipeline.d.ts +9 -0
- package/dist/lib/snapshot-pipeline.d.ts.map +1 -1
- package/dist/lib/snapshot-pipeline.js +33 -9
- package/dist/store/use-viewer.d.ts +7 -6
- 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/floor-elevation/floor-elevation-system.d.ts.map +1 -1
- package/dist/systems/floor-elevation/floor-elevation-system.js +9 -0
- package/dist/systems/geometry/geometry-system.d.ts.map +1 -1
- package/dist/systems/geometry/geometry-system.js +5 -28
- package/dist/systems/level/level-system.d.ts.map +1 -1
- package/dist/systems/level/level-system.js +8 -2
- 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 +10 -0
- package/dist/systems/wall/level-miter-cache.d.ts.map +1 -0
- package/dist/systems/wall/level-miter-cache.js +40 -0
- 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 +24 -1
- package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
- package/dist/systems/wall/wall-cutout.js +33 -140
- 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 +13 -2
- package/dist/systems/wall/wall-system.d.ts.map +1 -1
- package/dist/systems/wall/wall-system.js +413 -148
- 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 +18 -3
- package/dist/components/viewer/scene-bvh.test.d.ts +0 -2
- package/dist/components/viewer/scene-bvh.test.d.ts.map +0 -1
- package/dist/components/viewer/scene-bvh.test.js +0 -13
- package/dist/lib/csg-utils.test.d.ts +0 -2
- package/dist/lib/csg-utils.test.d.ts.map +0 -1
- package/dist/lib/csg-utils.test.js +0 -45
- package/dist/lib/materials.test.d.ts +0 -2
- package/dist/lib/materials.test.d.ts.map +0 -1
- package/dist/lib/materials.test.js +0 -25
- package/dist/lib/merged-outline-node.test.d.ts +0 -2
- package/dist/lib/merged-outline-node.test.d.ts.map +0 -1
- package/dist/lib/merged-outline-node.test.js +0 -22
- 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/dist/lib/polygon-union.test.d.ts +0 -2
- package/dist/lib/polygon-union.test.d.ts.map +0 -1
- package/dist/lib/polygon-union.test.js +0 -105
- package/dist/lib/texture-reference.test.d.ts +0 -2
- package/dist/lib/texture-reference.test.d.ts.map +0 -1
- package/dist/lib/texture-reference.test.js +0 -86
- package/dist/store/use-viewer.test.d.ts +0 -2
- package/dist/store/use-viewer.test.d.ts.map +0 -1
- package/dist/store/use-viewer.test.js +0 -61
- package/dist/systems/door/door-animation.test.d.ts +0 -2
- package/dist/systems/door/door-animation.test.d.ts.map +0 -1
- package/dist/systems/door/door-animation.test.js +0 -51
- package/dist/systems/door/door-floor-alignment.test.d.ts +0 -2
- package/dist/systems/door/door-floor-alignment.test.d.ts.map +0 -1
- package/dist/systems/door/door-floor-alignment.test.js +0 -50
- package/dist/systems/geometry/geometry-system.test.d.ts +0 -2
- package/dist/systems/geometry/geometry-system.test.d.ts.map +0 -1
- package/dist/systems/geometry/geometry-system.test.js +0 -30
- package/dist/systems/slab/slab-system.test.d.ts +0 -2
- package/dist/systems/slab/slab-system.test.d.ts.map +0 -1
- package/dist/systems/slab/slab-system.test.js +0 -102
- package/dist/systems/wall/opening-cutout-geometry.test.d.ts +0 -2
- package/dist/systems/wall/opening-cutout-geometry.test.d.ts.map +0 -1
- package/dist/systems/wall/opening-cutout-geometry.test.js +0 -172
- package/dist/systems/wall/wall-opening-cutout.test.d.ts +0 -2
- package/dist/systems/wall/wall-opening-cutout.test.d.ts.map +0 -1
- package/dist/systems/wall/wall-opening-cutout.test.js +0 -59
- package/dist/systems/wall/wall-support-extension.test.d.ts +0 -2
- package/dist/systems/wall/wall-support-extension.test.d.ts.map +0 -1
- package/dist/systems/wall/wall-support-extension.test.js +0 -116
- package/dist/systems/wall/wall-terrain-bottom.test.d.ts +0 -2
- package/dist/systems/wall/wall-terrain-bottom.test.d.ts.map +0 -1
- package/dist/systems/wall/wall-terrain-bottom.test.js +0 -34
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PassNode, RenderPipeline } from 'three/webgpu';
|
|
2
|
+
import type { LayerPassIndex } from './layer-pass';
|
|
3
|
+
export declare class PostProcessingResources {
|
|
4
|
+
layerIndex: LayerPassIndex | null;
|
|
5
|
+
readonly passes: PassNode[];
|
|
6
|
+
outline: {
|
|
7
|
+
dispose(): void;
|
|
8
|
+
} | null;
|
|
9
|
+
pipeline: RenderPipeline | null;
|
|
10
|
+
dispose(): void;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=post-processing-resources.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post-processing-resources.d.ts","sourceRoot":"","sources":["../../src/lib/post-processing-resources.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAIlD,qBAAa,uBAAuB;IAClC,UAAU,EAAE,cAAc,GAAG,IAAI,CAAO;IACxC,QAAQ,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAK;IAChC,OAAO,EAAE;QAAE,OAAO,IAAI,IAAI,CAAA;KAAE,GAAG,IAAI,CAAO;IAC1C,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAO;IAEtC,OAAO;CAUR"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// RenderPipeline.dispose() only releases its fullscreen material, so the owner
|
|
2
|
+
// must also release the passes and scene observers on failure and teardown.
|
|
3
|
+
export class PostProcessingResources {
|
|
4
|
+
layerIndex = null;
|
|
5
|
+
passes = [];
|
|
6
|
+
outline = null;
|
|
7
|
+
pipeline = null;
|
|
8
|
+
dispose() {
|
|
9
|
+
this.layerIndex?.dispose();
|
|
10
|
+
this.layerIndex = null;
|
|
11
|
+
for (const pass of this.passes)
|
|
12
|
+
pass.dispose();
|
|
13
|
+
this.passes.length = 0;
|
|
14
|
+
this.outline?.dispose();
|
|
15
|
+
this.outline = null;
|
|
16
|
+
this.pipeline?.dispose();
|
|
17
|
+
this.pipeline = null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export type RendererCapabilityCanvas = {
|
|
2
|
+
getContext(contextId: 'webgl2'): unknown;
|
|
3
|
+
};
|
|
4
|
+
/** Mirrors `GPUPowerPreference` without pulling WebGPU ambient types into the declaration build. */
|
|
5
|
+
export type RendererPowerPreference = 'high-performance' | 'low-power';
|
|
6
|
+
type RendererGpuAdapter = {
|
|
7
|
+
features?: Iterable<string>;
|
|
8
|
+
requestDevice(descriptor?: {
|
|
9
|
+
requiredFeatures?: string[];
|
|
10
|
+
}): Promise<unknown>;
|
|
11
|
+
};
|
|
12
|
+
type RendererGpu = {
|
|
13
|
+
requestAdapter(options?: Record<string, unknown>): Promise<RendererGpuAdapter | null>;
|
|
14
|
+
};
|
|
15
|
+
export type RendererCapability = {
|
|
16
|
+
backend: 'webgpu';
|
|
17
|
+
device: unknown;
|
|
18
|
+
status: 'supported';
|
|
19
|
+
} | {
|
|
20
|
+
backend: 'webgl';
|
|
21
|
+
status: 'supported';
|
|
22
|
+
} | {
|
|
23
|
+
error?: unknown;
|
|
24
|
+
status: 'unsupported';
|
|
25
|
+
};
|
|
26
|
+
export type RendererBackendParameters = {
|
|
27
|
+
device?: unknown;
|
|
28
|
+
forceWebGL?: boolean;
|
|
29
|
+
};
|
|
30
|
+
type InitializableRenderer = {
|
|
31
|
+
dispose?: () => void;
|
|
32
|
+
init: () => Promise<unknown>;
|
|
33
|
+
};
|
|
34
|
+
export type RendererInitializationResult<Renderer> = {
|
|
35
|
+
backend: 'webgpu' | 'webgl';
|
|
36
|
+
renderer: Renderer;
|
|
37
|
+
status: 'ready';
|
|
38
|
+
} | {
|
|
39
|
+
error?: unknown;
|
|
40
|
+
status: 'unsupported';
|
|
41
|
+
};
|
|
42
|
+
export declare function detectRendererCapability({ canvas, gpu, powerPreference, webgpuTimeoutMs, }?: {
|
|
43
|
+
canvas?: RendererCapabilityCanvas | null;
|
|
44
|
+
gpu?: RendererGpu | null;
|
|
45
|
+
powerPreference?: RendererPowerPreference;
|
|
46
|
+
webgpuTimeoutMs?: number;
|
|
47
|
+
}): Promise<RendererCapability>;
|
|
48
|
+
export declare function initializeGpuRenderer<Renderer extends InitializableRenderer>({ createRenderer, gpu, powerPreference, probeCanvas, webgpuTimeoutMs, }: {
|
|
49
|
+
createRenderer: (parameters: RendererBackendParameters) => Renderer;
|
|
50
|
+
gpu?: RendererGpu | null;
|
|
51
|
+
powerPreference?: RendererPowerPreference;
|
|
52
|
+
probeCanvas?: RendererCapabilityCanvas | null;
|
|
53
|
+
webgpuTimeoutMs?: number;
|
|
54
|
+
}): Promise<RendererInitializationResult<Renderer>>;
|
|
55
|
+
export {};
|
|
56
|
+
//# sourceMappingURL=renderer-capability.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderer-capability.d.ts","sourceRoot":"","sources":["../../src/lib/renderer-capability.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,wBAAwB,GAAG;IACrC,UAAU,CAAC,SAAS,EAAE,QAAQ,GAAG,OAAO,CAAA;CACzC,CAAA;AAED,oGAAoG;AACpG,MAAM,MAAM,uBAAuB,GAAG,kBAAkB,GAAG,WAAW,CAAA;AAEtE,KAAK,kBAAkB,GAAG;IACxB,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAA;IAC3B,aAAa,CAAC,UAAU,CAAC,EAAE;QAAE,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CAC9E,CAAA;AAED,KAAK,WAAW,GAAG;IACjB,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAA;CACtF,CAAA;AAED,MAAM,MAAM,kBAAkB,GAC1B;IAAE,OAAO,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,GAC3D;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,GACzC;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,aAAa,CAAA;CAAE,CAAA;AAE9C,MAAM,MAAM,yBAAyB,GAAG;IACtC,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB,CAAA;AAED,KAAK,qBAAqB,GAAG;IAC3B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;CAC7B,CAAA;AAED,MAAM,MAAM,4BAA4B,CAAC,QAAQ,IAC7C;IAAE,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GACpE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,aAAa,CAAA;CAAE,CAAA;AAiD9C,wBAAsB,wBAAwB,CAAC,EAC7C,MAAwB,EACxB,GAAkB,EAClB,eAAe,EACf,eAAkD,GACnD,GAAE;IACD,MAAM,CAAC,EAAE,wBAAwB,GAAG,IAAI,CAAA;IACxC,GAAG,CAAC,EAAE,WAAW,GAAG,IAAI,CAAA;IACxB,eAAe,CAAC,EAAE,uBAAuB,CAAA;IACzC,eAAe,CAAC,EAAE,MAAM,CAAA;CACpB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA2BnC;AAED,wBAAsB,qBAAqB,CAAC,QAAQ,SAAS,qBAAqB,EAAE,EAClF,cAAc,EACd,GAAG,EACH,eAAe,EACf,WAA6B,EAC7B,eAAkD,GACnD,EAAE;IACD,cAAc,EAAE,CAAC,UAAU,EAAE,yBAAyB,KAAK,QAAQ,CAAA;IACnE,GAAG,CAAC,EAAE,WAAW,GAAG,IAAI,CAAA;IACxB,eAAe,CAAC,EAAE,uBAAuB,CAAA;IACzC,WAAW,CAAC,EAAE,wBAAwB,GAAG,IAAI,CAAA;IAC7C,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB,GAAG,OAAO,CAAC,4BAA4B,CAAC,QAAQ,CAAC,CAAC,CAyClD"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
const WEBGPU_INITIALIZATION_TIMEOUT_MS = 4000;
|
|
2
|
+
function browserGpu() {
|
|
3
|
+
if (typeof navigator === 'undefined')
|
|
4
|
+
return null;
|
|
5
|
+
return navigator.gpu ?? null;
|
|
6
|
+
}
|
|
7
|
+
function browserCanvas() {
|
|
8
|
+
if (typeof document === 'undefined')
|
|
9
|
+
return null;
|
|
10
|
+
return document.createElement('canvas');
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Because we hand the device to `WebGPURenderer` ourselves, three treats it as
|
|
14
|
+
* caller-owned and never destroys it, and `Renderer.dispose()` is a no-op when
|
|
15
|
+
* `init()` failed. Any device we request and then abandon has to be released
|
|
16
|
+
* here or it counts against the browser's concurrent-device limit for the rest
|
|
17
|
+
* of the page's life.
|
|
18
|
+
*/
|
|
19
|
+
function releaseDevice(device) {
|
|
20
|
+
try {
|
|
21
|
+
;
|
|
22
|
+
device?.destroy?.();
|
|
23
|
+
}
|
|
24
|
+
catch { }
|
|
25
|
+
}
|
|
26
|
+
function withTimeout(promise, timeoutMs, operation) {
|
|
27
|
+
let timeout;
|
|
28
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
29
|
+
timeout = setTimeout(() => {
|
|
30
|
+
reject(new Error(`${operation} timed out after ${timeoutMs}ms`));
|
|
31
|
+
}, timeoutMs);
|
|
32
|
+
});
|
|
33
|
+
return Promise.race([promise, timeoutPromise]).finally(() => clearTimeout(timeout));
|
|
34
|
+
}
|
|
35
|
+
async function requestWebGpuDevice(gpu, powerPreference) {
|
|
36
|
+
const adapter = await gpu.requestAdapter({
|
|
37
|
+
featureLevel: 'compatibility',
|
|
38
|
+
...(powerPreference ? { powerPreference } : {}),
|
|
39
|
+
});
|
|
40
|
+
if (!adapter)
|
|
41
|
+
return null;
|
|
42
|
+
const requiredFeatures = adapter.features ? Array.from(adapter.features) : undefined;
|
|
43
|
+
return adapter.requestDevice(requiredFeatures?.length ? { requiredFeatures } : undefined);
|
|
44
|
+
}
|
|
45
|
+
export async function detectRendererCapability({ canvas = browserCanvas(), gpu = browserGpu(), powerPreference, webgpuTimeoutMs = WEBGPU_INITIALIZATION_TIMEOUT_MS, } = {}) {
|
|
46
|
+
let capabilityError;
|
|
47
|
+
if (gpu) {
|
|
48
|
+
const pending = requestWebGpuDevice(gpu, powerPreference);
|
|
49
|
+
try {
|
|
50
|
+
const device = await withTimeout(pending, webgpuTimeoutMs, 'WebGPU adapter/device request');
|
|
51
|
+
if (device)
|
|
52
|
+
return { backend: 'webgpu', device, status: 'supported' };
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
capabilityError = error;
|
|
56
|
+
// Losing the race means a device may still arrive after we have given up
|
|
57
|
+
// on it, so reclaim it. Attaching the handler only on this path keeps
|
|
58
|
+
// ownership of a device we did return unambiguous.
|
|
59
|
+
pending.then(releaseDevice).catch(() => { });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (canvas) {
|
|
63
|
+
try {
|
|
64
|
+
const context = canvas.getContext('webgl2');
|
|
65
|
+
if (context)
|
|
66
|
+
return { backend: 'webgl', status: 'supported' };
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
capabilityError ??= error;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return { error: capabilityError, status: 'unsupported' };
|
|
73
|
+
}
|
|
74
|
+
export async function initializeGpuRenderer({ createRenderer, gpu, powerPreference, probeCanvas = browserCanvas(), webgpuTimeoutMs = WEBGPU_INITIALIZATION_TIMEOUT_MS, }) {
|
|
75
|
+
const capability = await detectRendererCapability({
|
|
76
|
+
canvas: probeCanvas,
|
|
77
|
+
gpu,
|
|
78
|
+
powerPreference,
|
|
79
|
+
webgpuTimeoutMs,
|
|
80
|
+
});
|
|
81
|
+
if (capability.status === 'unsupported')
|
|
82
|
+
return capability;
|
|
83
|
+
let renderer;
|
|
84
|
+
try {
|
|
85
|
+
renderer = createRenderer(capability.backend === 'webgpu' ? { device: capability.device } : { forceWebGL: true });
|
|
86
|
+
const initPromise = renderer.init();
|
|
87
|
+
await (capability.backend === 'webgpu'
|
|
88
|
+
? withTimeout(initPromise, webgpuTimeoutMs, 'WebGPU renderer initialization')
|
|
89
|
+
: initPromise);
|
|
90
|
+
return { backend: capability.backend, renderer, status: 'ready' };
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
try {
|
|
94
|
+
// r186 made dispose() async: let it finish before the device is released
|
|
95
|
+
// and the WebGL fallback starts on the same canvas.
|
|
96
|
+
await renderer?.dispose?.();
|
|
97
|
+
}
|
|
98
|
+
catch { }
|
|
99
|
+
if (capability.backend !== 'webgpu')
|
|
100
|
+
return { error, status: 'unsupported' };
|
|
101
|
+
releaseDevice(capability.device);
|
|
102
|
+
renderer = undefined;
|
|
103
|
+
try {
|
|
104
|
+
renderer = createRenderer({ forceWebGL: true });
|
|
105
|
+
await renderer.init();
|
|
106
|
+
return { backend: 'webgl', renderer, status: 'ready' };
|
|
107
|
+
}
|
|
108
|
+
catch (fallbackError) {
|
|
109
|
+
try {
|
|
110
|
+
await renderer?.dispose?.();
|
|
111
|
+
}
|
|
112
|
+
catch { }
|
|
113
|
+
return { error: fallbackError, status: 'unsupported' };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Object3D } from 'three';
|
|
2
|
+
/**
|
|
3
|
+
* Why an object is currently held off the scene layer.
|
|
4
|
+
*
|
|
5
|
+
* - `isolated` — outside the focused subtree of the viewer's isolation filter.
|
|
6
|
+
* - `shadow-only` — solo mode: out of the color passes, still casting shadows.
|
|
7
|
+
* - `batched` — a level's merged wall mesh draws this wall now.
|
|
8
|
+
*/
|
|
9
|
+
export type HiddenReason = 'isolated' | 'shadow-only' | 'batched' | 'wall-batched';
|
|
10
|
+
/** Holds `obj` off the scene layer for `reason`. Idempotent per reason. */
|
|
11
|
+
export declare function hideFromScene(obj: Object3D, reason: HiddenReason): void;
|
|
12
|
+
/** Drops `reason`, restoring the mask `obj` had before the first one arrived. */
|
|
13
|
+
export declare function showInScene(obj: Object3D, reason: HiddenReason): void;
|
|
14
|
+
export declare function temporarilyShowShadowOnly(root: Object3D): () => void;
|
|
15
|
+
//# sourceMappingURL=scene-visibility.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scene-visibility.d.ts","sourceRoot":"","sources":["../../src/lib/scene-visibility.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAGrC;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,aAAa,GAAG,SAAS,GAAG,cAAc,CAAA;AAmBlF,2EAA2E;AAC3E,wBAAgB,aAAa,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAMvE;AAED,iFAAiF;AACjF,wBAAgB,WAAW,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAarE;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,IAAI,CAgBpE"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { BATCHED_LAYER, SCENE_LAYER, SHADOW_ONLY_LAYER } from './layers';
|
|
2
|
+
/**
|
|
3
|
+
* Single owner of `Object3D.layers` for every feature that hides an object.
|
|
4
|
+
*
|
|
5
|
+
* Isolation, solo's shadow-caster pass and wall batching all hide by clearing
|
|
6
|
+
* {@link SCENE_LAYER}, and they overlap freely — a wall can be sewn into a
|
|
7
|
+
* batch, then soloed, then isolated. While each stashed and restored the mask privately, the
|
|
8
|
+
* second to finish wrote back a mask the first had since changed. Recording
|
|
9
|
+
* *reasons* rather than masks makes the order irrelevant: the mask is
|
|
10
|
+
* recomputed from the one snapshot taken when the first reason arrived, and
|
|
11
|
+
* handed back only when the last one leaves.
|
|
12
|
+
*/
|
|
13
|
+
const HOLD = Symbol('pascal:scene-visibility:hold');
|
|
14
|
+
/** Holds `obj` off the scene layer for `reason`. Idempotent per reason. */
|
|
15
|
+
export function hideFromScene(obj, reason) {
|
|
16
|
+
const holder = obj;
|
|
17
|
+
const hold = holder[HOLD] ?? { original: obj.layers.mask, reasons: new Set() };
|
|
18
|
+
holder[HOLD] = hold;
|
|
19
|
+
hold.reasons.add(reason);
|
|
20
|
+
applyHold(obj, hold);
|
|
21
|
+
}
|
|
22
|
+
/** Drops `reason`, restoring the mask `obj` had before the first one arrived. */
|
|
23
|
+
export function showInScene(obj, reason) {
|
|
24
|
+
const holder = obj;
|
|
25
|
+
const hold = holder[HOLD];
|
|
26
|
+
if (!hold)
|
|
27
|
+
return;
|
|
28
|
+
hold.reasons.delete(reason);
|
|
29
|
+
if (hold.reasons.size > 0) {
|
|
30
|
+
applyHold(obj, hold);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
obj.layers.mask = hold.original;
|
|
34
|
+
delete holder[HOLD];
|
|
35
|
+
}
|
|
36
|
+
export function temporarilyShowShadowOnly(root) {
|
|
37
|
+
const masks = new Map();
|
|
38
|
+
root.traverse((obj) => {
|
|
39
|
+
const hold = obj[HOLD];
|
|
40
|
+
if (!hold?.reasons.has('shadow-only'))
|
|
41
|
+
return;
|
|
42
|
+
masks.set(obj, obj.layers.mask);
|
|
43
|
+
const reasons = new Set(hold.reasons);
|
|
44
|
+
reasons.delete('shadow-only');
|
|
45
|
+
// A capture needs the original scene geometry, while editor overlays and
|
|
46
|
+
// objects hidden by isolation or batching must retain their own masks.
|
|
47
|
+
if (reasons.size === 0)
|
|
48
|
+
obj.layers.mask = hold.original;
|
|
49
|
+
else
|
|
50
|
+
applyHold(obj, { original: hold.original, reasons });
|
|
51
|
+
});
|
|
52
|
+
return () => {
|
|
53
|
+
for (const [obj, mask] of masks)
|
|
54
|
+
obj.layers.mask = mask;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function applyHold(obj, hold) {
|
|
58
|
+
obj.layers.mask = hold.original;
|
|
59
|
+
obj.layers.disable(SCENE_LAYER);
|
|
60
|
+
// A batched wall is both drawn and shadowed by the merged mesh, so it stays
|
|
61
|
+
// out of the shadow pass too — enabling the shadow-only bit would submit its
|
|
62
|
+
// triangles a second time, on top of the copy the batch already casts.
|
|
63
|
+
if (hold.reasons.has('batched') || hold.reasons.has('wall-batched')) {
|
|
64
|
+
obj.layers.enable(BATCHED_LAYER);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (hold.reasons.has('shadow-only'))
|
|
68
|
+
obj.layers.enable(SHADOW_ONLY_LAYER);
|
|
69
|
+
}
|
|
@@ -1,4 +1,20 @@
|
|
|
1
1
|
import type { Object3D } from 'three';
|
|
2
|
+
/**
|
|
3
|
+
* Shadow-caster-only hiding: removes an object (and its descendants) from the
|
|
4
|
+
* color passes while keeping it in the shadow map, so a hidden roof or level
|
|
5
|
+
* still shadows the interior and sun enters through windows correctly.
|
|
6
|
+
*
|
|
7
|
+
* Uses layer masks instead of `visible = false` for two reasons: `visible`
|
|
8
|
+
* cascades (and, critically, prunes the object from the shadow pass too),
|
|
9
|
+
* while layers are tested per-object against the rendering camera — the main
|
|
10
|
+
* camera never enables the shadow-only layer, but every shadow-casting
|
|
11
|
+
* light's shadow camera does (see `lights.tsx`).
|
|
12
|
+
*
|
|
13
|
+
* The mask itself belongs to `lib/scene-visibility.ts`, which reconciles this
|
|
14
|
+
* with the isolation filter. Both calls are idempotent and cheap to reapply —
|
|
15
|
+
* solo re-runs `applyShadowOnly` every frame so meshes rebuilt while hidden
|
|
16
|
+
* get re-hidden.
|
|
17
|
+
*/
|
|
2
18
|
export declare function applyShadowOnly(root: Object3D): void;
|
|
3
19
|
export declare function clearShadowOnly(root: Object3D): void;
|
|
4
20
|
//# sourceMappingURL=shadow-only.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shadow-only.d.ts","sourceRoot":"","sources":["../../src/lib/shadow-only.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"shadow-only.d.ts","sourceRoot":"","sources":["../../src/lib/shadow-only.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAGrC;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,CAIpD;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,CAIpD"}
|
package/dist/lib/shadow-only.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { hideFromScene, showInScene } from './scene-visibility';
|
|
2
2
|
/**
|
|
3
3
|
* Shadow-caster-only hiding: removes an object (and its descendants) from the
|
|
4
4
|
* color passes while keeping it in the shadow map, so a hidden roof or level
|
|
@@ -7,30 +7,21 @@ import { SCENE_LAYER, SHADOW_ONLY_LAYER } from './layers';
|
|
|
7
7
|
* Uses layer masks instead of `visible = false` for two reasons: `visible`
|
|
8
8
|
* cascades (and, critically, prunes the object from the shadow pass too),
|
|
9
9
|
* while layers are tested per-object against the rendering camera — the main
|
|
10
|
-
* camera never enables
|
|
10
|
+
* camera never enables the shadow-only layer, but every shadow-casting
|
|
11
11
|
* light's shadow camera does (see `lights.tsx`).
|
|
12
12
|
*
|
|
13
|
-
* The
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* The mask itself belongs to `lib/scene-visibility.ts`, which reconciles this
|
|
14
|
+
* with the isolation filter. Both calls are idempotent and cheap to reapply —
|
|
15
|
+
* solo re-runs `applyShadowOnly` every frame so meshes rebuilt while hidden
|
|
16
|
+
* get re-hidden.
|
|
16
17
|
*/
|
|
17
|
-
const ORIGINAL_LAYERS = Symbol('pascal:shadow-only:original-layers');
|
|
18
18
|
export function applyShadowOnly(root) {
|
|
19
19
|
root.traverse((obj) => {
|
|
20
|
-
|
|
21
|
-
if (carrier[ORIGINAL_LAYERS] === undefined) {
|
|
22
|
-
carrier[ORIGINAL_LAYERS] = obj.layers.mask;
|
|
23
|
-
}
|
|
24
|
-
obj.layers.disable(SCENE_LAYER);
|
|
25
|
-
obj.layers.enable(SHADOW_ONLY_LAYER);
|
|
20
|
+
hideFromScene(obj, 'shadow-only');
|
|
26
21
|
});
|
|
27
22
|
}
|
|
28
23
|
export function clearShadowOnly(root) {
|
|
29
24
|
root.traverse((obj) => {
|
|
30
|
-
|
|
31
|
-
if (carrier[ORIGINAL_LAYERS] === undefined)
|
|
32
|
-
return;
|
|
33
|
-
obj.layers.mask = carrier[ORIGINAL_LAYERS];
|
|
34
|
-
delete carrier[ORIGINAL_LAYERS];
|
|
25
|
+
showInScene(obj, 'shadow-only');
|
|
35
26
|
});
|
|
36
27
|
}
|
|
@@ -3,6 +3,15 @@ import { type WebGPURenderer } from 'three/webgpu';
|
|
|
3
3
|
import { type EdgeMode } from './edge-style';
|
|
4
4
|
export declare const THUMBNAIL_WIDTH = 1920;
|
|
5
5
|
export declare const THUMBNAIL_HEIGHT = 1080;
|
|
6
|
+
/**
|
|
7
|
+
* Captures are re-renderable artifacts, not user originals, so they encode as
|
|
8
|
+
* webp: a 1920×1080 hero shot lands roughly an order of magnitude under PNG,
|
|
9
|
+
* which is what listings and the catalog actually ship over the wire. Alpha
|
|
10
|
+
* survives, so transparent item/preset captures keep working.
|
|
11
|
+
*/
|
|
12
|
+
export declare const SNAPSHOT_MIME = "image/webp";
|
|
13
|
+
export declare const SNAPSHOT_QUALITY = 0.9;
|
|
14
|
+
export declare const SNAPSHOT_MAX_EDGE = 2048;
|
|
6
15
|
export type SnapshotCaptureMode = 'standard' | 'viewport' | 'area';
|
|
7
16
|
export type SnapshotCropRegion = {
|
|
8
17
|
x: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"snapshot-pipeline.d.ts","sourceRoot":"","sources":["../../src/lib/snapshot-pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAkB,KAAK,KAAK,EAAoB,MAAM,OAAO,CAAA;AAqBjF,OAAO,EAAgC,KAAK,cAAc,EAAE,MAAM,cAAc,CAAA;AAGhF,OAAO,EAAE,KAAK,QAAQ,EAAqC,MAAM,cAAc,CAAA;AAK/E,eAAO,MAAM,eAAe,OAAO,CAAA;AACnC,eAAO,MAAM,gBAAgB,OAAO,CAAA;AAEpC,MAAM,MAAM,mBAAmB,GAAG,UAAU,GAAG,UAAU,GAAG,MAAM,CAAA;AAElE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;CACV,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,IAAI,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,gBAAgB,EAAE,CAAC,EACjB,KAAK,EACL,WAAW,EACX,KAAK,EACL,KAAK,EACL,MAAM,GACP,EAAE;QACD,KAAK,EAAE,MAAM,CAAA;QACb,WAAW,EAAE,OAAO,CAAA;QACpB,KAAK,EAAE,OAAO,CAAA;QACd,KAAK,EAAE,QAAQ,CAAA;QACf,MAAM,EAAE,MAAM,CAAA;KACf,KAAK,IAAI,CAAA;IACV,OAAO,EAAE,CAAC,EACR,WAAW,EACX,UAAU,EACV,YAAY,GACb,EAAE;QACD,WAAW,CAAC,EAAE,mBAAmB,CAAA;QACjC,UAAU,CAAC,EAAE,kBAAkB,CAAA;QAC/B,YAAY,CAAC,EAAE,YAAY,CAAA;KAC5B,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAA;IACpC,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB,CAAA;AAED,wBAAsB,sBAAsB,CAAC,EAC3C,QAAQ,EACR,KAAK,EACL,MAAM,GACP,EAAE;IACD,QAAQ,EAAE,cAAc,CAAA;IACxB,KAAK,EAAE,KAAK,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;CACf,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"snapshot-pipeline.d.ts","sourceRoot":"","sources":["../../src/lib/snapshot-pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAkB,KAAK,KAAK,EAAoB,MAAM,OAAO,CAAA;AAqBjF,OAAO,EAAgC,KAAK,cAAc,EAAE,MAAM,cAAc,CAAA;AAGhF,OAAO,EAAE,KAAK,QAAQ,EAAqC,MAAM,cAAc,CAAA;AAK/E,eAAO,MAAM,eAAe,OAAO,CAAA;AACnC,eAAO,MAAM,gBAAgB,OAAO,CAAA;AAEpC;;;;;GAKG;AACH,eAAO,MAAM,aAAa,eAAe,CAAA;AACzC,eAAO,MAAM,gBAAgB,MAAM,CAAA;AAEnC,eAAO,MAAM,iBAAiB,OAAO,CAAA;AAUrC,MAAM,MAAM,mBAAmB,GAAG,UAAU,GAAG,UAAU,GAAG,MAAM,CAAA;AAElE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;CACV,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,IAAI,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,gBAAgB,EAAE,CAAC,EACjB,KAAK,EACL,WAAW,EACX,KAAK,EACL,KAAK,EACL,MAAM,GACP,EAAE;QACD,KAAK,EAAE,MAAM,CAAA;QACb,WAAW,EAAE,OAAO,CAAA;QACpB,KAAK,EAAE,OAAO,CAAA;QACd,KAAK,EAAE,QAAQ,CAAA;QACf,MAAM,EAAE,MAAM,CAAA;KACf,KAAK,IAAI,CAAA;IACV,OAAO,EAAE,CAAC,EACR,WAAW,EACX,UAAU,EACV,YAAY,GACb,EAAE;QACD,WAAW,CAAC,EAAE,mBAAmB,CAAA;QACjC,UAAU,CAAC,EAAE,kBAAkB,CAAA;QAC/B,YAAY,CAAC,EAAE,YAAY,CAAA;KAC5B,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAA;IACpC,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB,CAAA;AAED,wBAAsB,sBAAsB,CAAC,EAC3C,QAAQ,EACR,KAAK,EACL,MAAM,GACP,EAAE;IACD,QAAQ,EAAE,cAAc,CAAA;IACxB,KAAK,EAAE,KAAK,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;CACf,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAwSnC"}
|
|
@@ -12,6 +12,23 @@ import { getSceneTheme } from './scene-themes';
|
|
|
12
12
|
import { packNormalToRGB, unpackRGBToNormal } from './tsl-compat';
|
|
13
13
|
export const THUMBNAIL_WIDTH = 1920;
|
|
14
14
|
export const THUMBNAIL_HEIGHT = 1080;
|
|
15
|
+
/**
|
|
16
|
+
* Captures are re-renderable artifacts, not user originals, so they encode as
|
|
17
|
+
* webp: a 1920×1080 hero shot lands roughly an order of magnitude under PNG,
|
|
18
|
+
* which is what listings and the catalog actually ship over the wire. Alpha
|
|
19
|
+
* survives, so transparent item/preset captures keep working.
|
|
20
|
+
*/
|
|
21
|
+
export const SNAPSHOT_MIME = 'image/webp';
|
|
22
|
+
export const SNAPSHOT_QUALITY = 0.9;
|
|
23
|
+
// Retina canvases make viewport/area captures multi-MB; 2048 keeps them near the 1920 presets.
|
|
24
|
+
export const SNAPSHOT_MAX_EDGE = 2048;
|
|
25
|
+
function clampSnapshotSize(width, height) {
|
|
26
|
+
const maxEdge = Math.max(width, height);
|
|
27
|
+
if (maxEdge <= SNAPSHOT_MAX_EDGE)
|
|
28
|
+
return { w: width, h: height };
|
|
29
|
+
const scale = SNAPSHOT_MAX_EDGE / maxEdge;
|
|
30
|
+
return { w: Math.round(width * scale), h: Math.round(height * scale) };
|
|
31
|
+
}
|
|
15
32
|
export async function createSnapshotPipeline({ renderer, scene, camera, }) {
|
|
16
33
|
try {
|
|
17
34
|
if (renderer.init)
|
|
@@ -200,20 +217,27 @@ export async function createSnapshotPipeline({ renderer, scene, camera, }) {
|
|
|
200
217
|
let outH;
|
|
201
218
|
let blob;
|
|
202
219
|
if (captureMode === 'viewport') {
|
|
203
|
-
|
|
204
|
-
outH = captureHeight;
|
|
220
|
+
;
|
|
221
|
+
({ w: outW, h: outH } = clampSnapshotSize(captureWidth, captureHeight));
|
|
205
222
|
const offscreen = new OffscreenCanvas(outW, outH);
|
|
206
|
-
offscreen.getContext('2d')
|
|
207
|
-
|
|
223
|
+
const ctx = offscreen.getContext('2d');
|
|
224
|
+
if (outW !== captureWidth || outH !== captureHeight)
|
|
225
|
+
ctx.imageSmoothingQuality = 'high';
|
|
226
|
+
ctx.drawImage(srcCanvas, 0, 0, captureWidth, captureHeight, 0, 0, outW, outH);
|
|
227
|
+
blob = await offscreen.convertToBlob({ type: SNAPSHOT_MIME, quality: SNAPSHOT_QUALITY });
|
|
208
228
|
}
|
|
209
229
|
else if (captureMode === 'area' && cropRegion) {
|
|
210
230
|
const sx = Math.round(cropRegion.x * captureWidth);
|
|
211
231
|
const sy = Math.round(cropRegion.y * captureHeight);
|
|
212
|
-
|
|
213
|
-
|
|
232
|
+
const sourceW = Math.round(cropRegion.width * captureWidth);
|
|
233
|
+
const sourceH = Math.round(cropRegion.height * captureHeight);
|
|
234
|
+
({ w: outW, h: outH } = clampSnapshotSize(sourceW, sourceH));
|
|
214
235
|
const offscreen = new OffscreenCanvas(outW, outH);
|
|
215
|
-
offscreen.getContext('2d')
|
|
216
|
-
|
|
236
|
+
const ctx = offscreen.getContext('2d');
|
|
237
|
+
if (outW !== sourceW || outH !== sourceH)
|
|
238
|
+
ctx.imageSmoothingQuality = 'high';
|
|
239
|
+
ctx.drawImage(srcCanvas, sx, sy, sourceW, sourceH, 0, 0, outW, outH);
|
|
240
|
+
blob = await offscreen.convertToBlob({ type: SNAPSHOT_MIME, quality: SNAPSHOT_QUALITY });
|
|
217
241
|
}
|
|
218
242
|
else {
|
|
219
243
|
// Standard: center-crop to the requested aspect (default 1920×1080)
|
|
@@ -237,7 +261,7 @@ export async function createSnapshotPipeline({ renderer, scene, camera, }) {
|
|
|
237
261
|
offscreen
|
|
238
262
|
.getContext('2d')
|
|
239
263
|
.drawImage(srcCanvas, sx, sy, sWidth, sHeight, 0, 0, outW, outH);
|
|
240
|
-
blob = await offscreen.convertToBlob({ type:
|
|
264
|
+
blob = await offscreen.convertToBlob({ type: SNAPSHOT_MIME, quality: SNAPSHOT_QUALITY });
|
|
241
265
|
}
|
|
242
266
|
return { blob, outW, outH };
|
|
243
267
|
},
|
|
@@ -4,6 +4,7 @@ import type { EdgeMode } from '../lib/edge-style';
|
|
|
4
4
|
import type { ColorPreset, RenderShading } from '../lib/materials';
|
|
5
5
|
export type RenderContext = 'editor' | 'viewer';
|
|
6
6
|
export type MetricNotation = 'meters' | 'millimeters';
|
|
7
|
+
export type WallMode = 'up' | 'cutaway' | 'down' | 'translucent';
|
|
7
8
|
type SelectionPath = {
|
|
8
9
|
buildingId: BuildingNode['id'] | null;
|
|
9
10
|
levelId: LevelNode['id'] | null;
|
|
@@ -70,8 +71,8 @@ type ViewerState = {
|
|
|
70
71
|
unitExplicit: boolean;
|
|
71
72
|
levelMode: 'stacked' | 'exploded' | 'solo' | 'manual';
|
|
72
73
|
setLevelMode: (mode: 'stacked' | 'exploded' | 'solo' | 'manual') => void;
|
|
73
|
-
wallMode:
|
|
74
|
-
setWallMode: (mode:
|
|
74
|
+
wallMode: WallMode;
|
|
75
|
+
setWallMode: (mode: WallMode) => void;
|
|
75
76
|
showScans: boolean;
|
|
76
77
|
setShowScans: (show: boolean) => void;
|
|
77
78
|
showGuides: boolean;
|
|
@@ -101,8 +102,8 @@ type ViewerState = {
|
|
|
101
102
|
* effects can re-apply to the freshly swapped meshes. */
|
|
102
103
|
geometryRevision: number;
|
|
103
104
|
bumpGeometryRevision: () => void;
|
|
104
|
-
exportScene: ((format?: 'glb' | 'stl' | 'obj') => Promise<void>) | null;
|
|
105
|
-
setExportScene: (fn: ((format?: 'glb' | 'stl' | 'obj') => Promise<void>) | null) => void;
|
|
105
|
+
exportScene: ((format?: 'glb' | 'usdz' | 'stl' | 'obj') => Promise<void>) | null;
|
|
106
|
+
setExportScene: (fn: ((format?: 'glb' | 'usdz' | 'stl' | 'obj') => Promise<void>) | null) => void;
|
|
106
107
|
debugColors: boolean;
|
|
107
108
|
setDebugColors: (enabled: boolean) => void;
|
|
108
109
|
walkthroughMode: boolean;
|
|
@@ -133,7 +134,7 @@ declare const useViewer: import("zustand").UseBoundStore<Omit<import("zustand").
|
|
|
133
134
|
setOptions: (options: Partial<import("zustand/middleware").PersistOptions<ViewerState, {
|
|
134
135
|
metricNotation: MetricNotation;
|
|
135
136
|
levelMode: "manual" | "stacked" | "exploded" | "solo";
|
|
136
|
-
wallMode:
|
|
137
|
+
wallMode: WallMode;
|
|
137
138
|
projectPreferences: Record<string, {
|
|
138
139
|
showScans?: boolean;
|
|
139
140
|
showGuides?: boolean;
|
|
@@ -157,7 +158,7 @@ declare const useViewer: import("zustand").UseBoundStore<Omit<import("zustand").
|
|
|
157
158
|
getOptions: () => Partial<import("zustand/middleware").PersistOptions<ViewerState, {
|
|
158
159
|
metricNotation: MetricNotation;
|
|
159
160
|
levelMode: "manual" | "stacked" | "exploded" | "solo";
|
|
160
|
-
wallMode:
|
|
161
|
+
wallMode: WallMode;
|
|
161
162
|
projectPreferences: Record<string, {
|
|
162
163
|
showScans?: boolean;
|
|
163
164
|
showGuides?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-viewer.d.ts","sourceRoot":"","sources":["../../src/store/use-viewer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5F,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAIrC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAGlE,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAC/C,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"use-viewer.d.ts","sourceRoot":"","sources":["../../src/store/use-viewer.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"door-animation-system.d.ts","sourceRoot":"","sources":["../../../src/systems/door/door-animation-system.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"door-animation-system.d.ts","sourceRoot":"","sources":["../../../src/systems/door/door-animation-system.tsx"],"names":[],"mappings":"AAKA,eAAO,MAAM,mBAAmB,YAkD/B,CAAA"}
|
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
import { emitter, useInteractive, useScene } from '@pascal-app/core';
|
|
2
2
|
import { useFrame } from '@react-three/fiber';
|
|
3
3
|
const easeDoorAnimation = (value) => value * value * (3 - 2 * value);
|
|
4
|
-
function markDoorDirty(doorId) {
|
|
5
|
-
const scene = useScene.getState();
|
|
6
|
-
const node = scene.nodes[doorId];
|
|
7
|
-
scene.dirtyNodes.add(doorId);
|
|
8
|
-
if (node?.parentId)
|
|
9
|
-
scene.dirtyNodes.add(node.parentId);
|
|
10
|
-
}
|
|
11
4
|
export const DoorAnimationSystem = () => {
|
|
12
5
|
useFrame(({ clock }) => {
|
|
13
6
|
const interactive = useInteractive.getState();
|
|
@@ -30,19 +23,23 @@ export const DoorAnimationSystem = () => {
|
|
|
30
23
|
}
|
|
31
24
|
const progress = Math.min(1, (now - startedAt) / animation.durationMs);
|
|
32
25
|
const value = animation.from + (animation.to - animation.from) * easeDoorAnimation(progress);
|
|
26
|
+
// No dirty mark per tick: DoorSystem rebuilds any door with an entry in
|
|
27
|
+
// `doorAnimations`, and a dirty mark is a one-shot work item, not a
|
|
28
|
+
// needs-frame signal — per-tick marks kept the scene from ever settling.
|
|
33
29
|
interactive.setDoorOpenState(typedDoorId, { [animation.field]: value });
|
|
34
|
-
markDoorDirty(typedDoorId);
|
|
35
30
|
if (progress < 1)
|
|
36
31
|
continue;
|
|
37
32
|
interactive.cancelDoorAnimation(typedDoorId);
|
|
38
33
|
if (animation.persist) {
|
|
39
34
|
scene.updateNode(typedDoorId, { [animation.field]: animation.to });
|
|
40
35
|
interactive.removeDoorOpenState(typedDoorId);
|
|
41
|
-
markDoorDirty(typedDoorId);
|
|
42
36
|
}
|
|
43
37
|
else {
|
|
44
38
|
interactive.setDoorOpenState(typedDoorId, { [animation.field]: animation.to });
|
|
45
39
|
}
|
|
40
|
+
// One final mark so the settled pose gets a rebuild after the animation
|
|
41
|
+
// entry is gone (the persist branch's updateNode also marks, harmlessly).
|
|
42
|
+
scene.markDirty(typedDoorId);
|
|
46
43
|
emitter.emit('door:animation-completed', {
|
|
47
44
|
doorId: typedDoorId,
|
|
48
45
|
field: animation.field,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"door-system.d.ts","sourceRoot":"","sources":["../../../src/systems/door/door-system.tsx"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,QAAQ,EAYd,MAAM,kBAAkB,CAAA;AAGzB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"door-system.d.ts","sourceRoot":"","sources":["../../../src/systems/door/door-system.tsx"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,QAAQ,EAYd,MAAM,kBAAkB,CAAA;AAGzB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AA2D9B,eAAO,MAAM,UAAU,YAsHtB,CAAA;AAupCD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,QAAQ,EACd,IAAI,EAAE,KAAK,CAAC,QAAQ,GAAG,SAAS,EAChC,KAAK,EAAE,MAAM,GACZ,OAAO,CA6GT;AAyuCD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,QAAQ,GAAG,KAAK,CAAC,IAAI,CAI/D"}
|