@pascal-app/viewer 0.8.0 → 0.9.2

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 (284) hide show
  1. package/dist/components/error-boundary.d.ts +6 -0
  2. package/dist/components/error-boundary.d.ts.map +1 -1
  3. package/dist/components/error-boundary.js +6 -0
  4. package/dist/components/renderers/node-renderer.d.ts +6 -2
  5. package/dist/components/renderers/node-renderer.d.ts.map +1 -1
  6. package/dist/components/renderers/node-renderer.js +41 -22
  7. package/dist/components/renderers/parametric-node-renderer.d.ts +5 -0
  8. package/dist/components/renderers/parametric-node-renderer.d.ts.map +1 -0
  9. package/dist/components/renderers/parametric-node-renderer.js +37 -0
  10. package/dist/components/renderers/scene-renderer.d.ts +1 -1
  11. package/dist/components/renderers/scene-renderer.d.ts.map +1 -1
  12. package/dist/components/viewer/bvh-ecctrl.d.ts +77 -0
  13. package/dist/components/viewer/bvh-ecctrl.d.ts.map +1 -0
  14. package/dist/components/viewer/bvh-ecctrl.js +552 -0
  15. package/dist/components/viewer/frame-limiter.d.ts.map +1 -1
  16. package/dist/components/viewer/frame-limiter.js +39 -4
  17. package/dist/components/viewer/glb-interactive.d.ts +41 -0
  18. package/dist/components/viewer/glb-interactive.d.ts.map +1 -0
  19. package/dist/components/viewer/glb-interactive.js +394 -0
  20. package/dist/components/viewer/glb-reference-nodes.d.ts +29 -0
  21. package/dist/components/viewer/glb-reference-nodes.d.ts.map +1 -0
  22. package/dist/components/viewer/glb-reference-nodes.js +80 -0
  23. package/dist/components/viewer/glb-replace-instances.d.ts +18 -0
  24. package/dist/components/viewer/glb-replace-instances.d.ts.map +1 -0
  25. package/dist/components/viewer/glb-replace-instances.js +61 -0
  26. package/dist/components/viewer/glb-scene.d.ts +54 -0
  27. package/dist/components/viewer/glb-scene.d.ts.map +1 -0
  28. package/dist/components/viewer/glb-scene.js +920 -0
  29. package/dist/components/viewer/glb-walkthrough-controller.d.ts +12 -0
  30. package/dist/components/viewer/glb-walkthrough-controller.d.ts.map +1 -0
  31. package/dist/components/viewer/glb-walkthrough-controller.js +357 -0
  32. package/dist/components/viewer/index.d.ts +56 -3
  33. package/dist/components/viewer/index.d.ts.map +1 -1
  34. package/dist/components/viewer/index.js +280 -65
  35. package/dist/components/viewer/lights.d.ts +1 -1
  36. package/dist/components/viewer/lights.d.ts.map +1 -1
  37. package/dist/components/viewer/lights.js +201 -43
  38. package/dist/components/viewer/perf-monitor.d.ts +1 -1
  39. package/dist/components/viewer/perf-monitor.d.ts.map +1 -1
  40. package/dist/components/viewer/perf-monitor.js +115 -12
  41. package/dist/components/viewer/post-processing.d.ts +7 -1
  42. package/dist/components/viewer/post-processing.d.ts.map +1 -1
  43. package/dist/components/viewer/post-processing.js +395 -63
  44. package/dist/components/viewer/registered-systems.d.ts +12 -0
  45. package/dist/components/viewer/registered-systems.d.ts.map +1 -0
  46. package/dist/components/viewer/registered-systems.js +49 -0
  47. package/dist/components/viewer/scene-bvh.d.ts +18 -0
  48. package/dist/components/viewer/scene-bvh.d.ts.map +1 -0
  49. package/dist/components/viewer/scene-bvh.js +97 -0
  50. package/dist/components/viewer/scene-bvh.test.d.ts +2 -0
  51. package/dist/components/viewer/scene-bvh.test.d.ts.map +1 -0
  52. package/dist/components/viewer/scene-bvh.test.js +13 -0
  53. package/dist/components/viewer/scene-environment.d.ts +2 -0
  54. package/dist/components/viewer/scene-environment.d.ts.map +1 -0
  55. package/dist/components/viewer/scene-environment.js +79 -0
  56. package/dist/components/viewer/selection-manager.d.ts +1 -1
  57. package/dist/components/viewer/selection-manager.d.ts.map +1 -1
  58. package/dist/components/viewer/selection-manager.js +37 -14
  59. package/dist/components/viewer/viewer-camera.d.ts +1 -1
  60. package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
  61. package/dist/components/viewer/walkthrough-controls.d.ts +2 -1
  62. package/dist/components/viewer/walkthrough-controls.d.ts.map +1 -1
  63. package/dist/components/viewer/walkthrough-controls.js +20 -0
  64. package/dist/hooks/use-gltf-ktx2.d.ts.map +1 -1
  65. package/dist/hooks/use-gltf-ktx2.js +3 -22
  66. package/dist/hooks/use-node-events.d.ts +6 -74
  67. package/dist/hooks/use-node-events.d.ts.map +1 -1
  68. package/dist/hooks/use-node-events.js +28 -8
  69. package/dist/index.d.ts +48 -5
  70. package/dist/index.d.ts.map +1 -1
  71. package/dist/index.js +82 -4
  72. package/dist/lib/backdrop.d.ts +33 -0
  73. package/dist/lib/backdrop.d.ts.map +1 -0
  74. package/dist/lib/backdrop.js +92 -0
  75. package/dist/lib/box-uv.d.ts +13 -0
  76. package/dist/lib/box-uv.d.ts.map +1 -0
  77. package/dist/lib/box-uv.js +32 -0
  78. package/dist/lib/csg-utils.d.ts +10 -0
  79. package/dist/lib/csg-utils.d.ts.map +1 -0
  80. package/dist/lib/csg-utils.js +93 -0
  81. package/dist/lib/csg-utils.test.d.ts +2 -0
  82. package/dist/lib/csg-utils.test.d.ts.map +1 -0
  83. package/dist/lib/csg-utils.test.js +45 -0
  84. package/dist/lib/drawable-geometry.d.ts +21 -0
  85. package/dist/lib/drawable-geometry.d.ts.map +1 -0
  86. package/dist/lib/drawable-geometry.js +22 -0
  87. package/dist/lib/edge-style.d.ts +4 -0
  88. package/dist/lib/edge-style.d.ts.map +1 -0
  89. package/dist/lib/edge-style.js +31 -0
  90. package/dist/lib/gpu-perf.d.ts +4 -0
  91. package/dist/lib/gpu-perf.d.ts.map +1 -0
  92. package/dist/lib/gpu-perf.js +23 -0
  93. package/dist/lib/ink-edges.d.ts +9 -0
  94. package/dist/lib/ink-edges.d.ts.map +1 -0
  95. package/dist/lib/ink-edges.js +49 -0
  96. package/dist/lib/isolation.d.ts +39 -0
  97. package/dist/lib/isolation.d.ts.map +1 -0
  98. package/dist/lib/isolation.js +103 -0
  99. package/dist/lib/ktx2-loader.d.ts +43 -0
  100. package/dist/lib/ktx2-loader.d.ts.map +1 -0
  101. package/dist/lib/ktx2-loader.js +136 -0
  102. package/dist/lib/layers.d.ts +28 -0
  103. package/dist/lib/layers.d.ts.map +1 -1
  104. package/dist/lib/layers.js +28 -0
  105. package/dist/lib/materials.d.ts +38 -15
  106. package/dist/lib/materials.d.ts.map +1 -1
  107. package/dist/lib/materials.js +364 -72
  108. package/dist/lib/merged-outline-node.d.ts.map +1 -1
  109. package/dist/lib/merged-outline-node.js +7 -0
  110. package/dist/lib/polygon-union.d.ts +4 -0
  111. package/dist/lib/polygon-union.d.ts.map +1 -0
  112. package/dist/lib/polygon-union.js +289 -0
  113. package/dist/lib/polygon-union.test.d.ts +2 -0
  114. package/dist/lib/polygon-union.test.d.ts.map +1 -0
  115. package/dist/lib/polygon-union.test.js +105 -0
  116. package/dist/lib/scene-themes.d.ts +30 -0
  117. package/dist/lib/scene-themes.d.ts.map +1 -0
  118. package/dist/lib/scene-themes.js +202 -0
  119. package/dist/lib/shadow-only.d.ts +4 -0
  120. package/dist/lib/shadow-only.d.ts.map +1 -0
  121. package/dist/lib/shadow-only.js +36 -0
  122. package/dist/lib/tsl-compat.d.ts +12 -0
  123. package/dist/lib/tsl-compat.d.ts.map +1 -0
  124. package/dist/lib/tsl-compat.js +13 -0
  125. package/dist/store/use-viewer.d.ts +92 -12
  126. package/dist/store/use-viewer.d.ts.map +1 -1
  127. package/dist/store/use-viewer.js +201 -6
  128. package/dist/store/use-viewer.test.d.ts +2 -0
  129. package/dist/store/use-viewer.test.d.ts.map +1 -0
  130. package/dist/store/use-viewer.test.js +42 -0
  131. package/dist/systems/ceiling/ceiling-system.d.ts +6 -2
  132. package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
  133. package/dist/systems/ceiling/ceiling-system.js +66 -13
  134. package/dist/systems/door/door-animation.test.d.ts +2 -0
  135. package/dist/systems/door/door-animation.test.d.ts.map +1 -0
  136. package/dist/systems/door/door-animation.test.js +51 -0
  137. package/dist/systems/door/door-floor-alignment.test.d.ts +2 -0
  138. package/dist/systems/door/door-floor-alignment.test.d.ts.map +1 -0
  139. package/dist/systems/door/door-floor-alignment.test.js +50 -0
  140. package/dist/systems/door/door-system.d.ts +24 -0
  141. package/dist/systems/door/door-system.d.ts.map +1 -1
  142. package/dist/systems/door/door-system.js +864 -180
  143. package/dist/systems/elevator/elevator-interaction-system.d.ts +2 -0
  144. package/dist/systems/elevator/elevator-interaction-system.d.ts.map +1 -0
  145. package/dist/systems/elevator/elevator-interaction-system.js +62 -0
  146. package/dist/systems/fence/fence-system.d.ts +20 -0
  147. package/dist/systems/fence/fence-system.d.ts.map +1 -1
  148. package/dist/systems/fence/fence-system.js +229 -57
  149. package/dist/systems/floor-elevation/floor-elevation-system.d.ts +20 -0
  150. package/dist/systems/floor-elevation/floor-elevation-system.d.ts.map +1 -0
  151. package/dist/systems/floor-elevation/floor-elevation-system.js +78 -0
  152. package/dist/systems/geometry/geometry-system.d.ts +41 -0
  153. package/dist/systems/geometry/geometry-system.d.ts.map +1 -0
  154. package/dist/systems/geometry/geometry-system.js +348 -0
  155. package/dist/systems/geometry/geometry-system.test.d.ts +2 -0
  156. package/dist/systems/geometry/geometry-system.test.d.ts.map +1 -0
  157. package/dist/systems/geometry/geometry-system.test.js +30 -0
  158. package/dist/systems/interactive/control-widget.d.ts +10 -0
  159. package/dist/systems/interactive/control-widget.d.ts.map +1 -0
  160. package/dist/systems/interactive/control-widget.js +35 -0
  161. package/dist/systems/interactive/interactive-system.d.ts +1 -1
  162. package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
  163. package/dist/systems/interactive/interactive-system.js +55 -56
  164. package/dist/systems/item/item-system.d.ts +7 -0
  165. package/dist/systems/item/item-system.d.ts.map +1 -1
  166. package/dist/systems/item/item-system.js +28 -17
  167. package/dist/systems/item-light/item-light-system.d.ts +1 -1
  168. package/dist/systems/item-light/item-light-system.d.ts.map +1 -1
  169. package/dist/systems/item-light/item-light-system.js +18 -5
  170. package/dist/systems/level/level-system.d.ts.map +1 -1
  171. package/dist/systems/level/level-system.js +28 -4
  172. package/dist/systems/level/level-utils.d.ts +0 -3
  173. package/dist/systems/level/level-utils.d.ts.map +1 -1
  174. package/dist/systems/level/level-utils.js +2 -41
  175. package/dist/systems/roof/roof-materials.d.ts +3 -2
  176. package/dist/systems/roof/roof-materials.d.ts.map +1 -1
  177. package/dist/systems/roof/roof-materials.js +59 -14
  178. package/dist/systems/roof/roof-system.d.ts +46 -6
  179. package/dist/systems/roof/roof-system.d.ts.map +1 -1
  180. package/dist/systems/roof/roof-system.js +1365 -170
  181. package/dist/systems/slab/slab-system.d.ts +5 -3
  182. package/dist/systems/slab/slab-system.d.ts.map +1 -1
  183. package/dist/systems/slab/slab-system.js +204 -68
  184. package/dist/systems/slab/slab-system.test.d.ts +2 -0
  185. package/dist/systems/slab/slab-system.test.d.ts.map +1 -0
  186. package/dist/systems/slab/slab-system.test.js +63 -0
  187. package/dist/systems/stair/stair-materials.d.ts +4 -3
  188. package/dist/systems/stair/stair-materials.d.ts.map +1 -1
  189. package/dist/systems/stair/stair-materials.js +30 -15
  190. package/dist/systems/stair/stair-system.d.ts.map +1 -1
  191. package/dist/systems/stair/stair-system.js +55 -63
  192. package/dist/systems/surface-hole-geometry.d.ts +3 -0
  193. package/dist/systems/surface-hole-geometry.d.ts.map +1 -0
  194. package/dist/systems/surface-hole-geometry.js +4 -0
  195. package/dist/systems/wall/opening-cutout-geometry.d.ts +31 -0
  196. package/dist/systems/wall/opening-cutout-geometry.d.ts.map +1 -0
  197. package/dist/systems/wall/opening-cutout-geometry.js +167 -0
  198. package/dist/systems/wall/opening-cutout-geometry.test.d.ts +2 -0
  199. package/dist/systems/wall/opening-cutout-geometry.test.d.ts.map +1 -0
  200. package/dist/systems/wall/opening-cutout-geometry.test.js +172 -0
  201. package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
  202. package/dist/systems/wall/wall-cutout.js +62 -17
  203. package/dist/systems/wall/wall-materials.d.ts +12 -7
  204. package/dist/systems/wall/wall-materials.d.ts.map +1 -1
  205. package/dist/systems/wall/wall-materials.js +279 -46
  206. package/dist/systems/wall/wall-opening-cutout.test.d.ts +2 -0
  207. package/dist/systems/wall/wall-opening-cutout.test.d.ts.map +1 -0
  208. package/dist/systems/wall/wall-opening-cutout.test.js +59 -0
  209. package/dist/systems/wall/wall-support-extension.test.d.ts +2 -0
  210. package/dist/systems/wall/wall-support-extension.test.d.ts.map +1 -0
  211. package/dist/systems/wall/wall-support-extension.test.js +48 -0
  212. package/dist/systems/wall/wall-system.d.ts +2 -2
  213. package/dist/systems/wall/wall-system.d.ts.map +1 -1
  214. package/dist/systems/wall/wall-system.js +493 -193
  215. package/dist/systems/window/window-animation-system.d.ts +12 -0
  216. package/dist/systems/window/window-animation-system.d.ts.map +1 -1
  217. package/dist/systems/window/window-animation-system.js +16 -5
  218. package/dist/systems/window/window-system.d.ts +7 -0
  219. package/dist/systems/window/window-system.d.ts.map +1 -1
  220. package/dist/systems/window/window-system.js +308 -18
  221. package/package.json +5 -6
  222. package/dist/components/renderers/building/building-renderer.d.ts +0 -5
  223. package/dist/components/renderers/building/building-renderer.d.ts.map +0 -1
  224. package/dist/components/renderers/building/building-renderer.js +0 -11
  225. package/dist/components/renderers/ceiling/ceiling-renderer.d.ts +0 -5
  226. package/dist/components/renderers/ceiling/ceiling-renderer.d.ts.map +0 -1
  227. package/dist/components/renderers/ceiling/ceiling-renderer.js +0 -58
  228. package/dist/components/renderers/column/column-renderer.d.ts +0 -5
  229. package/dist/components/renderers/column/column-renderer.d.ts.map +0 -1
  230. package/dist/components/renderers/column/column-renderer.js +0 -597
  231. package/dist/components/renderers/door/door-renderer.d.ts +0 -5
  232. package/dist/components/renderers/door/door-renderer.d.ts.map +0 -1
  233. package/dist/components/renderers/door/door-renderer.js +0 -16
  234. package/dist/components/renderers/fence/fence-renderer.d.ts +0 -5
  235. package/dist/components/renderers/fence/fence-renderer.d.ts.map +0 -1
  236. package/dist/components/renderers/fence/fence-renderer.js +0 -15
  237. package/dist/components/renderers/guide/guide-renderer.d.ts +0 -5
  238. package/dist/components/renderers/guide/guide-renderer.d.ts.map +0 -1
  239. package/dist/components/renderers/guide/guide-renderer.js +0 -38
  240. package/dist/components/renderers/item/item-renderer.d.ts +0 -5
  241. package/dist/components/renderers/item/item-renderer.d.ts.map +0 -1
  242. package/dist/components/renderers/item/item-renderer.js +0 -172
  243. package/dist/components/renderers/level/level-renderer.d.ts +0 -5
  244. package/dist/components/renderers/level/level-renderer.d.ts.map +0 -1
  245. package/dist/components/renderers/level/level-renderer.js +0 -11
  246. package/dist/components/renderers/roof/roof-materials.d.ts +0 -4
  247. package/dist/components/renderers/roof/roof-materials.d.ts.map +0 -1
  248. package/dist/components/renderers/roof/roof-materials.js +0 -16
  249. package/dist/components/renderers/roof/roof-renderer.d.ts +0 -5
  250. package/dist/components/renderers/roof/roof-renderer.d.ts.map +0 -1
  251. package/dist/components/renderers/roof/roof-renderer.js +0 -32
  252. package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts +0 -5
  253. package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts.map +0 -1
  254. package/dist/components/renderers/roof-segment/roof-segment-renderer.js +0 -40
  255. package/dist/components/renderers/scan/scan-renderer.d.ts +0 -5
  256. package/dist/components/renderers/scan/scan-renderer.d.ts.map +0 -1
  257. package/dist/components/renderers/scan/scan-renderer.js +0 -54
  258. package/dist/components/renderers/site/site-renderer.d.ts +0 -5
  259. package/dist/components/renderers/site/site-renderer.d.ts.map +0 -1
  260. package/dist/components/renderers/site/site-renderer.js +0 -106
  261. package/dist/components/renderers/slab/slab-renderer.d.ts +0 -5
  262. package/dist/components/renderers/slab/slab-renderer.d.ts.map +0 -1
  263. package/dist/components/renderers/slab/slab-renderer.js +0 -58
  264. package/dist/components/renderers/spawn/spawn-renderer.d.ts +0 -5
  265. package/dist/components/renderers/spawn/spawn-renderer.d.ts.map +0 -1
  266. package/dist/components/renderers/spawn/spawn-renderer.js +0 -32
  267. package/dist/components/renderers/stair/stair-renderer.d.ts +0 -5
  268. package/dist/components/renderers/stair/stair-renderer.d.ts.map +0 -1
  269. package/dist/components/renderers/stair/stair-renderer.js +0 -603
  270. package/dist/components/renderers/stair-segment/stair-segment-renderer.d.ts +0 -5
  271. package/dist/components/renderers/stair-segment/stair-segment-renderer.d.ts.map +0 -1
  272. package/dist/components/renderers/stair-segment/stair-segment-renderer.js +0 -51
  273. package/dist/components/renderers/wall/wall-renderer.d.ts +0 -5
  274. package/dist/components/renderers/wall/wall-renderer.d.ts.map +0 -1
  275. package/dist/components/renderers/wall/wall-renderer.js +0 -16
  276. package/dist/components/renderers/window/window-renderer.d.ts +0 -5
  277. package/dist/components/renderers/window/window-renderer.d.ts.map +0 -1
  278. package/dist/components/renderers/window/window-renderer.js +0 -21
  279. package/dist/components/renderers/zone/zone-renderer.d.ts +0 -5
  280. package/dist/components/renderers/zone/zone-renderer.d.ts.map +0 -1
  281. package/dist/components/renderers/zone/zone-renderer.js +0 -181
  282. package/dist/components/viewer/ground-occluder.d.ts +0 -2
  283. package/dist/components/viewer/ground-occluder.d.ts.map +0 -1
  284. package/dist/components/viewer/ground-occluder.js +0 -79
