@pascal-app/viewer 1.0.1 → 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 (173) 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 +26 -0
  19. package/dist/components/viewer/index.d.ts.map +1 -1
  20. package/dist/components/viewer/index.js +108 -29
  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 +19 -1
  38. package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
  39. package/dist/components/viewer/viewer-camera.js +53 -5
  40. package/dist/index.d.ts +28 -15
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +26 -18
  43. package/dist/lib/asset-url.d.ts.map +1 -1
  44. package/dist/lib/asset-url.js +7 -1
  45. package/dist/lib/csg-utils.d.ts +1 -1
  46. package/dist/lib/csg-utils.d.ts.map +1 -1
  47. package/dist/lib/csg-utils.js +1 -1
  48. package/dist/lib/display-state.d.ts +25 -0
  49. package/dist/lib/display-state.d.ts.map +1 -0
  50. package/dist/lib/display-state.js +1 -0
  51. package/dist/lib/hero-pose.d.ts +12 -2
  52. package/dist/lib/hero-pose.d.ts.map +1 -1
  53. package/dist/lib/hero-pose.js +72 -5
  54. package/dist/lib/ink-edges.d.ts +4 -2
  55. package/dist/lib/ink-edges.d.ts.map +1 -1
  56. package/dist/lib/ink-edges.js +17 -7
  57. package/dist/lib/key-light-override.d.ts +11 -0
  58. package/dist/lib/key-light-override.d.ts.map +1 -0
  59. package/dist/lib/key-light-override.js +14 -0
  60. package/dist/lib/live-frame-hold.d.ts +14 -0
  61. package/dist/lib/live-frame-hold.d.ts.map +1 -0
  62. package/dist/lib/live-frame-hold.js +28 -0
  63. package/dist/lib/materials.d.ts +14 -7
  64. package/dist/lib/materials.d.ts.map +1 -1
  65. package/dist/lib/materials.js +31 -5
  66. package/dist/lib/node-top-surface-height.d.ts +6 -0
  67. package/dist/lib/node-top-surface-height.d.ts.map +1 -0
  68. package/dist/lib/node-top-surface-height.js +74 -0
  69. package/dist/lib/perf-probe-state.d.ts +39 -0
  70. package/dist/lib/perf-probe-state.d.ts.map +1 -0
  71. package/dist/lib/perf-probe-state.js +17 -0
  72. package/dist/lib/pointer-events.d.ts +8 -0
  73. package/dist/lib/pointer-events.d.ts.map +1 -1
  74. package/dist/lib/pointer-events.js +27 -4
  75. package/dist/lib/procedural-emission.d.ts +7 -0
  76. package/dist/lib/procedural-emission.d.ts.map +1 -0
  77. package/dist/lib/procedural-emission.js +54 -0
  78. package/dist/lib/renderer-capability.d.ts +2 -1
  79. package/dist/lib/renderer-capability.d.ts.map +1 -1
  80. package/dist/lib/renderer-capability.js +2 -2
  81. package/dist/lib/scene-support-height.d.ts +4 -0
  82. package/dist/lib/scene-support-height.d.ts.map +1 -0
  83. package/dist/lib/scene-support-height.js +34 -0
  84. package/dist/lib/scene-visibility.d.ts +6 -0
  85. package/dist/lib/scene-visibility.d.ts.map +1 -1
  86. package/dist/lib/scene-visibility.js +15 -3
  87. package/dist/lib/scripted-opening.d.ts +11 -0
  88. package/dist/lib/scripted-opening.d.ts.map +1 -0
  89. package/dist/lib/scripted-opening.js +20 -0
  90. package/dist/lib/snapshot-pipeline.d.ts +32 -2
  91. package/dist/lib/snapshot-pipeline.d.ts.map +1 -1
  92. package/dist/lib/snapshot-pipeline.js +215 -105
  93. package/dist/lib/terrain-perimeter-fill.d.ts.map +1 -1
  94. package/dist/lib/terrain-perimeter-fill.js +22 -14
  95. package/dist/lib/zone-geometry.d.ts +11 -0
  96. package/dist/lib/zone-geometry.d.ts.map +1 -0
  97. package/dist/lib/zone-geometry.js +46 -0
  98. package/dist/store/use-item-light-pool.d.ts +14 -10
  99. package/dist/store/use-item-light-pool.d.ts.map +1 -1
  100. package/dist/store/use-item-light-pool.js +52 -26
  101. package/dist/store/use-viewer.d.ts +11 -0
  102. package/dist/store/use-viewer.d.ts.map +1 -1
  103. package/dist/store/use-viewer.js +13 -6
  104. package/dist/systems/ceiling/ceiling-system.d.ts +15 -1
  105. package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
  106. package/dist/systems/ceiling/ceiling-system.js +86 -6
  107. package/dist/systems/door/door-system.d.ts.map +1 -1
  108. package/dist/systems/door/door-system.js +73 -22
  109. package/dist/systems/floor-elevation/floor-elevation-system.d.ts +3 -1
  110. package/dist/systems/floor-elevation/floor-elevation-system.d.ts.map +1 -1
  111. package/dist/systems/floor-elevation/floor-elevation-system.js +148 -5
  112. package/dist/systems/geometry/geometry-system.d.ts.map +1 -1
  113. package/dist/systems/geometry/geometry-system.js +71 -11
  114. package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
  115. package/dist/systems/interactive/interactive-system.js +40 -15
  116. package/dist/systems/interactive/procedural-controls.d.ts +13 -0
  117. package/dist/systems/interactive/procedural-controls.d.ts.map +1 -0
  118. package/dist/systems/interactive/procedural-controls.js +17 -0
  119. package/dist/systems/interactive/scripted-clips.d.ts +19 -0
  120. package/dist/systems/interactive/scripted-clips.d.ts.map +1 -0
  121. package/dist/systems/interactive/scripted-clips.js +134 -0
  122. package/dist/systems/item/item-system.d.ts.map +1 -1
  123. package/dist/systems/item/item-system.js +9 -3
  124. package/dist/systems/item/object-cut-invalidation.d.ts +3 -0
  125. package/dist/systems/item/object-cut-invalidation.d.ts.map +1 -0
  126. package/dist/systems/item/object-cut-invalidation.js +68 -0
  127. package/dist/systems/item-light/item-light-system.d.ts +1 -1
  128. package/dist/systems/item-light/item-light-system.d.ts.map +1 -1
  129. package/dist/systems/item-light/item-light-system.js +47 -52
  130. package/dist/systems/level/level-system.d.ts.map +1 -1
  131. package/dist/systems/level/level-system.js +17 -19
  132. package/dist/systems/level/level-utils.d.ts +28 -1
  133. package/dist/systems/level/level-utils.d.ts.map +1 -1
  134. package/dist/systems/level/level-utils.js +36 -4
  135. package/dist/systems/level/shadow-only-levels.d.ts +9 -0
  136. package/dist/systems/level/shadow-only-levels.d.ts.map +1 -0
  137. package/dist/systems/level/shadow-only-levels.js +7 -0
  138. package/dist/systems/roof/roof-materials.d.ts +14 -2
  139. package/dist/systems/roof/roof-materials.d.ts.map +1 -1
  140. package/dist/systems/roof/roof-materials.js +54 -15
  141. package/dist/systems/roof/roof-system.d.ts +13 -3
  142. package/dist/systems/roof/roof-system.d.ts.map +1 -1
  143. package/dist/systems/roof/roof-system.js +188 -13
  144. package/dist/systems/slab/slab-system.d.ts +1 -1
  145. package/dist/systems/slab/slab-system.d.ts.map +1 -1
  146. package/dist/systems/slab/slab-system.js +49 -19
  147. package/dist/systems/stair/stair-system.d.ts.map +1 -1
  148. package/dist/systems/stair/stair-system.js +133 -354
  149. package/dist/systems/wall/wall-cutout-cache.d.ts +11 -1
  150. package/dist/systems/wall/wall-cutout-cache.d.ts.map +1 -1
  151. package/dist/systems/wall/wall-cutout-cache.js +58 -20
  152. package/dist/systems/wall/wall-cutout.d.ts +9 -1
  153. package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
  154. package/dist/systems/wall/wall-cutout.js +46 -32
  155. package/dist/systems/wall/wall-finish-data.d.ts +22 -0
  156. package/dist/systems/wall/wall-finish-data.d.ts.map +1 -0
  157. package/dist/systems/wall/wall-finish-data.js +17 -0
  158. package/dist/systems/wall/wall-materials.d.ts +17 -3
  159. package/dist/systems/wall/wall-materials.d.ts.map +1 -1
  160. package/dist/systems/wall/wall-materials.js +109 -156
  161. package/dist/systems/wall/wall-system.d.ts +43 -4
  162. package/dist/systems/wall/wall-system.d.ts.map +1 -1
  163. package/dist/systems/wall/wall-system.js +601 -163
  164. package/dist/systems/window/window-system.d.ts +1 -1
  165. package/dist/systems/window/window-system.d.ts.map +1 -1
  166. package/dist/systems/window/window-system.js +44 -24
  167. package/dist/xr/presentation-context.d.ts +7 -0
  168. package/dist/xr/presentation-context.d.ts.map +1 -0
  169. package/dist/xr/presentation-context.js +10 -0
  170. package/package.json +6 -6
  171. package/dist/systems/fence/fence-system.d.ts +0 -22
  172. package/dist/systems/fence/fence-system.d.ts.map +0 -1
  173. 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 { getLibraryMaterialsVersion, getWallFaceBandConfig, getWallPlaneTop, resolveLevelId, resolveWallEffectiveHeight, sceneRegistry, spatialGridManager, 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;
@@ -59,6 +64,7 @@ export function wallFacingNegative(dot, previous) {
59
64
  }
60
65
  export class WallCutoutCache {
61
66
  viewerStore;
67
+ materialResolver;
62
68
  walls = new Map();
63
69
  rebuilt = new Set();
64
70
  viewer;
@@ -76,8 +82,10 @@ export class WallCutoutCache {
76
82
  selected = new Set();
77
83
  highlightKey = '';
78
84
  transformed = new Set();
79
- constructor(viewerStore = useViewer) {
85
+ overrides = useLiveNodeOverrides.getState().overrides;
86
+ constructor(viewerStore = useViewer, materialResolver = getMaterialsForWall) {
80
87
  this.viewerStore = viewerStore;
88
+ this.materialResolver = materialResolver;
81
89
  }
82
90
  subscribeLiveTransforms() {
83
91
  return useLiveTransforms.subscribe((state, previous) => {
@@ -98,6 +106,8 @@ export class WallCutoutCache {
98
106
  const libraryVersion = getLibraryMaterialsVersion();
99
107
  const textureVersion = getMaterialTextureVersion();
100
108
  const previous = this.viewer;
109
+ const overrides = useLiveNodeOverrides.getState().overrides;
110
+ const overridesChanged = this.overrides !== overrides;
101
111
  const nodesChanged = this.nodes !== scene.nodes;
102
112
  const registryChanged = this.registryRevision !== sceneRegistry.revision || this.wallCount !== wallIds.size;
103
113
  let highlightChanged = false;
@@ -126,23 +136,25 @@ export class WallCutoutCache {
126
136
  const previewId = (state) => state && state.hoverHighlightMode !== 'default' && state.hoverHighlightMode !== 'delete'
127
137
  ? state.hoveredId
128
138
  : null;
129
- const releasedPreview = previewId(previous) !== previewId(viewer) ? previewId(previous) : null;
139
+ const releasedPreview = previewId(previous) === previewId(viewer) ? null : previewId(previous);
130
140
  this.viewer = viewer;
131
- const invalidated = appearanceChanged ||
141
+ const invalidated = overridesChanged ||
142
+ appearanceChanged ||
132
143
  nodesChanged ||
133
144
  registryChanged ||
134
145
  this.rebuilt.size > 0 ||
135
146
  this.transformed.size > 0 ||
136
147
  releasedPreview !== null;
137
- if (!invalidated && viewer.wallMode !== 'cutaway')
148
+ const cutaway = viewer.wallMode === 'cutaway';
149
+ if (!invalidated && !cutaway)
138
150
  return;
139
151
  camera.getWorldDirection(this.cameraDirection);
140
152
  this.cameraTarget.copy(this.cameraDirection).add(camera.position);
141
- const cameraChanged = viewer.wallMode === 'cutaway' &&
153
+ const cameraChanged = cutaway &&
142
154
  time - this.lastUpdateTime > 0.1 &&
143
155
  (camera.position.distanceTo(this.lastCameraPosition) > 0.5 ||
144
156
  this.cameraTarget.distanceTo(this.lastCameraTarget) > 0.3);
145
- if (!invalidated && !cameraChanged)
157
+ if (!(invalidated || cameraChanged))
146
158
  return;
147
159
  if (registryChanged || nodesChanged) {
148
160
  for (const [id, wall] of this.walls) {
@@ -188,6 +200,7 @@ export class WallCutoutCache {
188
200
  assignedMaterials: undefined,
189
201
  visibleVariant: { key: 'visible', materials: [] },
190
202
  hiddenVariant: { key: 'invisible', materials: [] },
203
+ paletteKey: '',
191
204
  };
192
205
  this.walls.set(id, wall);
193
206
  }
@@ -195,16 +208,20 @@ export class WallCutoutCache {
195
208
  const rebuilt = this.rebuilt.has(id);
196
209
  if (added || changed || rebuilt)
197
210
  this.refreshNormal(wall, visited);
198
- wall.node = node;
199
- if (added || appearanceChanged || (nodesChanged && changed))
211
+ const overrideChanged = this.overrides.get(id) !== overrides.get(id);
212
+ wall.node = getEffectiveNode(node);
213
+ const repaletted = rebuilt && wallPaletteKey(wall.mesh) !== wall.paletteKey;
214
+ const refresh = added || appearanceChanged || overrideChanged || (nodesChanged && changed) || repaletted;
215
+ if (refresh)
200
216
  this.refreshAppearance(wall);
201
217
  if (added ||
202
218
  appearanceChanged ||
219
+ overrideChanged ||
203
220
  changed ||
204
221
  rebuilt ||
205
222
  releasedPreview === id ||
206
223
  cameraChanged) {
207
- this.apply(wall, viewer.wallMode, added || appearanceChanged || (nodesChanged && changed));
224
+ this.apply(wall, viewer.wallMode, refresh);
208
225
  }
209
226
  }
210
227
  }
@@ -214,6 +231,7 @@ export class WallCutoutCache {
214
231
  }
215
232
  this.rebuilt.clear();
216
233
  this.transformed.clear();
234
+ this.overrides = overrides;
217
235
  this.nodes = scene.nodes;
218
236
  this.materials = scene.materials;
219
237
  this.registryRevision = sceneRegistry.revision;
@@ -226,19 +244,35 @@ export class WallCutoutCache {
226
244
  this.lastUpdateTime = time;
227
245
  }
228
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
+ }
229
266
  refreshAppearance(wall) {
230
267
  const viewer = this.viewer;
231
268
  const scene = useScene.getState();
232
269
  const node = wall.node;
233
270
  const deleted = viewer.hoverHighlightMode === 'delete' && viewer.hoveredId === node.id;
234
- let selectionHighlighted = !deleted && this.selected.has(node.id);
235
- if (selectionHighlighted) {
236
- const levelId = resolveLevelId(node, scene.nodes);
237
- const support = spatialGridManager.getSlabSupportForWall(levelId, node.start, node.end, node.curveOffset ?? 0, node.thickness, node.supportSlabId);
238
- const height = resolveWallEffectiveHeight(node, getWallPlaneTop(node, levelId, scene.nodes), support.elevation);
239
- selectionHighlighted = !getWallFaceBandConfig(node, height).enabled;
240
- }
241
- const materials = getMaterialsForWall(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);
242
276
  const variant = (hidden) => {
243
277
  const key = resolveWallMaterialVariant({
244
278
  translucentMode: viewer.wallMode === 'translucent',
@@ -280,11 +314,15 @@ export class WallCutoutCache {
280
314
  if (wall.mesh.userData.wallHidden !== stamp)
281
315
  wall.mesh.userData.wallHidden = stamp;
282
316
  const variant = hidden ? wall.hiddenVariant : wall.visibleVariant;
283
- // 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.
284
321
  const viewer = this.viewer;
285
322
  if (viewer.hoveredId === wall.node.id &&
286
323
  viewer.hoverHighlightMode !== 'default' &&
287
- viewer.hoverHighlightMode !== 'delete') {
324
+ viewer.hoverHighlightMode !== 'delete' &&
325
+ hasMaterialsForGroups(wall.mesh)) {
288
326
  if (refresh)
289
327
  wall.variantKey = undefined;
290
328
  return;
@@ -3,6 +3,7 @@ import type { Camera } from 'three';
3
3
  import { type Mesh, Vector3 } from 'three/webgpu';
4
4
  import { type WallMode } from '../../store/use-viewer';
5
5
  import { WallCutoutCache, type WallCutoutViewerStore } from './wall-cutout-cache';
6
+ import { type WallMaterialsResolver } from './wall-materials';
6
7
  export declare const WALL_CUTOUT_FRAME_PRIORITY = 0;
7
8
  export declare function runWallCutoutFrame(cache: WallCutoutCache, { camera, clock }: {
8
9
  camera: Camera;
@@ -19,7 +20,14 @@ export declare function runWallCutoutFrame(cache: WallCutoutCache, { camera, clo
19
20
  * from the assigned material variant.
20
21
  */
21
22
  export declare function getWallHideState(wallNode: WallNode, wallMesh: Mesh, wallMode: WallMode, cameraDir: Vector3): boolean;
22
- export declare const WallCutout: ({ viewerStore, }: {
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;
29
+ export declare const WallCutout: ({ viewerStore, materialResolver, }: {
23
30
  viewerStore?: WallCutoutViewerStore;
31
+ materialResolver?: WallMaterialsResolver;
24
32
  }) => null;
25
33
  //# sourceMappingURL=wall-cutout.d.ts.map
@@ -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;AAM5B,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,kBAExB;IACD,WAAW,CAAC,EAAE,qBAAqB,CAAA;CACpC,SA2DA,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,7 +4,8 @@ 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 { getMaterialsForWall, getSelectionHighlightMaterials } from './wall-materials';
7
+ import { getWallFinishRefs } from './wall-finish-data';
8
+ import { getMaterialsForWall, getSelectionHighlightMaterials, } from './wall-materials';
8
9
  import { subscribeWallRebuilds } from './wall-rebuild-notifications';
9
10
  const v = new Vector3();
10
11
  export const WALL_CUTOUT_FRAME_PRIORITY = 0;
@@ -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
  }
30
- export const WallCutout = ({ viewerStore = useViewer, }) => {
31
- const cache = useMemo(() => new WallCutoutCache(viewerStore), [viewerStore]);
32
- useEffect(() => subscribeWallRebuilds((id) => cache.rebuilt.add(id)), [cache]);
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
+ }
66
+ export const WallCutout = ({ viewerStore = useViewer, materialResolver = getMaterialsForWall, }) => {
67
+ const cache = useMemo(() => new WallCutoutCache(viewerStore, materialResolver), [viewerStore, materialResolver]);
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 = getMaterialsForWall(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);
@@ -71,6 +85,6 @@ export const WallCutout = ({ viewerStore = useViewer, }) => {
71
85
  emitter.off('thumbnail:before-capture', restoreForCapture);
72
86
  emitter.off('thumbnail:after-capture', reapplyAfterCapture);
73
87
  };
74
- }, [viewerStore]);
88
+ }, [viewerStore, materialResolver]);
75
89
  return null;
76
90
  };
@@ -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
+ }
@@ -11,7 +11,16 @@ export interface WallMaterials {
11
11
  deleteInvisible: WallMaterialArray;
12
12
  deleteTranslucent: WallMaterialArray;
13
13
  materialHash: string;
14
+ ownedVisible?: Material[];
14
15
  }
16
+ export type WallMaterialOverride = {
17
+ hash: string;
18
+ create: () => {
19
+ visible: WallMaterialArray;
20
+ owned?: Material[];
21
+ };
22
+ };
23
+ export type WallMaterialsResolver = (wallNode: WallNode, shading?: RenderShading, textures?: boolean, colorPreset?: ColorPreset, sceneTheme?: string, sceneMaterials?: SceneMaterials, finishRefs?: readonly string[]) => WallMaterials;
15
24
  /** Lazy light-emissive selection variant of a wall's material array (keeps texture). */
16
25
  export declare function getSelectionHighlightMaterials(materials: WallMaterialArray): WallMaterialArray;
17
26
  /**
@@ -20,8 +29,13 @@ export declare function getSelectionHighlightMaterials(materials: WallMaterialAr
20
29
  * film so the wall the click would select reads under the cursor).
21
30
  */
22
31
  export declare function getHoverHighlightMaterials(materials: WallMaterialArray): WallMaterialArray;
23
- export declare function getWallMaterialHash(wallNode: WallNode, shading: RenderShading, sceneMaterials?: SceneMaterials): string;
24
- export declare function getMaterialsForWall(wallNode: WallNode, shading?: RenderShading, textures?: boolean, colorPreset?: ColorPreset, sceneTheme?: string, sceneMaterials?: SceneMaterials): WallMaterials;
25
- 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;
26
40
  export {};
27
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;CACrB;AA+SD,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,GAC9B,MAAM,CAcR;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,GAC9B,aAAa,CAgIf;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"}