@pascal-app/viewer 0.7.0 → 0.9.1

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 (216) hide show
  1. package/dist/components/renderers/node-renderer.d.ts +1 -1
  2. package/dist/components/renderers/node-renderer.d.ts.map +1 -1
  3. package/dist/components/renderers/node-renderer.js +38 -22
  4. package/dist/components/renderers/parametric-node-renderer.d.ts +5 -0
  5. package/dist/components/renderers/parametric-node-renderer.d.ts.map +1 -0
  6. package/dist/components/renderers/parametric-node-renderer.js +34 -0
  7. package/dist/components/renderers/scene-renderer.d.ts +1 -1
  8. package/dist/components/renderers/scene-renderer.d.ts.map +1 -1
  9. package/dist/components/viewer/frame-limiter.d.ts.map +1 -1
  10. package/dist/components/viewer/frame-limiter.js +5 -1
  11. package/dist/components/viewer/ground-occluder.d.ts +1 -1
  12. package/dist/components/viewer/ground-occluder.d.ts.map +1 -1
  13. package/dist/components/viewer/ground-occluder.js +12 -24
  14. package/dist/components/viewer/index.d.ts +38 -1
  15. package/dist/components/viewer/index.d.ts.map +1 -1
  16. package/dist/components/viewer/index.js +183 -60
  17. package/dist/components/viewer/lights.d.ts +1 -1
  18. package/dist/components/viewer/lights.d.ts.map +1 -1
  19. package/dist/components/viewer/lights.js +170 -43
  20. package/dist/components/viewer/perf-monitor.d.ts +1 -1
  21. package/dist/components/viewer/perf-monitor.d.ts.map +1 -1
  22. package/dist/components/viewer/perf-monitor.js +99 -11
  23. package/dist/components/viewer/post-processing.d.ts.map +1 -1
  24. package/dist/components/viewer/post-processing.js +217 -61
  25. package/dist/components/viewer/registered-systems.d.ts +12 -0
  26. package/dist/components/viewer/registered-systems.d.ts.map +1 -0
  27. package/dist/components/viewer/registered-systems.js +47 -0
  28. package/dist/components/viewer/scene-bvh.d.ts +17 -0
  29. package/dist/components/viewer/scene-bvh.d.ts.map +1 -0
  30. package/dist/components/viewer/scene-bvh.js +94 -0
  31. package/dist/components/viewer/selection-manager.d.ts +1 -1
  32. package/dist/components/viewer/selection-manager.d.ts.map +1 -1
  33. package/dist/components/viewer/selection-manager.js +20 -5
  34. package/dist/components/viewer/viewer-camera.d.ts +1 -1
  35. package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
  36. package/dist/components/viewer/walkthrough-controls.d.ts +1 -1
  37. package/dist/components/viewer/walkthrough-controls.d.ts.map +1 -1
  38. package/dist/hooks/use-gltf-ktx2.d.ts.map +1 -1
  39. package/dist/hooks/use-node-events.d.ts +5 -73
  40. package/dist/hooks/use-node-events.d.ts.map +1 -1
  41. package/dist/hooks/use-node-events.js +19 -7
  42. package/dist/index.d.ts +36 -4
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +67 -3
  45. package/dist/lib/csg-utils.d.ts +16 -0
  46. package/dist/lib/csg-utils.d.ts.map +1 -0
  47. package/dist/lib/csg-utils.js +33 -0
  48. package/dist/lib/drawable-geometry.d.ts +21 -0
  49. package/dist/lib/drawable-geometry.d.ts.map +1 -0
  50. package/dist/lib/drawable-geometry.js +22 -0
  51. package/dist/lib/edge-style.d.ts +3 -0
  52. package/dist/lib/edge-style.d.ts.map +1 -0
  53. package/dist/lib/edge-style.js +11 -0
  54. package/dist/lib/gpu-perf.d.ts +4 -0
  55. package/dist/lib/gpu-perf.d.ts.map +1 -0
  56. package/dist/lib/gpu-perf.js +23 -0
  57. package/dist/lib/ink-edges.d.ts +9 -0
  58. package/dist/lib/ink-edges.d.ts.map +1 -0
  59. package/dist/lib/ink-edges.js +43 -0
  60. package/dist/lib/isolation.d.ts +39 -0
  61. package/dist/lib/isolation.d.ts.map +1 -0
  62. package/dist/lib/isolation.js +103 -0
  63. package/dist/lib/layers.d.ts +18 -0
  64. package/dist/lib/layers.d.ts.map +1 -1
  65. package/dist/lib/layers.js +18 -0
  66. package/dist/lib/materials.d.ts +25 -15
  67. package/dist/lib/materials.d.ts.map +1 -1
  68. package/dist/lib/materials.js +277 -63
  69. package/dist/lib/merged-outline-node.d.ts.map +1 -1
  70. package/dist/lib/merged-outline-node.js +11 -11
  71. package/dist/lib/polygon-union.d.ts +4 -0
  72. package/dist/lib/polygon-union.d.ts.map +1 -0
  73. package/dist/lib/polygon-union.js +289 -0
  74. package/dist/lib/polygon-union.test.d.ts +2 -0
  75. package/dist/lib/polygon-union.test.d.ts.map +1 -0
  76. package/dist/lib/polygon-union.test.js +105 -0
  77. package/dist/lib/scene-themes.d.ts +29 -0
  78. package/dist/lib/scene-themes.d.ts.map +1 -0
  79. package/dist/lib/scene-themes.js +193 -0
  80. package/dist/store/use-viewer.d.ts +42 -4
  81. package/dist/store/use-viewer.d.ts.map +1 -1
  82. package/dist/store/use-viewer.js +26 -3
  83. package/dist/systems/ceiling/ceiling-system.d.ts +6 -2
  84. package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
  85. package/dist/systems/ceiling/ceiling-system.js +62 -12
  86. package/dist/systems/door/door-system.d.ts.map +1 -1
  87. package/dist/systems/door/door-system.js +373 -22
  88. package/dist/systems/elevator/elevator-interaction-system.d.ts +2 -0
  89. package/dist/systems/elevator/elevator-interaction-system.d.ts.map +1 -0
  90. package/dist/systems/elevator/elevator-interaction-system.js +62 -0
  91. package/dist/systems/fence/fence-system.d.ts +3 -0
  92. package/dist/systems/fence/fence-system.d.ts.map +1 -1
  93. package/dist/systems/fence/fence-system.js +10 -6
  94. package/dist/systems/floor-elevation/floor-elevation-system.d.ts +20 -0
  95. package/dist/systems/floor-elevation/floor-elevation-system.d.ts.map +1 -0
  96. package/dist/systems/floor-elevation/floor-elevation-system.js +78 -0
  97. package/dist/systems/geometry/geometry-system.d.ts +34 -0
  98. package/dist/systems/geometry/geometry-system.d.ts.map +1 -0
  99. package/dist/systems/geometry/geometry-system.js +279 -0
  100. package/dist/systems/guide/guide-system.d.ts.map +1 -1
  101. package/dist/systems/guide/guide-system.js +1 -26
  102. package/dist/systems/interactive/interactive-system.d.ts +1 -1
  103. package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
  104. package/dist/systems/item/item-system.d.ts +7 -0
  105. package/dist/systems/item/item-system.d.ts.map +1 -1
  106. package/dist/systems/item/item-system.js +8 -11
  107. package/dist/systems/item-light/item-light-system.d.ts +1 -1
  108. package/dist/systems/item-light/item-light-system.d.ts.map +1 -1
  109. package/dist/systems/item-light/item-light-system.js +18 -5
  110. package/dist/systems/roof/roof-materials.d.ts +3 -2
  111. package/dist/systems/roof/roof-materials.d.ts.map +1 -1
  112. package/dist/systems/roof/roof-materials.js +38 -14
  113. package/dist/systems/roof/roof-system.d.ts +27 -0
  114. package/dist/systems/roof/roof-system.d.ts.map +1 -1
  115. package/dist/systems/roof/roof-system.js +411 -71
  116. package/dist/systems/scan/scan-system.d.ts.map +1 -1
  117. package/dist/systems/scan/scan-system.js +1 -26
  118. package/dist/systems/slab/slab-system.d.ts.map +1 -1
  119. package/dist/systems/slab/slab-system.js +167 -49
  120. package/dist/systems/slab/slab-system.test.d.ts +2 -0
  121. package/dist/systems/slab/slab-system.test.d.ts.map +1 -0
  122. package/dist/systems/slab/slab-system.test.js +62 -0
  123. package/dist/systems/stair/stair-materials.d.ts +4 -3
  124. package/dist/systems/stair/stair-materials.d.ts.map +1 -1
  125. package/dist/systems/stair/stair-materials.js +30 -15
  126. package/dist/systems/stair/stair-system.d.ts.map +1 -1
  127. package/dist/systems/stair/stair-system.js +58 -71
  128. package/dist/systems/surface-hole-geometry.d.ts +3 -0
  129. package/dist/systems/surface-hole-geometry.d.ts.map +1 -0
  130. package/dist/systems/surface-hole-geometry.js +4 -0
  131. package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
  132. package/dist/systems/wall/wall-cutout.js +18 -2
  133. package/dist/systems/wall/wall-materials.d.ts +4 -3
  134. package/dist/systems/wall/wall-materials.d.ts.map +1 -1
  135. package/dist/systems/wall/wall-materials.js +52 -28
  136. package/dist/systems/wall/wall-system.d.ts +1 -1
  137. package/dist/systems/wall/wall-system.d.ts.map +1 -1
  138. package/dist/systems/wall/wall-system.js +179 -31
  139. package/dist/systems/window/window-system.d.ts.map +1 -1
  140. package/dist/systems/window/window-system.js +71 -7
  141. package/package.json +4 -5
  142. package/dist/components/renderers/building/building-renderer.d.ts +0 -5
  143. package/dist/components/renderers/building/building-renderer.d.ts.map +0 -1
  144. package/dist/components/renderers/building/building-renderer.js +0 -11
  145. package/dist/components/renderers/ceiling/ceiling-renderer.d.ts +0 -5
  146. package/dist/components/renderers/ceiling/ceiling-renderer.d.ts.map +0 -1
  147. package/dist/components/renderers/ceiling/ceiling-renderer.js +0 -58
  148. package/dist/components/renderers/column/column-renderer.d.ts +0 -5
  149. package/dist/components/renderers/column/column-renderer.d.ts.map +0 -1
  150. package/dist/components/renderers/column/column-renderer.js +0 -594
  151. package/dist/components/renderers/door/door-renderer.d.ts +0 -5
  152. package/dist/components/renderers/door/door-renderer.d.ts.map +0 -1
  153. package/dist/components/renderers/door/door-renderer.js +0 -16
  154. package/dist/components/renderers/fence/fence-renderer.d.ts +0 -5
  155. package/dist/components/renderers/fence/fence-renderer.d.ts.map +0 -1
  156. package/dist/components/renderers/fence/fence-renderer.js +0 -29
  157. package/dist/components/renderers/guide/guide-renderer.d.ts +0 -5
  158. package/dist/components/renderers/guide/guide-renderer.d.ts.map +0 -1
  159. package/dist/components/renderers/guide/guide-renderer.js +0 -38
  160. package/dist/components/renderers/item/item-renderer.d.ts +0 -5
  161. package/dist/components/renderers/item/item-renderer.d.ts.map +0 -1
  162. package/dist/components/renderers/item/item-renderer.js +0 -172
  163. package/dist/components/renderers/level/level-renderer.d.ts +0 -5
  164. package/dist/components/renderers/level/level-renderer.d.ts.map +0 -1
  165. package/dist/components/renderers/level/level-renderer.js +0 -11
  166. package/dist/components/renderers/roof/roof-materials.d.ts +0 -4
  167. package/dist/components/renderers/roof/roof-materials.d.ts.map +0 -1
  168. package/dist/components/renderers/roof/roof-materials.js +0 -16
  169. package/dist/components/renderers/roof/roof-renderer.d.ts +0 -5
  170. package/dist/components/renderers/roof/roof-renderer.d.ts.map +0 -1
  171. package/dist/components/renderers/roof/roof-renderer.js +0 -32
  172. package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts +0 -5
  173. package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts.map +0 -1
  174. package/dist/components/renderers/roof-segment/roof-segment-renderer.js +0 -40
  175. package/dist/components/renderers/scan/scan-renderer.d.ts +0 -5
  176. package/dist/components/renderers/scan/scan-renderer.d.ts.map +0 -1
  177. package/dist/components/renderers/scan/scan-renderer.js +0 -54
  178. package/dist/components/renderers/site/site-renderer.d.ts +0 -5
  179. package/dist/components/renderers/site/site-renderer.d.ts.map +0 -1
  180. package/dist/components/renderers/site/site-renderer.js +0 -106
  181. package/dist/components/renderers/slab/slab-renderer.d.ts +0 -5
  182. package/dist/components/renderers/slab/slab-renderer.d.ts.map +0 -1
  183. package/dist/components/renderers/slab/slab-renderer.js +0 -58
  184. package/dist/components/renderers/spawn/spawn-renderer.d.ts +0 -5
  185. package/dist/components/renderers/spawn/spawn-renderer.d.ts.map +0 -1
  186. package/dist/components/renderers/spawn/spawn-renderer.js +0 -32
  187. package/dist/components/renderers/stair/stair-renderer.d.ts +0 -5
  188. package/dist/components/renderers/stair/stair-renderer.d.ts.map +0 -1
  189. package/dist/components/renderers/stair/stair-renderer.js +0 -603
  190. package/dist/components/renderers/stair-segment/stair-segment-renderer.d.ts +0 -5
  191. package/dist/components/renderers/stair-segment/stair-segment-renderer.d.ts.map +0 -1
  192. package/dist/components/renderers/stair-segment/stair-segment-renderer.js +0 -32
  193. package/dist/components/renderers/wall/wall-renderer.d.ts +0 -5
  194. package/dist/components/renderers/wall/wall-renderer.d.ts.map +0 -1
  195. package/dist/components/renderers/wall/wall-renderer.js +0 -16
  196. package/dist/components/renderers/window/window-renderer.d.ts +0 -5
  197. package/dist/components/renderers/window/window-renderer.d.ts.map +0 -1
  198. package/dist/components/renderers/window/window-renderer.js +0 -21
  199. package/dist/components/renderers/zone/zone-renderer.d.ts +0 -5
  200. package/dist/components/renderers/zone/zone-renderer.d.ts.map +0 -1
  201. package/dist/components/renderers/zone/zone-renderer.js +0 -181
  202. package/dist/lib/suppress-three-clock-warning.d.ts +0 -25
  203. package/dist/lib/suppress-three-clock-warning.d.ts.map +0 -1
  204. package/dist/lib/suppress-three-clock-warning.js +0 -51
  205. package/dist/systems/export/export-system.d.ts +0 -2
  206. package/dist/systems/export/export-system.d.ts.map +0 -1
  207. package/dist/systems/export/export-system.js +0 -60
  208. package/dist/systems/item-mesh-metadata/compute-item-mesh-metadata.d.ts +0 -14
  209. package/dist/systems/item-mesh-metadata/compute-item-mesh-metadata.d.ts.map +0 -1
  210. package/dist/systems/item-mesh-metadata/compute-item-mesh-metadata.js +0 -155
  211. package/dist/systems/item-mesh-metadata/item-mesh-metadata-system.d.ts +0 -6
  212. package/dist/systems/item-mesh-metadata/item-mesh-metadata-system.d.ts.map +0 -1
  213. package/dist/systems/item-mesh-metadata/item-mesh-metadata-system.js +0 -75
  214. package/dist/systems/item-mesh-metadata/sync-request.d.ts +0 -7
  215. package/dist/systems/item-mesh-metadata/sync-request.d.ts.map +0 -1
  216. package/dist/systems/item-mesh-metadata/sync-request.js +0 -25