@@ -0,0 +1,22 @@
1
+ /**
2
+ * True when `geometry` has a bound, non-empty `position` attribute — i.e. it is
3
+ * safe to submit to the WebGPU renderer.
4
+ *
5
+ * A geometry whose `position` attribute has `count === 0` (or no `position` at
6
+ * all) leaves WebGPU **vertex buffer slot 0 unbound**. The validator rejects the
7
+ * draw with "Vertex buffer slot 0 … was not set", and — critically — that single
8
+ * rejected draw **poisons the entire command encoder**: every other draw in the
9
+ * frame (the whole scene + every editor overlay) is discarded on the next queue
10
+ * submit ("Invalid CommandBuffer"). The visible result is the whole canvas
11
+ * flickering/garbling, not just the offending mesh.
12
+ *
13
+ * Individual call-sites guard against *creating* empty geometry (see
14
+ * `createPlaceholderGeometry`, the ceiling/door degenerate fallbacks, etc.), but
15
+ * transient/derived geometries can still slip through. This predicate is the
16
+ * renderer-level safety net: skipping a count-0 draw is a no-op visually (it
17
+ * would draw nothing anyway) while keeping the command encoder healthy.
18
+ */
19
+ export function hasDrawableGeometry(geometry) {
20
+ const position = geometry?.attributes?.position;
21
+ return Boolean(position && position.count > 0);
22
+ }
@@ -0,0 +1,4 @@
1
+ export type EdgeMode = 'off' | 'soft' | 'strong';
2
+ export declare function edgeColorFor(background: string): string;
3
+ export declare function edgeOpacityScaleFor(background: string): number;
4
+ //# sourceMappingURL=edge-style.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edge-style.d.ts","sourceRoot":"","sources":["../../src/lib/edge-style.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,CAAA;AAgBhD,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAWvD;AAID,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE9D"}
@@ -0,0 +1,31 @@
1
+ function lumaOf(background) {
2
+ const hex = background.replace('#', '');
3
+ const r = Number.parseInt(hex.slice(0, 2), 16) / 255;
4
+ const g = Number.parseInt(hex.slice(2, 4), 16) / 255;
5
+ const b = Number.parseInt(hex.slice(4, 6), 16) / 255;
6
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
7
+ }
8
+ // Ink line colour follows background luminance — light backgrounds get
9
+ // near-black lines (the rule Mapbox uses for label outlines). Dark backgrounds
10
+ // get a tint *derived from the background* rather than a near-white constant:
11
+ // full-white lines glow harshly against night/twilight scenes, while lifting
12
+ // the background's own hue toward white stays legible but reads native to the
13
+ // theme.
14
+ export function edgeColorFor(background) {
15
+ if (lumaOf(background) > 0.5)
16
+ return '#1a1d24';
17
+ const hex = background.replace('#', '');
18
+ let out = '#';
19
+ for (let i = 0; i < 3; i++) {
20
+ const c = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16) / 255;
21
+ out += Math.round((c + (1 - c) * 0.42) * 255)
22
+ .toString(16)
23
+ .padStart(2, '0');
24
+ }
25
+ return out;
26
+ }
27
+ // Dark scenes additionally run the ink slightly transparent — even a muted
28
+ // tint at full alpha reads as glowing wireframe against a low-luma backdrop.
29
+ export function edgeOpacityScaleFor(background) {
30
+ return lumaOf(background) > 0.5 ? 1 : 0.7;
31
+ }
@@ -0,0 +1,4 @@
1
+ export declare const PERF_OVERLAY_ENABLED: boolean;
2
+ export declare function pushGpuSample(ms: number): void;
3
+ export declare function drainGpuSamples(): number[];
4
+ //# sourceMappingURL=gpu-perf.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gpu-perf.d.ts","sourceRoot":"","sources":["../../src/lib/gpu-perf.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,oBAAoB,SACyD,CAAA;AAK1F,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAG9C;AAED,wBAAgB,eAAe,IAAI,MAAM,EAAE,CAK1C"}
@@ -0,0 +1,23 @@
1
+ // GPU work-time measurement, gated by `?perf` in the URL.
2
+ //
3
+ // We can't use WebGPU timestamp queries here because the editor renders via
4
+ // a custom `RenderPipeline.render()` path that bypasses three.js's built-in
5
+ // timestamp infrastructure. Instead we use `device.queue.onSubmittedWorkDone()`,
6
+ // which resolves when the GPU finishes all submitted work — measuring the
7
+ // CPU→GPU-done delta gives a clean approximation of per-frame GPU duration
8
+ // regardless of which render path produced it.
9
+ export const PERF_OVERLAY_ENABLED = typeof window !== 'undefined' && new URLSearchParams(window.location.search).has('perf');
10
+ const MAX_SAMPLES = 256;
11
+ const samples = [];
12
+ export function pushGpuSample(ms) {
13
+ samples.push(ms);
14
+ if (samples.length > MAX_SAMPLES)
15
+ samples.shift();
16
+ }
17
+ export function drainGpuSamples() {
18
+ if (samples.length === 0)
19
+ return [];
20
+ const out = samples.slice();
21
+ samples.length = 0;
22
+ return out;
23
+ }
@@ -0,0 +1,9 @@
1
+ export declare function inkedEdges({ depthTex, normalTex, inkColor, radius, opacity, sceneRgb, }: {
2
+ depthTex: any;
3
+ normalTex: any;
4
+ inkColor: any;
5
+ radius: number;
6
+ opacity: any;
7
+ sceneRgb: any;
8
+ }): any;
9
+ //# sourceMappingURL=ink-edges.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ink-edges.d.ts","sourceRoot":"","sources":["../../src/lib/ink-edges.ts"],"names":[],"mappings":"AAoBA,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,MAAM,EACN,OAAO,EACP,QAAQ,GACT,EAAE;IACD,QAAQ,EAAE,GAAG,CAAA;IACb,SAAS,EAAE,GAAG,CAAA;IACd,QAAQ,EAAE,GAAG,CAAA;IAKb,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,GAAG,CAAA;IACZ,QAAQ,EAAE,GAAG,CAAA;CACd,OAsCA"}
@@ -0,0 +1,49 @@
1
+ import { abs, float, max, min, mix, screenSize, screenUV, smoothstep, vec2 } from 'three/tsl';
2
+ import { unpackRGBToNormal } from './tsl-compat';
3
+ // Screen-space ink outline (SketchUp / Moebius look). Reads the scene-pass
4
+ // depth + normal MRT and inks two signals:
5
+ //
6
+ // 1. Crease — center normal vs each neighbour (`1 - dot`): 0 on a flat
7
+ // surface, ~1 at a 90° corner. Catches wall↔roof, wall↔wall and window-
8
+ // reveal creases. Normals are re-normalized because the 8-bit normal MRT
9
+ // decodes to non-unit vectors, which otherwise crushes the signal.
10
+ // 2. Depth — raw-depth Laplacian (screen-linear across planes → ~0 on flat
11
+ // ground, no banding) normalized by (1 - depth)² so it becomes ≈
12
+ // worldStep / near, i.e. DISTANCE-INDEPENDENT (a window reveal reads the
13
+ // same zoomed in or out). A raw-Laplacian gate rejects flat-plane
14
+ // quantization noise so the far ground/roof never inks.
15
+ //
16
+ // Topology-agnostic: works on CSG triangle soup, organic GLBs, anything — it
17
+ // only sees the rendered buffers. `intensity` scales the final mask; `inkColor`
18
+ // should track the background luminance (dark lines on light scenes).
19
+ export function inkedEdges({ depthTex, normalTex, inkColor, radius, opacity, sceneRgb, }) {
20
+ const px = vec2(1, 1).div(screenSize).mul(radius);
21
+ const uvN = screenUV;
22
+ const dC = depthTex.sample(uvN).r;
23
+ const dR = depthTex.sample(uvN.add(vec2(px.x, 0))).r;
24
+ const dL = depthTex.sample(uvN.sub(vec2(px.x, 0))).r;
25
+ const dU = depthTex.sample(uvN.add(vec2(0, px.y))).r;
26
+ const dD = depthTex.sample(uvN.sub(vec2(0, px.y))).r;
27
+ const depthLap = abs(dR.add(dL).add(dU).add(dD).sub(dC.mul(4)));
28
+ const invDepth = float(1).sub(dC);
29
+ const depthMetric = depthLap.div(invDepth.mul(invDepth).add(float(0.00002)));
30
+ const noiseGate = smoothstep(float(0.00002), float(0.00006), depthLap);
31
+ // ≈ metres of step / near (near≈0.1): ~5cm starts a line, ~25cm solid.
32
+ const depthEdge = smoothstep(float(0.5), float(2.5), depthMetric).mul(noiseGate);
33
+ const nC = unpackRGBToNormal(normalTex.sample(uvN)).normalize();
34
+ const nR = unpackRGBToNormal(normalTex.sample(uvN.add(vec2(px.x, 0)))).normalize();
35
+ const nL = unpackRGBToNormal(normalTex.sample(uvN.sub(vec2(px.x, 0)))).normalize();
36
+ const nU = unpackRGBToNormal(normalTex.sample(uvN.add(vec2(0, px.y)))).normalize();
37
+ const nD = unpackRGBToNormal(normalTex.sample(uvN.sub(vec2(0, px.y)))).normalize();
38
+ // Ink is a near/mid-field affordance: fade it out with raw depth so the
39
+ // horizon (the infinite ground disc vanishing against the backdrop) and
40
+ // other far-field depth cliffs never draw a line across the sky junction.
41
+ // ~full ink below ≈150 m, none beyond ≈350 m (perspective near 0.1/far 1000).
42
+ const distanceFade = float(1).sub(smoothstep(float(0.9994), float(0.9998), dC));
43
+ const nDiff = max(max(float(1).sub(nC.dot(nR)), float(1).sub(nC.dot(nL))), max(float(1).sub(nC.dot(nU)), float(1).sub(nC.dot(nD))));
44
+ const normalEdge = smoothstep(float(0.01), float(0.05), nDiff);
45
+ // TSL's typed overloads are finicky across versions; the runtime is proven in
46
+ // the aesthetic sandbox, so cast at the mask/mix boundary.
47
+ const edgeMask = min(max(depthEdge, normalEdge).mul(opacity).mul(distanceFade), float(1));
48
+ return mix(sceneRgb, inkColor, edgeMask);
49
+ }
@@ -0,0 +1,39 @@
1
+ import type { AnyNodeId } from '@pascal-app/core';
2
+ import type { Object3D } from 'three';
3
+ /** True while an isolation filter is applied (see {@link applyIsolation}). */
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
+ */
14
+ export declare function collectIsolationSubtree(ids: ReadonlyArray<string>): Set<Object3D>;
15
+ /**
16
+ * Imperative visibility filter on the live `sceneRegistry`. Hides every
17
+ * registered group (and its synthesized child meshes) outside the
18
+ * isolated subtree by disabling the {@link SCENE_LAYER} bit on the
19
+ * relevant `Object3D.layers` masks.
20
+ *
21
+ * Why layers instead of `obj.visible = false`? Three.js's visibility
22
+ * flag *cascades* — hiding a parent hides every descendant — so we
23
+ * can't hide a host wall while keeping a door rendered inside it.
24
+ * Layer masks are per-object and don't cascade: `WebGLRenderer
25
+ * .projectObject` skips objects whose layer mask doesn't intersect the
26
+ * camera's, but always recurses into their children. So we can disable
27
+ * `SCENE_LAYER` on the wall and the door (hosted under it in the
28
+ * scene graph) still renders, with its local position relative to the
29
+ * wall preserved automatically by the matrix walk.
30
+ *
31
+ * The original `layers.mask` is stashed under a private Symbol so
32
+ * {@link clearIsolation} can restore the exact prior state.
33
+ *
34
+ * Pass `null` to clear isolation (equivalent to calling
35
+ * {@link clearIsolation}).
36
+ */
37
+ export declare function applyIsolation(ids: ReadonlyArray<AnyNodeId> | null): void;
38
+ export declare function clearIsolation(): void;
39
+ //# sourceMappingURL=isolation.d.ts.map
@@ -0,0 +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;AAgBrC,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;AAcD,wBAAgB,cAAc,IAAI,IAAI,CAcrC"}
@@ -0,0 +1,103 @@
1
+ 'use client';
2
+ import { sceneRegistry } from '@pascal-app/core';
3
+ import { SCENE_LAYER } from './layers';
4
+ // Marker on each Object3D we modify during isolation so we can restore
5
+ // the original `layers.mask` bitfield. Stored under a `Symbol` so it
6
+ // can't collide with any kind's own userData fields.
7
+ const ORIGINAL_LAYERS = Symbol('isolation:original-layers');
8
+ // Whether a subtree is currently isolated (some objects have SCENE_LAYER
9
+ // disabled). Read by consumers that must not act on the partial view — e.g.
10
+ // the project-thumbnail autosave skips capturing while isolated so it never
11
+ // snapshots a single focused item as the whole project's thumbnail.
12
+ let isolationActive = false;
13
+ /** True while an isolation filter is applied (see {@link applyIsolation}). */
14
+ export function isIsolationActive() {
15
+ return isolationActive;
16
+ }
17
+ /**
18
+ * Compute the union of every isolated subtree's `Object3D` descendants.
19
+ *
20
+ * Pure traversal — exported so future "focus mode" / debug tooling can
21
+ * reuse the same definition of "what's in the isolated set". Each root
22
+ * is walked via `Object3D.traverse` (the live Three.js graph, not the
23
+ * data-model `children` array — those can disagree when systems mount
24
+ * synthesized sub-meshes that the data model doesn't track).
25
+ */
26
+ export function collectIsolationSubtree(ids) {
27
+ const keep = new Set();
28
+ for (const id of ids) {
29
+ const root = sceneRegistry.nodes.get(id);
30
+ if (!root)
31
+ continue;
32
+ root.traverse((child) => {
33
+ keep.add(child);
34
+ });
35
+ }
36
+ return keep;
37
+ }
38
+ /**
39
+ * Imperative visibility filter on the live `sceneRegistry`. Hides every
40
+ * registered group (and its synthesized child meshes) outside the
41
+ * isolated subtree by disabling the {@link SCENE_LAYER} bit on the
42
+ * relevant `Object3D.layers` masks.
43
+ *
44
+ * Why layers instead of `obj.visible = false`? Three.js's visibility
45
+ * flag *cascades* — hiding a parent hides every descendant — so we
46
+ * can't hide a host wall while keeping a door rendered inside it.
47
+ * Layer masks are per-object and don't cascade: `WebGLRenderer
48
+ * .projectObject` skips objects whose layer mask doesn't intersect the
49
+ * camera's, but always recurses into their children. So we can disable
50
+ * `SCENE_LAYER` on the wall and the door (hosted under it in the
51
+ * scene graph) still renders, with its local position relative to the
52
+ * wall preserved automatically by the matrix walk.
53
+ *
54
+ * The original `layers.mask` is stashed under a private Symbol so
55
+ * {@link clearIsolation} can restore the exact prior state.
56
+ *
57
+ * Pass `null` to clear isolation (equivalent to calling
58
+ * {@link clearIsolation}).
59
+ */
60
+ export function applyIsolation(ids) {
61
+ if (ids == null || ids.length === 0) {
62
+ clearIsolation();
63
+ return;
64
+ }
65
+ const keep = collectIsolationSubtree(ids);
66
+ // Iterate registered roots. For each one outside the keep set,
67
+ // disable `SCENE_LAYER` on it and on every descendant — *except*
68
+ // descendants that are themselves in `keep` (a kept node nested under
69
+ // a non-kept host: the isolated door under the hidden wall).
70
+ for (const [, obj] of sceneRegistry.nodes) {
71
+ if (keep.has(obj))
72
+ continue;
73
+ hideRecursive(obj, keep);
74
+ }
75
+ isolationActive = true;
76
+ }
77
+ function hideRecursive(obj, keep) {
78
+ if (keep.has(obj))
79
+ return;
80
+ const carrier = obj;
81
+ if (carrier[ORIGINAL_LAYERS] === undefined) {
82
+ carrier[ORIGINAL_LAYERS] = obj.layers.mask;
83
+ }
84
+ obj.layers.disable(SCENE_LAYER);
85
+ for (const child of obj.children) {
86
+ hideRecursive(child, keep);
87
+ }
88
+ }
89
+ export function clearIsolation() {
90
+ // We don't know which objects were touched without re-walking, so
91
+ // walk every registered root + its descendants and restore any
92
+ // stashed original-mask. `traverse` is cheap and idempotent here.
93
+ for (const [, obj] of sceneRegistry.nodes) {
94
+ obj.traverse((child) => {
95
+ const carrier = child;
96
+ if (carrier[ORIGINAL_LAYERS] !== undefined) {
97
+ child.layers.mask = carrier[ORIGINAL_LAYERS];
98
+ delete carrier[ORIGINAL_LAYERS];
99
+ }
100
+ });
101
+ }
102
+ isolationActive = false;
103
+ }
@@ -0,0 +1,43 @@
1
+ import { KTX2Loader } from 'three/examples/jsm/loaders/KTX2Loader.js';
2
+ /**
3
+ * KTX2Loader that survives block-misaligned textures. WebGPU rejects
4
+ * block-compressed textures whose base dimensions aren't multiples of 4;
5
+ * three's KTX2Loader only warns and transcodes anyway, and the resulting
6
+ * invalid texture poisons every render pass that binds it (endless
7
+ * "Invalid BindGroup … Invalid CommandBuffer" spam — baked GLBs carrying an
8
+ * odd-sized user-item texture triggered exactly this). Such payloads are
9
+ * routed to a fallback loader whose support flags report no compressed
10
+ * formats, which makes the transcoder emit plain RGBA32 — uncompressed
11
+ * textures have no alignment requirement.
12
+ */
13
+ declare class AlignmentSafeKTX2Loader extends KTX2Loader {
14
+ private rgbaFallback;
15
+ private fallbackLoader;
16
+ /** Both `load()` and GLTFLoader's KHR_texture_basisu path funnel through
17
+ * this internal — overriding it covers every entry point. */
18
+ _createTexture(buffer: ArrayBuffer, config?: Record<string, unknown>): Promise<unknown>;
19
+ dispose(): this;
20
+ }
21
+ /**
22
+ * Single shared KTX2 loader for the whole viewer — used both by the GLB loader
23
+ * (`use-gltf-ktx2`) and by catalog finish textures (`materials.ts`). KTX2 must
24
+ * be transcoded at load via the Basis WASM, and `detectSupport(renderer)` has to
25
+ * run once before any `.ktx2` is loaded so the loader picks a GPU format the
26
+ * device supports. `ensureKtx2Support` is idempotent per renderer and is called
27
+ * from the viewer root the moment the renderer is ready (even when no GLB is in
28
+ * the scene, so catalog `.ktx2` finishes still load).
29
+ */
30
+ export declare const ktx2Loader: AlignmentSafeKTX2Loader;
31
+ /**
32
+ * Resolves once `detectSupport` has succeeded for any renderer. `.ktx2` loads
33
+ * issued before that point would throw inside KTX2Loader ("Missing
34
+ * initialization with `.detectSupport( renderer )`"), so texture loaders await
35
+ * this instead of failing — covers materials created while the renderer is
36
+ * still initializing (e.g. a standalone capture canvas).
37
+ */
38
+ export declare function whenKtx2Ready(): Promise<void>;
39
+ /** Returns true once support has been detected for this renderer (KTX2 safe to load). */
40
+ export declare function ensureKtx2Support(renderer: unknown): boolean;
41
+ export declare function isKtx2Url(url: string): boolean;
42
+ export {};
43
+ //# sourceMappingURL=ktx2-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ktx2-loader.d.ts","sourceRoot":"","sources":["../../src/lib/ktx2-loader.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAA;AAwBrE;;;;;;;;;;GAUG;AACH,cAAM,uBAAwB,SAAQ,UAAU;IAC9C,OAAO,CAAC,YAAY,CAA0B;IAE9C,OAAO,CAAC,cAAc;IAkBtB;kEAC8D;IACxD,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAmC7F,OAAO,IAAI,IAAI;CAMhB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,yBAAgC,CAAA;AAWvD;;;;;;GAMG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAE7C;AAED,yFAAyF;AACzF,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAkB5D;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE9C"}
@@ -0,0 +1,136 @@
1
+ import { DataTexture, LinearFilter, LinearMipmapLinearFilter, RGBAFormat, UnsignedByteType, } from 'three';
2
+ // Deep path, NOT the `Addons.js` aggregate: the aggregate re-exports
3
+ // LottieLoader/TTFLoader, whose CDN URL imports (`lottie-web`, `opentype.js`)
4
+ // crash bun's test runtime for every consumer of the viewer barrel.
5
+ import { KTX2Loader } from 'three/examples/jsm/loaders/KTX2Loader.js';
6
+ /** Base width/height of a Basis-supercompressed KTX2 payload needing transcode
7
+ * (vkFormat 0 = ETC1S/UASTC) whose dimensions aren't block-aligned, or null
8
+ * for anything safe. Little-endian u32s: vkFormat at byte 12, pixelWidth at
9
+ * 20, pixelHeight at 24. */
10
+ function readMisalignedBasisSize(buffer) {
11
+ if (buffer.byteLength < 28)
12
+ return null;
13
+ const view = new DataView(buffer);
14
+ const vkFormat = view.getUint32(12, true);
15
+ if (vkFormat !== 0)
16
+ return null;
17
+ const width = view.getUint32(20, true);
18
+ const height = view.getUint32(24, true);
19
+ return width % 4 !== 0 || height % 4 !== 0 ? { width, height } : null;
20
+ }
21
+ /**
22
+ * KTX2Loader that survives block-misaligned textures. WebGPU rejects
23
+ * block-compressed textures whose base dimensions aren't multiples of 4;
24
+ * three's KTX2Loader only warns and transcodes anyway, and the resulting
25
+ * invalid texture poisons every render pass that binds it (endless
26
+ * "Invalid BindGroup … Invalid CommandBuffer" spam — baked GLBs carrying an
27
+ * odd-sized user-item texture triggered exactly this). Such payloads are
28
+ * routed to a fallback loader whose support flags report no compressed
29
+ * formats, which makes the transcoder emit plain RGBA32 — uncompressed
30
+ * textures have no alignment requirement.
31
+ */
32
+ class AlignmentSafeKTX2Loader extends KTX2Loader {
33
+ rgbaFallback = null;
34
+ fallbackLoader() {
35
+ if (!this.rgbaFallback) {
36
+ this.rgbaFallback = new KTX2Loader(this.manager);
37
+ this.rgbaFallback.setTranscoderPath(this.transcoderPath);
38
+ this.rgbaFallback.workerConfig = {
39
+ astcSupported: false,
40
+ astcHDRSupported: false,
41
+ etc1Supported: false,
42
+ etc2Supported: false,
43
+ dxtSupported: false,
44
+ bptcSupported: false,
45
+ pvrtcSupported: false,
46
+ };
47
+ }
48
+ return this.rgbaFallback;
49
+ }
50
+ /** Both `load()` and GLTFLoader's KHR_texture_basisu path funnel through
51
+ * this internal — overriding it covers every entry point. */
52
+ async _createTexture(buffer, config) {
53
+ const misaligned = readMisalignedBasisSize(buffer);
54
+ if (misaligned) {
55
+ console.warn(`[viewer] KTX2 texture is ${misaligned.width}x${misaligned.height} (not multiple-of-4); ` +
56
+ 'transcoding uncompressed so WebGPU can create it.');
57
+ const transcoded = (await this.fallbackLoader()._createTexture(buffer, config));
58
+ // The RGBA32 result still arrives as a CompressedTexture; three's WebGPU
59
+ // texture upload treats every CompressedTexture as block-compressed and
60
+ // crashes looking up a block descriptor for rgba8. Repackage the decoded
61
+ // pixels as a plain DataTexture instead.
62
+ const mip = transcoded.mipmaps?.[0];
63
+ if (!mip)
64
+ return transcoded;
65
+ const texture = new DataTexture(mip.data, mip.width, mip.height, RGBAFormat, UnsignedByteType);
66
+ texture.colorSpace = transcoded.colorSpace;
67
+ texture.generateMipmaps = true;
68
+ texture.minFilter = LinearMipmapLinearFilter;
69
+ texture.magFilter = LinearFilter;
70
+ texture.needsUpdate = true;
71
+ transcoded.dispose();
72
+ return texture;
73
+ }
74
+ return KTX2Loader.prototype._createTexture.call(this, buffer, config);
75
+ }
76
+ dispose() {
77
+ this.rgbaFallback?.dispose();
78
+ this.rgbaFallback = null;
79
+ super.dispose();
80
+ return this;
81
+ }
82
+ }
83
+ /**
84
+ * Single shared KTX2 loader for the whole viewer — used both by the GLB loader
85
+ * (`use-gltf-ktx2`) and by catalog finish textures (`materials.ts`). KTX2 must
86
+ * be transcoded at load via the Basis WASM, and `detectSupport(renderer)` has to
87
+ * run once before any `.ktx2` is loaded so the loader picks a GPU format the
88
+ * device supports. `ensureKtx2Support` is idempotent per renderer and is called
89
+ * from the viewer root the moment the renderer is ready (even when no GLB is in
90
+ * the scene, so catalog `.ktx2` finishes still load).
91
+ */
92
+ export const ktx2Loader = new AlignmentSafeKTX2Loader();
93
+ ktx2Loader.setTranscoderPath('https://cdn.jsdelivr.net/gh/pmndrs/drei-assets@master/basis/');
94
+ const configuredRenderers = new WeakSet();
95
+ const warnedRenderers = new WeakSet();
96
+ let resolveKtx2Ready;
97
+ const ktx2ReadyPromise = new Promise((resolve) => {
98
+ resolveKtx2Ready = resolve;
99
+ });
100
+ /**
101
+ * Resolves once `detectSupport` has succeeded for any renderer. `.ktx2` loads
102
+ * issued before that point would throw inside KTX2Loader ("Missing
103
+ * initialization with `.detectSupport( renderer )`"), so texture loaders await
104
+ * this instead of failing — covers materials created while the renderer is
105
+ * still initializing (e.g. a standalone capture canvas).
106
+ */
107
+ export function whenKtx2Ready() {
108
+ return ktx2ReadyPromise;
109
+ }
110
+ /** Returns true once support has been detected for this renderer (KTX2 safe to load). */
111
+ export function ensureKtx2Support(renderer) {
112
+ const key = renderer;
113
+ if (!key)
114
+ return false;
115
+ if (configuredRenderers.has(key))
116
+ return true;
117
+ try {
118
+ ;
119
+ ktx2Loader.detectSupport(renderer);
120
+ configuredRenderers.add(key);
121
+ resolveKtx2Ready();
122
+ return true;
123
+ }
124
+ catch (error) {
125
+ // Some WebGPU flows can transiently call this before backend init; don't
126
+ // crash the scene — a later call (or the next render) retries.
127
+ if (!warnedRenderers.has(key)) {
128
+ console.warn('[viewer] Skipping KTX2 support detection for now.', error);
129
+ warnedRenderers.add(key);
130
+ }
131
+ return false;
132
+ }
133
+ }
134
+ export function isKtx2Url(url) {
135
+ return url.toLowerCase().endsWith('.ktx2');
136
+ }
@@ -1,5 +1,33 @@
1
1
  /** Default Three.js layer for main scene geometry. */
