@pascal-app/viewer 1.0.0 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/README.md +12 -0
  2. package/dist/capture/capture-runtime.js +2 -32
  3. package/dist/capture/layers/device-motion-layer.d.ts.map +1 -1
  4. package/dist/capture/layers/device-motion-layer.js +5 -7
  5. package/dist/capture/stream-rendering.d.ts.map +1 -1
  6. package/dist/capture/stream-rendering.js +2 -1
  7. package/dist/capture/use-json-artifact.d.ts +2 -0
  8. package/dist/capture/use-json-artifact.d.ts.map +1 -0
  9. package/dist/capture/use-json-artifact.js +42 -0
  10. package/dist/components/viewer/glb-scene.d.ts.map +1 -1
  11. package/dist/components/viewer/glb-scene.js +3 -1
  12. package/dist/components/viewer/index.d.ts +12 -0
  13. package/dist/components/viewer/index.d.ts.map +1 -1
  14. package/dist/components/viewer/index.js +28 -7
  15. package/dist/components/viewer/post-processing.d.ts.map +1 -1
  16. package/dist/components/viewer/post-processing.js +6 -0
  17. package/dist/components/viewer/selection-manager.d.ts.map +1 -1
  18. package/dist/components/viewer/selection-manager.js +8 -16
  19. package/dist/components/viewer/viewer-camera.d.ts +15 -1
  20. package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
  21. package/dist/components/viewer/viewer-camera.js +43 -5
  22. package/dist/index.d.ts +8 -6
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +8 -6
  25. package/dist/lib/csg-utils.d.ts +1 -1
  26. package/dist/lib/csg-utils.d.ts.map +1 -1
  27. package/dist/lib/csg-utils.js +1 -1
  28. package/dist/lib/isolation.d.ts +14 -30
  29. package/dist/lib/isolation.d.ts.map +1 -1
  30. package/dist/lib/isolation.js +62 -66
  31. package/dist/lib/materials.d.ts.map +1 -1
  32. package/dist/lib/materials.js +2 -1
  33. package/dist/lib/renderer-capability.d.ts +2 -1
  34. package/dist/lib/renderer-capability.d.ts.map +1 -1
  35. package/dist/lib/renderer-capability.js +2 -2
  36. package/dist/lib/scene-visibility.js +3 -1
  37. package/dist/lib/snapshot-pipeline.d.ts +3 -1
  38. package/dist/lib/snapshot-pipeline.d.ts.map +1 -1
  39. package/dist/lib/snapshot-pipeline.js +32 -16
  40. package/dist/store/use-viewer.d.ts +3 -1
  41. package/dist/store/use-viewer.d.ts.map +1 -1
  42. package/dist/store/use-viewer.js +2 -0
  43. package/dist/systems/door/door-system.d.ts.map +1 -1
  44. package/dist/systems/door/door-system.js +18 -5
  45. package/dist/systems/floor-elevation/floor-elevation-system.d.ts.map +1 -1
  46. package/dist/systems/floor-elevation/floor-elevation-system.js +122 -3
  47. package/dist/systems/geometry/geometry-system.d.ts.map +1 -1
  48. package/dist/systems/geometry/geometry-system.js +4 -0
  49. package/dist/systems/level/level-system.js +14 -14
  50. package/dist/systems/level/level-utils.d.ts +25 -1
  51. package/dist/systems/level/level-utils.d.ts.map +1 -1
  52. package/dist/systems/level/level-utils.js +25 -4
  53. package/dist/systems/wall/wall-cutout-cache.d.ts +4 -1
  54. package/dist/systems/wall/wall-cutout-cache.d.ts.map +1 -1
  55. package/dist/systems/wall/wall-cutout-cache.js +16 -7
  56. package/dist/systems/wall/wall-cutout.d.ts +3 -1
  57. package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
  58. package/dist/systems/wall/wall-cutout.js +5 -5
  59. package/dist/systems/wall/wall-materials.d.ts +11 -2
  60. package/dist/systems/wall/wall-materials.d.ts.map +1 -1
  61. package/dist/systems/wall/wall-materials.js +24 -18
  62. package/dist/systems/wall/wall-system.d.ts +15 -2
  63. package/dist/systems/wall/wall-system.d.ts.map +1 -1
  64. package/dist/systems/wall/wall-system.js +20 -21
  65. package/dist/systems/window/window-system.d.ts +1 -1
  66. package/dist/systems/window/window-system.d.ts.map +1 -1
  67. package/dist/systems/window/window-system.js +3 -2
  68. package/dist/systems/zone/zone-system.d.ts.map +1 -1
  69. package/dist/systems/zone/zone-system.js +28 -2
  70. package/dist/xr/presentation-context.d.ts +7 -0
  71. package/dist/xr/presentation-context.d.ts.map +1 -0
  72. package/dist/xr/presentation-context.js +10 -0
  73. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ export { ErrorBoundary } from './components/error-boundary';
10
10
  // `@pascal-app/nodes/<kind>/renderer.tsx` and are loaded by the registry
11
11
  // — no per-kind re-exports needed.
12
12
  export { NodeRenderer } from './components/renderers/node-renderer';
13
- export { default as Viewer } from './components/viewer';
13
+ export { default as Viewer, } from './components/viewer';
14
14
  export { default as BVHEcctrl, } from './components/viewer/bvh-ecctrl';
15
15
  export { buildGlbInteractiveItems, GlbInteractive, } from './components/viewer/glb-interactive';
16
16
  export { buildGlbReferenceNodes, buildGlbReplaceNodes, } from './components/viewer/glb-reference-nodes';
@@ -31,11 +31,11 @@ export { applyWorldScaleBoxUVs } from './lib/box-uv';
31
31
  // CSG primitives — used by chimney's roof-trim and other kinds whose
