@pascal-app/viewer 1.0.3 → 1.0.4

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 (164) hide show
  1. package/dist/components/renderers/node-renderer.d.ts.map +1 -1
  2. package/dist/components/renderers/node-renderer.js +8 -3
  3. package/dist/components/viewer/frame-limiter.d.ts +1 -0
  4. package/dist/components/viewer/frame-limiter.d.ts.map +1 -1
  5. package/dist/components/viewer/frame-limiter.js +22 -3
  6. package/dist/components/viewer/glb-interactive.d.ts +25 -1
  7. package/dist/components/viewer/glb-interactive.d.ts.map +1 -1
  8. package/dist/components/viewer/glb-interactive.js +252 -85
  9. package/dist/components/viewer/glb-mechanisms.d.ts +10 -0
  10. package/dist/components/viewer/glb-mechanisms.d.ts.map +1 -0
  11. package/dist/components/viewer/glb-mechanisms.js +19 -0
  12. package/dist/components/viewer/glb-scene.d.ts +45 -27
  13. package/dist/components/viewer/glb-scene.d.ts.map +1 -1
  14. package/dist/components/viewer/glb-scene.js +420 -506
  15. package/dist/components/viewer/glb-walkthrough-controller.d.ts +1 -1
  16. package/dist/components/viewer/glb-walkthrough-controller.d.ts.map +1 -1
  17. package/dist/components/viewer/glb-walkthrough-controller.js +12 -1
  18. package/dist/components/viewer/index.d.ts +14 -0
  19. package/dist/components/viewer/index.d.ts.map +1 -1
  20. package/dist/components/viewer/index.js +84 -26
  21. package/dist/components/viewer/lights.d.ts.map +1 -1
  22. package/dist/components/viewer/lights.js +6 -1
  23. package/dist/components/viewer/perf-monitor.d.ts.map +1 -1
  24. package/dist/components/viewer/perf-monitor.js +6 -0
  25. package/dist/components/viewer/post-processing.d.ts.map +1 -1
  26. package/dist/components/viewer/post-processing.js +8 -3
  27. package/dist/components/viewer/registered-systems.d.ts.map +1 -1
  28. package/dist/components/viewer/registered-systems.js +16 -2
  29. package/dist/components/viewer/render-error.d.ts +18 -0
  30. package/dist/components/viewer/render-error.d.ts.map +1 -0
  31. package/dist/components/viewer/render-error.js +29 -0
  32. package/dist/components/viewer/scene-ground-replacement.d.ts +6 -0
  33. package/dist/components/viewer/scene-ground-replacement.d.ts.map +1 -1
  34. package/dist/components/viewer/scene-ground-replacement.js +15 -0
  35. package/dist/components/viewer/selection-manager.d.ts.map +1 -1
  36. package/dist/components/viewer/selection-manager.js +10 -0
  37. package/dist/components/viewer/viewer-camera.d.ts +4 -0
  38. package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
  39. package/dist/components/viewer/viewer-camera.js +11 -1
  40. package/dist/index.d.ts +22 -11
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +20 -14
  43. package/dist/lib/asset-url.d.ts.map +1 -1
  44. package/dist/lib/asset-url.js +7 -1
  45. package/dist/lib/display-state.d.ts +25 -0
  46. package/dist/lib/display-state.d.ts.map +1 -0
  47. package/dist/lib/display-state.js +1 -0
  48. package/dist/lib/hero-pose.d.ts +12 -2
  49. package/dist/lib/hero-pose.d.ts.map +1 -1
  50. package/dist/lib/hero-pose.js +72 -5
  51. package/dist/lib/ink-edges.d.ts +4 -2
  52. package/dist/lib/ink-edges.d.ts.map +1 -1
  53. package/dist/lib/ink-edges.js +17 -7
  54. package/dist/lib/key-light-override.d.ts +11 -0
  55. package/dist/lib/key-light-override.d.ts.map +1 -0
  56. package/dist/lib/key-light-override.js +14 -0
  57. package/dist/lib/live-frame-hold.d.ts +14 -0
  58. package/dist/lib/live-frame-hold.d.ts.map +1 -0
  59. package/dist/lib/live-frame-hold.js +28 -0
  60. package/dist/lib/materials.d.ts +14 -7
  61. package/dist/lib/materials.d.ts.map +1 -1
  62. package/dist/lib/materials.js +31 -5
  63. package/dist/lib/node-top-surface-height.d.ts +6 -0
  64. package/dist/lib/node-top-surface-height.d.ts.map +1 -0
  65. package/dist/lib/node-top-surface-height.js +74 -0
  66. package/dist/lib/perf-probe-state.d.ts +39 -0
  67. package/dist/lib/perf-probe-state.d.ts.map +1 -0
  68. package/dist/lib/perf-probe-state.js +17 -0
  69. package/dist/lib/pointer-events.d.ts +8 -0
  70. package/dist/lib/pointer-events.d.ts.map +1 -1
  71. package/dist/lib/pointer-events.js +27 -4
  72. package/dist/lib/procedural-emission.d.ts +7 -0
  73. package/dist/lib/procedural-emission.d.ts.map +1 -0
  74. package/dist/lib/procedural-emission.js +54 -0
  75. package/dist/lib/scene-support-height.d.ts +4 -0
  76. package/dist/lib/scene-support-height.d.ts.map +1 -0
  77. package/dist/lib/scene-support-height.js +34 -0
  78. package/dist/lib/scene-visibility.d.ts +6 -0
  79. package/dist/lib/scene-visibility.d.ts.map +1 -1
  80. package/dist/lib/scene-visibility.js +15 -3
  81. package/dist/lib/scripted-opening.d.ts +11 -0
  82. package/dist/lib/scripted-opening.d.ts.map +1 -0
  83. package/dist/lib/scripted-opening.js +20 -0
  84. package/dist/lib/snapshot-pipeline.d.ts +32 -2
  85. package/dist/lib/snapshot-pipeline.d.ts.map +1 -1
  86. package/dist/lib/snapshot-pipeline.js +215 -105
  87. package/dist/lib/terrain-perimeter-fill.d.ts.map +1 -1
  88. package/dist/lib/terrain-perimeter-fill.js +22 -14
  89. package/dist/lib/zone-geometry.d.ts +11 -0
  90. package/dist/lib/zone-geometry.d.ts.map +1 -0
  91. package/dist/lib/zone-geometry.js +46 -0
  92. package/dist/store/use-item-light-pool.d.ts +14 -10
  93. package/dist/store/use-item-light-pool.d.ts.map +1 -1
  94. package/dist/store/use-item-light-pool.js +52 -26
  95. package/dist/store/use-viewer.d.ts +11 -0
  96. package/dist/store/use-viewer.d.ts.map +1 -1
  97. package/dist/store/use-viewer.js +13 -6
  98. package/dist/systems/ceiling/ceiling-system.d.ts +15 -1
  99. package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
  100. package/dist/systems/ceiling/ceiling-system.js +86 -6
  101. package/dist/systems/door/door-system.d.ts.map +1 -1
  102. package/dist/systems/door/door-system.js +55 -17
  103. package/dist/systems/floor-elevation/floor-elevation-system.d.ts +3 -1
  104. package/dist/systems/floor-elevation/floor-elevation-system.d.ts.map +1 -1
  105. package/dist/systems/floor-elevation/floor-elevation-system.js +33 -5
  106. package/dist/systems/geometry/geometry-system.d.ts.map +1 -1
  107. package/dist/systems/geometry/geometry-system.js +71 -11
  108. package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
  109. package/dist/systems/interactive/interactive-system.js +40 -15
  110. package/dist/systems/interactive/procedural-controls.d.ts +13 -0
  111. package/dist/systems/interactive/procedural-controls.d.ts.map +1 -0
  112. package/dist/systems/interactive/procedural-controls.js +17 -0
  113. package/dist/systems/interactive/scripted-clips.d.ts +19 -0
  114. package/dist/systems/interactive/scripted-clips.d.ts.map +1 -0
  115. package/dist/systems/interactive/scripted-clips.js +134 -0
  116. package/dist/systems/item/item-system.d.ts.map +1 -1
  117. package/dist/systems/item/item-system.js +9 -3
  118. package/dist/systems/item/object-cut-invalidation.d.ts +3 -0
  119. package/dist/systems/item/object-cut-invalidation.d.ts.map +1 -0
  120. package/dist/systems/item/object-cut-invalidation.js +68 -0
  121. package/dist/systems/item-light/item-light-system.d.ts +1 -1
  122. package/dist/systems/item-light/item-light-system.d.ts.map +1 -1
  123. package/dist/systems/item-light/item-light-system.js +47 -52
  124. package/dist/systems/level/level-system.d.ts.map +1 -1
  125. package/dist/systems/level/level-system.js +3 -5
  126. package/dist/systems/level/level-utils.d.ts +7 -4
  127. package/dist/systems/level/level-utils.d.ts.map +1 -1
  128. package/dist/systems/level/level-utils.js +17 -6
  129. package/dist/systems/level/shadow-only-levels.d.ts +9 -0
  130. package/dist/systems/level/shadow-only-levels.d.ts.map +1 -0
  131. package/dist/systems/level/shadow-only-levels.js +7 -0
  132. package/dist/systems/roof/roof-materials.d.ts +14 -2
  133. package/dist/systems/roof/roof-materials.d.ts.map +1 -1
  134. package/dist/systems/roof/roof-materials.js +54 -15
  135. package/dist/systems/roof/roof-system.d.ts +13 -3
  136. package/dist/systems/roof/roof-system.d.ts.map +1 -1
  137. package/dist/systems/roof/roof-system.js +188 -13
  138. package/dist/systems/slab/slab-system.d.ts +1 -1
  139. package/dist/systems/slab/slab-system.d.ts.map +1 -1
  140. package/dist/systems/slab/slab-system.js +49 -19
  141. package/dist/systems/stair/stair-system.d.ts.map +1 -1
  142. package/dist/systems/stair/stair-system.js +133 -354
  143. package/dist/systems/wall/wall-cutout-cache.d.ts +7 -0
  144. package/dist/systems/wall/wall-cutout-cache.d.ts.map +1 -1
  145. package/dist/systems/wall/wall-cutout-cache.js +46 -17
  146. package/dist/systems/wall/wall-cutout.d.ts +6 -0
  147. package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
  148. package/dist/systems/wall/wall-cutout.js +42 -28
  149. package/dist/systems/wall/wall-finish-data.d.ts +22 -0
  150. package/dist/systems/wall/wall-finish-data.d.ts.map +1 -0
  151. package/dist/systems/wall/wall-finish-data.js +17 -0
  152. package/dist/systems/wall/wall-materials.d.ts +9 -4
  153. package/dist/systems/wall/wall-materials.d.ts.map +1 -1
  154. package/dist/systems/wall/wall-materials.js +99 -152
  155. package/dist/systems/wall/wall-system.d.ts +28 -2
  156. package/dist/systems/wall/wall-system.d.ts.map +1 -1
  157. package/dist/systems/wall/wall-system.js +582 -143
  158. package/dist/systems/window/window-system.d.ts +1 -1
  159. package/dist/systems/window/window-system.d.ts.map +1 -1
  160. package/dist/systems/window/window-system.js +41 -22
  161. package/package.json +5 -5
  162. package/dist/systems/fence/fence-system.d.ts +0 -22
  163. package/dist/systems/fence/fence-system.d.ts.map +0 -1
  164. package/dist/systems/fence/fence-system.js +0 -357