@@ -1,18 +1,66 @@
1
1
  import { getMaterialPresetByRef, 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 { MeshLambertNodeMaterial, MeshStandardNodeMaterial } from 'three/webgpu';
4
+ import { resolveCdnUrl } from './asset-url';
5
+ import { getSceneTheme } from './scene-themes';
6
+ export const CLAY_PALETTE = {
7
+ wall: '#dcd6c7',
8
+ floor: '#cfc8b6',
9
+ ceiling: '#e4ded0',
10
+ roof: '#b8ad96',
11
+ joinery: '#c4bba6',
12
+ glazing: '#c8d4dc',
13
+ furnishing: '#d2ccbe',
14
+ };
15
+ export const WHITE_PALETTE = {
16
+ wall: '#f4f3ef',
17
+ floor: '#ece9e2',
18
+ ceiling: '#fbfaf6',
19
+ roof: '#dedbd2',
20
+ joinery: '#e8e5dc',
21
+ glazing: '#dbe8ee',
22
+ furnishing: '#efede7',
23
+ };
24
+ export const MONO_PALETTE = {
25
+ wall: '#c8c8c8',
26
+ floor: '#b8b8b8',
27
+ ceiling: '#d8d8d8',
28
+ roof: '#9a9a9a',
29
+ joinery: '#adadad',
30
+ glazing: '#c2cbd0',
31
+ furnishing: '#c0c0c0',
32
+ };
33
+ export const BLUEPRINT_PALETTE = {
34
+ wall: '#90a9c7',
35
+ floor: '#7f98ba',
36
+ ceiling: '#aec0d8',
37
+ roof: '#5f789b',
38
+ joinery: '#6f86a8',
39
+ glazing: '#b6d7ea',
40
+ furnishing: '#8ba2bf',
41
+ };
42
+ export const PRESET_PALETTES = {
43
+ clay: CLAY_PALETTE,
44
+ white: WHITE_PALETTE,
45
+ mono: MONO_PALETTE,
46
+ blueprint: BLUEPRINT_PALETTE,
47
+ };
48
+ export function resolveSurfaceColor(role, preset, sceneThemeId) {
49
+ // The active scene theme may tint individual roles (e.g. Mediterranean's blue
50
+ // roof); fall back to the chosen colour preset's palette when it doesn't.
51
+ const tints = sceneThemeId ? getSceneTheme(sceneThemeId).clayTints : undefined;
52
+ return tints?.[role] ?? PRESET_PALETTES[preset][role];
53
+ }
54
+ // DoubleSide on any NodeMaterial inside the MRT scenePass (SSGI's output /
55
+ // diffuseColor / normal targets) causes WebGPU to create a render pipeline
56
+ // whose back-face shader variant doesn't declare outputs for every MRT target
57
+ // — the validator rejects it and poisons the entire render context. FrontSide
58
+ // avoids that code path. Same pattern as MeshStandardMaterial in renderer.tsx.
59
+ export const glassMaterial = new MeshLambertNodeMaterial({
10
60
  color: '#e0f2fe',
11
- roughness: 0.05,
12
- metalness: 0.0,
13
61
  transparent: true,
14
62
  opacity: 0.35,
15
- side: THREE.DoubleSide,
63
+ side: THREE.FrontSide,
16
64
  });
17
65
  const sideMap = {
18
66
  front: THREE.FrontSide,
@@ -20,6 +68,8 @@ const sideMap = {
20
68
  double: THREE.DoubleSide,
21
69
  };
22
70
  const materialCache = new Map();
71
+ const defaultMaterialCache = new Map();
72
+ const surfaceRoleMaterialCache = new Map();
23
73
  const textureCache = new Map();
24
74
  const textureLoadPromises = new Map();
25
75
  const textureLoader = new THREE.TextureLoader();
@@ -29,8 +79,26 @@ const wrapMap = {
29
79
  MirroredRepeat: THREE.MirroredRepeatWrapping,
30
80
  };
31
81
  const SRGB_TEXTURE_SLOTS = ['map', 'emissiveMap'];
32
- function getCacheKey(props) {
33
- return `${props.color}-${props.roughness}-${props.metalness}-${props.opacity}-${props.transparent}-${props.side}`;
82
+ const TEXTURE_SLOTS = [
83
+ 'map',
84
+ 'normalMap',
85
+ 'roughnessMap',
86
+ 'metalnessMap',
87
+ 'displacementMap',
88
+ 'aoMap',
89
+ 'bumpMap',
90
+ 'alphaMap',
91
+ 'lightMap',
92
+ 'emissiveMap',
93
+ ];
94
+ function getTextureChannel(slot) {
95
+ if (slot === 'aoMap' || slot === 'lightMap') {
96
+ return 2;
97
+ }
98
+ return 0;
99
+ }
100
+ function getCacheKey(props, shading) {
101
+ return `${shading}-${props.color}-${props.roughness}-${props.metalness}-${props.opacity}-${props.transparent}-${props.side}`;
34
102
  }
35
103
  function getTextureKey(material) {
36
104
  const texture = material?.texture;
@@ -54,13 +122,18 @@ function getTexture(material) {
54
122
  const repeatX = textureConfig.repeat?.[0] ?? textureConfig.scale ?? 1;
55
123
  const repeatY = textureConfig.repeat?.[1] ?? textureConfig.scale ?? 1;
56
124
  texture.repeat.set(repeatX, repeatY);
125
+ texture.updateMatrix();
57
126
  texture.colorSpace = THREE.SRGBColorSpace;
58
127
  textureCache.set(cacheKey, texture);
59
128
  return texture;
60
129
  }
61
130
  function isStandardMaterial(material) {
62
131
  return (material instanceof THREE.MeshStandardMaterial ||
63
- material instanceof THREE.MeshPhysicalMaterial);
132
+ material instanceof THREE.MeshPhysicalMaterial ||
133
+ material instanceof MeshStandardNodeMaterial);
134
+ }
135
+ function isCommonMaterial(material) {
136
+ return 'color' in material && material.color instanceof THREE.Color;
64
137
  }
65
138
  function applyTextureProperties(texture, props, slot) {
66
139
  texture.wrapS = wrapMap[props.wrapS];
@@ -68,27 +141,54 @@ function applyTextureProperties(texture, props, slot) {
68
141
  texture.repeat.set(props.repeatX, props.repeatY);
69
142
  texture.rotation = props.rotation;
70
143
  texture.flipY = props.flipY;
144
+ texture.updateMatrix();
145
+ texture.channel = getTextureChannel(slot);
71
146
  texture.colorSpace = SRGB_TEXTURE_SLOTS.includes(slot ?? 'map')
72
147
  ? THREE.SRGBColorSpace
73
148
  : THREE.NoColorSpace;
74
149
  texture.needsUpdate = true;
75
150
  return texture;
76
151
  }
152
+ function setTextureCacheKey(texture, cacheKey) {
153
+ texture.userData.pascalTextureCacheKey = cacheKey;
154
+ return texture;
155
+ }
77
156
  function getPresetTextureCacheKey(path, props, slot) {
78
157
  return `${path}-${props.repeatX}-${props.repeatY}-${props.rotation}-${props.wrapS}-${props.wrapT}-${props.flipY}-${slot ?? 'map'}`;
79
158
  }
80
159
  function getPresetTexture(path, props, slot) {
81
- const cacheKey = getPresetTextureCacheKey(path, props, slot);
160
+ const resolvedPath = resolveCdnUrl(path) ?? path;
161
+ const cacheKey = getPresetTextureCacheKey(resolvedPath, props, slot);
82
162
  const cached = textureCache.get(cacheKey);
83
163
  if (cached)
84
164
  return cached;
85
- const texture = textureLoader.load(path);
165
+ const texture = textureLoader.load(resolvedPath);
86
166
  applyTextureProperties(texture, props, slot);
167
+ setTextureCacheKey(texture, cacheKey);
87
168
  textureCache.set(cacheKey, texture);
88
169
  return texture;
89
170
  }
171
+ function createAssignedTexture(source, props, slot) {
172
+ const texture = source.clone();
173
+ const cacheKey = source.userData.pascalTextureCacheKey;
174
+ if (typeof cacheKey === 'string') {
175
+ setTextureCacheKey(texture, cacheKey);
176
+ }
177
+ return applyTextureProperties(texture, props, slot);
178
+ }
179
+ function applyTexturePropertiesToMaterial(material, props) {
180
+ const slots = isStandardMaterial(material) ? TEXTURE_SLOTS : ['map'];
181
+ const textureMaterial = material;
182
+ for (const slot of slots) {
183
+ const texture = textureMaterial[slot];
184
+ if (!texture)
185
+ continue;
186
+ applyTextureProperties(texture, props, slot);
187
+ }
188
+ }
90
189
  async function loadPresetTexture(path, props, slot) {
91
- const cacheKey = getPresetTextureCacheKey(path, props, slot);
190
+ const resolvedPath = resolveCdnUrl(path) ?? path;
191
+ const cacheKey = getPresetTextureCacheKey(resolvedPath, props, slot);
92
192
  const cached = textureCache.get(cacheKey);
93
193
  if (cached)
94
194
  return cached;
@@ -96,15 +196,16 @@ async function loadPresetTexture(path, props, slot) {
96
196
  if (existingPromise)
97
197
  return existingPromise;
98
198
  const promise = textureLoader
99
- .loadAsync(path)
199
+ .loadAsync(resolvedPath)
100
200
  .then((texture) => {
101
201
  applyTextureProperties(texture, props, slot);
202
+ setTextureCacheKey(texture, cacheKey);
102
203
  textureCache.set(cacheKey, texture);
103
204
  textureLoadPromises.delete(cacheKey);
104
205
  return texture;
105
206
  })
106
207
  .catch((error) => {
107
- console.warn('[viewer] Failed to load material texture', path, error);
208
+ console.warn('[viewer] Failed to load material texture', resolvedPath, error);
108
209
  textureLoadPromises.delete(cacheKey);
109
210
  return null;
110
211
  });
@@ -112,34 +213,48 @@ async function loadPresetTexture(path, props, slot) {
112
213
  return promise;
113
214
  }
114
215
  function queueTextureAssignment(material, slot, path, props) {
216
+ const textureMaterial = material;
115
217
  if (!path) {
116
- material[slot] = null;
218
+ textureMaterial[slot] = null;
219
+ return;
220
+ }
221
+ const resolvedPath = resolveCdnUrl(path) ?? path;
222
+ const cacheKey = getPresetTextureCacheKey(resolvedPath, props, slot);
223
+ if (textureMaterial[slot]?.userData.pascalTextureCacheKey === cacheKey) {
224
+ applyTextureProperties(textureMaterial[slot], props, slot);
117
225
  return;
118
226
  }
119
- const cacheKey = getPresetTextureCacheKey(path, props, slot);
120
227
  const cached = textureCache.get(cacheKey);
121
228
  if (cached) {
122
- material[slot] = cached;
229
+ textureMaterial[slot] = createAssignedTexture(cached, props, slot);
230
+ material.needsUpdate = true;
123
231
  return;
124
232
  }
125
- material[slot] = null;
126
- void loadPresetTexture(path, props, slot).then((texture) => {
233
+ textureMaterial[slot] = null;
234
+ loadPresetTexture(path, props, slot).then((texture) => {
127
235
  if (!texture)
128
236
  return;
129
- material[slot] = texture;
237
+ textureMaterial[slot] = createAssignedTexture(texture, props, slot);
130
238
  material.needsUpdate = true;
131
239
  });
132
240
  }
133
241
  function applyMaterialMapProperties(material, mapProperties) {
134
242
  material.color.set(mapProperties.color);
135
- material.roughness = mapProperties.roughness;
136
- material.metalness = mapProperties.metalness;
137
- material.emissiveIntensity = mapProperties.emissiveIntensity;
138
- material.emissive.set(mapProperties.emissiveColor);
139
- material.displacementScale = mapProperties.displacementScale;
140
- material.bumpScale = mapProperties.bumpScale;
141
- material.aoMapIntensity = mapProperties.aoMapIntensity;
142
- material.lightMapIntensity = mapProperties.lightMapIntensity;
243
+ if (isStandardMaterial(material)) {
244
+ material.roughness = mapProperties.roughness;
245
+ material.metalness = mapProperties.metalness;
246
+ material.displacementScale = mapProperties.displacementScale;
247
+ material.bumpScale = mapProperties.bumpScale;
248
+ material.aoMapIntensity = mapProperties.aoMapIntensity;
249
+ material.lightMapIntensity = mapProperties.lightMapIntensity;
250
+ material.normalScale.set(mapProperties.normalScaleX, mapProperties.normalScaleY);
251
+ }
252
+ if (material.emissive) {
253
+ material.emissive.set(mapProperties.emissiveColor);
254
+ }
255
+ if ('emissiveIntensity' in material) {
256
+ material.emissiveIntensity = mapProperties.emissiveIntensity;
257
+ }
143
258
  material.transparent = mapProperties.transparent;
144
259
  material.opacity = mapProperties.opacity;
145
260
  material.side =
@@ -148,12 +263,16 @@ function applyMaterialMapProperties(material, mapProperties) {
148
263
  : mapProperties.side === 1
149
264
  ? THREE.BackSide
150
265
  : THREE.DoubleSide;
151
- material.normalScale.set(mapProperties.normalScaleX, mapProperties.normalScaleY);
266
+ applyTexturePropertiesToMaterial(material, mapProperties);
152
267
  material.needsUpdate = true;
153
268
  }
154
269
  function applyMaterialPresetTextures(material, preset) {
155
270
  const { maps, mapProperties } = preset;
156
271
  queueTextureAssignment(material, 'map', maps.albedoMap, mapProperties);
272
+ if (!isStandardMaterial(material)) {
273
+ material.needsUpdate = true;
274
+ return;
275
+ }
157
276
  queueTextureAssignment(material, 'normalMap', maps.normalMap, mapProperties);
158
277
  queueTextureAssignment(material, 'roughnessMap', maps.roughnessMap, mapProperties);
159
278
  queueTextureAssignment(material, 'metalnessMap', maps.metalnessMap, mapProperties);
@@ -168,7 +287,7 @@ function applyMaterialPresetTextures(material, preset) {
168
287
  export function applyMaterialPresetToMaterials(materialInput, preset) {
169
288
  if (!preset)
170
289
  return;
171
- const materials = (Array.isArray(materialInput) ? materialInput : [materialInput]).filter(isStandardMaterial);
290
+ const materials = (Array.isArray(materialInput) ? materialInput : [materialInput]).filter(isCommonMaterial);
172
291
  if (materials.length === 0)
173
292
  return;
174
293
  for (const material of materials) {
@@ -176,63 +295,150 @@ export function applyMaterialPresetToMaterials(materialInput, preset) {
176
295
  applyMaterialPresetTextures(material, preset);
177
296
  }
178
297
  }
179
- export function createMaterialFromPreset(preset) {
180
- const cacheKey = JSON.stringify(preset);
298
+ export function createMaterialFromPreset(preset, shading = 'rendered') {
299
+ const cacheKey = `${shading}-${JSON.stringify(preset)}`;
181
300
  if (materialCache.has(cacheKey)) {
182
301
  return materialCache.get(cacheKey);
183
302
  }
184
- const material = new THREE.MeshStandardMaterial();
303
+ const material = shading === 'solid' ? new MeshLambertNodeMaterial() : new MeshStandardNodeMaterial();
185
304
  applyMaterialPresetToMaterials(material, preset);
186
305
  materialCache.set(cacheKey, material);
187
306
  return material;
188
307
  }
189
- export function createMaterialFromPresetRef(materialPreset) {
308
+ export function createMaterialFromPresetRef(materialPreset, shading = 'rendered') {
190
309
  const preset = getMaterialPresetByRef(materialPreset);
191
310
  if (!preset)
192
311
  return null;
193
- return createMaterialFromPreset(preset);
312
+ return createMaterialFromPreset(preset, shading);
194
313
  }
195
- export function createMaterial(material) {
314
+ export function createMaterial(material, shading = 'rendered') {
196
315
  const props = resolveMaterial(material);
197
- const cacheKey = `${getCacheKey(props)}-${getTextureKey(material)}`;
316
+ const cacheKey = `${getCacheKey(props, shading)}-${getTextureKey(material)}`;
198
317
  if (materialCache.has(cacheKey)) {
199
318
  return materialCache.get(cacheKey);
200
319
  }
201
320
  const map = getTexture(material);
202
- const threeMaterial = new THREE.MeshStandardMaterial({
321
+ const materialParams = {
203
322
  color: props.color,
204
- roughness: props.roughness,
205
- metalness: props.metalness,
206
323
  opacity: props.opacity,
207
324
  transparent: props.transparent,
208
325
  side: sideMap[props.side],
209
- map,
210
- });
326
+ };
327
+ if (map)
328
+ materialParams.map = map;
329
+ const threeMaterial = shading === 'solid'
330
+ ? new MeshLambertNodeMaterial(materialParams)
331
+ : new MeshStandardNodeMaterial({
332
+ ...materialParams,
333
+ roughness: props.roughness,
334
+ metalness: props.metalness,
335
+ });
211
336
  materialCache.set(cacheKey, threeMaterial);
212
337
  return threeMaterial;
213
338
  }
214
- export function createDefaultMaterial(color = '#ffffff', roughness = 0.9) {
215
- return new THREE.MeshStandardMaterial({
339
+ export function createDefaultMaterial(color = '#ffffff', roughness = 0.9, shading = 'rendered', side = THREE.FrontSide) {
340
+ if (shading === 'solid') {
341
+ return new MeshLambertNodeMaterial({
342
+ color,
343
+ side,
344
+ });
345
+ }
346
+ return new MeshStandardNodeMaterial({
216
347
  color,
217
348
  roughness,
218
349
  metalness: 0,
219
- side: THREE.FrontSide,
350
+ side,
220
351
  });
221
352
  }
222
- export const DEFAULT_WALL_MATERIAL = createDefaultMaterial('#ffffff', 0.9);
223
- export const DEFAULT_SLAB_MATERIAL = createDefaultMaterial('#e5e5e5', 0.8);
224
- export const DEFAULT_DOOR_MATERIAL = createDefaultMaterial('#8b4513', 0.7);
225
- export const DEFAULT_WINDOW_MATERIAL = new THREE.MeshStandardMaterial({
226
- color: '#87ceeb',
227
- roughness: 0.1,
228
- metalness: 0.1,
229
- opacity: 0.3,
230
- transparent: true,
231
- side: THREE.DoubleSide,
232
- });
233
- export const DEFAULT_CEILING_MATERIAL = createDefaultMaterial('#f5f5dc', 0.95);
234
- export const DEFAULT_ROOF_MATERIAL = createDefaultMaterial('#808080', 0.85);
235
- export const DEFAULT_STAIR_MATERIAL = createDefaultMaterial('#ffffff', 0.9);
353
+ function cachedDefaultMaterial(key, color, roughness, shading, side = THREE.FrontSide) {
354
+ const cacheKey = `${key}-${shading}`;
355
+ const cached = defaultMaterialCache.get(cacheKey);
356
+ if (cached)
357
+ return cached;
358
+ const material = createDefaultMaterial(color, roughness, shading, side);
359
+ defaultMaterialCache.set(cacheKey, material);
360
+ return material;
361
+ }
362
+ export function createSurfaceRoleMaterial(role, preset, side = THREE.FrontSide, sceneThemeId) {
363
+ // DoubleSide on glazing trips the MRT back-face pipeline issue documented
364
+ // on `glassMaterial` above — the validator rejects the back-face variant
365
+ // for missing MRT outputs and poisons the render context (manifests as
366
+ // "Color target has no corresponding fragment stage output" on scene
367
+ // open, since the dormer's window-assembly mounts the glazing material
368
+ // on both gable faces on the first frame). Callers that need both sides
369
+ // visible (e.g. dormer back gable) must rotate the host mesh 180° so the
370
+ // FrontSide faces the viewer.
371
+ const resolvedSide = role === 'glazing' ? THREE.FrontSide : side;
372
+ const cacheKey = `${role}-${preset}-${resolvedSide}-${sceneThemeId ?? 'base'}`;
373
+ const cached = surfaceRoleMaterialCache.get(cacheKey);
374
+ if (cached)
375
+ return cached;
376
+ const material = role === 'glazing'
377
+ ? new MeshLambertNodeMaterial({
378
+ color: resolveSurfaceColor(role, preset, sceneThemeId),
379
+ depthWrite: false,
380
+ opacity: 0.25,
381
+ side: resolvedSide,
382
+ transparent: true,
383
+ })
384
+ : new MeshLambertNodeMaterial({
385
+ color: resolveSurfaceColor(role, preset, sceneThemeId),
386
+ side: resolvedSide,
387
+ });
388
+ material.userData.__pascalCachedMaterial = true;
389
+ surfaceRoleMaterialCache.set(cacheKey, material);
390
+ return material;
391
+ }
392
+ export function baseMaterial(shading = 'rendered') {
393
+ return cachedDefaultMaterial('base', '#f2f0ed', 0.5, shading);
394
+ }
395
+ export function DEFAULT_WALL_MATERIAL(shading = 'rendered') {
396
+ return cachedDefaultMaterial('wall', '#ffffff', 0.9, shading);
397
+ }
398
+ export function DEFAULT_SLAB_MATERIAL(shading = 'rendered') {
399
+ return cachedDefaultMaterial('slab', '#e5e5e5', 0.8, shading);
400
+ }
401
+ export function DEFAULT_DOOR_MATERIAL(shading = 'rendered') {
402
+ return cachedDefaultMaterial('door', '#8b4513', 0.7, shading);
403
+ }
404
+ export function DEFAULT_WINDOW_MATERIAL(shading = 'rendered') {
405
+ const cacheKey = `window-${shading}`;
406
+ const cached = defaultMaterialCache.get(cacheKey);
407
+ if (cached)
408
+ return cached;
409
+ // DoubleSide on a NodeMaterial inside the MRT scene pass compiles a back-face
410
+ // pipeline variant whose fragment outputs don't cover every MRT target — the
411
+ // validator rejects it and poisons the render context (see the note above
412
+ // `glassMaterial`). FrontSide; flip the consumer's back-face group 180° if a
413
+ // back face is actually visible.
414
+ const params = {
415
+ color: '#87ceeb',
416
+ opacity: 0.3,
417
+ transparent: true,
418
+ side: THREE.FrontSide,
419
+ };
420
+ const material = shading === 'solid'
421
+ ? new MeshLambertNodeMaterial(params)
422
+ : new MeshStandardNodeMaterial({
423
+ ...params,
424
+ roughness: 0.1,
425
+ metalness: 0.1,
426
+ });
427
+ defaultMaterialCache.set(cacheKey, material);
428
+ return material;
429
+ }
430
+ export function DEFAULT_CEILING_MATERIAL(shading = 'rendered') {
431
+ return cachedDefaultMaterial('ceiling', '#f5f5dc', 0.95, shading);
432
+ }
433
+ export function DEFAULT_ROOF_MATERIAL(shading = 'rendered') {
434
+ return cachedDefaultMaterial('roof', '#808080', 0.85, shading);
435
+ }
436
+ export function DEFAULT_SHELF_MATERIAL(shading = 'rendered') {
437
+ return cachedDefaultMaterial('shelf', '#ffffff', 0.9, shading);
438
+ }
439
+ export function DEFAULT_STAIR_MATERIAL(shading = 'rendered') {
440
+ return cachedDefaultMaterial('stair', '#ffffff', 0.9, shading);
441
+ }
236
442
  export function disposeMaterial(material) {
237
443
  material.dispose();
238
444
  }
@@ -241,6 +447,14 @@ export function clearMaterialCache() {
241
447
  material.dispose();
242
448
  }
243
449
  materialCache.clear();
450
+ for (const material of defaultMaterialCache.values()) {
451
+ material.dispose();
452
+ }
453
+ defaultMaterialCache.clear();
454
+ for (const material of surfaceRoleMaterialCache.values()) {
455
+ material.dispose();
456
+ }
457
+ surfaceRoleMaterialCache.clear();
244
458
  for (const texture of textureCache.values()) {
245
459
  texture.dispose();
246
460
  }
@@ -1 +1 @@
1
- {"version":3,"file":"merged-outline-node.d.ts","sourceRoot":"","sources":["../../src/lib/merged-outline-node.ts"],"names":[],"mappings":"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"}
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);
@@ -460,17 +467,10 @@ export class MergedOutlineNode extends TempNode {
460
467
  }
461
468
  _buildCache(objects, cache) {
462
469
  for (const obj of objects) {
463
- if (!obj || !obj.traverse)
464
- continue;
465
- try {
466
- obj.traverse((child) => {
467
- if (child.isMesh || child.isSprite)
468
- cache.add(child);
469
- });
470
- }
471
- catch {
472
- // Skip objects that were disposed or removed from the scene graph
473
- }
470
+ obj.traverse((child) => {
471
+ if (child.isMesh || child.isSprite)
472
+ cache.add(child);
473
+ });
474
474
  }
475
475
  }
476
476
  }
@@ -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"}