@pascal-app/viewer 1.0.3 → 1.0.5

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 (276) hide show
  1. package/dist/capture/capture-runtime.js +1 -1
  2. package/dist/components/error-boundary.d.ts.map +1 -1
  3. package/dist/components/renderers/node-renderer.d.ts +1 -1
  4. package/dist/components/renderers/node-renderer.d.ts.map +1 -1
  5. package/dist/components/renderers/node-renderer.js +12 -4
  6. package/dist/components/renderers/parametric-node-renderer.d.ts.map +1 -1
  7. package/dist/components/viewer/bvh-ecctrl.d.ts.map +1 -1
  8. package/dist/components/viewer/frame-limiter.d.ts +1 -0
  9. package/dist/components/viewer/frame-limiter.d.ts.map +1 -1
  10. package/dist/components/viewer/frame-limiter.js +22 -3
  11. package/dist/components/viewer/glb-interactive.d.ts +25 -1
  12. package/dist/components/viewer/glb-interactive.d.ts.map +1 -1
  13. package/dist/components/viewer/glb-interactive.js +253 -86
  14. package/dist/components/viewer/glb-mechanisms.d.ts +10 -0
  15. package/dist/components/viewer/glb-mechanisms.d.ts.map +1 -0
  16. package/dist/components/viewer/glb-mechanisms.js +19 -0
  17. package/dist/components/viewer/glb-reference-nodes.js +1 -1
  18. package/dist/components/viewer/glb-replace-instances.js +1 -1
  19. package/dist/components/viewer/glb-scene.d.ts +45 -27
  20. package/dist/components/viewer/glb-scene.d.ts.map +1 -1
  21. package/dist/components/viewer/glb-scene.js +421 -508
  22. package/dist/components/viewer/glb-walkthrough-controller.d.ts +1 -1
  23. package/dist/components/viewer/glb-walkthrough-controller.d.ts.map +1 -1
  24. package/dist/components/viewer/glb-walkthrough-controller.js +13 -2
  25. package/dist/components/viewer/index.d.ts +24 -1
  26. package/dist/components/viewer/index.d.ts.map +1 -1
  27. package/dist/components/viewer/index.js +111 -37
  28. package/dist/components/viewer/lights.d.ts.map +1 -1
  29. package/dist/components/viewer/lights.js +113 -22
  30. package/dist/components/viewer/perf-monitor.d.ts.map +1 -1
  31. package/dist/components/viewer/perf-monitor.js +6 -0
  32. package/dist/components/viewer/perf-panel.d.ts.map +1 -1
  33. package/dist/components/viewer/perf-panel.js +1 -1
  34. package/dist/components/viewer/post-processing.d.ts.map +1 -1
  35. package/dist/components/viewer/post-processing.js +104 -36
  36. package/dist/components/viewer/registered-systems.d.ts.map +1 -1
  37. package/dist/components/viewer/registered-systems.js +17 -3
  38. package/dist/components/viewer/render-error.d.ts +18 -0
  39. package/dist/components/viewer/render-error.d.ts.map +1 -0
  40. package/dist/components/viewer/render-error.js +29 -0
  41. package/dist/components/viewer/scene-bvh.d.ts.map +1 -1
  42. package/dist/components/viewer/scene-bvh.js +1 -1
  43. package/dist/components/viewer/scene-environment.d.ts.map +1 -1
  44. package/dist/components/viewer/scene-environment.js +31 -11
  45. package/dist/components/viewer/scene-ground-replacement.d.ts +6 -0
  46. package/dist/components/viewer/scene-ground-replacement.d.ts.map +1 -1
  47. package/dist/components/viewer/scene-ground-replacement.js +15 -0
  48. package/dist/components/viewer/selection-manager.d.ts.map +1 -1
  49. package/dist/components/viewer/selection-manager.js +25 -7
  50. package/dist/components/viewer/underground-ambiance.d.ts +9 -0
  51. package/dist/components/viewer/underground-ambiance.d.ts.map +1 -0
  52. package/dist/components/viewer/underground-ambiance.js +105 -0
  53. package/dist/components/viewer/viewer-camera.d.ts +4 -0
  54. package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
  55. package/dist/components/viewer/viewer-camera.js +12 -2
  56. package/dist/components/viewer/viewer-presentations.d.ts.map +1 -1
  57. package/dist/hooks/use-gltf-ktx2.d.ts.map +1 -1
  58. package/dist/hooks/use-gltf-ktx2.js +2 -0
  59. package/dist/hooks/use-node-events.d.ts.map +1 -1
  60. package/dist/index.d.ts +36 -16
  61. package/dist/index.d.ts.map +1 -1
  62. package/dist/index.js +34 -19
  63. package/dist/lib/asset-url.d.ts.map +1 -1
  64. package/dist/lib/asset-url.js +7 -1
  65. package/dist/lib/backdrop.d.ts +17 -0
  66. package/dist/lib/backdrop.d.ts.map +1 -1
  67. package/dist/lib/backdrop.js +18 -1
  68. package/dist/lib/construction-dust.d.ts +57 -0
  69. package/dist/lib/construction-dust.d.ts.map +1 -0
  70. package/dist/lib/construction-dust.js +187 -0
  71. package/dist/lib/csg-utils.js +1 -1
  72. package/dist/lib/display-state.d.ts +25 -0
  73. package/dist/lib/display-state.d.ts.map +1 -0
  74. package/dist/lib/display-state.js +1 -0
  75. package/dist/lib/foundation-materials.d.ts +26 -0
  76. package/dist/lib/foundation-materials.d.ts.map +1 -0
  77. package/dist/lib/foundation-materials.js +65 -0
  78. package/dist/lib/gltf-unlit.d.ts +10 -0
  79. package/dist/lib/gltf-unlit.d.ts.map +1 -0
  80. package/dist/lib/gltf-unlit.js +23 -0
  81. package/dist/lib/ground-window.d.ts +33 -0
  82. package/dist/lib/ground-window.d.ts.map +1 -0
  83. package/dist/lib/ground-window.js +114 -0
  84. package/dist/lib/hero-pose.d.ts +13 -3
  85. package/dist/lib/hero-pose.d.ts.map +1 -1
  86. package/dist/lib/hero-pose.js +72 -5
  87. package/dist/lib/ink-edges.d.ts +6 -3
  88. package/dist/lib/ink-edges.d.ts.map +1 -1
  89. package/dist/lib/ink-edges.js +17 -7
  90. package/dist/lib/key-light-override.d.ts +11 -0
  91. package/dist/lib/key-light-override.d.ts.map +1 -0
  92. package/dist/lib/key-light-override.js +14 -0
  93. package/dist/lib/ktx2-loader.d.ts +7 -0
  94. package/dist/lib/ktx2-loader.d.ts.map +1 -1
  95. package/dist/lib/ktx2-loader.js +35 -0
  96. package/dist/lib/layer-pass.d.ts.map +1 -1
  97. package/dist/lib/layers.d.ts +11 -7
  98. package/dist/lib/layers.d.ts.map +1 -1
  99. package/dist/lib/layers.js +16 -6
  100. package/dist/lib/live-frame-hold.d.ts +14 -0
  101. package/dist/lib/live-frame-hold.d.ts.map +1 -0
  102. package/dist/lib/live-frame-hold.js +28 -0
  103. package/dist/lib/materials.d.ts +19 -7
  104. package/dist/lib/materials.d.ts.map +1 -1
  105. package/dist/lib/materials.js +65 -9
  106. package/dist/lib/merged-outline-node.d.ts +5 -0
  107. package/dist/lib/merged-outline-node.d.ts.map +1 -1
  108. package/dist/lib/merged-outline-node.js +80 -2
  109. package/dist/lib/node-top-surface-height.d.ts +6 -0
  110. package/dist/lib/node-top-surface-height.d.ts.map +1 -0
  111. package/dist/lib/node-top-surface-height.js +74 -0
  112. package/dist/lib/perf-probe-state.d.ts +39 -0
  113. package/dist/lib/perf-probe-state.d.ts.map +1 -0
  114. package/dist/lib/perf-probe-state.js +17 -0
  115. package/dist/lib/pointer-events.d.ts +26 -2
  116. package/dist/lib/pointer-events.d.ts.map +1 -1
  117. package/dist/lib/pointer-events.js +99 -12
  118. package/dist/lib/post-processing-resources.d.ts.map +1 -1
  119. package/dist/lib/procedural-emission.d.ts +7 -0
  120. package/dist/lib/procedural-emission.d.ts.map +1 -0
  121. package/dist/lib/procedural-emission.js +54 -0
  122. package/dist/lib/reveal-candidates.d.ts +80 -0
  123. package/dist/lib/reveal-candidates.d.ts.map +1 -0
  124. package/dist/lib/reveal-candidates.js +524 -0
  125. package/dist/lib/reveal-ghost.d.ts +9 -0
  126. package/dist/lib/reveal-ghost.d.ts.map +1 -0
  127. package/dist/lib/reveal-ghost.js +62 -0
  128. package/dist/lib/reveal-pending.d.ts +12 -0
  129. package/dist/lib/reveal-pending.d.ts.map +1 -0
  130. package/dist/lib/reveal-pending.js +12 -0
  131. package/dist/lib/reveal-pose.d.ts +70 -0
  132. package/dist/lib/reveal-pose.d.ts.map +1 -0
  133. package/dist/lib/reveal-pose.js +248 -0
  134. package/dist/lib/reveal-schedule.d.ts +208 -0
  135. package/dist/lib/reveal-schedule.d.ts.map +1 -0
  136. package/dist/lib/reveal-schedule.js +410 -0
  137. package/dist/lib/reveal-texture-scale.d.ts +5 -0
  138. package/dist/lib/reveal-texture-scale.d.ts.map +1 -0
  139. package/dist/lib/reveal-texture-scale.js +113 -0
  140. package/dist/lib/reveal-windows.d.ts +23 -0
  141. package/dist/lib/reveal-windows.d.ts.map +1 -0
  142. package/dist/lib/reveal-windows.js +31 -0
  143. package/dist/lib/reveal-wipe.d.ts +71 -0
  144. package/dist/lib/reveal-wipe.d.ts.map +1 -0
  145. package/dist/lib/reveal-wipe.js +105 -0
  146. package/dist/lib/scene-pass-mrt.d.ts +12 -0
  147. package/dist/lib/scene-pass-mrt.d.ts.map +1 -0
  148. package/dist/lib/scene-pass-mrt.js +23 -0
  149. package/dist/lib/scene-support-height.d.ts +4 -0
  150. package/dist/lib/scene-support-height.d.ts.map +1 -0
  151. package/dist/lib/scene-support-height.js +34 -0
  152. package/dist/lib/scene-themes.d.ts +30 -8
  153. package/dist/lib/scene-themes.d.ts.map +1 -1
  154. package/dist/lib/scene-themes.js +36 -0
  155. package/dist/lib/scene-visibility.d.ts +17 -1
  156. package/dist/lib/scene-visibility.d.ts.map +1 -1
  157. package/dist/lib/scene-visibility.js +53 -3
  158. package/dist/lib/scripted-opening.d.ts +11 -0
  159. package/dist/lib/scripted-opening.d.ts.map +1 -0
  160. package/dist/lib/scripted-opening.js +20 -0
  161. package/dist/lib/site-surfaces.d.ts +32 -0
  162. package/dist/lib/site-surfaces.d.ts.map +1 -0
  163. package/dist/lib/site-surfaces.js +70 -0
  164. package/dist/lib/snapshot-pipeline.d.ts +34 -3
  165. package/dist/lib/snapshot-pipeline.d.ts.map +1 -1
  166. package/dist/lib/snapshot-pipeline.js +247 -117
  167. package/dist/lib/terrain-perimeter-fill.d.ts.map +1 -1
  168. package/dist/lib/terrain-perimeter-fill.js +22 -14
  169. package/dist/lib/tsl-compat.d.ts +2 -2
  170. package/dist/lib/tsl-compat.d.ts.map +1 -1
  171. package/dist/lib/underground-ambiance.d.ts +168 -0
  172. package/dist/lib/underground-ambiance.d.ts.map +1 -0
  173. package/dist/lib/underground-ambiance.js +334 -0
  174. package/dist/lib/zone-geometry.d.ts +11 -0
  175. package/dist/lib/zone-geometry.d.ts.map +1 -0
  176. package/dist/lib/zone-geometry.js +46 -0
  177. package/dist/store/use-item-light-pool.d.ts +14 -10
  178. package/dist/store/use-item-light-pool.d.ts.map +1 -1
  179. package/dist/store/use-item-light-pool.js +52 -26
  180. package/dist/store/use-viewer.d.ts +194 -26
  181. package/dist/store/use-viewer.d.ts.map +1 -1
  182. package/dist/store/use-viewer.js +164 -21
  183. package/dist/systems/ceiling/ceiling-system.d.ts +15 -1
  184. package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
  185. package/dist/systems/ceiling/ceiling-system.js +88 -6
  186. package/dist/systems/construction-reveal/construction-dust.d.ts +16 -0
  187. package/dist/systems/construction-reveal/construction-dust.d.ts.map +1 -0
  188. package/dist/systems/construction-reveal/construction-dust.js +92 -0
  189. package/dist/systems/construction-reveal/construction-reveal.d.ts +276 -0
  190. package/dist/systems/construction-reveal/construction-reveal.d.ts.map +1 -0
  191. package/dist/systems/construction-reveal/construction-reveal.js +2383 -0
  192. package/dist/systems/construction-reveal/dust-material.d.ts +10 -0
  193. package/dist/systems/construction-reveal/dust-material.d.ts.map +1 -0
  194. package/dist/systems/construction-reveal/dust-material.js +17 -0
  195. package/dist/systems/construction-reveal/ghost-dress.d.ts +10 -0
  196. package/dist/systems/construction-reveal/ghost-dress.d.ts.map +1 -0
  197. package/dist/systems/construction-reveal/ghost-dress.js +24 -0
  198. package/dist/systems/door/door-system.d.ts.map +1 -1
  199. package/dist/systems/door/door-system.js +55 -17
  200. package/dist/systems/elevator/elevator-interaction-system.d.ts +7 -0
  201. package/dist/systems/elevator/elevator-interaction-system.d.ts.map +1 -1
  202. package/dist/systems/elevator/elevator-interaction-system.js +18 -1
  203. package/dist/systems/floor-elevation/floor-elevation-system.d.ts +3 -1
  204. package/dist/systems/floor-elevation/floor-elevation-system.d.ts.map +1 -1
  205. package/dist/systems/floor-elevation/floor-elevation-system.js +33 -5
  206. package/dist/systems/geometry/geometry-system.d.ts.map +1 -1
  207. package/dist/systems/geometry/geometry-system.js +71 -11
  208. package/dist/systems/interactive/control-widget.d.ts.map +1 -1
  209. package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
  210. package/dist/systems/interactive/interactive-system.js +52 -17
  211. package/dist/systems/interactive/procedural-controls.d.ts +13 -0
  212. package/dist/systems/interactive/procedural-controls.d.ts.map +1 -0
  213. package/dist/systems/interactive/procedural-controls.js +17 -0
  214. package/dist/systems/interactive/scripted-clips.d.ts +19 -0
  215. package/dist/systems/interactive/scripted-clips.d.ts.map +1 -0
  216. package/dist/systems/interactive/scripted-clips.js +134 -0
  217. package/dist/systems/item/item-system.d.ts.map +1 -1
  218. package/dist/systems/item/item-system.js +9 -3
  219. package/dist/systems/item/object-cut-invalidation.d.ts +3 -0
  220. package/dist/systems/item/object-cut-invalidation.d.ts.map +1 -0
  221. package/dist/systems/item/object-cut-invalidation.js +68 -0
  222. package/dist/systems/item-light/item-light-system.d.ts +1 -1
  223. package/dist/systems/item-light/item-light-system.d.ts.map +1 -1
  224. package/dist/systems/item-light/item-light-system.js +48 -53
  225. package/dist/systems/level/level-hold.d.ts +4 -0
  226. package/dist/systems/level/level-hold.d.ts.map +1 -0
  227. package/dist/systems/level/level-hold.js +14 -0
  228. package/dist/systems/level/level-system.d.ts.map +1 -1
  229. package/dist/systems/level/level-system.js +19 -6
  230. package/dist/systems/level/level-utils.d.ts +19 -6
  231. package/dist/systems/level/level-utils.d.ts.map +1 -1
  232. package/dist/systems/level/level-utils.js +68 -9
  233. package/dist/systems/level/shadow-only-levels.d.ts +9 -0
  234. package/dist/systems/level/shadow-only-levels.d.ts.map +1 -0
  235. package/dist/systems/level/shadow-only-levels.js +7 -0
  236. package/dist/systems/roof/__fixtures__/roof-plane-cases.d.ts +103 -0
  237. package/dist/systems/roof/__fixtures__/roof-plane-cases.d.ts.map +1 -0
  238. package/dist/systems/roof/__fixtures__/roof-plane-cases.js +317 -0
  239. package/dist/systems/roof/roof-materials.d.ts +16 -2
  240. package/dist/systems/roof/roof-materials.d.ts.map +1 -1
  241. package/dist/systems/roof/roof-materials.js +64 -31
  242. package/dist/systems/roof/roof-system.d.ts +21 -4
  243. package/dist/systems/roof/roof-system.d.ts.map +1 -1
  244. package/dist/systems/roof/roof-system.js +374 -49
  245. package/dist/systems/slab/slab-system.d.ts +1 -1
  246. package/dist/systems/slab/slab-system.d.ts.map +1 -1
  247. package/dist/systems/slab/slab-system.js +49 -19
  248. package/dist/systems/stair/stair-system.d.ts.map +1 -1
  249. package/dist/systems/stair/stair-system.js +133 -354
  250. package/dist/systems/wall/wall-cutout-cache.d.ts +39 -3
  251. package/dist/systems/wall/wall-cutout-cache.d.ts.map +1 -1
  252. package/dist/systems/wall/wall-cutout-cache.js +141 -19
  253. package/dist/systems/wall/wall-cutout.d.ts +6 -0
  254. package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
  255. package/dist/systems/wall/wall-cutout.js +42 -28
  256. package/dist/systems/wall/wall-finish-data.d.ts +22 -0
  257. package/dist/systems/wall/wall-finish-data.d.ts.map +1 -0
  258. package/dist/systems/wall/wall-finish-data.js +17 -0
  259. package/dist/systems/wall/wall-material-variant.d.ts.map +1 -1
  260. package/dist/systems/wall/wall-materials.d.ts +9 -4
  261. package/dist/systems/wall/wall-materials.d.ts.map +1 -1
  262. package/dist/systems/wall/wall-materials.js +135 -153
  263. package/dist/systems/wall/wall-placeholder-sweep.d.ts.map +1 -1
  264. package/dist/systems/wall/wall-system.d.ts +33 -2
  265. package/dist/systems/wall/wall-system.d.ts.map +1 -1
  266. package/dist/systems/wall/wall-system.js +621 -159
  267. package/dist/systems/window/corner-frame.d.ts +10 -0
  268. package/dist/systems/window/corner-frame.d.ts.map +1 -0
  269. package/dist/systems/window/corner-frame.js +17 -0
  270. package/dist/systems/window/window-system.d.ts +1 -1
  271. package/dist/systems/window/window-system.d.ts.map +1 -1
  272. package/dist/systems/window/window-system.js +71 -27
  273. package/package.json +10 -10
  274. package/dist/systems/fence/fence-system.d.ts +0 -22
  275. package/dist/systems/fence/fence-system.d.ts.map +0 -1
  276. package/dist/systems/fence/fence-system.js +0 -357