@@ -1,9 +1,11 @@
1
1
  // New kind-specific modules belong in nodes; viewer must not depend on nodes.
2
2
  // This extends the existing viewer-owned wall cutout and material implementation.
3
- import { getEffectiveNode, getLibraryMaterialsVersion, getWallFaceBandConfig, getWallPlaneTop, resolveLevelId, resolveWallEffectiveHeight, sceneRegistry, spatialGridManager, useLiveNodeOverrides, useLiveTransforms, useScene, } from '@pascal-app/core';
3
+ import { getEffectiveNode, getLibraryMaterialsVersion, sceneRegistry, useLiveNodeOverrides, useLiveTransforms, useScene, } from '@pascal-app/core';
4
4
  import { Matrix4, Vector3 } from 'three';
5
5
  import { getMaterialTextureVersion } from '../../lib/materials';
6
+ import { hasMaterialsForGroups } from '../../lib/pointer-events';
6
7
  import useViewer, {} from '../../store/use-viewer';
8
+ import { getWallFinishRefs } from './wall-finish-data';
7
9
  import { resolveWallMaterialVariant } from './wall-material-variant';
8
10
  import { getHoverHighlightMaterials, getMaterialsForWall, getSelectionHighlightMaterials, } from './wall-materials';
9
11
  export function sameMaterialArray(a, b) {
@@ -38,6 +40,9 @@ function materialsForVariant(variant, materials) {
38
40
  }
39
41
  }