2
2
  export declare const SCENE_LAYER = 0;
3
+ /**
4
+ * Layer for editor-only overlays (gizmos, move handles, tool previews, grid).
5
+ * The post-processing pipeline excludes this layer from the depth/normal MRT
6
+ * scene pass so the screen-space ink and SSGI never treat overlays as geometry,
7
+ * then composites it back on top via a dedicated overlay pass.
8
+ *
9
+ * Editor's `EDITOR_LAYER` (packages/editor) re-exports this — they MUST match.
10
+ */
11
+ export declare const OVERLAY_LAYER = 1;
3
12
  /** Layer used for zone rendering (floor fills and wall borders). */
4
13
  export declare const ZONE_LAYER = 2;
14
+ /**
15
+ * Layer for the editor ground grid. Rendered *inside* the scene pass (so scene
16
+ * geometry depth-occludes it instead of it bleeding through walls/objects) — it
17
+ * is a flat, depth-non-writing plane, so the screen-space ink never picks it up.
18
+ * Kept off OVERLAY_LAYER because overlays composite on top with no scene-depth
19
+ * test, which is exactly what we don't want for a full-floor plane. Excluded
20
+ * from thumbnails like the other editor-only layers.
21
+ */
22
+ export declare const GRID_LAYER = 3;
23
+ /**
24
+ * Layer for geometry hidden from the color passes but still rendered into the
25
+ * shadow map ("shadow-caster-only"). Used when cutaway/solo views hide roofs
26
+ * or non-selected levels: the sun keeps casting their shadows, so interiors
27
+ * get window-shaped light patches instead of flooding with uniform sun.
28
+ * No camera or pass enables this layer — only each shadow-casting light's
29
+ * shadow camera does (see `lights.tsx`). Applied per-object (layers don't
30
+ * cascade) via `applyShadowOnly` / `clearShadowOnly` in `lib/shadow-only.ts`.
31
+ */
32
+ export declare const SHADOW_ONLY_LAYER = 4;
5
33
  //# sourceMappingURL=layers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"layers.d.ts","sourceRoot":"","sources":["../../src/lib/layers.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,eAAO,MAAM,WAAW,IAAI,CAAA;AAE5B,oEAAoE;AACpE,eAAO,MAAM,UAAU,IAAI,CAAA"}
