@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
@@ -1,36 +1,119 @@
1
- import { getMaterialPresetByRef, resolveMaterial, } from '@pascal-app/core';
1
+ import { getMaterialPresetByRef, parseMaterialRef, resolveMaterial, } from '@pascal-app/core';
2
2
  import * as THREE from 'three';
3
- import { MeshStandardNodeMaterial } from 'three/webgpu';
4
- export const baseMaterial = new MeshStandardNodeMaterial({
5
- color: '#f2f0ed',
6
- roughness: 0.5,
7
- metalness: 0.0,
8
- });
9
- export const glassMaterial = new MeshStandardNodeMaterial({
3
+ import { float, mix, positionViewDirection, transformedNormalView } from 'three/tsl';
4
+ import { MeshLambertNodeMaterial, MeshStandardNodeMaterial } from 'three/webgpu';
5
+ import { resolveCdnUrl } from './asset-url';
6
+ import { isKtx2Url, ktx2Loader, whenKtx2Ready } from './ktx2-loader';
7
+ import { getSceneTheme } from './scene-themes';
8
+ export const CLAY_PALETTE = {
9
+ wall: '#dcd6c7',
10
+ floor: '#cfc8b6',
11
+ ceiling: '#e4ded0',
12
+ roof: '#b8ad96',
13
+ joinery: '#c4bba6',
14
+ glazing: '#c8d4dc',
15
+ furnishing: '#d2ccbe',
16
+ };
17
+ // Albedos are clamped to ≈0.83 linear (max channel #eb): real white paint
18
+ // reflects ~80%, and pure-white albedo kills GI/shadow contrast.
19
+ export const WHITE_PALETTE = {
20
+ wall: '#ebeae6',
21
+ floor: '#e7e4dd',
22
+ ceiling: '#ebeae6',
23
+ roof: '#dedbd2',
24
+ joinery: '#e5e2d9',
25
+ glazing: '#dbe8ee',
26
+ furnishing: '#e9e7e1',
27
+ };
28
+ export const MONO_PALETTE = {
29
+ wall: '#c8c8c8',
30
+ floor: '#b8b8b8',
31
+ ceiling: '#d8d8d8',
32
+ roof: '#9a9a9a',
33
+ joinery: '#adadad',
34
+ glazing: '#c2cbd0',
35
+ furnishing: '#c0c0c0',
36
+ };
37
+ export const BLUEPRINT_PALETTE = {
38
+ wall: '#90a9c7',
39
+ floor: '#7f98ba',
40
+ ceiling: '#aec0d8',
41
+ roof: '#5f789b',
42
+ joinery: '#6f86a8',
43
+ glazing: '#b6d7ea',
44
+ furnishing: '#8ba2bf',
45
+ };
46
+ export const PRESET_PALETTES = {
47
+ clay: CLAY_PALETTE,
48
+ white: WHITE_PALETTE,
49
+ mono: MONO_PALETTE,
50
+ blueprint: BLUEPRINT_PALETTE,
51
+ };
52
+ export function resolveSurfaceColor(role, preset, sceneThemeId) {
53
+ // The active scene theme may tint individual roles (e.g. Mediterranean's blue
54
+ // roof); fall back to the chosen colour preset's palette when it doesn't.
55
+ const tints = sceneThemeId ? getSceneTheme(sceneThemeId).clayTints : undefined;
56
+ return tints?.[role] ?? (PRESET_PALETTES[preset] ?? CLAY_PALETTE)[role];
57
+ }
58
+ // DoubleSide on any NodeMaterial inside the MRT scenePass (SSGI's output /
59
+ // diffuseColor / normal targets) causes WebGPU to create a render pipeline
60
+ // whose back-face shader variant doesn't declare outputs for every MRT target
61
+ // — the validator rejects it and poisons the entire render context. FrontSide
62
+ // avoids that code path. Same pattern as MeshStandardMaterial in renderer.tsx.
63
+ export const glassMaterial = new MeshLambertNodeMaterial({
10
64
  color: '#e0f2fe',
11
- roughness: 0.05,
12
- metalness: 0.0,
13
65
  transparent: true,
14
66
  opacity: 0.35,
15
- side: THREE.DoubleSide,
67
+ side: THREE.FrontSide,
16
68
  });
69
+ glassMaterial.userData.__pascalCachedMaterial = true;
70
+ function resolveNodeMaterialSide(side) {
71
+ return side === THREE.DoubleSide ? THREE.FrontSide : side;
72
+ }
17
73
  const sideMap = {
18
74
  front: THREE.FrontSide,
19
75
  back: THREE.BackSide,
20
- double: THREE.DoubleSide,
76
+ double: THREE.FrontSide,
21
77
  };
22
78
  const materialCache = new Map();
79
+ const defaultMaterialCache = new Map();
80
+ const surfaceRoleMaterialCache = new Map();
23
81
  const textureCache = new Map();
24
82
  const textureLoadPromises = new Map();
25
83
  const textureLoader = new THREE.TextureLoader();
84
+ // `.ktx2` finish maps transcode through the shared KTX2 loader (support is
85
+ // detected once at viewer init); everything else loads as a normal image.
86
+ function pickTextureLoader(url) {
87
+ // KTX2Loader's load/loadAsync are call-compatible with TextureLoader (url →
88
+ // Texture / Promise<Texture>); cast for typing.
89
+ return isKtx2Url(url) ? ktx2Loader : textureLoader;
90
+ }
26
91
  const wrapMap = {
27
92
  Repeat: THREE.RepeatWrapping,
28
93
  ClampToEdge: THREE.ClampToEdgeWrapping,
29
94
  MirroredRepeat: THREE.MirroredRepeatWrapping,
30
95
  };
31
96
  const SRGB_TEXTURE_SLOTS = ['map', 'emissiveMap'];
32
- function getCacheKey(props) {
33
- return `${props.color}-${props.roughness}-${props.metalness}-${props.opacity}-${props.transparent}-${props.side}`;
97
+ const TEXTURE_SLOTS = [
98
+ 'map',
99
+ 'normalMap',
100
+ 'roughnessMap',
101
+ 'metalnessMap',
102
+ 'displacementMap',
103
+ 'aoMap',
104
+ 'bumpMap',
105
+ 'alphaMap',
106
+ 'lightMap',
107
+ 'emissiveMap',
108
+ ];
109
+ function getTextureChannel(slot) {
110
+ if (slot === 'aoMap' || slot === 'lightMap') {
111
+ return 2;
112
+ }
113
+ return 0;
114
+ }
115
+ function getCacheKey(props, shading) {
116
+ return `${shading}-${props.color}-${props.roughness}-${props.metalness}-${props.opacity}-${props.transparent}-${props.side}`;
34
117
  }
35
118
  function getTextureKey(material) {
36
119
  const texture = material?.texture;
@@ -48,18 +131,24 @@ function getTexture(material) {
48
131
  const cached = textureCache.get(cacheKey);
49
132
  if (cached)
50
133
  return cached;
51
- const texture = textureLoader.load(textureConfig.url);
134
+ const texture = pickTextureLoader(textureConfig.url).load(textureConfig.url);
52
135
  texture.wrapS = THREE.RepeatWrapping;
53
136
  texture.wrapT = THREE.RepeatWrapping;
54
137
  const repeatX = textureConfig.repeat?.[0] ?? textureConfig.scale ?? 1;
55
138
  const repeatY = textureConfig.repeat?.[1] ?? textureConfig.scale ?? 1;
56
139
  texture.repeat.set(repeatX, repeatY);
140
+ texture.updateMatrix();
57
141
  texture.colorSpace = THREE.SRGBColorSpace;
58
142
  textureCache.set(cacheKey, texture);
59
143
  return texture;
60
144
  }
61
145
  function isStandardMaterial(material) {
62
- return (material instanceof THREE.MeshStandardMaterial || material instanceof THREE.MeshPhysicalMaterial);
146
+ return (material instanceof THREE.MeshStandardMaterial ||
147
+ material instanceof THREE.MeshPhysicalMaterial ||
148
+ material instanceof MeshStandardNodeMaterial);
149
+ }
150
+ function isCommonMaterial(material) {
151
+ return 'color' in material && material.color instanceof THREE.Color;
63
152
  }
64
153
  function applyTextureProperties(texture, props, slot) {
65
154
  texture.wrapS = wrapMap[props.wrapS];
@@ -67,43 +156,77 @@ function applyTextureProperties(texture, props, slot) {
67
156
  texture.repeat.set(props.repeatX, props.repeatY);
68
157
  texture.rotation = props.rotation;
69
158
  texture.flipY = props.flipY;
159
+ texture.updateMatrix();
160
+ texture.channel = getTextureChannel(slot);
70
161
  texture.colorSpace = SRGB_TEXTURE_SLOTS.includes(slot ?? 'map')
71
162
  ? THREE.SRGBColorSpace
72
163
  : THREE.NoColorSpace;
73
164
  texture.needsUpdate = true;
74
165
  return texture;
75
166
  }
167
+ function setTextureCacheKey(texture, cacheKey) {
168
+ texture.userData.pascalTextureCacheKey = cacheKey;
169
+ return texture;
170
+ }
76
171
  function getPresetTextureCacheKey(path, props, slot) {
77
172
  return `${path}-${props.repeatX}-${props.repeatY}-${props.rotation}-${props.wrapS}-${props.wrapT}-${props.flipY}-${slot ?? 'map'}`;
78
173
  }
79
174
  function getPresetTexture(path, props, slot) {
80
- const cacheKey = getPresetTextureCacheKey(path, props, slot);
175
+ const resolvedPath = resolveCdnUrl(path) ?? path;
176
+ const cacheKey = getPresetTextureCacheKey(resolvedPath, props, slot);
81
177
  const cached = textureCache.get(cacheKey);
82
178
  if (cached)
83
179
  return cached;
84
- const texture = textureLoader.load(path);
180
+ const texture = pickTextureLoader(resolvedPath).load(resolvedPath);
85
181
  applyTextureProperties(texture, props, slot);
182
+ setTextureCacheKey(texture, cacheKey);
86
183
  textureCache.set(cacheKey, texture);
87
184
  return texture;
88
185
  }
186
+ function createAssignedTexture(source, props, slot) {
187
+ const texture = source.clone();
188
+ const cacheKey = source.userData.pascalTextureCacheKey;
189
+ if (typeof cacheKey === 'string') {
190
+ setTextureCacheKey(texture, cacheKey);
191
+ }
192
+ return applyTextureProperties(texture, props, slot);
193
+ }
194
+ function applyTexturePropertiesToMaterial(material, props) {
195
+ const slots = isStandardMaterial(material) ? TEXTURE_SLOTS : ['map'];
196
+ const textureMaterial = material;
197
+ for (const slot of slots) {
198
+ const texture = textureMaterial[slot];
199
+ if (!texture)
200
+ continue;
201
+ applyTextureProperties(texture, props, slot);
202
+ }
203
+ }
89
204
  async function loadPresetTexture(path, props, slot) {
90
- const cacheKey = getPresetTextureCacheKey(path, props, slot);
205
+ const resolvedPath = resolveCdnUrl(path) ?? path;
206
+ const cacheKey = getPresetTextureCacheKey(resolvedPath, props, slot);
91
207
  const cached = textureCache.get(cacheKey);
92
208
  if (cached)
93
209
  return cached;
94
210
  const existingPromise = textureLoadPromises.get(cacheKey);
95
211
  if (existingPromise)
96
212
  return existingPromise;
97
- const promise = textureLoader
98
- .loadAsync(path)
213
+ // `.ktx2` loads wait for `detectSupport` (KTX2Loader.load throws before it) —
214
+ // materials can be created while a capture canvas's renderer is still
215
+ // initializing, and failing here would cache the material permanently
216
+ // texture-less (white).
217
+ const load = isKtx2Url(resolvedPath)
218
+ ? whenKtx2Ready().then(() => ktx2Loader.loadAsync(resolvedPath))
219
+ : textureLoader.loadAsync(resolvedPath);
220
+ const promise = load
99
221
  .then((texture) => {
100
222
  applyTextureProperties(texture, props, slot);
223
+ setTextureCacheKey(texture, cacheKey);
101
224
  textureCache.set(cacheKey, texture);
102
225
  textureLoadPromises.delete(cacheKey);
103
226
  return texture;
104
227
  })
105
228
  .catch((error) => {
106
- console.warn('[viewer] Failed to load material texture', path, error);
229
+ console.warn('[viewer] Failed to load material texture', resolvedPath, error);
107
230
  textureLoadPromises.delete(cacheKey);
108
231
  return null;
109
232
  });
@@ -111,48 +234,86 @@ async function loadPresetTexture(path, props, slot) {
111
234
  return promise;
112
235
  }
113
236
  function queueTextureAssignment(material, slot, path, props) {
237
+ const textureMaterial = material;
114
238
  if (!path) {
115
- material[slot] = null;
239
+ textureMaterial[slot] = null;
240
+ return;
241
+ }
242
+ const resolvedPath = resolveCdnUrl(path) ?? path;
243
+ const cacheKey = getPresetTextureCacheKey(resolvedPath, props, slot);
244
+ if (textureMaterial[slot]?.userData.pascalTextureCacheKey === cacheKey) {
245
+ applyTextureProperties(textureMaterial[slot], props, slot);
116
246
  return;
117
247
  }
118
- const cacheKey = getPresetTextureCacheKey(path, props, slot);
119
248
  const cached = textureCache.get(cacheKey);
120
249
  if (cached) {
121
- material[slot] = cached;
250
+ textureMaterial[slot] = createAssignedTexture(cached, props, slot);
251
+ material.needsUpdate = true;
122
252
  return;
123
253
  }
124
- material[slot] = null;
254
+ textureMaterial[slot] = null;
125
255
  loadPresetTexture(path, props, slot).then((texture) => {
126
256
  if (!texture)
127
257
  return;
128
- material[slot] = texture;
258
+ textureMaterial[slot] = createAssignedTexture(texture, props, slot);
129
259
  material.needsUpdate = true;
130
260
  });
131
261
  }
132
262
  function applyMaterialMapProperties(material, mapProperties) {
133
263
  material.color.set(mapProperties.color);
134
- material.roughness = mapProperties.roughness;
135
- material.metalness = mapProperties.metalness;
136
- material.emissiveIntensity = mapProperties.emissiveIntensity;
137
- material.emissive.set(mapProperties.emissiveColor);
138
- material.displacementScale = mapProperties.displacementScale;
139
- material.bumpScale = mapProperties.bumpScale;
140
- material.aoMapIntensity = mapProperties.aoMapIntensity;
141
- material.lightMapIntensity = mapProperties.lightMapIntensity;
264
+ if (isStandardMaterial(material)) {
265
+ material.roughness = mapProperties.roughness;
266
+ material.metalness = mapProperties.metalness;
267
+ material.displacementScale = mapProperties.displacementScale;
268
+ material.bumpScale = mapProperties.bumpScale;
269
+ material.aoMapIntensity = mapProperties.aoMapIntensity;
270
+ material.lightMapIntensity = mapProperties.lightMapIntensity;
271
+ material.normalScale.set(mapProperties.normalScaleX, mapProperties.normalScaleY);
272
+ }
273
+ if (material.emissive) {
274
+ material.emissive.set(mapProperties.emissiveColor);
275
+ }
276
+ if ('emissiveIntensity' in material) {
277
+ material.emissiveIntensity = mapProperties.emissiveIntensity;
278
+ }
142
279
  material.transparent = mapProperties.transparent;
143
280
  material.opacity = mapProperties.opacity;
144
- material.side =
145
- mapProperties.side === 0
146
- ? THREE.FrontSide
147
- : mapProperties.side === 1
148
- ? THREE.BackSide
149
- : THREE.DoubleSide;
150
- material.normalScale.set(mapProperties.normalScaleX, mapProperties.normalScaleY);
281
+ material.side = resolveNodeMaterialSide(mapProperties.side === 0
282
+ ? THREE.FrontSide
283
+ : mapProperties.side === 1
284
+ ? THREE.BackSide
285
+ : THREE.DoubleSide);
286
+ applyTexturePropertiesToMaterial(material, mapProperties);
151
287
  material.needsUpdate = true;
152
288
  }
289
+ // Glass-like transparency threshold: any standard material authored as
290
+ // `transparent` with opacity below this gets the fresnel treatment.
291
+ const GLASS_OPACITY_THRESHOLD = 0.6;
292
+ /**
293
+ * Fresnel-driven opacity for glass: nearly the authored opacity head-on,
294
+ * increasingly opaque (showing the environment reflection) at grazing angles.
295
+ * This is what makes glass read as a surface instead of a flat blue tint.
296
+ */
297
+ function applyGlassFresnel(material) {
298
+ const facing = transformedNormalView.dot(positionViewDirection).clamp(0, 1);
299
+ const fresnel = facing.oneMinus().pow(3);
300
+ material.opacityNode = mix(float(material.opacity), float(0.92), fresnel);
301
+ material.envMapIntensity = 1.4;
302
+ }
303
+ function maybeApplyGlassFresnel(material) {
304
+ if (material instanceof MeshStandardNodeMaterial &&
305
+ material.transparent &&
306
+ material.opacity < GLASS_OPACITY_THRESHOLD) {
307
+ applyGlassFresnel(material);
308
+ }
309
+ }
153
310
  function applyMaterialPresetTextures(material, preset) {
154
311
  const { maps, mapProperties } = preset;
155
312
  queueTextureAssignment(material, 'map', maps.albedoMap, mapProperties);
313
+ if (!isStandardMaterial(material)) {
314
+ material.needsUpdate = true;
315
+ return;
316
+ }
156
317
  queueTextureAssignment(material, 'normalMap', maps.normalMap, mapProperties);
157
318
  queueTextureAssignment(material, 'roughnessMap', maps.roughnessMap, mapProperties);
158
319
  queueTextureAssignment(material, 'metalnessMap', maps.metalnessMap, mapProperties);
@@ -167,7 +328,7 @@ function applyMaterialPresetTextures(material, preset) {
167
328
  export function applyMaterialPresetToMaterials(materialInput, preset) {
168
329
  if (!preset)
169
330
  return;
170
- const materials = (Array.isArray(materialInput) ? materialInput : [materialInput]).filter(isStandardMaterial);
331
+ const materials = (Array.isArray(materialInput) ? materialInput : [materialInput]).filter(isCommonMaterial);
171
332
  if (materials.length === 0)
172
333
  return;
173
334
  for (const material of materials) {
@@ -175,63 +336,186 @@ export function applyMaterialPresetToMaterials(materialInput, preset) {
175
336
  applyMaterialPresetTextures(material, preset);
176
337
  }
177
338
  }
178
- export function createMaterialFromPreset(preset) {
179
- const cacheKey = JSON.stringify(preset);
339
+ export function createMaterialFromPreset(preset, shading = 'rendered') {
340
+ const cacheKey = `${shading}-${JSON.stringify(preset)}`;
180
341
  if (materialCache.has(cacheKey)) {
181
342
  return materialCache.get(cacheKey);
182
343
  }
183
- const material = new THREE.MeshStandardMaterial();
344
+ const material = shading === 'solid' ? new MeshLambertNodeMaterial() : new MeshStandardNodeMaterial();
184
345
  applyMaterialPresetToMaterials(material, preset);
346
+ maybeApplyGlassFresnel(material);
347
+ material.userData.__pascalCachedMaterial = true;
185
348
  materialCache.set(cacheKey, material);
186
349
  return material;
187
350
  }
188
- export function createMaterialFromPresetRef(materialPreset) {
351
+ export function createMaterialFromPresetRef(materialPreset, shading = 'rendered') {
189
352
  const preset = getMaterialPresetByRef(materialPreset);
190
353
  if (!preset)
191
354
  return null;
192
- return createMaterialFromPreset(preset);
355
+ return createMaterialFromPreset(preset, shading);
193
356
  }
194
- export function createMaterial(material) {
357
+ export function createMaterial(material, shading = 'rendered') {
195
358
  const props = resolveMaterial(material);
196
- const cacheKey = `${getCacheKey(props)}-${getTextureKey(material)}`;
359
+ const cacheKey = `${getCacheKey(props, shading)}-${getTextureKey(material)}`;
197
360
  if (materialCache.has(cacheKey)) {
198
361
  return materialCache.get(cacheKey);
199
362
  }
200
363
  const map = getTexture(material);
201
- const threeMaterial = new THREE.MeshStandardMaterial({
364
+ const materialParams = {
202
365
  color: props.color,
203
- roughness: props.roughness,
204
- metalness: props.metalness,
205
366
  opacity: props.opacity,
206
367
  transparent: props.transparent,
207
368
  side: sideMap[props.side],
208
- map,
209
- });
369
+ };
370
+ if (map)
371
+ materialParams.map = map;
372
+ const threeMaterial = shading === 'solid'
373
+ ? new MeshLambertNodeMaterial(materialParams)
374
+ : new MeshStandardNodeMaterial({
375
+ ...materialParams,
376
+ roughness: props.roughness,
377
+ metalness: props.metalness,
378
+ });
379
+ maybeApplyGlassFresnel(threeMaterial);
380
+ threeMaterial.userData.__pascalCachedMaterial = true;
210
381
  materialCache.set(cacheKey, threeMaterial);
211
382
  return threeMaterial;
212
383
  }
213
- export function createDefaultMaterial(color = '#ffffff', roughness = 0.9) {
214
- return new THREE.MeshStandardMaterial({
384
+ /**
385
+ * Resolve a MaterialRef ('library:<id>' | 'scene:<id>') to a three.js material.
386
+ * Returns null for an unknown / dangling ref so callers fall back to the
387
+ * slot's default (authored material, then themed default). Never throws.
388
+ */
389
+ export function resolveMaterialRef(ref, sceneMaterials, shading = 'rendered') {
390
+ const parsed = parseMaterialRef(ref);
391
+ if (!parsed)
392
+ return null;
393
+ if (parsed.kind === 'library')
394
+ return createMaterialFromPresetRef(ref, shading);
395
+ const sceneMaterial = sceneMaterials?.[parsed.id];
396
+ if (!sceneMaterial)
397
+ return null;
398
+ return createMaterial(sceneMaterial.material, shading);
399
+ }
400
+ /**
401
+ * Resolve a node kind's declared slot default — either a catalog `library:<id>`
402
+ * finish or a flat `#rrggbb` colour — to a renderable material. Shared by the
403
+ * procedural kinds whose colored-mode unpainted appearance comes from a
404
+ * declarative default (slab, wall).
405
+ */
406
+ export function resolveSlotDefaultMaterial(slotDefault, shading = 'rendered', roughness = 0.9) {
407
+ if (parseMaterialRef(slotDefault)?.kind === 'library') {
408
+ return (createMaterialFromPresetRef(slotDefault, shading) ??
409
+ createDefaultMaterial('#ffffff', roughness, shading));
410
+ }
411
+ return createDefaultMaterial(slotDefault, roughness, shading);
412
+ }
413
+ export function createDefaultMaterial(color = '#ffffff', roughness = 0.9, shading = 'rendered', side = THREE.FrontSide) {
414
+ const resolvedSide = resolveNodeMaterialSide(side);
415
+ if (shading === 'solid') {
416
+ return new MeshLambertNodeMaterial({
417
+ color,
418
+ side: resolvedSide,
419
+ });
420
+ }
421
+ return new MeshStandardNodeMaterial({
215
422
  color,
216
423
  roughness,
217
424
  metalness: 0,
218
- side: THREE.FrontSide,
425
+ side: resolvedSide,
219
426
  });
220
427
  }
221
- export const DEFAULT_WALL_MATERIAL = createDefaultMaterial('#ffffff', 0.9);
222
- export const DEFAULT_SLAB_MATERIAL = createDefaultMaterial('#e5e5e5', 0.8);
223
- export const DEFAULT_DOOR_MATERIAL = createDefaultMaterial('#8b4513', 0.7);
224
- export const DEFAULT_WINDOW_MATERIAL = new THREE.MeshStandardMaterial({
225
- color: '#87ceeb',
226
- roughness: 0.1,
227
- metalness: 0.1,
228
- opacity: 0.3,
229
- transparent: true,
230
- side: THREE.DoubleSide,
231
- });
232
- export const DEFAULT_CEILING_MATERIAL = createDefaultMaterial('#f5f5dc', 0.95);
233
- export const DEFAULT_ROOF_MATERIAL = createDefaultMaterial('#808080', 0.85);
234
- export const DEFAULT_STAIR_MATERIAL = createDefaultMaterial('#ffffff', 0.9);
428
+ function cachedDefaultMaterial(key, color, roughness, shading, side = THREE.FrontSide) {
429
+ const cacheKey = `${key}-${shading}`;
430
+ const cached = defaultMaterialCache.get(cacheKey);
431
+ if (cached)
432
+ return cached;
433
+ const material = createDefaultMaterial(color, roughness, shading, side);
434
+ material.userData.__pascalCachedMaterial = true;
435
+ defaultMaterialCache.set(cacheKey, material);
436
+ return material;
437
+ }
438
+ export function createSurfaceRoleMaterial(role, preset, side = THREE.FrontSide, sceneThemeId) {
439
+ // DoubleSide on glazing trips the MRT back-face pipeline issue documented
440
+ // on `glassMaterial` above — the validator rejects the back-face variant
441
+ // for missing MRT outputs and poisons the render context (manifests as
442
+ // "Color target has no corresponding fragment stage output" on scene
443
+ // open, since the dormer's window-assembly mounts the glazing material
444
+ // on both gable faces on the first frame). Callers that need both sides
445
+ // visible (e.g. dormer back gable) must rotate the host mesh 180° so the
446
+ // FrontSide faces the viewer.
447
+ const resolvedSide = role === 'glazing' ? THREE.FrontSide : resolveNodeMaterialSide(side ?? THREE.FrontSide);
448
+ const cacheKey = `${role}-${preset}-${resolvedSide}-${sceneThemeId ?? 'base'}`;
449
+ const cached = surfaceRoleMaterialCache.get(cacheKey);
450
+ if (cached)
451
+ return cached;
452
+ const material = role === 'glazing'
453
+ ? new MeshLambertNodeMaterial({
454
+ color: resolveSurfaceColor(role, preset, sceneThemeId),
455
+ depthWrite: false,
456
+ opacity: 0.25,
457
+ side: resolvedSide,
458
+ transparent: true,
459
+ })
460
+ : new MeshLambertNodeMaterial({
461
+ color: resolveSurfaceColor(role, preset, sceneThemeId),
462
+ side: resolvedSide,
463
+ });
464
+ material.userData.__pascalCachedMaterial = true;
465
+ surfaceRoleMaterialCache.set(cacheKey, material);
466
+ return material;
467
+ }
468
+ export function baseMaterial(shading = 'rendered') {
469
+ return cachedDefaultMaterial('base', '#e9e7e3', 0.5, shading);
470
+ }
471
+ export function DEFAULT_WALL_MATERIAL(shading = 'rendered') {
472
+ return cachedDefaultMaterial('wall', '#e9e6e0', 0.9, shading);
473
+ }
474
+ export function DEFAULT_SLAB_MATERIAL(shading = 'rendered') {
475
+ return cachedDefaultMaterial('slab', '#e5e5e5', 0.8, shading);
476
+ }
477
+ export function DEFAULT_DOOR_MATERIAL(shading = 'rendered') {
478
+ return cachedDefaultMaterial('door', '#8b4513', 0.7, shading);
479
+ }
480
+ export function DEFAULT_WINDOW_MATERIAL(shading = 'rendered') {
481
+ const cacheKey = `window-${shading}`;
482
+ const cached = defaultMaterialCache.get(cacheKey);
483
+ if (cached)
484
+ return cached;
485
+ // DoubleSide on a NodeMaterial inside the MRT scene pass compiles a back-face
486
+ // pipeline variant whose fragment outputs don't cover every MRT target — the
487
+ // validator rejects it and poisons the render context (see the note above
488
+ // `glassMaterial`). FrontSide; flip the consumer's back-face group 180° if a
489
+ // back face is actually visible.
490
+ const params = {
491
+ color: '#87ceeb',
492
+ opacity: 0.3,
493
+ transparent: true,
494
+ side: THREE.FrontSide,
495
+ };
496
+ const material = shading === 'solid'
497
+ ? new MeshLambertNodeMaterial(params)
498
+ : new MeshStandardNodeMaterial({
499
+ ...params,
500
+ roughness: 0.1,
501
+ metalness: 0.1,
502
+ });
503
+ maybeApplyGlassFresnel(material);
504
+ defaultMaterialCache.set(cacheKey, material);
505
+ return material;
506
+ }
507
+ export function DEFAULT_CEILING_MATERIAL(shading = 'rendered') {
508
+ return cachedDefaultMaterial('ceiling', '#ebebd3', 0.95, shading);
509
+ }
510
+ export function DEFAULT_ROOF_MATERIAL(shading = 'rendered') {
511
+ return cachedDefaultMaterial('roof', '#808080', 0.85, shading);
512
+ }
513
+ export function DEFAULT_SHELF_MATERIAL(shading = 'rendered') {
514
+ return cachedDefaultMaterial('shelf', '#e9e6e0', 0.9, shading);
515
+ }
516
+ export function DEFAULT_STAIR_MATERIAL(shading = 'rendered') {
517
+ return cachedDefaultMaterial('stair', '#e9e6e0', 0.9, shading);
518
+ }
235
519
  export function disposeMaterial(material) {
236
520
  material.dispose();
237
521
  }
@@ -240,6 +524,14 @@ export function clearMaterialCache() {
240
524
  material.dispose();
241
525
  }
242
526
  materialCache.clear();
527
+ for (const material of defaultMaterialCache.values()) {
528
+ material.dispose();
529
+ }
530
+ defaultMaterialCache.clear();
531
+ for (const material of surfaceRoleMaterialCache.values()) {
532
+ material.dispose();
533
+ }
534
+ surfaceRoleMaterialCache.clear();
243
535
  for (const texture of textureCache.values()) {
244
536
  texture.dispose();
245
537
  }
@@ -1 +1 @@
1
- {"version":3,"file":"merged-outline-node.d.ts","sourceRoot":"","sources":["../../src/lib/merged-outline-node.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAA2B,KAAK,QAAQ,EAAyB,MAAM,OAAO,CAAA;AAyBrF,OAAO,EAML,QAAQ,EACT,MAAM,cAAc,CAAA;AA8DrB,qBAAa,iBAAkB,SAAQ,QAAQ;IAC7C,MAAM,KAAK,IAAI,WAEd;IAED,KAAK,EAAE,GAAG,CAAA;IACV,MAAM,EAAE,GAAG,CAAA;IACX,cAAc,EAAE,QAAQ,EAAE,CAAA;IAC1B,gBAAgB,EAAE,QAAQ,EAAE,CAAA;IAC5B,wBAAwB,EAAE,GAAG,CAAA;IAC7B,0BAA0B,EAAE,GAAG,CAAA;IAC/B,mBAAmB,EAAE,GAAG,CAAA;IACxB,qBAAqB,EAAE,GAAG,CAAA;IAC1B,eAAe,EAAE,MAAM,CAAA;IACvB,gBAAgB,EAAE,MAAM,CAAA;IAExB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAc;IACvC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAK;IAEtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAK;IAC/B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAK;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAK;IAChC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAK;IAChC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAK;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAK;IAC/B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAK;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAK;IAChC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAK;IAChC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAK;IACpC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAK;IACrC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAK;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAK;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAK;IAEhC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAc;IAC7C,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAoB;IACzD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAc;IAC/C,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAoB;IAC3D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;IACxC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAc;IAC9C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAc;IACzC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAc;IACzC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAc;IAC7C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAc;IAC/C,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAoB;IAC3D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;IACxC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAc;IAC9C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAc;IACzC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAc;IACzC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAc;IAE7C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAO9C,OAAO,CAAC,qBAAqB,CAAQ;IACrC,OAAO,CAAC,qBAAqB,CAAQ;IAErC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAK;IACnC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAK;gBAGjC,KAAK,EAAE,GAAG,EACV,MAAM,EAAE,GAAG,EACX,MAAM,GAAE;QACN,cAAc,CAAC,EAAE,QAAQ,EAAE,CAAA;QAC3B,gBAAgB,CAAC,EAAE,QAAQ,EAAE,CAAA;QAC7B,oBAAoB,CAAC,EAAE,GAAG,CAAA;QAC1B,sBAAsB,CAAC,EAAE,GAAG,CAAA;QAC5B,eAAe,CAAC,EAAE,GAAG,CAAA;QACrB,iBAAiB,CAAC,EAAE,GAAG,CAAA;QACvB,eAAe,CAAC,EAAE,MAAM,CAAA;KACpB;IA4FR,IAAI,kBAAkB,QAErB;IACD,IAAI,iBAAiB,QAEpB;IACD,IAAI,oBAAoB,QAEvB;IACD,IAAI,mBAAmB,QAEtB;IAED,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAMrC,YAAY,CAAC,KAAK,EAAE,GAAG;IA0GvB,OAAO,CAAC,gBAAgB;IA8CxB,KAAK,CAAC,QAAQ,EAAE,GAAG;IAiJnB,OAAO;IAwBP,OAAO,CAAC,WAAW;CAOpB;AAED,eAAO,MAAM,aAAa,GACxB,OAAO,GAAG,EACV,QAAQ,GAAG,EACX,SAAS,qBAAqB,CAAC,OAAO,iBAAiB,CAAC,CAAC,CAAC,CAAC,sBACZ,CAAA"}
1
+ {"version":3,"file":"merged-outline-node.d.ts","sourceRoot":"","sources":["../../src/lib/merged-outline-node.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAA2B,KAAK,QAAQ,EAAyB,MAAM,OAAO,CAAA;AAyBrF,OAAO,EAML,QAAQ,EACT,MAAM,cAAc,CAAA;AA+DrB,qBAAa,iBAAkB,SAAQ,QAAQ;IAC7C,MAAM,KAAK,IAAI,WAEd;IAED,KAAK,EAAE,GAAG,CAAA;IACV,MAAM,EAAE,GAAG,CAAA;IACX,cAAc,EAAE,QAAQ,EAAE,CAAA;IAC1B,gBAAgB,EAAE,QAAQ,EAAE,CAAA;IAC5B,wBAAwB,EAAE,GAAG,CAAA;IAC7B,0BAA0B,EAAE,GAAG,CAAA;IAC/B,mBAAmB,EAAE,GAAG,CAAA;IACxB,qBAAqB,EAAE,GAAG,CAAA;IAC1B,eAAe,EAAE,MAAM,CAAA;IACvB,gBAAgB,EAAE,MAAM,CAAA;IAExB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAc;IACvC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAK;IAEtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAK;IAC/B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAK;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAK;IAChC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAK;IAChC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAK;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAK;IAC/B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAK;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAK;IAChC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAK;IAChC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAK;IACpC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAK;IACrC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAK;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAK;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAK;IAEhC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAc;IAC7C,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAoB;IACzD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAc;IAC/C,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAoB;IAC3D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;IACxC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAc;IAC9C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAc;IACzC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAc;IACzC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAc;IAC7C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAc;IAC/C,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAoB;IAC3D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;IACxC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAc;IAC9C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAc;IACzC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAc;IACzC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAc;IAE7C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAO9C,OAAO,CAAC,qBAAqB,CAAQ;IACrC,OAAO,CAAC,qBAAqB,CAAQ;IAErC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAK;IACnC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAK;gBAGjC,KAAK,EAAE,GAAG,EACV,MAAM,EAAE,GAAG,EACX,MAAM,GAAE;QACN,cAAc,CAAC,EAAE,QAAQ,EAAE,CAAA;QAC3B,gBAAgB,CAAC,EAAE,QAAQ,EAAE,CAAA;QAC7B,oBAAoB,CAAC,EAAE,GAAG,CAAA;QAC1B,sBAAsB,CAAC,EAAE,GAAG,CAAA;QAC5B,eAAe,CAAC,EAAE,GAAG,CAAA;QACrB,iBAAiB,CAAC,EAAE,GAAG,CAAA;QACvB,eAAe,CAAC,EAAE,MAAM,CAAA;KACpB;IA4FR,IAAI,kBAAkB,QAErB;IACD,IAAI,iBAAiB,QAEpB;IACD,IAAI,oBAAoB,QAEvB;IACD,IAAI,mBAAmB,QAEtB;IAED,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAMrC,YAAY,CAAC,KAAK,EAAE,GAAG;IA6GvB,OAAO,CAAC,gBAAgB;IA8CxB,KAAK,CAAC,QAAQ,EAAE,GAAG;IAiJnB,OAAO;IAwBP,OAAO,CAAC,WAAW;CAOpB;AAED,eAAO,MAAM,aAAa,GACxB,OAAO,GAAG,EACV,QAAQ,GAAG,EACX,SAAS,qBAAqB,CAAC,OAAO,iBAAiB,CAAC,CAAC,CAAC,CAAC,sBACZ,CAAA"}
@@ -17,6 +17,7 @@
17
17
  import { DepthTexture, FloatType, RenderTarget, Vector2 } from 'three';
18
18
  import { color, exp, Fn, float, int, Loop, min, mul, nodeObject, orthographicDepthToViewZ, passTexture, perspectiveDepthToViewZ, positionView, reference, screenUV, texture, textureSize, uniform, uv, vec2, vec3, vec4, } from 'three/tsl';
19
19
  import { NodeMaterial, NodeUpdateType, QuadMesh, RendererUtils, SpriteNodeMaterial, TempNode, } from 'three/webgpu';
20
+ import { hasDrawableGeometry } from './drawable-geometry';
20
21
  const _quadMesh = new QuadMesh();
21
22
  const _size = new Vector2();
22
23
  const _BLUR_X = new Vector2(1.0, 0.0);
@@ -262,6 +263,8 @@ export class MergedOutlineNode extends TempNode {
262
263
  // ── 1. Shared depth pass: all objects NOT in either group ─────────────────
263
264
  renderer.setRenderTarget(this._depthRT);
264
265
  renderer.setRenderObjectFunction((obj, sc, cam, geo, _mat, grp, lights, clip) => {
266
+ if (!hasDrawableGeometry(geo))
267
+ return;
265
268
  const inCache = this._cacheA.has(obj) || this._cacheB.has(obj);
266
269
  if (!inCache) {
267
270
  const m = obj.isSprite ? this._depthSpriteMaterial : this._depthMaterial;
@@ -274,6 +277,8 @@ export class MergedOutlineNode extends TempNode {
274
277
  if (hasPrimary) {
275
278
  renderer.setRenderTarget(this._groupA.maskBuffer);
276
279
  renderer.setRenderObjectFunction((obj, sc, cam, geo, _mat, grp, lights, clip) => {
280
+ if (!hasDrawableGeometry(geo))
281
+ return;
277
282
  if (this._cacheA.has(obj)) {
278
283
  const m = obj.isSprite ? this._prepareMaskSpriteMatA : this._prepareMaskMatA;
279
284
  renderer.renderObject(obj, sc, cam, geo, m, grp, lights, clip);
@@ -286,6 +291,8 @@ export class MergedOutlineNode extends TempNode {
286
291
  if (hasSecondary) {
287
292
  renderer.setRenderTarget(this._groupB.maskBuffer);
288
293
  renderer.setRenderObjectFunction((obj, sc, cam, geo, _mat, grp, lights, clip) => {
294
+ if (!hasDrawableGeometry(geo))
295
+ return;
289
296
  if (this._cacheB.has(obj)) {
290
297
  const m = obj.isSprite ? this._prepareMaskSpriteMatB : this._prepareMaskMatB;
291
298
  renderer.renderObject(obj, sc, cam, geo, m, grp, lights, clip);
@@ -0,0 +1,4 @@
1
+ export type Point2D = [number, number];
2
+ export declare function unionPolygons(polygons: Point2D[][]): Point2D[][];
3
+ export declare function subtractPolygonsFromPolygon(subject: Point2D[], cutters: Point2D[][]): Point2D[][];
4
+ //# sourceMappingURL=polygon-union.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"polygon-union.d.ts","sourceRoot":"","sources":["../../src/lib/polygon-union.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAwStC,wBAAgB,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,GAAG,OAAO,EAAE,EAAE,CAShE;AA4CD,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,GAAG,OAAO,EAAE,EAAE,CAkBjG"}