40
42
  }
43
+ function wallPaletteKey(mesh) {
44
+ return getWallFinishRefs(mesh.geometry).join('|');
45
+ }
41
46
  // About 0.06 degrees: retain the previous side at edge-on poses without
42
47
  // introducing a perceptible delay when an orbit crosses a wall's plane.
43
48
  export const WALL_FACING_HYSTERESIS = 0.001;
@@ -131,7 +136,7 @@ export class WallCutoutCache {
131
136
  const previewId = (state) => state && state.hoverHighlightMode !== 'default' && state.hoverHighlightMode !== 'delete'
132
137
  ? state.hoveredId
133
138
  : null;
134
- const releasedPreview = previewId(previous) !== previewId(viewer) ? previewId(previous) : null;
139
+ const releasedPreview = previewId(previous) === previewId(viewer) ? null : previewId(previous);
135
140
  this.viewer = viewer;
136
141
  const invalidated = overridesChanged ||
137
142
  appearanceChanged ||
@@ -140,15 +145,16 @@ export class WallCutoutCache {
140
145
  this.rebuilt.size > 0 ||
141
146
  this.transformed.size > 0 ||
142
147
  releasedPreview !== null;
143
- if (!invalidated && viewer.wallMode !== 'cutaway')
148
+ const cutaway = viewer.wallMode === 'cutaway';
149
+ if (!invalidated && !cutaway)
144
150
  return;
145
151
  camera.getWorldDirection(this.cameraDirection);
146
152
  this.cameraTarget.copy(this.cameraDirection).add(camera.position);
147
- const cameraChanged = viewer.wallMode === 'cutaway' &&
153
+ const cameraChanged = cutaway &&
148
154
  time - this.lastUpdateTime > 0.1 &&
149
155
  (camera.position.distanceTo(this.lastCameraPosition) > 0.5 ||
150
156
  this.cameraTarget.distanceTo(this.lastCameraTarget) > 0.3);
151
- if (!invalidated && !cameraChanged)
157
+ if (!(invalidated || cameraChanged))
152
158
  return;
153
159
  if (registryChanged || nodesChanged) {
154
160
  for (const [id, wall] of this.walls) {
@@ -194,6 +200,7 @@ export class WallCutoutCache {
194
200
  assignedMaterials: undefined,
195
201
  visibleVariant: { key: 'visible', materials: [] },
196
202
  hiddenVariant: { key: 'invisible', materials: [] },
203
+ paletteKey: '',
197
204
  };
198
205
  this.walls.set(id, wall);
199
206
  }
@@ -203,7 +210,9 @@ export class WallCutoutCache {
203
210
  this.refreshNormal(wall, visited);
204
211
  const overrideChanged = this.overrides.get(id) !== overrides.get(id);
205
212
  wall.node = getEffectiveNode(node);
206
- if (added || appearanceChanged || overrideChanged || (nodesChanged && changed))
213
+ const repaletted = rebuilt && wallPaletteKey(wall.mesh) !== wall.paletteKey;
214
+ const refresh = added || appearanceChanged || overrideChanged || (nodesChanged && changed) || repaletted;
215
+ if (refresh)
207
216
  this.refreshAppearance(wall);
208
217
  if (added ||
209
218
  appearanceChanged ||
@@ -212,7 +221,7 @@ export class WallCutoutCache {
212
221
  rebuilt ||
213
222
  releasedPreview === id ||
214
223
  cameraChanged) {
215
- this.apply(wall, viewer.wallMode, added || appearanceChanged || overrideChanged || (nodesChanged && changed));
224
+ this.apply(wall, viewer.wallMode, refresh);
216
225
  }
217
226
  }
218
227
  }
@@ -235,19 +244,35 @@ export class WallCutoutCache {
235
244
  this.lastUpdateTime = time;
236
245
  }
237
246
  }
247
+ /**
248
+ * A rebuilt wall whose finish palette changed must swap materials in the same
249
+ * frame: its new groups already index the new palette.
250
+ */
251
+ handleRebuilt(id) {
252
+ this.rebuilt.add(id);
253
+ const wall = this.walls.get(id);
254
+ const viewer = this.viewer;
255
+ if (!(wall && viewer) || wall.mesh !== sceneRegistry.nodes.get(id))
256
+ return;
257
+ if (wallPaletteKey(wall.mesh) === wall.paletteKey)
258
+ return;
259
+ const node = useScene.getState().nodes[id];
260
+ if (node?.type !== 'wall')
261
+ return;
262
+ wall.node = getEffectiveNode(node);
263
+ this.refreshAppearance(wall);
264
+ this.apply(wall, viewer.wallMode, true);
265
+ }
238
266
  refreshAppearance(wall) {
239
267
  const viewer = this.viewer;
240
268
  const scene = useScene.getState();
241
269
  const node = wall.node;
242
270
  const deleted = viewer.hoverHighlightMode === 'delete' && viewer.hoveredId === node.id;
243
- let selectionHighlighted = !deleted && this.selected.has(node.id);
244
- if (selectionHighlighted) {
245
- const levelId = resolveLevelId(node, scene.nodes);
246
- const support = spatialGridManager.getSlabSupportForWall(levelId, node.start, node.end, node.curveOffset ?? 0, node.thickness, node.supportSlabId);
247
- const height = resolveWallEffectiveHeight(node, getWallPlaneTop(node, levelId, scene.nodes), support.elevation);
248
- selectionHighlighted = !getWallFaceBandConfig(node, height).enabled;
249
- }
250
- const materials = this.materialResolver(node, viewer.shading, viewer.textures, viewer.colorPreset, viewer.sceneTheme, scene.materials);
271
+ // Painted parts stay readable while their wall is selected for editing.
272
+ const selectionHighlighted = !deleted && this.selected.has(node.id) && !(node.faceRegions?.length ?? 0);
273
+ const finishRefs = getWallFinishRefs(wall.mesh.geometry);
274
+ wall.paletteKey = finishRefs.join('|');
275
+ const materials = this.materialResolver(node, viewer.shading, viewer.textures, viewer.colorPreset, viewer.sceneTheme, scene.materials, finishRefs);
251
276
  const variant = (hidden) => {
252
277
  const key = resolveWallMaterialVariant({
253
278
  translucentMode: viewer.wallMode === 'translucent',
@@ -289,11 +314,15 @@ export class WallCutoutCache {
289
314
  if (wall.mesh.userData.wallHidden !== stamp)
290
315
  wall.mesh.userData.wallHidden = stamp;
291
316
  const variant = hidden ? wall.hiddenVariant : wall.visibleVariant;
292
- // Non-highlight hover owns a temporary material until its restore callback runs.
317
+ // Non-highlight hover (a paint preview) owns a temporary material until its
318
+ // restore callback runs — unless the rebuilt geometry now draws a finish that
319
+ // array has no entry for (a click just added one): then the new palette goes
320
+ // on and the preview is dropped, rather than leaving groups with no material.
293
321
  const viewer = this.viewer;
294
322
  if (viewer.hoveredId === wall.node.id &&
295
323
  viewer.hoverHighlightMode !== 'default' &&
296
- viewer.hoverHighlightMode !== 'delete') {
324
+ viewer.hoverHighlightMode !== 'delete' &&
325
+ hasMaterialsForGroups(wall.mesh)) {
297
326
  if (refresh)
298
327
  wall.variantKey = undefined;
299
328
  return;
@@ -20,6 +20,12 @@ export declare function runWallCutoutFrame(cache: WallCutoutCache, { camera, clo
20
20
  * from the assigned material variant.
21
21
  */
22
22
  export declare function getWallHideState(wallNode: WallNode, wallMesh: Mesh, wallMode: WallMode, cameraDir: Vector3): boolean;
23
+ /**
24
+ * The walls as a capture draws them, until the returned function puts them back: a delete or a
25
+ * selection highlight taken off and, with `wholeWalls`, every wall whole whatever the wall mode.
26
+ * The palettes come from the renderer's own resolver, so a curtain wall keeps its glass.
27
+ */
28
+ export declare function wallsForCapture(materialResolver: WallMaterialsResolver, viewerStore: WallCutoutViewerStore, wholeWalls: boolean): () => void;
23
29
  export declare const WallCutout: ({ viewerStore, materialResolver, }: {
24
30
  viewerStore?: WallCutoutViewerStore;
25
31
  materialResolver?: WallMaterialsResolver;
@@ -1 +1 @@
1
- {"version":3,"file":"wall-cutout.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-cutout.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAoD,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAGlG,OAAO,KAAK,EAAE,MAAM,EAAY,MAAM,OAAO,CAAA;AAC7C,OAAO,EAAE,KAAK,IAAI,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACjD,OAAkB,EAAE,KAAK,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AACjE,OAAO,EAEL,eAAe,EACf,KAAK,qBAAqB,EAE3B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,kBAAkB,CAAA;AAKzB,eAAO,MAAM,0BAA0B,IAAI,CAAA;AAE3C,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,eAAe,EACtB,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,QAGtE;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,IAAI,EACd,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,OAAO,GACjB,OAAO,CAKT;AAED,eAAO,MAAM,UAAU,GAAI,oCAGxB;IACD,WAAW,CAAC,EAAE,qBAAqB,CAAA;IACnC,gBAAgB,CAAC,EAAE,qBAAqB,CAAA;CACzC,SA8DA,CAAA"}
1
+ {"version":3,"file":"wall-cutout.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-cutout.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAoD,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAGlG,OAAO,KAAK,EAAE,MAAM,EAAY,MAAM,OAAO,CAAA;AAC7C,OAAO,EAAE,KAAK,IAAI,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACjD,OAAkB,EAAE,KAAK,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AACjE,OAAO,EAEL,eAAe,EACf,KAAK,qBAAqB,EAE3B,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,kBAAkB,CAAA;AAKzB,eAAO,MAAM,0BAA0B,IAAI,CAAA;AAE3C,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,eAAe,EACtB,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,QAGtE;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,IAAI,EACd,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,OAAO,GACjB,OAAO,CAKT;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,gBAAgB,EAAE,qBAAqB,EACvC,WAAW,EAAE,qBAAqB,EAClC,UAAU,EAAE,OAAO,GAClB,MAAM,IAAI,CAmCZ;AAED,eAAO,MAAM,UAAU,GAAI,oCAGxB;IACD,WAAW,CAAC,EAAE,qBAAqB,CAAA;IACnC,gBAAgB,CAAC,EAAE,qBAAqB,CAAA;CACzC,SAiCA,CAAA"}
@@ -4,6 +4,7 @@ import { useEffect, useMemo } from 'react';
4
4
  import { Vector3 } from 'three/webgpu';
5
5
  import useViewer, {} from '../../store/use-viewer';
6
6
  import { sameMaterialArray, WallCutoutCache, wallHiddenFromFacing, } from './wall-cutout-cache';
7
+ import { getWallFinishRefs } from './wall-finish-data';
7
8
  import { getMaterialsForWall, getSelectionHighlightMaterials, } from './wall-materials';
8
9
  import { subscribeWallRebuilds } from './wall-rebuild-notifications';
9
10
  const v = new Vector3();
@@ -27,43 +28,56 @@ export function getWallHideState(wallNode, wallMesh, wallMode, cameraDir) {
27
28
  wallMesh.getWorldDirection(v);
28
29
  return wallHiddenFromFacing(wallNode, wallMode, v.dot(cameraDir) < 0);
29
30
  }
31
+ /**
32
+ * The walls as a capture draws them, until the returned function puts them back: a delete or a
33
+ * selection highlight taken off and, with `wholeWalls`, every wall whole whatever the wall mode.
34
+ * The palettes come from the renderer's own resolver, so a curtain wall keeps its glass.
35
+ */
36
+ export function wallsForCapture(materialResolver, viewerStore, wholeWalls) {
37
+ const snapshot = new Map();
38
+ const viewer = viewerStore.getState();
39
+ sceneRegistry.byType.wall.forEach((wallId) => {
40
+ const wallMesh = sceneRegistry.nodes.get(wallId);
41
+ if (!wallMesh)
42
+ return;
43
+ const wallNode = useScene.getState().nodes[wallId];
44
+ if (wallNode?.type !== 'wall')
45
+ return;
46
+ const mats = materialResolver(wallNode, viewer.shading, viewer.textures, viewer.colorPreset, viewer.sceneTheme, useScene.getState().materials, getWallFinishRefs(wallMesh.geometry));
47
+ const current = wallMesh.material;
48
+ snapshot.set(wallMesh, current);
49
+ if (wholeWalls || current === mats.deleteVisible) {
50
+ wallMesh.material = mats.visible;
51
+ }
52
+ else if (current === mats.deleteInvisible) {
53
+ wallMesh.material = mats.invisible;
54
+ }
55
+ else if (current === mats.deleteTranslucent ||
56
+ sameMaterialArray(current, getSelectionHighlightMaterials(mats.translucent))) {
57
+ wallMesh.material = mats.translucent;
58
+ }
59
+ });
60
+ return () => {
61
+ snapshot.forEach((material, mesh) => {
62
+ mesh.material = material;
63
+ });
64
+ };
65
+ }
30
66
  export const WallCutout = ({ viewerStore = useViewer, materialResolver = getMaterialsForWall, }) => {
31
67
  const cache = useMemo(() => new WallCutoutCache(viewerStore, materialResolver), [viewerStore, materialResolver]);
32
- useEffect(() => subscribeWallRebuilds((id) => cache.rebuilt.add(id)), [cache]);
68
+ useEffect(() => subscribeWallRebuilds((id) => cache.handleRebuilt(id)), [cache]);
33
69
  useEffect(() => cache.subscribeLiveTransforms(), [cache]);
34
70
  // Camera changes reach PostProcessing (1) in this frame. WallSystem (4)
35
71
  // notifies the next frame; WallBatchSystem (5) reads this frame's stamps.
36
72
  useFrame((state) => runWallCutoutFrame(cache, state), WALL_CUTOUT_FRAME_PRIORITY);
37
73
  useEffect(() => {
38
- const snapshot = new Map();
39
- const restoreForCapture = () => {
40
- sceneRegistry.byType.wall.forEach((wallId) => {
41
- const wallMesh = sceneRegistry.nodes.get(wallId);
42
- if (!wallMesh)
43
- return;
44
- const wallNode = useScene.getState().nodes[wallId];
45
- if (wallNode?.type !== 'wall')
46
- return;
47
- const mats = materialResolver(wallNode, viewerStore.getState().shading, viewerStore.getState().textures, viewerStore.getState().colorPreset, viewerStore.getState().sceneTheme, useScene.getState().materials);
48
- const current = wallMesh.material;
49
- snapshot.set(wallMesh, current);
50
- if (current === mats.deleteVisible) {
51
- wallMesh.material = mats.visible;
52
- }
53
- else if (current === mats.deleteInvisible) {
54
- wallMesh.material = mats.invisible;
55
- }
56
- else if (current === mats.deleteTranslucent ||
57
- sameMaterialArray(current, getSelectionHighlightMaterials(mats.translucent))) {
58
- wallMesh.material = mats.translucent;
59
- }
60
- });
74
+ let restore = null;
75
+ const restoreForCapture = (event) => {
76
+ restore = wallsForCapture(materialResolver, viewerStore, event?.wholeWalls === true);
61
77
  };
62
78
  const reapplyAfterCapture = () => {
63
- snapshot.forEach((mat, mesh) => {
64
- mesh.material = mat;
65
- });
66
- snapshot.clear();
79
+ restore?.();
80
+ restore = null;
67
81
  };
68
82
  emitter.on('thumbnail:before-capture', restoreForCapture);
69
83
  emitter.on('thumbnail:after-capture', reapplyAfterCapture);
@@ -0,0 +1,22 @@
1
+ import type { BufferGeometry } from 'three';
2
+ /** A run of one face's base, in wall-local x (m) and local y. */
3
+ export type WallFaceBaseRun = {
4
+ start: number;
5
+ end: number;
6
+ y: number;
7
+ };
8
+ /**
9
+ * What the wall's material array and the paint resolver need from a build: the
10
+ * finish refs behind material indices 3.. and each face's base runs (null when
11
+ * both faces stand on the wall's own base).
12
+ */
13
+ export type WallFinishGeometryData = {
14
+ refs: readonly string[];
15
+ faceBase: Record<'a' | 'b', WallFaceBaseRun[]> | null;
16
+ };
17
+ export declare function getWallFinishData(geometry: BufferGeometry | undefined | null): WallFinishGeometryData | undefined;
18
+ /** Finish refs a built wall geometry's material indices 3.. point at. */
19
+ export declare function getWallFinishRefs(geometry: BufferGeometry | undefined | null): readonly string[];
20
+ /** Local y of a face's base at wall-local station `x` (0 when the faces share the wall base). */
21
+ export declare function getWallFaceBaseAt(data: Pick<WallFinishGeometryData, 'faceBase'> | undefined, face: 'a' | 'b', x: number): number;
22
+ //# sourceMappingURL=wall-finish-data.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wall-finish-data.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-finish-data.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AAE3C,iEAAiE;AACjE,MAAM,MAAM,eAAe,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAEvE;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;IACvB,QAAQ,EAAE,MAAM,CAAC,GAAG,GAAG,GAAG,EAAE,eAAe,EAAE,CAAC,GAAG,IAAI,CAAA;CACtD,CAAA;AAED,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,cAAc,GAAG,SAAS,GAAG,IAAI,GAC1C,sBAAsB,GAAG,SAAS,CAEpC;AAED,yEAAyE;AACzE,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,cAAc,GAAG,SAAS,GAAG,IAAI,GAAG,SAAS,MAAM,EAAE,CAEhG;AAED,iGAAiG;AACjG,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,IAAI,CAAC,sBAAsB,EAAE,UAAU,CAAC,GAAG,SAAS,EAC1D,IAAI,EAAE,GAAG,GAAG,GAAG,EACf,CAAC,EAAE,MAAM,GACR,MAAM,CAKR"}
@@ -0,0 +1,17 @@
1
+ export function getWallFinishData(geometry) {
2
+ return geometry?.userData.wallFinish;
3
+ }
4
+ /** Finish refs a built wall geometry's material indices 3.. point at. */
5
+ export function getWallFinishRefs(geometry) {
6
+ return getWallFinishData(geometry)?.refs ?? [];
7
+ }
8
+ /** Local y of a face's base at wall-local station `x` (0 when the faces share the wall base). */
9
+ export function getWallFaceBaseAt(data, face, x) {
10
+ const runs = data?.faceBase?.[face];
11
+ if (!runs?.length)
12
+ return 0;
13
+ for (const run of runs)
14
+ if (x >= run.start && x < run.end)
15
+ return run.y;
16
+ return x < runs[0].start ? runs[0].y : runs.at(-1).y;
17
+ }
@@ -20,7 +20,7 @@ export type WallMaterialOverride = {
20
20
  owned?: Material[];
21
21
  };
22
22
  };
23
- export type WallMaterialsResolver = (wallNode: WallNode, shading?: RenderShading, textures?: boolean, colorPreset?: ColorPreset, sceneTheme?: string, sceneMaterials?: SceneMaterials) => WallMaterials;
23
+ export type WallMaterialsResolver = (wallNode: WallNode, shading?: RenderShading, textures?: boolean, colorPreset?: ColorPreset, sceneTheme?: string, sceneMaterials?: SceneMaterials, finishRefs?: readonly string[]) => WallMaterials;
24
24
  /** Lazy light-emissive selection variant of a wall's material array (keeps texture). */
25
25
  export declare function getSelectionHighlightMaterials(materials: WallMaterialArray): WallMaterialArray;
26
26
  /**
@@ -29,8 +29,13 @@ export declare function getSelectionHighlightMaterials(materials: WallMaterialAr
29
29
  * film so the wall the click would select reads under the cursor).
30
30
  */
31
31
  export declare function getHoverHighlightMaterials(materials: WallMaterialArray): WallMaterialArray;
32
- export declare function getWallMaterialHash(wallNode: WallNode, shading: RenderShading, sceneMaterials?: SceneMaterials, overrideHash?: string): string;
33
- export declare function getMaterialsForWall(wallNode: WallNode, shading?: RenderShading, textures?: boolean, colorPreset?: ColorPreset, sceneTheme?: string, sceneMaterials?: SceneMaterials, override?: WallMaterialOverride): WallMaterials;
34
- export declare function getVisibleWallMaterials(wallNode: WallNode, shading?: RenderShading, textures?: boolean, colorPreset?: ColorPreset, sceneTheme?: string, sceneMaterials?: SceneMaterials): WallMaterialArray;
32
+ export declare function getWallMaterialHash(wallNode: WallNode, shading: RenderShading, sceneMaterials?: SceneMaterials, finishRefs?: readonly string[], overrideHash?: string): string;
33
+ /**
34
+ * A wall's material palette: 0 caps/edges (themed role), 1 face a, 2 face b,
35
+ * then one entry per `finishRefs` — the region and room finishes the built
36
+ * geometry's groups point at (`getWallFinishRefs(mesh.geometry)`).
37
+ */
38
+ export declare function getMaterialsForWall(wallNode: WallNode, shading?: RenderShading, textures?: boolean, colorPreset?: ColorPreset, sceneTheme?: string, sceneMaterials?: SceneMaterials, finishRefs?: readonly string[], override?: WallMaterialOverride): WallMaterials;
39
+ export declare function getVisibleWallMaterials(wallNode: WallNode, shading?: RenderShading, textures?: boolean, colorPreset?: ColorPreset, sceneTheme?: string, sceneMaterials?: SceneMaterials, finishRefs?: readonly string[]): WallMaterialArray;
35
40
  export {};
36
41
  //# sourceMappingURL=wall-materials.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"wall-materials.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-materials.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,aAAa,EAClB,KAAK,eAAe,EAGpB,KAAK,QAAQ,EAId,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAS,KAAK,QAAQ,EAAE,MAAM,OAAO,CAAA;AAG5C,OAAO,EAEL,KAAK,WAAW,EAMhB,KAAK,aAAa,EAGnB,MAAM,qBAAqB,CAAA;AAE5B,KAAK,cAAc,GAAG,MAAM,CAAC,eAAe,EAAE,aAAa,CAAC,GAAG,SAAS,CAAA;AAexE,MAAM,MAAM,iBAAiB,GAAG,QAAQ,EAAE,CAAA;AAE1C,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,iBAAiB,CAAA;IAC1B,SAAS,EAAE,iBAAiB,CAAA;IAC5B,WAAW,EAAE,iBAAiB,CAAA;IAC9B,aAAa,EAAE,iBAAiB,CAAA;IAChC,eAAe,EAAE,iBAAiB,CAAA;IAClC,iBAAiB,EAAE,iBAAiB,CAAA;IACpC,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,QAAQ,EAAE,CAAA;CAC1B;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM;QAAE,OAAO,EAAE,iBAAiB,CAAC;QAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAA;KAAE,CAAA;CACjE,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG,CAClC,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE,aAAa,EACvB,QAAQ,CAAC,EAAE,OAAO,EAClB,WAAW,CAAC,EAAE,WAAW,EACzB,UAAU,CAAC,EAAE,MAAM,EACnB,cAAc,CAAC,EAAE,cAAc,KAC5B,aAAa,CAAA;AA+SlB,wFAAwF;AACxF,wBAAgB,8BAA8B,CAAC,SAAS,EAAE,iBAAiB,GAAG,iBAAiB,CAS9F;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,iBAAiB,GAAG,iBAAiB,CAS1F;AA4DD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,aAAa,EACtB,cAAc,CAAC,EAAE,cAAc,EAC/B,YAAY,CAAC,EAAE,MAAM,GACpB,MAAM,CAeR;AAED,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,QAAQ,EAClB,OAAO,GAAE,aAA0B,EACnC,QAAQ,UAAO,EACf,WAAW,GAAE,WAAoB,EACjC,UAAU,CAAC,EAAE,MAAM,EACnB,cAAc,CAAC,EAAE,cAAc,EAC/B,QAAQ,CAAC,EAAE,oBAAoB,GAC9B,aAAa,CAqIf;AAED,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,QAAQ,EAClB,OAAO,GAAE,aAA0B,EACnC,QAAQ,UAAO,EACf,WAAW,GAAE,WAAoB,EACjC,UAAU,CAAC,EAAE,MAAM,EACnB,cAAc,CAAC,EAAE,cAAc,GAC9B,iBAAiB,CAGnB"}
1
+ {"version":3,"file":"wall-materials.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-materials.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,KAAK,aAAa,EAClB,KAAK,eAAe,EAGpB,KAAK,QAAQ,EAGd,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAS,KAAK,QAAQ,EAAE,MAAM,OAAO,CAAA;AAG5C,OAAO,EAEL,KAAK,WAAW,EAMhB,KAAK,aAAa,EAInB,MAAM,qBAAqB,CAAA;AAE5B,KAAK,cAAc,GAAG,MAAM,CAAC,eAAe,EAAE,aAAa,CAAC,GAAG,SAAS,CAAA;AAexE,MAAM,MAAM,iBAAiB,GAAG,QAAQ,EAAE,CAAA;AAE1C,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,iBAAiB,CAAA;IAC1B,SAAS,EAAE,iBAAiB,CAAA;IAC5B,WAAW,EAAE,iBAAiB,CAAA;IAC9B,aAAa,EAAE,iBAAiB,CAAA;IAChC,eAAe,EAAE,iBAAiB,CAAA;IAClC,iBAAiB,EAAE,iBAAiB,CAAA;IACpC,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,QAAQ,EAAE,CAAA;CAC1B;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM;QAAE,OAAO,EAAE,iBAAiB,CAAC;QAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAA;KAAE,CAAA;CACjE,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG,CAClC,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE,aAAa,EACvB,QAAQ,CAAC,EAAE,OAAO,EAClB,WAAW,CAAC,EAAE,WAAW,EACzB,UAAU,CAAC,EAAE,MAAM,EACnB,cAAc,CAAC,EAAE,cAAc,EAC/B,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,KAC3B,aAAa,CAAA;AA2QlB,wFAAwF;AACxF,wBAAgB,8BAA8B,CAAC,SAAS,EAAE,iBAAiB,GAAG,iBAAiB,CAS9F;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,iBAAiB,GAAG,iBAAiB,CAS1F;AA4DD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,aAAa,EACtB,cAAc,CAAC,EAAE,cAAc,EAC/B,UAAU,GAAE,SAAS,MAAM,EAAO,EAClC,YAAY,CAAC,EAAE,MAAM,GACpB,MAAM,CAQR;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,QAAQ,EAClB,OAAO,GAAE,aAA0B,EACnC,QAAQ,UAAO,EACf,WAAW,GAAE,WAAoB,EACjC,UAAU,CAAC,EAAE,MAAM,EACnB,cAAc,CAAC,EAAE,cAAc,EAC/B,UAAU,GAAE,SAAS,MAAM,EAAO,EAClC,QAAQ,CAAC,EAAE,oBAAoB,GAC9B,aAAa,CAmFf;AAED,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,QAAQ,EAClB,OAAO,GAAE,aAA0B,EACnC,QAAQ,UAAO,EACf,WAAW,GAAE,WAAoB,EACjC,UAAU,CAAC,EAAE,MAAM,EACnB,cAAc,CAAC,EAAE,cAAc,EAC/B,UAAU,GAAE,SAAS,MAAM,EAAO,GACjC,iBAAiB,CAUnB"}