32
32
  // geometry subtracts pieces against their host. Lives in viewer
33
33
  // because three-bvh-csg / three-mesh-bvh are viewer-only deps.
34
- export { ADDITION, Brush, computeGeometryBoundsTree, csgEvaluator, csgGeometry, csgMaterials, INTERSECTION, prepareBrushForCSG, SUBTRACTION, } from './lib/csg-utils';
34
+ export { ADDITION, Brush, computeGeometryBoundsTree, csgEvaluator, csgGeometry, csgMaterials, Evaluator, ensureRenderableGeometryAttributes, INTERSECTION, prepareBrushForCSG, SUBTRACTION, } from './lib/csg-utils';
35
35
  export { disposeObject3DResources } from './lib/dispose-object3d';
36
36
  export { PERF_OVERLAY_ENABLED } from './lib/gpu-perf';
37
37
  export { computeHeroFraming, DEFAULT_FRAMING_EXCLUDED_TYPES, heroCameraPose, temporarilyHideNodeTypes, unionRegisteredNodeBounds, } from './lib/hero-pose';
38
- export { applyIsolation, clearIsolation, collectIsolationSubtree, isIsolationActive, } from './lib/isolation';
38
+ export { applyIsolation, clearIsolation, collectIsolationSubtree, isIsolationActive, refreshIsolation, } from './lib/isolation';
39
39
  export { configureKtx2Support, ensureKtx2Support } from './lib/ktx2-loader';
40
40
  export { LayerPassIndex } from './lib/layer-pass';
41
41
  export { BATCHED_LAYER, GRID_LAYER, OVERLAY_LAYER, SCENE_LAYER, SHADOW_ONLY_LAYER, setSurfaceRaycastLayers, ZONE_LAYER, } from './lib/layers';
@@ -92,14 +92,16 @@ export { StairSystem } from './systems/stair/stair-system';
92
92
  // Pure opening-cutout profile math shared by the wall CSG pipeline and
93
93
  // roof-wall opening cuts in `@pascal-app/nodes` — keeps shaped holes
94
94
  // (arch / rounded / frameless opening) identical across both hosts.
95
- export { buildOpeningCutoutGeometry, getOpeningCutoutBottomPadding, hasFlatOpeningCutoutBottom, } from './systems/wall/opening-cutout-geometry';
95
+ export { buildOpeningCutoutGeometry, buildOpeningCutoutShape, getOpeningCutoutBottomPadding, hasFlatOpeningCutoutBottom, } from './systems/wall/opening-cutout-geometry';
96
96
  export { getWallHideState, WallCutout } from './systems/wall/wall-cutout';
97
- export { getVisibleWallMaterials } from './systems/wall/wall-materials';
97
+ export { WallCutoutCache, } from './systems/wall/wall-cutout-cache';
98
+ export { getMaterialsForWall, getVisibleWallMaterials, } from './systems/wall/wall-materials';
98
99
  // Wall internals re-exported so `@pascal-app/nodes`' registry-driven wall
99
100
  // definition can compose them into `def.system` without duplicating the
100
101
  // 800+ lines of CSG / mitering logic during Phase 3. These exports are
101
102
  // removed in Phase 6 when the legacy mount points are deleted.
102
- export { drainRebuiltWalls, getPendingWallRebuildCount, isWallInitialBuildActive, WallSystem, } from './systems/wall/wall-system';
103
+ export { drainRebuiltWalls, generateExtrudedWall, getPendingWallRebuildCount, isWallInitialBuildActive, runWallBuildFrame, WallSystem, } from './systems/wall/wall-system';
103
104
  export { poseWindowMovingParts, WindowAnimationSystem, } from './systems/window/window-animation-system';
104
105
  export { buildWindowPreviewMesh, WindowSystem } from './systems/window/window-system';
105
106
  export { ZoneSystem } from './systems/zone/zone-system';
107
+ export { useImmersiveXRPresentation } from './xr/presentation-context';
@@ -7,5 +7,5 @@ export declare const csgEvaluator: Evaluator;
7
7
  export declare function computeGeometryBoundsTree(geometry: THREE.BufferGeometry): void;
8
8
  export declare function prepareBrushForCSG(brush: Brush): void;
9
9
  export declare function subtractCsgBrush(left: Brush, right: Brush, evaluator: Evaluator): Brush;
10
- export { ADDITION, Brush, INTERSECTION, SUBTRACTION } from 'three-bvh-csg';
10
+ export { ADDITION, Brush, Evaluator, INTERSECTION, SUBTRACTION } from 'three-bvh-csg';
11
11
  //# sourceMappingURL=csg-utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"csg-utils.d.ts","sourceRoot":"","sources":["../../src/lib/csg-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,KAAK,KAAK,EAAE,SAAS,EAAe,MAAM,eAAe,CAAA;AA4ClE,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,KAAK,CAAC,cAAc,GAC7B,KAAK,CAAC,cAAc,CAoCtB;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAAC,cAAc,CAE9D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAAC,QAAQ,EAAE,CAG3D;AAED,eAAO,MAAM,YAAY,WAAkB,CAAA;AAK3C,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,KAAK,CAAC,cAAc,QAMvE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,QAI9C;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,GAAG,KAAK,CAIvF;AAID,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA"}