@@ -0,0 +1,105 @@
1
+ import { positionWorld, uniform } from 'three/tsl';
2
+ /** A node that rises less than this (m) is flat: it sweeps along its longer plan side. */
3
+ export const COVER_MIN_RISE_M = 0.6;
4
+ /** The sweep of a node drawn inside `box` (world space). */
5
+ export function coverSweepOf(box) {
6
+ // Nothing drawn yet: an empty sweep, so the covering has nothing to close.
7
+ if (!(box.max.y >= box.min.y))
8
+ return { axis: 'y', from: 0, to: 0 };
9
+ if (box.max.y - box.min.y >= COVER_MIN_RISE_M) {
10
+ return { axis: 'y', from: box.min.y, to: box.max.y };
11
+ }
12
+ const alongX = box.max.x - box.min.x >= box.max.z - box.min.z;
13
+ return alongX
14
+ ? { axis: 'x', from: box.min.x, to: box.max.x }
15
+ : { axis: 'z', from: box.min.z, to: box.max.z };
16
+ }
17
+ /** The time a covering of `sweep` takes in `range`. */
18
+ export function coverDurationMs(sweep, range) {
19
+ const length = Math.max(0, sweep.to - sweep.from);
20
+ return Math.min(range.maxMs, Math.max(range.minMs, length * range.perMeterMs));
21
+ }
22
+ /**
23
+ * How much of the sweep the covering has closed `elapsedMs` into `durationMs`, as a share in [0, 1].
24
+ * It moves continuously: an ease in and out (a sine), so it starts and stops without a jolt, and it
25
+ * is exactly 0 at the start and 1 at the end.
26
+ */
27
+ export function coverShareAt(elapsedMs, durationMs) {
28
+ if (!(durationMs > 0))
29
+ return elapsedMs >= 0 ? 1 : 0;
30
+ const t = Math.min(1, Math.max(0, elapsedMs / durationMs));
31
+ return 0.5 - 0.5 * Math.cos(Math.PI * t);
32
+ }
33
+ const worn = new WeakMap();
34
+ const sweeps = new WeakMap();
35
+ const eachOf = (material) => Array.isArray(material) ? material : [material];
36
+ const isMesh = (object) => object.isMesh === true && Boolean(object.material);
37
+ function maskOf(sweep) {
38
+ const coordinate = sweep.axis === 'y' ? positionWorld.y : sweep.axis === 'x' ? positionWorld.x : positionWorld.z;
39
+ return coordinate.lessThanEqual(sweep.level);
40
+ }
41
+ function wipedClone(material, mask) {
42
+ const clone = material.clone();
43
+ clone.maskNode = clone.maskNode ? clone.maskNode.and(mask) : mask;
44
+ return clone;
45
+ }
46
+ /**
47
+ * Shows what is below `at` (world metres along the sweep's axis) of everything drawn under `root`,
48
+ * and hides the rest. Idempotent: a frame later it only moves the level; a mesh whose renderer put
49
+ * its own materials back is dressed again. Cleared by `clearRevealWipe`.
50
+ */
51
+ export function setRevealWipe(root, sweep, at) {
52
+ let state = sweeps.get(root);
53
+ if (!state || state.axis !== sweep.axis) {
54
+ if (state)
55
+ clearRevealWipe(root);
56
+ state = { level: uniform(at), axis: sweep.axis };
57
+ sweeps.set(root, state);
58
+ }
59
+ state.level.value = at;
60
+ const mask = maskOf(state);
61
+ root.traverse((child) => {
62
+ if (!isMesh(child))
63
+ return;
64
+ let entry = worn.get(child);
65
+ if (entry && child.material !== entry.wiped) {
66
+ // Its renderer set the real materials again: they are the originals now.
67
+ for (const material of eachOf(entry.wiped))
68
+ material.dispose();
69
+ entry = undefined;
70
+ }
71
+ if (!entry) {
72
+ const original = child.material;
73
+ const wiped = Array.isArray(original)
74
+ ? original.map((material) => wipedClone(material, mask))
75
+ : wipedClone(original, mask);
76
+ entry = { original, wiped, cast: child.castShadow };
77
+ worn.set(child, entry);
78
+ child.material = wiped;
79
+ }
80
+ // The depth pass has no mask: a row not yet laid would still throw its shadow.
81
+ child.castShadow = false;
82
+ });
83
+ }
84
+ /** Gives every mesh under `root` its own materials and its shadow back. */
85
+ export function clearRevealWipe(root) {
86
+ if (!sweeps.delete(root))
87
+ return;
88
+ root.traverse((child) => {
89
+ if (!isMesh(child))
90
+ return;
91
+ const entry = worn.get(child);
92
+ if (!entry)
93
+ return;
94
+ if (child.material === entry.wiped)
95
+ child.material = entry.original;
96
+ for (const material of eachOf(entry.wiped))
97
+ material.dispose();
98
+ child.castShadow = entry.cast;
99
+ worn.delete(child);
100
+ });
101
+ }
102
+ /** The world coordinate the covering has reached on `root`, or null when it is not under a wipe. */
103
+ export function revealWipeLevelOf(root) {
104
+ return sweeps.get(root)?.level.value ?? null;
105
+ }
@@ -0,0 +1,12 @@
1
+ import { mrt } from 'three/tsl';
2
+ /** Unlit assets already contain their lighting; screen-space AO/GI must not light them again. */
3
+ export declare function sceneLightingMask(): import("three/webgpu").VarNode<"vec4", import("three/webgpu").JoinNode<"vec4">>;
4
+ /**
5
+ * The scene pass's attachments. three blends `output` by the material and leaves every other
6
+ * attachment unblended, so a transparent draw overwrites the whole of what it covers in the normals
7
+ * and the diffuse colour, however faint it is: a dust sprite's square was inked as an edge. With the
8
+ * material's blending on them, a draw that writes alpha 0 there (the dust's `mrtNode`) leaves them
9
+ * as they are, and an opaque draw, which has no blending, still overwrites.
10
+ */
11
+ export declare function scenePassMrt(outputs: Parameters<typeof mrt>[0]): import("three/webgpu").MRTNode;
12
+ //# sourceMappingURL=scene-pass-mrt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene-pass-mrt.d.ts","sourceRoot":"","sources":["../../src/lib/scene-pass-mrt.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,GAAG,EAAiB,MAAM,WAAW,CAAA;AAM5D,iGAAiG;AACjG,wBAAgB,iBAAiB,oFAKhC;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,kCAK9D"}
@@ -0,0 +1,23 @@
1
+ import { MaterialBlending } from 'three';
2
+ import { diffuseColor, mrt, uniform, vec4 } from 'three/tsl';
3
+ import { BlendMode } from 'three/webgpu';
4
+ import { importedUnlitMaterial } from './gltf-unlit';
5
+ const materialBlend = new BlendMode(MaterialBlending);
6
+ /** Unlit assets already contain their lighting; screen-space AO/GI must not light them again. */
7
+ export function sceneLightingMask() {
8
+ const weight = uniform(1).onObjectUpdate(({ material }) => importedUnlitMaterial(material) ? 0 : 1);
9
+ return vec4(weight, 0, 0, diffuseColor.a);
10
+ }
11
+ /**
12
+ * The scene pass's attachments. three blends `output` by the material and leaves every other
13
+ * attachment unblended, so a transparent draw overwrites the whole of what it covers in the normals
14
+ * and the diffuse colour, however faint it is: a dust sprite's square was inked as an edge. With the
15
+ * material's blending on them, a draw that writes alpha 0 there (the dust's `mrtNode`) leaves them
16
+ * as they are, and an opaque draw, which has no blending, still overwrites.
17
+ */
18
+ export function scenePassMrt(outputs) {
19
+ return mrt(outputs)
20
+ .setBlendMode('diffuseColor', materialBlend)
21
+ .setBlendMode('normal', materialBlend)
22
+ .setBlendMode('lighting', materialBlend);
23
+ }
@@ -0,0 +1,4 @@
1
+ import { type AnyNode, type AnyNodeId } from '@pascal-app/core';
2
+ /** The highest walkable surface at a level-local point, including rendered shaped tops. */
3
+ export declare function createSceneSupportHeightSampler(nodes: Readonly<Record<AnyNodeId, AnyNode>>, levelId: AnyNodeId, selectedHostId?: AnyNodeId): (x: number, z: number) => number;
4
+ //# sourceMappingURL=scene-support-height.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene-support-height.d.ts","sourceRoot":"","sources":["../../src/lib/scene-support-height.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,SAAS,EAIf,MAAM,kBAAkB,CAAA;AAGzB,2FAA2F;AAC3F,wBAAgB,+BAA+B,CAC7C,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,EAC3C,OAAO,EAAE,SAAS,EAClB,cAAc,CAAC,EAAE,SAAS,GACzB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,MAAM,CAiClC"}
@@ -0,0 +1,34 @@
1
+ import { findLevelAncestorId, levelBaseElevationAt, nodeRegistry, } from '@pascal-app/core';
2
+ import { createNodeTopSurfaceHeightSampler } from './node-top-surface-height';
3
+ /** The highest walkable surface at a level-local point, including rendered shaped tops. */
4
+ export function createSceneSupportHeightSampler(nodes, levelId, selectedHostId) {
5
+ const supports = Object.values(nodes)
6
+ .filter((node) => node.visible !== false &&
7
+ (!selectedHostId || node.id === selectedHostId) &&
8
+ findLevelAncestorId(node.id, nodes) === levelId &&
9
+ !!nodeRegistry.get(node.type)?.capabilities.surfaces?.top)
10
+ .map((node) => {
11
+ const top = nodeRegistry.get(node.type)?.capabilities.surfaces?.top;
12
+ const resolveSupportHeight = top?.supportHeight;
13
+ return {
14
+ node,
15
+ resolveDataHeight: resolveSupportHeight
16
+ ? (x, z) => resolveSupportHeight(node, x, z, { nodes })
17
+ : null,
18
+ sampleRenderedHeight: resolveSupportHeight
19
+ ? null
20
+ : createNodeTopSurfaceHeightSampler(node.id, levelId),
21
+ };
22
+ });
23
+ return (x, z) => {
24
+ let height = levelBaseElevationAt(nodes, levelId, x, z);
25
+ for (const support of supports) {
26
+ const top = support.resolveDataHeight
27
+ ? support.resolveDataHeight(x, z)
28
+ : (support.sampleRenderedHeight?.(x, z) ?? null);
29
+ if (top !== null)
30
+ height = Math.max(height, top);
31
+ }
32
+ return height;
33
+ };
34
+ }
@@ -1,10 +1,10 @@
1
- import type { SurfaceRole } from '@pascal-app/core';
1
+ import type { SitePresentation, SurfaceRole } from '@pascal-app/core';
2
2
  export type SceneTheme = {
3
3
  id: string;
4
4
  name: string;
5
5
  appearance: 'light' | 'dark';
6
6
  background: string;
7
- backgroundSky?: string;
7
+ backgroundSky?: string | null;
8
8
  ground: string;
9
9
  ambient: {
10
10
  color: string;
@@ -15,16 +15,38 @@ export type SceneTheme = {
15
15
  ground: string;
16
16
  intensity: number;
17
17
  };
18
- lights: Array<{
19
- position: [number, number, number];
20
- color: string;
21
- intensity: number;
22
- castShadow?: boolean;
23
- }>;
18
+ lights: NonNullable<NonNullable<SitePresentation['lighting']>['lights']>;
24
19
  toneMappingExposure: number;
20
+ environment?: NonNullable<SitePresentation['lighting']>['environment'];
21
+ postprocessing?: SitePresentation['postprocessing'];
25
22
  clayTints?: Partial<Record<SurfaceRole, string>>;
26
23
  };
24
+ export declare function projectSceneTheme(theme: SceneTheme, presentation?: SitePresentation): SceneTheme;
27
25
  export declare const SCENE_THEMES: SceneTheme[];
28
26
  export declare const SCENE_THEME_IDS: string[];
29
27
  export declare function getSceneTheme(id: string): SceneTheme;
28
+ /**
29
+ * The dark mode the eye dives into under the ground: a neutral anthracite / slate grey, no hue.
30
+ * Neither dark scene theme is neutral (`night` is navy, `twilight` is purple), and a theme would
31
+ * be a thing the person picks, so the underground has its own palette, here beside the theme
32
+ * tokens (`lib/underground-ambiance.ts` is its one reader). Every stop is a grey (R = G = B);
33
+ * the backdrop is the theme backdrop's own gradient (`lib/backdrop.ts`) in these stops.
34
+ */
35
+ export declare const UNDERGROUND_PALETTE: {
36
+ /** The backdrop looking down, most of the frame under the lid: slate grey. */
37
+ readonly background: '#4a4a4a';
38
+ /** The thin band hugging the horizon. */
39
+ readonly haze: '#666666';
40
+ /** The sky above the horizon, and its zenith. */
41
+ readonly sky: '#1b1b1b';
42
+ readonly skyDeep: '#111111';
43
+ /** The corners the vignette closes in to. */
44
+ readonly vignette: '#0c0c0c';
45
+ /** The thin rim along the cut of the hole in the ground, from above. */
46
+ readonly rim: '#3c3c3c';
47
+ /** The grade ring under the ground: a light rim where the ground meets the level. */
48
+ readonly ring: '#b4b4b4';
49
+ /** The sky light's tint (ambient and hemisphere pulled toward it under the ground). */
50
+ readonly tint: '#c8c8c8';
51
+ };
30
52
  //# sourceMappingURL=scene-themes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scene-themes.d.ts","sourceRoot":"","sources":["../../src/lib/scene-themes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAEnD,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IAIZ,UAAU,EAAE,OAAO,GAAG,MAAM,CAAA;IAC5B,UAAU,EAAE,MAAM,CAAA;IAIlB,aAAa,CAAC,EAAE,MAAM,CAAA;IAItB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IAC7C,IAAI,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IACzD,MAAM,EAAE,KAAK,CAAC;QACZ,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;QAClC,KAAK,EAAE,MAAM,CAAA;QACb,SAAS,EAAE,MAAM,CAAA;QACjB,UAAU,CAAC,EAAE,OAAO,CAAA;KACrB,CAAC,CAAA;IACF,mBAAmB,EAAE,MAAM,CAAA;IAC3B,SAAS,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAA;CACjD,CAAA;AAED,eAAO,MAAM,YAAY,EAAE,UAAU,EAoMpC,CAAA;AAED,eAAO,MAAM,eAAe,UAAwC,CAAA;AAIpE,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,CAEpD"}
1
+ {"version":3,"file":"scene-themes.d.ts","sourceRoot":"","sources":["../../src/lib/scene-themes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAErE,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IAIZ,UAAU,EAAE,OAAO,GAAG,MAAM,CAAA;IAC5B,UAAU,EAAE,MAAM,CAAA;IAIlB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAI7B,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IAC7C,IAAI,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IACzD,MAAM,EAAE,WAAW,CAAC,WAAW,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAA;IACxE,mBAAmB,EAAE,MAAM,CAAA;IAC3B,WAAW,CAAC,EAAE,WAAW,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,CAAC,CAAA;IACtE,cAAc,CAAC,EAAE,gBAAgB,CAAC,gBAAgB,CAAC,CAAA;IACnD,SAAS,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAA;CACjD,CAAA;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,EAAE,YAAY,CAAC,EAAE,gBAAgB,GAAG,UAAU,CAUhG;AAED,eAAO,MAAM,YAAY,EAAE,UAAU,EAoMpC,CAAA;AAED,eAAO,MAAM,eAAe,UAAwC,CAAA;AAIpE,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,CAEpD;AAED;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB;IAC9B,8EAA8E;aAC9E,UAAU,EAAE,SAAS;IACrB,yCAAyC;aACzC,IAAI,EAAE,SAAS;IACf,iDAAiD;aACjD,GAAG,EAAE,SAAS;aACd,OAAO,EAAE,SAAS;IAClB,6CAA6C;aAC7C,QAAQ,EAAE,SAAS;IACnB,wEAAwE;aACxE,GAAG,EAAE,SAAS;IACd,qFAAqF;aACrF,IAAI,EAAE,SAAS;IACf,uFAAuF;aACvF,IAAI,EAAE,SAAS;CACP,CAAA"}
@@ -1,3 +1,15 @@
1
+ export function projectSceneTheme(theme, presentation) {
2
+ if (!presentation)
3
+ return theme;
4
+ const { backgroundSky, ...lighting } = presentation.lighting ?? {};
5
+ return {
6
+ ...theme,
7
+ ...lighting,
8
+ ...(backgroundSky === undefined ? {} : { backgroundSky }),
9
+ toneMappingExposure: presentation.exposure ?? theme.toneMappingExposure,
10
+ postprocessing: presentation.postprocessing,
11
+ };
12
+ }
1
13
  export const SCENE_THEMES = [
2
14
  {
3
15
  id: 'studio',
@@ -200,3 +212,27 @@ const SCENE_THEME_BY_ID = new Map(SCENE_THEMES.map((theme) => [theme.id, theme])
200
212
  export function getSceneTheme(id) {
201
213
  return SCENE_THEME_BY_ID.get(id) ?? SCENE_THEMES[0];
202
214
  }
215
+ /**
216
+ * The dark mode the eye dives into under the ground: a neutral anthracite / slate grey, no hue.
217
+ * Neither dark scene theme is neutral (`night` is navy, `twilight` is purple), and a theme would
218
+ * be a thing the person picks, so the underground has its own palette, here beside the theme
219
+ * tokens (`lib/underground-ambiance.ts` is its one reader). Every stop is a grey (R = G = B);
220
+ * the backdrop is the theme backdrop's own gradient (`lib/backdrop.ts`) in these stops.
221
+ */
222
+ export const UNDERGROUND_PALETTE = {
223
+ /** The backdrop looking down, most of the frame under the lid: slate grey. */
224
+ background: '#4a4a4a',
225
+ /** The thin band hugging the horizon. */
226
+ haze: '#666666',
227
+ /** The sky above the horizon, and its zenith. */
228
+ sky: '#1b1b1b',
229
+ skyDeep: '#111111',
230
+ /** The corners the vignette closes in to. */
231
+ vignette: '#0c0c0c',
232
+ /** The thin rim along the cut of the hole in the ground, from above. */
233
+ rim: '#3c3c3c',
234
+ /** The grade ring under the ground: a light rim where the ground meets the level. */
235
+ ring: '#b4b4b4',
236
+ /** The sky light's tint (ambient and hemisphere pulled toward it under the ground). */
237
+ tint: '#c8c8c8',
238
+ };
@@ -5,11 +5,27 @@ import type { Object3D } from 'three';
5
5
  * - `isolated` — outside the focused subtree of the viewer's isolation filter.
6
6
  * - `shadow-only` — solo mode: out of the color passes, still casting shadows.
7
7
  * - `batched` — a level's merged wall mesh draws this wall now.
8
+ * - `ground-aside` — the site's ground while a level below grade is active: the floor is seen,
9
+ * and clicked, where the ground was.
8
10
  */
9
- export type HiddenReason = 'isolated' | 'shadow-only' | 'batched' | 'wall-batched';
11
+ export type HiddenReason = 'isolated' | 'shadow-only' | 'batched' | 'wall-batched' | 'ground-aside';
10
12
  /** Holds `obj` off the scene layer for `reason`. Idempotent per reason. */
11
13
  export declare function hideFromScene(obj: Object3D, reason: HiddenReason): void;
12
14
  /** Drops `reason`, restoring the mask `obj` had before the first one arrived. */
13
15
  export declare function showInScene(obj: Object3D, reason: HiddenReason): void;
14
16
  export declare function temporarilyShowShadowOnly(root: Object3D): () => void;
17
+ /**
18
+ * Lifts what the user's view holds back under `root` — solo's shadow-only
19
+ * levels and isolation — for one capture: an interior view shows its floor
20
+ * whole, whatever the user isolated.
21
+ */
22
+ export declare function temporarilyShowPresentation(root: Object3D): () => void;
23
+ /**
24
+ * The site's ground surfaces set aside, or stood back on, for one capture, whatever the person's
25
+ * view holds: a picture of a floor below grade sees through the ground, one of the whole building
26
+ * stands on it. The returned function gives the person's view back.
27
+ */
28
+ export declare function temporarilySetGroundAside(surfaces: readonly Object3D[], aside: boolean): () => void;
29
+ /** Add a capture-only mask without changing the live visibility reasons or batching. */
30
+ export declare function temporarilyHideFromScene(root: Object3D, reason: HiddenReason): () => void;
15
31
  //# sourceMappingURL=scene-visibility.d.ts.map
@@ -1 +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"}
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;;;;;;;;GAQG;AACH,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,aAAa,GAAG,SAAS,GAAG,cAAc,GAAG,cAAc,CAAA;AAmBnG,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,CAEpE;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,IAAI,CAEtE;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,SAAS,QAAQ,EAAE,EAC7B,KAAK,EAAE,OAAO,GACb,MAAM,IAAI,CAYZ;AAED,wFAAwF;AACxF,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,GAAG,MAAM,IAAI,CAczF"}
@@ -34,16 +34,66 @@ export function showInScene(obj, reason) {
34
34
  delete holder[HOLD];
35
35
  }
36
36
  export function temporarilyShowShadowOnly(root) {
37
+ return temporarilyShowHeld(root, ['shadow-only']);
38
+ }
39
+ /**
40
+ * Lifts what the user's view holds back under `root` — solo's shadow-only
41
+ * levels and isolation — for one capture: an interior view shows its floor
42
+ * whole, whatever the user isolated.
43
+ */
44
+ export function temporarilyShowPresentation(root) {
45
+ return temporarilyShowHeld(root, ['shadow-only', 'isolated']);
46
+ }
47
+ /**
48
+ * The site's ground surfaces set aside, or stood back on, for one capture, whatever the person's
49
+ * view holds: a picture of a floor below grade sees through the ground, one of the whole building
50
+ * stands on it. The returned function gives the person's view back.
51
+ */
52
+ export function temporarilySetGroundAside(surfaces, aside) {
53
+ if (!aside) {
54
+ const restores = surfaces.map((surface) => temporarilyShowHeld(surface, ['ground-aside']));
55
+ return () => {
56
+ for (const restore of restores)
57
+ restore();
58
+ };
59
+ }
60
+ const held = surfaces.filter((surface) => !surface[HOLD]?.reasons.has('ground-aside'));
61
+ for (const surface of held)
62
+ hideFromScene(surface, 'ground-aside');
63
+ return () => {
64
+ for (const surface of held)
65
+ showInScene(surface, 'ground-aside');
66
+ };
67
+ }
68
+ /** Add a capture-only mask without changing the live visibility reasons or batching. */
69
+ export function temporarilyHideFromScene(root, reason) {
70
+ const masks = new Map();
71
+ root.traverse((obj) => {
72
+ masks.set(obj, obj.layers.mask);
73
+ const hold = obj[HOLD];
74
+ const reasons = new Set(hold?.reasons);
75
+ // Changing level presentation must not reveal isolated geometry or duplicate batch shadows.
76
+ reasons.delete('shadow-only');
77
+ reasons.add(reason);
78
+ applyHold(obj, { original: hold?.original ?? obj.layers.mask, reasons });
79
+ });
80
+ return () => {
81
+ for (const [obj, mask] of masks)
82
+ obj.layers.mask = mask;
83
+ };
84
+ }
85
+ function temporarilyShowHeld(root, lifted) {
37
86
  const masks = new Map();
38
87
  root.traverse((obj) => {
39
88
  const hold = obj[HOLD];
40
- if (!hold?.reasons.has('shadow-only'))
89
+ if (!hold || !lifted.some((reason) => hold.reasons.has(reason)))
41
90
  return;
42
91
  masks.set(obj, obj.layers.mask);
43
92
  const reasons = new Set(hold.reasons);
44
- reasons.delete('shadow-only');
93
+ for (const reason of lifted)
94
+ reasons.delete(reason);
45
95
  // A capture needs the original scene geometry, while editor overlays and
46
- // objects hidden by isolation or batching must retain their own masks.
96
+ // the reasons not lifted (batching) must retain their own masks.
47
97
  if (reasons.size === 0)
48
98
  obj.layers.mask = hold.original;
49
99
  else
@@ -0,0 +1,11 @@
1
+ import type * as THREE from 'three';
2
+ /** The group a scripted window or door renders its artifact into; the opening's system leaves it alone. */
3
+ export declare const SCRIPTED_MODEL_FLAG = "scriptedModel";
4
+ /**
5
+ * A window or door built from a script renders its artifact, not the
6
+ * parametric frame: drop what an earlier parametric build left under the
7
+ * mesh, and report whether the artifact has loaded (until then the node
8
+ * stays dirty, so scene-ready and bakes wait for it, as for items).
9
+ */
10
+ export declare function settleScriptedOpening(mesh: THREE.Object3D): boolean;
11
+ //# sourceMappingURL=scripted-opening.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scripted-opening.d.ts","sourceRoot":"","sources":["../../src/lib/scripted-opening.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAA;AAEnC,2GAA2G;AAC3G,eAAO,MAAM,mBAAmB,kBAAkB,CAAA;AAElD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,KAAK,CAAC,QAAQ,GAAG,OAAO,CAWnE"}
@@ -0,0 +1,20 @@
1
+ /** The group a scripted window or door renders its artifact into; the opening's system leaves it alone. */
2
+ export const SCRIPTED_MODEL_FLAG = 'scriptedModel';
3
+ /**
4
+ * A window or door built from a script renders its artifact, not the
5
+ * parametric frame: drop what an earlier parametric build left under the
6
+ * mesh, and report whether the artifact has loaded (until then the node
7
+ * stays dirty, so scene-ready and bakes wait for it, as for items).
8
+ */
9
+ export function settleScriptedOpening(mesh) {
10
+ let settled = false;
11
+ for (const child of [...mesh.children]) {
12
+ if (child.userData[SCRIPTED_MODEL_FLAG]) {
13
+ settled = child.userData.itemModelSettled === true;
14
+ continue;
15
+ }
16
+ mesh.remove(child);
17
+ child.traverse((object) => object.geometry?.dispose());
18
+ }
19
+ return settled;
20
+ }
@@ -0,0 +1,32 @@
1
+ import type { Object3D } from 'three';
2
+ /**
3
+ * `userData` key marking the site's ground surfaces with their {@link SiteGroundRole}: the lines
4
+ * drawn on the ground (property line, setbacks, contours) carry none.
5
+ */
6
+ export declare const SITE_GROUND = "siteGround";
7
+ /**
8
+ * `userData` key marking the generic foundation under the buildings (footing and stem, drawn under
9
+ * the ground): a capture that sees through the veil leaves it out, as it left it out before it was drawn.
10
+ */
11
+ export declare const SITE_FOUNDATION = "siteFoundation";
12
+ /**
13
+ * `solid`: the lot's fill, its sculpted terrain, the horizon disc. `veil`: what is drawn in their
14
+ * place over a level below grade, the grade ring with it.
15
+ */
16
+ export type SiteGroundRole = 'solid' | 'veil';
17
+ /**
18
+ * The site's own shown surfaces (ground, horizon disc, property and setback lines, contours), not
19
+ * its registered children (buildings, items), which are groups.
20
+ */
21
+ export declare function siteSurfaces(): Object3D[];
22
+ /** The site's generic foundation meshes, shown or not. */
23
+ export declare function siteFoundation(): Object3D[];
24
+ /** The site's ground by role, shown or not: the veil is hidden until a level below grade is shown. */
25
+ export declare function siteGround(): Record<SiteGroundRole, Object3D[]>;
26
+ /**
27
+ * The ground veiled, or solid, for one capture, whatever the person's view shows: a picture of a
28
+ * floor below grade sees it through the veil, a picture of the whole building stands on solid
29
+ * ground. The returned function gives the person's view back.
30
+ */
31
+ export declare function temporarilyVeilGround(veiled: boolean): () => void;
32
+ //# sourceMappingURL=site-surfaces.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"site-surfaces.d.ts","sourceRoot":"","sources":["../../src/lib/site-surfaces.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAGrC;;;GAGG;AACH,eAAO,MAAM,WAAW,eAAe,CAAA;AAEvC;;;GAGG;AACH,eAAO,MAAM,eAAe,mBAAmB,CAAA;AAE/C;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,MAAM,CAAA;AAE7C;;;GAGG;AACH,wBAAgB,YAAY,IAAI,QAAQ,EAAE,CAUzC;AAED,0DAA0D;AAC1D,wBAAgB,cAAc,IAAI,QAAQ,EAAE,CAQ3C;AAED,sGAAsG;AACtG,wBAAgB,UAAU,IAAI,MAAM,CAAC,cAAc,EAAE,QAAQ,EAAE,CAAC,CAS/D;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,IAAI,CAYjE"}
@@ -0,0 +1,70 @@
1
+ import { sceneRegistry } from '@pascal-app/core';
2
+ import { temporarilySetGroundAside } from './scene-visibility';
3
+ /**
4
+ * `userData` key marking the site's ground surfaces with their {@link SiteGroundRole}: the lines
5
+ * drawn on the ground (property line, setbacks, contours) carry none.
6
+ */
7
+ export const SITE_GROUND = 'siteGround';
8
+ /**
9
+ * `userData` key marking the generic foundation under the buildings (footing and stem, drawn under
10
+ * the ground): a capture that sees through the veil leaves it out, as it left it out before it was drawn.
11
+ */
12
+ export const SITE_FOUNDATION = 'siteFoundation';
13
+ /**
14
+ * The site's own shown surfaces (ground, horizon disc, property and setback lines, contours), not
15
+ * its registered children (buildings, items), which are groups.
16
+ */
17
+ export function siteSurfaces() {
18
+ const surfaces = [];
19
+ for (const siteId of sceneRegistry.byType.site ?? []) {
20
+ for (const child of sceneRegistry.nodes.get(siteId)?.children ?? []) {
21
+ if (child.visible && !(child.type === 'Group' && child.children.length > 0)) {
22
+ surfaces.push(child);
23
+ }
24
+ }
25
+ }
26
+ return surfaces;
27
+ }
28
+ /** The site's generic foundation meshes, shown or not. */
29
+ export function siteFoundation() {
30
+ const foundation = [];
31
+ for (const siteId of sceneRegistry.byType.site ?? []) {
32
+ for (const child of sceneRegistry.nodes.get(siteId)?.children ?? []) {
33
+ if (child.userData[SITE_FOUNDATION] === true)
34
+ foundation.push(child);
35
+ }
36
+ }
37
+ return foundation;
38
+ }
39
+ /** The site's ground by role, shown or not: the veil is hidden until a level below grade is shown. */
40
+ export function siteGround() {
41
+ const ground = { solid: [], veil: [] };
42
+ for (const siteId of sceneRegistry.byType.site ?? []) {
43
+ for (const child of sceneRegistry.nodes.get(siteId)?.children ?? []) {
44
+ const role = child.userData[SITE_GROUND];
45
+ if (role === 'solid' || role === 'veil')
46
+ ground[role].push(child);
47
+ }
48
+ }
49
+ return ground;
50
+ }
51
+ /**
52
+ * The ground veiled, or solid, for one capture, whatever the person's view shows: a picture of a
53
+ * floor below grade sees it through the veil, a picture of the whole building stands on solid
54
+ * ground. The returned function gives the person's view back.
55
+ */
56
+ export function temporarilyVeilGround(veiled) {
57
+ const { solid, veil } = siteGround();
58
+ const restoreSolid = temporarilySetGroundAside(solid, veiled);
59
+ // Seen through a veil the footing under a floor below grade would show: the picture has none.
60
+ const restoreFoundation = veiled ? temporarilySetGroundAside(siteFoundation(), true) : () => { };
61
+ const flipped = veil.filter((surface) => surface.visible !== veiled);
62
+ for (const surface of flipped)
63
+ surface.visible = veiled;
64
+ return () => {
65
+ for (const surface of flipped)
66
+ surface.visible = !veiled;
67
+ restoreFoundation();
68
+ restoreSolid();
69
+ };
70
+ }
@@ -2,6 +2,7 @@ import { type Camera, type Scene } from 'three';
2
2
  import { type WebGPURenderer } from 'three/webgpu';
3
3
  import type { SceneAtmosphereSource } from '../components/viewer/scene-atmosphere';
4
4
  import { type EdgeMode } from './edge-style';
5
+ import type { SceneTheme } from './scene-themes';
5
6
  export declare const THUMBNAIL_WIDTH = 1920;
6
7
  export declare const THUMBNAIL_HEIGHT = 1080;
7
8
  /**
@@ -29,25 +30,55 @@ export type SnapshotCaptureResult = {
29
30
  outW: number;
30
31
  outH: number;
31
32
  };
33
+ /**
34
+ * An opaque studio backdrop in place of the theme sky: a radial gradient in
35
+ * exact sRGB (never tone mapped, so the hexes land as authored). Fixed at
36
+ * pipeline creation — callers without one get the untouched theme-backdrop
37
+ * pipeline. Ground shadows come from the caller's own shadow catcher, which
38
+ * composites over it through the scene alpha.
39
+ */
40
+ export type StudioBackdrop = {
41
+ /** sRGB hex at `center`. */
42
+ inner: string;
43
+ /** sRGB hex at the frame corner farthest from `center`. */
44
+ outer: string;
45
+ /** Gradient centre in screen UV (0,0 = top-left). */
46
+ center: [number, number];
47
+ };
32
48
  export type SnapshotPipeline = {
33
49
  applyEnvironment: ({ theme, transparent, grade, edges, camera, }: {
34
- theme: string;
50
+ theme: SceneTheme;
35
51
  transparent: boolean;
36
52
  grade: boolean;
37
53
  edges: EdgeMode;
38
54
  camera: Camera;
39
55
  }) => void;
40
- capture: ({ captureMode, cropRegion, standardSize, }: {
56
+ capture: ({ captureMode, cropRegion, standardSize, mime, }: {
41
57
  captureMode?: SnapshotCaptureMode;
42
58
  cropRegion?: SnapshotCropRegion;
43
59
  standardSize?: SnapshotSize;
60
+ /** The encoding, when not the snapshot default (a print picture wants a lossless `image/png`). */
61
+ mime?: string;
44
62
  }) => Promise<SnapshotCaptureResult>;
45
63
  dispose: () => void;
46
64
  };
47
- export declare function createSnapshotPipeline({ renderer, scene, camera, atmosphere, }: {
65
+ export declare function createSnapshotPipeline({ renderer, scene, camera, atmosphere, studioBackdrop, }: {
48
66
  renderer: WebGPURenderer;
49
67
  scene: Scene;
50
68
  camera: Camera;
51
69
  atmosphere?: SceneAtmosphereSource | null;
70
+ studioBackdrop?: StudioBackdrop;
71
+ }): Promise<SnapshotPipeline | null>;
72
+ /**
73
+ * A capture WITHOUT the post-processing stack: the scene as the renderer
74
+ * draws it, into an offscreen target, read back and encoded like the
75
+ * post-processed frames. The sheets' orthographic elevation captures use
76
+ * it (2026-09-10): an SSGI pass built for a second, orthographic camera
77
+ * never delivered a frame and held the generator's guard forever.
78
+ */
79
+ export declare function createPlainSnapshotPipeline({ renderer, scene, camera, }: {
80
+ renderer: WebGPURenderer;
81
+ scene: Scene;
82
+ camera: Camera;
52
83
  }): Promise<SnapshotPipeline | null>;
53
84
  //# 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;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"}
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,EAAoC,MAAM,OAAO,CAAA;AAsBjG,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;AAG/E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAGhD,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;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAA;IACb,qDAAqD;IACrD,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACzB,CAAA;AAQD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,gBAAgB,EAAE,CAAC,EACjB,KAAK,EACL,WAAW,EACX,KAAK,EACL,KAAK,EACL,MAAM,GACP,EAAE;QACD,KAAK,EAAE,UAAU,CAAA;QACjB,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,EACZ,IAAI,GACL,EAAE;QACD,WAAW,CAAC,EAAE,mBAAmB,CAAA;QACjC,UAAU,CAAC,EAAE,kBAAkB,CAAA;QAC/B,YAAY,CAAC,EAAE,YAAY,CAAA;QAC3B,kGAAkG;QAClG,IAAI,CAAC,EAAE,MAAM,CAAA;KACd,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,EACjB,cAAc,GACf,EAAE;IACD,QAAQ,EAAE,cAAc,CAAA;IACxB,KAAK,EAAE,KAAK,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAAA;IACzC,cAAc,CAAC,EAAE,cAAc,CAAA;CAChC,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAmPnC;AA4ID;;;;;;GAMG;AACH,wBAAsB,2BAA2B,CAAC,EAChD,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,CAiEnC"}