1
+ {"version":3,"file":"layers.d.ts","sourceRoot":"","sources":["../../src/lib/layers.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,eAAO,MAAM,WAAW,IAAI,CAAA;AAE5B;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,IAAI,CAAA;AAE9B,oEAAoE;AACpE,eAAO,MAAM,UAAU,IAAI,CAAA;AAE3B;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,IAAI,CAAA;AAE3B;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,IAAI,CAAA"}
@@ -1,4 +1,32 @@
1
1
  /** Default Three.js layer for main scene geometry. */
2
2
  export const SCENE_LAYER = 0;
3
+ /**
4
+ * Layer for editor-only overlays (gizmos, move handles, tool previews, grid).
5
+ * The post-processing pipeline excludes this layer from the depth/normal MRT
6
+ * scene pass so the screen-space ink and SSGI never treat overlays as geometry,
7
+ * then composites it back on top via a dedicated overlay pass.
8
+ *
9
+ * Editor's `EDITOR_LAYER` (packages/editor) re-exports this — they MUST match.
10
+ */
11
+ export const OVERLAY_LAYER = 1;
3
12
  /** Layer used for zone rendering (floor fills and wall borders). */
4
13
  export const ZONE_LAYER = 2;
14
+ /**
15
+ * Layer for the editor ground grid. Rendered *inside* the scene pass (so scene
16
+ * geometry depth-occludes it instead of it bleeding through walls/objects) — it
17
+ * is a flat, depth-non-writing plane, so the screen-space ink never picks it up.
18
+ * Kept off OVERLAY_LAYER because overlays composite on top with no scene-depth
19
+ * test, which is exactly what we don't want for a full-floor plane. Excluded
20
+ * from thumbnails like the other editor-only layers.
21
+ */
22
+ export const GRID_LAYER = 3;
23
+ /**
24
+ * Layer for geometry hidden from the color passes but still rendered into the
25
+ * shadow map ("shadow-caster-only"). Used when cutaway/solo views hide roofs
26
+ * or non-selected levels: the sun keeps casting their shadows, so interiors
27
+ * get window-shaped light patches instead of flooding with uniform sun.
28
+ * No camera or pass enables this layer — only each shadow-casting light's
29
+ * shadow camera does (see `lights.tsx`). Applied per-object (layers don't
30
+ * cascade) via `applyShadowOnly` / `clearShadowOnly` in `lib/shadow-only.ts`.
31
+ */
32
+ export const SHADOW_ONLY_LAYER = 4;
@@ -1,20 +1,43 @@
1
- import { type MaterialPresetPayload, type MaterialSchema } from '@pascal-app/core';
1
+ import { type MaterialPresetPayload, type MaterialSchema, type SceneMaterial, type SceneMaterialId, type SurfaceRole } from '@pascal-app/core';
2
2
  import * as THREE from 'three';
3
- import { MeshStandardNodeMaterial } from 'three/webgpu';
4
- export declare const baseMaterial: MeshStandardNodeMaterial;
5
- export declare const glassMaterial: MeshStandardNodeMaterial;
3
+ import { MeshLambertNodeMaterial } from 'three/webgpu';
4
+ export type RenderShading = 'solid' | 'rendered';
5
+ export type ColorPreset = 'clay' | 'white' | 'mono' | 'blueprint';
6
+ export declare const CLAY_PALETTE: Record<SurfaceRole, string>;
7
+ export declare const WHITE_PALETTE: Record<SurfaceRole, string>;
8
+ export declare const MONO_PALETTE: Record<SurfaceRole, string>;
9
+ export declare const BLUEPRINT_PALETTE: Record<SurfaceRole, string>;
10
+ export declare const PRESET_PALETTES: Record<ColorPreset, Record<SurfaceRole, string>>;
11
+ export declare function resolveSurfaceColor(role: SurfaceRole, preset: ColorPreset, sceneThemeId?: string): string;
12
+ export declare const glassMaterial: MeshLambertNodeMaterial;
6
13
  export declare function applyMaterialPresetToMaterials(materialInput: THREE.Material | THREE.Material[], preset: MaterialPresetPayload | null | undefined): void;
7
- export declare function createMaterialFromPreset(preset: MaterialPresetPayload): THREE.MeshStandardMaterial;
8
- export declare function createMaterialFromPresetRef(materialPreset?: string): THREE.MeshStandardMaterial | null;
9
- export declare function createMaterial(material?: MaterialSchema): THREE.MeshStandardMaterial;
10
- export declare function createDefaultMaterial(color?: string, roughness?: number): THREE.MeshStandardMaterial;
11
- export declare const DEFAULT_WALL_MATERIAL: THREE.MeshStandardMaterial;
12
- export declare const DEFAULT_SLAB_MATERIAL: THREE.MeshStandardMaterial;
13
- export declare const DEFAULT_DOOR_MATERIAL: THREE.MeshStandardMaterial;
14
- export declare const DEFAULT_WINDOW_MATERIAL: THREE.MeshStandardMaterial;
15
- export declare const DEFAULT_CEILING_MATERIAL: THREE.MeshStandardMaterial;
16
- export declare const DEFAULT_ROOF_MATERIAL: THREE.MeshStandardMaterial;
17
- export declare const DEFAULT_STAIR_MATERIAL: THREE.MeshStandardMaterial;
14
+ export declare function createMaterialFromPreset(preset: MaterialPresetPayload, shading?: RenderShading): THREE.Material;
15
+ export declare function createMaterialFromPresetRef(materialPreset?: string, shading?: RenderShading): THREE.Material | null;
16
+ export declare function createMaterial(material?: MaterialSchema, shading?: RenderShading): THREE.Material;
17
+ /**
18
+ * Resolve a MaterialRef ('library:<id>' | 'scene:<id>') to a three.js material.
19
+ * Returns null for an unknown / dangling ref so callers fall back to the
20
+ * slot's default (authored material, then themed default). Never throws.
21
+ */
22
+ export declare function resolveMaterialRef(ref: string | undefined, sceneMaterials: Record<SceneMaterialId, SceneMaterial> | undefined, shading?: RenderShading): THREE.Material | null;
23
+ /**
24
+ * Resolve a node kind's declared slot default — either a catalog `library:<id>`
25
+ * finish or a flat `#rrggbb` colour — to a renderable material. Shared by the
26
+ * procedural kinds whose colored-mode unpainted appearance comes from a
27
+ * declarative default (slab, wall).
28
+ */
29
+ export declare function resolveSlotDefaultMaterial(slotDefault: string, shading?: RenderShading, roughness?: number): THREE.Material;
30
+ export declare function createDefaultMaterial(color?: string, roughness?: number, shading?: RenderShading, side?: THREE.Side): THREE.Material;
31
+ export declare function createSurfaceRoleMaterial(role: SurfaceRole, preset: ColorPreset, side?: THREE.Side, sceneThemeId?: string): THREE.Material;
32
+ export declare function baseMaterial(shading?: RenderShading): THREE.Material;
33
+ export declare function DEFAULT_WALL_MATERIAL(shading?: RenderShading): THREE.Material;
34
+ export declare function DEFAULT_SLAB_MATERIAL(shading?: RenderShading): THREE.Material;
35
+ export declare function DEFAULT_DOOR_MATERIAL(shading?: RenderShading): THREE.Material;
36
+ export declare function DEFAULT_WINDOW_MATERIAL(shading?: RenderShading): THREE.Material;
37
+ export declare function DEFAULT_CEILING_MATERIAL(shading?: RenderShading): THREE.Material;
38
+ export declare function DEFAULT_ROOF_MATERIAL(shading?: RenderShading): THREE.Material;
39
+ export declare function DEFAULT_SHELF_MATERIAL(shading?: RenderShading): THREE.Material;
40
+ export declare function DEFAULT_STAIR_MATERIAL(shading?: RenderShading): THREE.Material;
18
41
  export declare function disposeMaterial(material: THREE.Material): void;
19
42
  export declare function clearMaterialCache(): void;
20
43
  //# sourceMappingURL=materials.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"materials.d.ts","sourceRoot":"","sources":["../../src/lib/materials.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,qBAAqB,EAE1B,KAAK,cAAc,EAEpB,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAA;AAEvD,eAAO,MAAM,YAAY,0BAIvB,CAAA;AAEF,eAAO,MAAM,aAAa,0BAOxB,CAAA;AAkNF,wBAAgB,8BAA8B,CAC5C,aAAa,EAAE,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,EAAE,EAChD,MAAM,EAAE,qBAAqB,GAAG,IAAI,GAAG,SAAS,QAcjD;AAED,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,qBAAqB,GAC5B,KAAK,CAAC,oBAAoB,CAW5B;AAED,wBAAgB,2BAA2B,CACzC,cAAc,CAAC,EAAE,MAAM,GACtB,KAAK,CAAC,oBAAoB,GAAG,IAAI,CAInC;AAED,wBAAgB,cAAc,CAAC,QAAQ,CAAC,EAAE,cAAc,GAAG,KAAK,CAAC,oBAAoB,CAsBpF;AAED,wBAAgB,qBAAqB,CACnC,KAAK,SAAY,EACjB,SAAS,SAAM,GACd,KAAK,CAAC,oBAAoB,CAO5B;AAED,eAAO,MAAM,qBAAqB,4BAAwC,CAAA;AAC1E,eAAO,MAAM,qBAAqB,4BAAwC,CAAA;AAC1E,eAAO,MAAM,qBAAqB,4BAAwC,CAAA;AAC1E,eAAO,MAAM,uBAAuB,4BAOlC,CAAA;AACF,eAAO,MAAM,wBAAwB,4BAAyC,CAAA;AAC9E,eAAO,MAAM,qBAAqB,4BAAyC,CAAA;AAC3E,eAAO,MAAM,sBAAsB,4BAAwC,CAAA;AAE3E,wBAAgB,eAAe,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,GAAG,IAAI,CAE9D;AAED,wBAAgB,kBAAkB,IAAI,IAAI,CAWzC"}
1
+ {"version":3,"file":"materials.d.ts","sourceRoot":"","sources":["../../src/lib/materials.ts"],"names":[],"mappings":"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"}