1
+ {"version":3,"file":"csg-utils.d.ts","sourceRoot":"","sources":["../../src/lib/csg-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,KAAK,KAAK,EAAE,SAAS,EAAe,MAAM,eAAe,CAAA;AA4ClE,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,KAAK,CAAC,cAAc,GAC7B,KAAK,CAAC,cAAc,CAoCtB;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAAC,cAAc,CAE9D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAAC,QAAQ,EAAE,CAG3D;AAED,eAAO,MAAM,YAAY,WAAkB,CAAA;AAK3C,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,KAAK,CAAC,cAAc,QAMvE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,QAI9C;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,GAAG,KAAK,CAIvF;AAID,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA"}
@@ -95,4 +95,4 @@ export function subtractCsgBrush(left, right, evaluator) {
95
95
  }
96
96
  // Re-export Brush + SUBTRACTION + ADDITION + INTERSECTION so kinds don't need a
97
97
  // direct `three-bvh-csg` dependency.
98
- export { ADDITION, Brush, INTERSECTION, SUBTRACTION } from 'three-bvh-csg';
98
+ export { ADDITION, Brush, Evaluator, INTERSECTION, SUBTRACTION } from 'three-bvh-csg';
@@ -1,39 +1,23 @@
1
1
  import type { AnyNodeId } from '@pascal-app/core';
2
2
  import type { Object3D } from 'three';
3
- /** True while an isolation filter is applied (see {@link applyIsolation}). */
3
+ /** True while a subtree is isolated, including before its lazy meshes mount. */
4
4
  export declare function isIsolationActive(): boolean;
5
- /**
6
- * Compute the union of every isolated subtree's `Object3D` descendants.
7
- *
8
- * Pure traversal — exported so future "focus mode" / debug tooling can
9
- * reuse the same definition of "what's in the isolated set". Each root
10
- * is walked via `Object3D.traverse` (the live Three.js graph, not the
11
- * data-model `children` array — those can disagree when systems mount
12
- * synthesized sub-meshes that the data model doesn't track).
13
- */
5
+ /** Include synthesized meshes below a selected native root, including hosted openings. */
14
6
  export declare function collectIsolationSubtree(ids: ReadonlyArray<string>): Set<Object3D>;
15
7
  /**
16
- * Imperative visibility filter on the live `sceneRegistry`. Hides every
17
- * registered group (and its synthesized child meshes) outside the
18
- * isolated subtree by taking it off the scene layer.
19
- *
20
- * Why layers instead of `obj.visible = false`? Three.js's visibility
21
- * flag *cascades* — hiding a parent hides every descendant — so we
22
- * can't hide a host wall while keeping a door rendered inside it.
23
- * Layer masks are per-object and don't cascade: `WebGLRenderer
24
- * .projectObject` skips objects whose layer mask doesn't intersect the
25
- * camera's, but always recurses into their children. So we can hide
26
- * the wall and the door (hosted under it in the scene graph) still
27
- * renders, with its local position relative to the wall preserved
28
- * automatically by the matrix walk.
29
- *
30
- * The mask itself belongs to `lib/scene-visibility.ts`, which reconciles
31
- * isolation with solo's shadow-caster pass, so {@link clearIsolation}
32
- * gives an object back only what isolation took.
33
- *
34
- * Pass `null` to clear isolation (equivalent to calling
35
- * {@link clearIsolation}).
8
+ * Select native subtrees without mutating the saved scene or cascading visibility
9
+ * through their hosts. The next draw also filters unregistered presentation geometry.
36
10
  */
37
11
  export declare function applyIsolation(ids: ReadonlyArray<AnyNodeId> | null): void;
12
+ /**
13
+ * Reconcile immediately before viewport or snapshot rendering. Environment, sky,
14
+ * ground and instance batches can live outside sceneRegistry or mount asynchronously.
15
+ * Lights/cameras remain usable; masks do not cascade, so an isolated door can still
16
+ * render under its hidden wall. Collective renderers fall back to native geometry.
17
+ * The returned cleanup restores fog after the synchronous draw, before atmosphere
18
+ * owners can change it again. Do not hold this cleanup across async GPU readback.
19
+ */
20
+ export declare function refreshIsolation(scene?: Object3D): () => void;
21
+ /** Restore even detached/unregistered meshes, without undoing solo or batching holds. */
38
22
  export declare function clearIsolation(): void;
39
23
  //# sourceMappingURL=isolation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"isolation.d.ts","sourceRoot":"","sources":["../../src/lib/isolation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAEjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AASrC,8EAA8E;AAC9E,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,QAAQ,CAAC,CAUjF;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,CAAC,SAAS,CAAC,GAAG,IAAI,GAAG,IAAI,CAiBzE;AAUD,wBAAgB,cAAc,IAAI,IAAI,CAUrC"}
1
+ {"version":3,"file":"isolation.d.ts","sourceRoot":"","sources":["../../src/lib/isolation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAEjD,OAAO,KAAK,EAAE,QAAQ,EAAS,MAAM,OAAO,CAAA;AAQ5C,gFAAgF;AAChF,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED,0FAA0F;AAC1F,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,QAAQ,CAAC,CASjF;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,CAAC,SAAS,CAAC,GAAG,IAAI,GAAG,IAAI,CAOzE;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,CAAC,EAAE,QAAQ,GAAG,MAAM,IAAI,CA6B7D;AAED,yFAAyF;AACzF,wBAAgB,cAAc,IAAI,IAAI,CAKrC"}
@@ -1,91 +1,87 @@
1
1
  'use client';
2
2
  import { sceneRegistry } from '@pascal-app/core';
3
3
  import { hideFromScene, showInScene } from './scene-visibility';
4
- // Whether a subtree is currently isolated (some objects are held off the
5
- // scene layer). Read by consumers that must not act on the partial view — e.g.
6
- // the project-thumbnail autosave skips capturing while isolated so it never
7
- // snapshots a single focused item as the whole project's thumbnail.
8
- let isolationActive = false;
9
- /** True while an isolation filter is applied (see {@link applyIsolation}). */
4
+ let isolatedIds = null;
5
+ let heldObjects = new Set();
6
+ let liveScene;
7
+ const noop = () => { };
8
+ /** True while a subtree is isolated, including before its lazy meshes mount. */
10
9
  export function isIsolationActive() {
11
- return isolationActive;
10
+ return isolatedIds !== null;
12
11
  }
13
- /**
14
- * Compute the union of every isolated subtree's `Object3D` descendants.
15
- *
16
- * Pure traversal — exported so future "focus mode" / debug tooling can
17
- * reuse the same definition of "what's in the isolated set". Each root
18
- * is walked via `Object3D.traverse` (the live Three.js graph, not the
19
- * data-model `children` array — those can disagree when systems mount
20
- * synthesized sub-meshes that the data model doesn't track).
21
- */
12
+ /** Include synthesized meshes below a selected native root, including hosted openings. */
22
13
  export function collectIsolationSubtree(ids) {
23
14
  const keep = new Set();
24
15
  for (const id of ids) {
25
16
  const root = sceneRegistry.nodes.get(id);
26
- if (!root)
27
- continue;
28
- root.traverse((child) => {
17
+ root?.traverse((child) => {
29
18
  keep.add(child);
30
19
  });
31
20
  }
32
21
  return keep;
33
22
  }
34
23
  /**
35
- * Imperative visibility filter on the live `sceneRegistry`. Hides every
36
- * registered group (and its synthesized child meshes) outside the
37
- * isolated subtree by taking it off the scene layer.
38
- *
39
- * Why layers instead of `obj.visible = false`? Three.js's visibility
40
- * flag *cascades* — hiding a parent hides every descendant — so we
41
- * can't hide a host wall while keeping a door rendered inside it.
42
- * Layer masks are per-object and don't cascade: `WebGLRenderer
43
- * .projectObject` skips objects whose layer mask doesn't intersect the
44
- * camera's, but always recurses into their children. So we can hide
45
- * the wall and the door (hosted under it in the scene graph) still
46
- * renders, with its local position relative to the wall preserved
47
- * automatically by the matrix walk.
48
- *
49
- * The mask itself belongs to `lib/scene-visibility.ts`, which reconciles
50
- * isolation with solo's shadow-caster pass, so {@link clearIsolation}
51
- * gives an object back only what isolation took.
52
- *
53
- * Pass `null` to clear isolation (equivalent to calling
54
- * {@link clearIsolation}).
24
+ * Select native subtrees without mutating the saved scene or cascading visibility
25
+ * through their hosts. The next draw also filters unregistered presentation geometry.
55
26
  */
56
27
  export function applyIsolation(ids) {
57
28
  if (ids == null || ids.length === 0) {
58
29
  clearIsolation();
59
30
  return;
60
31
  }
61
- const keep = collectIsolationSubtree(ids);
62
- // Iterate registered roots. For each one outside the keep set,
63
- // hide it and every descendant — *except* descendants that are
64
- // themselves in `keep` (a kept node nested under a non-kept host:
65
- // the isolated door under the hidden wall).
66
- for (const [, obj] of sceneRegistry.nodes) {
67
- if (keep.has(obj))
68
- continue;
69
- hideRecursive(obj, keep);
70
- }
71
- isolationActive = true;
32
+ isolatedIds = [...ids];
33
+ refreshIsolation();
72
34
  }
73
- function hideRecursive(obj, keep) {
74
- if (keep.has(obj))
75
- return;
76
- hideFromScene(obj, 'isolated');
77
- for (const child of obj.children) {
78
- hideRecursive(child, keep);
79
- }
35
+ /**
36
+ * Reconcile immediately before viewport or snapshot rendering. Environment, sky,
37
+ * ground and instance batches can live outside sceneRegistry or mount asynchronously.
38
+ * Lights/cameras remain usable; masks do not cascade, so an isolated door can still
39
+ * render under its hidden wall. Collective renderers fall back to native geometry.
40
+ * The returned cleanup restores fog after the synchronous draw, before atmosphere
41
+ * owners can change it again. Do not hold this cleanup across async GPU readback.
42
+ */
43
+ export function refreshIsolation(scene) {
44
+ if (!isolatedIds)
45
+ return noop;
46
+ if (scene)
47
+ liveScene = scene;
48
+ const keep = collectIsolationSubtree(isolatedIds);
49
+ const visited = new Set();
50
+ const hidden = new Set();
51
+ const visit = (object) => {
52
+ if (visited.has(object))
53
+ return;
54
+ visited.add(object);
55
+ const flags = object;
56
+ if (keep.has(object) || flags.isLight || flags.isCamera)
57
+ return;
58
+ hideFromScene(object, 'isolated');
59
+ hidden.add(object);
60
+ };
61
+ liveScene?.traverse(visit);
62
+ // Also covers not-yet-attached registered roots and imperative callers without a scene.
63
+ for (const object of sceneRegistry.nodes.values())
64
+ object.traverse(visit);
65
+ for (const object of heldObjects)
66
+ if (!hidden.has(object))
67
+ showInScene(object, 'isolated');
68
+ heldObjects = hidden;
69
+ const atmosphere = scene;
70
+ if (!atmosphere?.isScene)
71
+ return noop;
72
+ const { fog, fogNode } = atmosphere;
73
+ atmosphere.fog = null;
74
+ atmosphere.fogNode = null;
75
+ return () => {
76
+ atmosphere.fog = fog;
77
+ atmosphere.fogNode = fogNode;
78
+ };
80
79
  }
80
+ /** Restore even detached/unregistered meshes, without undoing solo or batching holds. */
81
81
  export function clearIsolation() {
82
- // We don't know which objects were touched without re-walking, so
83
- // walk every registered root + its descendants and drop the isolation
84
- // reason wherever it was set. `traverse` is cheap and idempotent here.
85
- for (const [, obj] of sceneRegistry.nodes) {
86
- obj.traverse((child) => {
87
- showInScene(child, 'isolated');
88
- });
89
- }
90
- isolationActive = false;
82
+ for (const object of heldObjects)
83
+ showInScene(object, 'isolated');
84
+ heldObjects.clear();
85
+ isolatedIds = null;
86
+ liveScene = undefined;
91
87
  }
@@ -1 +1 @@
1
- {"version":3,"file":"materials.d.ts","sourceRoot":"","sources":["../../src/lib/materials.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"materials.d.ts","sourceRoot":"","sources":["../../src/lib/materials.ts"],"names":[],"mappings":"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"}
@@ -475,7 +475,8 @@ export function resolveSlotDefaultMaterial(slotDefault, shading = 'rendered', ro
475
475
  return (createMaterialFromPresetRef(slotDefault, shading) ??
476
476
  cachedDefaultMaterial(`slot-#ffffff-${roughness}`, '#ffffff', roughness, shading));
477
477
  }
478
- return cachedDefaultMaterial(`slot-${slotDefault}-${roughness}`, slotDefault, roughness, shading);
478
+ const color = slotDefault.startsWith('#') ? slotDefault.toLowerCase() : slotDefault;
479
+ return cachedDefaultMaterial(`slot-${color}-${roughness}`, color, roughness, shading);
479
480
  }
480
481
  export function createDefaultMaterial(color = '#ffffff', roughness = 0.9, shading = 'rendered', side = THREE.FrontSide) {
481
482
  const resolvedSide = resolveNodeMaterialSide(side);
@@ -45,8 +45,9 @@ export declare function detectRendererCapability({ canvas, gpu, powerPreference,
45
45
  powerPreference?: RendererPowerPreference;
46
46
  webgpuTimeoutMs?: number;
47
47
  }): Promise<RendererCapability>;
48
- export declare function initializeGpuRenderer<Renderer extends InitializableRenderer>({ createRenderer, gpu, powerPreference, probeCanvas, webgpuTimeoutMs, }: {
48
+ export declare function initializeGpuRenderer<Renderer extends InitializableRenderer>({ createRenderer, forceWebGL, gpu, powerPreference, probeCanvas, webgpuTimeoutMs, }: {
49
49
  createRenderer: (parameters: RendererBackendParameters) => Renderer;
50
+ forceWebGL?: boolean;
50
51
  gpu?: RendererGpu | null;
51
52
  powerPreference?: RendererPowerPreference;
52
53
  probeCanvas?: RendererCapabilityCanvas | null;
@@ -1 +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"}
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,UAAkB,EAClB,GAAG,EACH,eAAe,EACf,WAA6B,EAC7B,eAAkD,GACnD,EAAE;IACD,cAAc,EAAE,CAAC,UAAU,EAAE,yBAAyB,KAAK,QAAQ,CAAA;IACnE,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,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"}
@@ -71,10 +71,10 @@ export async function detectRendererCapability({ canvas = browserCanvas(), gpu =
71
71
  }
72
72
  return { error: capabilityError, status: 'unsupported' };
73
73
  }
74
- export async function initializeGpuRenderer({ createRenderer, gpu, powerPreference, probeCanvas = browserCanvas(), webgpuTimeoutMs = WEBGPU_INITIALIZATION_TIMEOUT_MS, }) {
74
+ export async function initializeGpuRenderer({ createRenderer, forceWebGL = false, gpu, powerPreference, probeCanvas = browserCanvas(), webgpuTimeoutMs = WEBGPU_INITIALIZATION_TIMEOUT_MS, }) {
75
75
  const capability = await detectRendererCapability({
76
76
  canvas: probeCanvas,
77
- gpu,
77
+ gpu: forceWebGL ? null : gpu,
78
78
  powerPreference,
79
79
  webgpuTimeoutMs,
80
80
  });
@@ -60,10 +60,12 @@ function applyHold(obj, hold) {
60
60
  // A batched wall is both drawn and shadowed by the merged mesh, so it stays
61
61
  // out of the shadow pass too — enabling the shadow-only bit would submit its
62
62
  // triangles a second time, on top of the copy the batch already casts.
63
+ if (hold.reasons.has('isolated'))
64
+ obj.layers.disable(SHADOW_ONLY_LAYER);
63
65
  if (hold.reasons.has('batched') || hold.reasons.has('wall-batched')) {
64
66
  obj.layers.enable(BATCHED_LAYER);
65
67
  return;
66
68
  }
67
- if (hold.reasons.has('shadow-only'))
69
+ if (!hold.reasons.has('isolated') && hold.reasons.has('shadow-only'))
68
70
  obj.layers.enable(SHADOW_ONLY_LAYER);
69
71
  }
@@ -1,5 +1,6 @@
1
1
  import { type Camera, type Scene } from 'three';
2
2
  import { type WebGPURenderer } from 'three/webgpu';
3
+ import type { SceneAtmosphereSource } from '../components/viewer/scene-atmosphere';
3
4
  import { type EdgeMode } from './edge-style';
4
5
  export declare const THUMBNAIL_WIDTH = 1920;
5
6
  export declare const THUMBNAIL_HEIGHT = 1080;
@@ -43,9 +44,10 @@ export type SnapshotPipeline = {
43
44
  }) => Promise<SnapshotCaptureResult>;
44
45
  dispose: () => void;
45
46
  };
46
- export declare function createSnapshotPipeline({ renderer, scene, camera, }: {
47
+ export declare function createSnapshotPipeline({ renderer, scene, camera, atmosphere, }: {
47
48
  renderer: WebGPURenderer;
48
49
  scene: Scene;
49
50
  camera: Camera;
51
+ atmosphere?: SceneAtmosphereSource | null;
50
52
  }): Promise<SnapshotPipeline | null>;
51
53
  //# sourceMappingURL=snapshot-pipeline.d.ts.map
@@ -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;;;;;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"}
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;AAoBjF,OAAO,EAKL,KAAK,cAAc,EACpB,MAAM,cAAc,CAAA;AAErB,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAA;AAElF,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;AAgBD,wBAAsB,sBAAsB,CAAC,EAC3C,QAAQ,EACR,KAAK,EACL,MAAM,EACN,UAAiB,GAClB,EAAE;IACD,QAAQ,EAAE,cAAc,CAAA;IACxB,KAAK,EAAE,KAAK,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAAA;CAC1C,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CA0SnC"}
@@ -2,8 +2,8 @@ import { Color, Matrix4, UnsignedByteType } from 'three';
2
2
  import { ssgi } from 'three/addons/tsl/display/SSGINode.js';
3
3
  import { denoise } from 'three/examples/jsm/tsl/display/DenoiseNode.js';
4
4
  import { fxaa } from 'three/examples/jsm/tsl/display/FXAANode.js';
5
- import { convertToTexture, diffuseColor, float, mix, mrt, normalView, output, pass, sample, saturation, screenUV, smoothstep, uniform, vec3, vec4, } from 'three/tsl';
6
- import { RenderPipeline, RenderTarget } from 'three/webgpu';
5
+ import { convertToTexture, diffuseColor, float, mix, mrt, normalView, output, sample, saturation, screenUV, smoothstep, uniform, vec3, vec4, } from 'three/tsl';
6
+ import { PassNode, RenderPipeline, RenderTarget, } from 'three/webgpu';
7
7
  import { GRADE_PARAMS, SSGI_PARAMS } from '../components/viewer/post-processing';
8
8
  import { backdropGradient, deepSkyColor, horizonHazeColor } from './backdrop';
9
9
  import { edgeColorFor, edgeOpacityScaleFor } from './edge-style';
@@ -29,7 +29,22 @@ function clampSnapshotSize(width, height) {
29
29
  const scale = SNAPSHOT_MAX_EDGE / maxEdge;
30
30
  return { w: Math.round(width * scale), h: Math.round(height * scale) };
31
31
  }
32
- export async function createSnapshotPipeline({ renderer, scene, camera, }) {
32
+ class SnapshotScenePass extends PassNode {
33
+ updateBefore(frame) {
34
+ const renderer = frame.renderer;
35
+ const clearAlpha = renderer.getClearAlpha();
36
+ // RTTNode resets clear alpha in r186. The geometry mask must be cleared
37
+ // inside this pass, not inherited from the outer snapshot render.
38
+ renderer.setClearAlpha(0);
39
+ try {
40
+ return super.updateBefore(frame);
41
+ }
42
+ finally {
43
+ renderer.setClearAlpha(clearAlpha);
44
+ }
45
+ }
46
+ }
47
+ export async function createSnapshotPipeline({ renderer, scene, camera, atmosphere = null, }) {
33
48
  try {
34
49
  if (renderer.init)
35
50
  await renderer.init();
@@ -49,10 +64,9 @@ export async function createSnapshotPipeline({ renderer, scene, camera, }) {
49
64
  const inkColorUniform = uniform(new Color('#1a1d24'));
50
65
  const inkOpacityUniform = uniform(0.5);
51
66
  const inkOpacityScaleUniform = uniform(1);
52
- // pass() handles MRT internally for all material types, including custom
53
- // shaders — unlike renderer.setMRT() which crashes on non-NodeMaterials.
54
- // pass() also respects camera.layers, so caller-disabled objects are filtered.
55
- const scenePass = pass(scene, camera);
67
+ // The scene pass owns MRT and its transparent clear, including when nested
68
+ // inside FXAA's intermediate render target.
69
+ const scenePass = new SnapshotScenePass('color', scene, camera);
56
70
  scenePass.setMRT(mrt({
57
71
  output,
58
72
  diffuseColor,
@@ -111,13 +125,15 @@ export async function createSnapshotPipeline({ renderer, scene, camera, }) {
111
125
  const ndc = vec4(screenUV.x.mul(2).sub(1), float(1).sub(screenUV.y).mul(2).sub(1), 1, 1);
112
126
  const viewRay = bgProjInvUniform.mul(ndc);
113
127
  const worldDir = bgCamWorldUniform.mul(vec4(viewRay.xyz, 0)).xyz.normalize();
114
- const ungradedBgGradient = backdropGradient({
115
- dirY: worldDir.y,
116
- background: bgColorUniform,
117
- haze: bgHazeUniform,
118
- sky: bgSkyUniform,
119
- skyDeep: bgSkyDeepUniform,
120
- });
128
+ const ungradedBgGradient = atmosphere
129
+ ? atmosphere.skyRadiance(worldDir)
130
+ : backdropGradient({
131
+ dirY: worldDir.y,
132
+ background: bgColorUniform,
133
+ haze: bgHazeUniform,
134
+ sky: bgSkyUniform,
135
+ skyDeep: bgSkyDeepUniform,
136
+ });
121
137
  const bgGradient = mix(ungradedBgGradient, gradeRgb(ungradedBgGradient), gradeMixUniform);
122
138
  const alpha = scenePassColor.a;
123
139
  const finalOutput = vec4(mix(sceneRgb, mix(bgGradient, sceneRgb, alpha), bgMixUniform), mix(alpha, float(1), bgMixUniform));
@@ -159,8 +175,6 @@ export async function createSnapshotPipeline({ renderer, scene, camera, }) {
159
175
  renderTarget.setSize(captureWidth, captureHeight);
160
176
  }
161
177
  try {
162
- ;
163
- renderer.setClearAlpha(0);
164
178
  renderer.setRenderTarget(renderTarget);
165
179
  pipeline.render();
166
180
  }
@@ -268,6 +282,8 @@ export async function createSnapshotPipeline({ renderer, scene, camera, }) {
268
282
  dispose: () => {
269
283
  pipeline.dispose();
270
284
  renderTarget.dispose();
285
+ scenePass.dispose();
286
+ giPass.dispose();
271
287
  },
272
288
  };
273
289
  }
@@ -1,4 +1,4 @@
1
- import type { AnyNode, BaseNode, BuildingNode, LevelNode, ZoneNode } from '@pascal-app/core';
1
+ import type { AnyNode, BaseNode, BuildingNode, LevelNode, UnitNode, ZoneNode } from '@pascal-app/core';
2
2
  import type { Object3D } from 'three';
3
3
  import type { EdgeMode } from '../lib/edge-style';
4
4
  import type { ColorPreset, RenderShading } from '../lib/materials';
@@ -17,6 +17,8 @@ type Outliner = {
17
17
  };
18
18
  type ViewerState = {
19
19
  selection: SelectionPath;
20
+ focusedUnitId: UnitNode['id'] | null;
21
+ setFocusedUnit: (id: UnitNode['id'] | null) => void;
20
22
  previewSelectedIds: BaseNode['id'][];
21
23
  setPreviewSelectedIds: (ids: BaseNode['id'][]) => void;
22
24
  /** Host-owned selection highlights rendered through the viewer's native
@@ -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;AACrD,MAAM,MAAM,QAAQ,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,aAAa,CAAA;AAEhE,KAAK,aAAa,GAAG;IACnB,UAAU,EAAE,YAAY,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IACrC,OAAO,EAAE,SAAS,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IAC/B,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IAC7B,WAAW,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAA;CAC9B,CAAA;AAED,KAAK,QAAQ,GAAG;IACd,eAAe,EAAE,QAAQ,EAAE,CAAA;IAC3B,cAAc,EAAE,QAAQ,EAAE,CAAA;CAC3B,CAAA;AAED,KAAK,WAAW,GAAG;IACjB,SAAS,EAAE,aAAa,CAAA;IACxB,kBAAkB,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAA;IACpC,qBAAqB,EAAE,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,IAAI,CAAA;IACtD;+EAC2E;IAC3E,mBAAmB,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAA;IACrC,sBAAsB,EAAE,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,IAAI,CAAA;IACvD,kBAAkB,EAAE,MAAM,CAAA;IAC1B,qBAAqB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7C,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IAChD,YAAY,EAAE,CAAC,EAAE,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,KAAK,IAAI,CAAA;IAEjE,UAAU,EAAE,aAAa,GAAG,cAAc,CAAA;IAC1C,aAAa,EAAE,CAAC,IAAI,EAAE,aAAa,GAAG,cAAc,KAAK,IAAI,CAAA;IAE7D,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAEnC,aAAa,EAAE,aAAa,CAAA;IAC5B,gBAAgB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAA;IAElD;;;;qBAIiB;IACjB,WAAW,EAAE,OAAO,CAAA;IACpB,YAAY,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IAEtC;;;4EAGwE;IACxE,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACxC,qBAAqB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;IAC5D,oBAAoB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAE9C,uFAAuF;IACvF,YAAY,EAAE,OAAO,CAAA;IACrB,eAAe,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IAEzC,OAAO,EAAE,aAAa,CAAA;IACtB,gBAAgB,EAAE,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC,CAAA;IAC/D,UAAU,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAA;IAE5C,QAAQ,EAAE,OAAO,CAAA;IACjB,WAAW,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAA;IAExC,WAAW,EAAE,WAAW,CAAA;IACxB,cAAc,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAA;IAE7C,KAAK,EAAE,QAAQ,CAAA;IACf,QAAQ,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAA;IAEnC,OAAO,EAAE,OAAO,CAAA;IAChB,UAAU,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IAEtC,IAAI,EAAE,QAAQ,GAAG,UAAU,CAAA;IAC3B,OAAO,EAAE,CAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,KAAK,IAAI,CAAA;IAC9C,cAAc,EAAE,cAAc,CAAA;IAC9B,iBAAiB,EAAE,CAAC,QAAQ,EAAE,cAAc,KAAK,IAAI,CAAA;IACrD;;sDAEkD;IAClD,YAAY,EAAE,OAAO,CAAA;IAErB,SAAS,EAAE,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,CAAA;IACrD,YAAY,EAAE,CAAC,IAAI,EAAE,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,KAAK,IAAI,CAAA;IAExE,QAAQ,EAAE,QAAQ,CAAA;IAClB,WAAW,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,CAAA;IAErC,SAAS,EAAE,OAAO,CAAA;IAClB,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IAErC,UAAU,EAAE,OAAO,CAAA;IACnB,aAAa,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IAEtC,QAAQ,EAAE,OAAO,CAAA;IACjB,WAAW,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IAEpC,gBAAgB,EAAE,OAAO,CAAA;IACzB,mBAAmB,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IAO5C,SAAS,EAAE,OAAO,CAAA;IAClB,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IAErC,qBAAqB,EAAE,OAAO,CAAA;IAC9B,wBAAwB,EAAE,CAAC,WAAW,EAAE,OAAO,KAAK,IAAI,CAAA;IAGxD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IAE/C,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IACzC,kBAAkB,EAAE,MAAM,CACxB,MAAM,EACN;QACE,SAAS,CAAC,EAAE,OAAO,CAAA;QACnB,UAAU,CAAC,EAAE,OAAO,CAAA;QACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;QAClB,gBAAgB,CAAC,EAAE,OAAO,CAAA;KAC3B,CACF,CAAA;IAGD,YAAY,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,aAAa,CAAC,KAAK,IAAI,CAAA;IACvD,cAAc,EAAE,MAAM,IAAI,CAAA;IAE1B,QAAQ,EAAE,QAAQ,CAAA;IAClB;6DACyD;IACzD,gBAAgB,EAAE,MAAM,CAAA;IACxB,oBAAoB,EAAE,MAAM,IAAI,CAAA;IAGhC,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAA;IAChF,cAAc,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,KAAK,IAAI,CAAA;IAEjG,WAAW,EAAE,OAAO,CAAA;IACpB,cAAc,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IAE1C,eAAe,EAAE,OAAO,CAAA;IACxB,kBAAkB,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IAE3C;4EACwE;IACxE,oBAAoB,EAAE,OAAO,CAAA;IAC7B,uBAAuB,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAA;IAErD,cAAc,EAAE,OAAO,CAAA;IACvB,iBAAiB,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAA;IAE9C;;;;;;;;;OASG;IACH,aAAa,EAAE,OAAO,CAAA;IACtB,gBAAgB,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAA;CAC9C,CAAA;AAmJD,QAAA,MAAM,SAAS;;;;;;;;;4BAlMG,OAAO;6BACN,OAAO;2BACT,OAAO;mCACC,OAAO;;;;;;;;;;;;;;;;;;;;;4BAHd,OAAO;6BACN,OAAO;2BACT,OAAO;mCACC,OAAO;;;;;;;;;;;;EA6a/B,CAAA;AAED;;;;cAIc;AACd,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,QAMzE;AAED,eAAe,SAAS,CAAA"}
1
+ {"version":3,"file":"use-viewer.d.ts","sourceRoot":"","sources":["../../src/store/use-viewer.ts"],"names":[],"mappings":"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"}
@@ -122,6 +122,8 @@ function normalizePersistedViewerState(value) {
122
122
  }
123
123
  const useViewer = create()(persist((set) => ({
124
124
  selection: { buildingId: null, levelId: null, zoneId: null, selectedIds: [] },
125
+ focusedUnitId: null,
126
+ setFocusedUnit: (focusedUnitId) => set({ focusedUnitId }),
125
127
  previewSelectedIds: [],
126
128
  setPreviewSelectedIds: (ids) => set({ previewSelectedIds: ids }),
127
129
  externalSelectedIds: [],
@@ -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;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"}
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,YAuHtB,CAAA;AA0pCD;;;;;;;;;;;;;;;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"}
@@ -154,8 +154,9 @@ export const DoorSystem = () => {
154
154
  // Rebuild the parent wall so its cutout reflects the updated door geometry
155
155
  // Avoid triggering expensive wall CSG rebuilds while the door is being interactively moved/duplicated.
156
156
  // The editor tools will request a final wall rebuild on commit.
157
- const isTransient = !!node.metadata?.isTransient;
158
- if (!isTransient && effectiveNode.parentId) {
157
+ const metadata = effectiveNode.metadata;
158
+ const deferParentRebuild = !!metadata?.isTransient || !!metadata?.deferParentRebuild;
159
+ if (!deferParentRebuild && effectiveNode.parentId) {
159
160
  useScene.getState().dirtyNodes.add(effectiveNode.parentId);
160
161
  }
161
162
  }
@@ -777,9 +778,21 @@ function addDoorLeaf(mesh, { leafWidth, leafHeight, leafCenterX, leafCenterY, le
777
778
  const hingeH = 0.1;
778
779
  const hingeW = 0.024;
779
780
  const hingeD = leafDepth + 0.016;
780
- addBox(mesh, hardwareMaterial, hingeW, hingeH, hingeD, hingeMarkerX, leafBottom + 0.25, 0);
781
- addBox(mesh, hardwareMaterial, hingeW, hingeH, hingeD, hingeMarkerX, (leafBottom + leafTop) / 2, 0);
782
- addBox(mesh, hardwareMaterial, hingeW, hingeH, hingeD, hingeMarkerX, leafTop - 0.25, 0);
781
+ const hingeRadii = roundedBoundary?.radii ?? openingTopRadii;
782
+ const cornerDrop = openingShape === 'rounded'
783
+ ? hingeSide === 'left'
784
+ ? hingeRadii.topLeft
785
+ : hingeRadii.topRight
786
+ : openingShape === 'arch'
787
+ ? getClampedArchHeight(leafWidth, leafHeight, archHeight)
788
+ : 0;
789
+ // Hinges need their full height supported by the straight jamb below the curve.
790
+ const straightEdgeTop = leafTop - cornerDrop;
791
+ for (const hingeY of [leafBottom + 0.25, (leafBottom + leafTop) / 2, leafTop - 0.25]) {
792
+ if (hingeY + hingeH / 2 > straightEdgeTop + 1e-6)
793
+ continue;
794
+ addBox(mesh, hardwareMaterial, hingeW, hingeH, hingeD, hingeMarkerX, hingeY, 0);
795
+ }
783
796
  }
784
797
  // When the leaf is swung open it projects into the room and would otherwise
785
798
  // win a top-down selection click over the floor beneath it. Drop only the
@@ -1 +1 @@
1
- {"version":3,"file":"floor-elevation-system.d.ts","sourceRoot":"","sources":["../../../src/systems/floor-elevation/floor-elevation-system.tsx"],"names":[],"mappings":"AAyCA;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,oBAAoB,YA6EhC,CAAA"}
1
+ {"version":3,"file":"floor-elevation-system.d.ts","sourceRoot":"","sources":["../../../src/systems/floor-elevation/floor-elevation-system.tsx"],"names":[],"mappings":"AA8DA;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,oBAAoB,YA2LhC,CAAA"}