@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,8 +1,8 @@
1
- import { getEffectiveWallSurfaceMaterial, getMaterialPresetByRef, getWallSurfaceMaterialSignature, getWallSurfaceSideFromBandSlot, parseMaterialRef, resolveMaterial, WALL_SLOT_DEFAULT, WALL_SURFACE_SLOT_DEFAULTS, } from '@pascal-app/core';
1
+ import { getEffectiveWallFaceMaterial, getMaterialPresetByRef, getWallSurfaceMaterialSignature, parseMaterialColor, parseMaterialRef, resolveMaterial, resolveWallExteriorSide, resolveWallFaceChain, WALL_SLOT_DEFAULT, wallAssemblyFinishRef, } from '@pascal-app/core';
2
2
  import { Color } from 'three';
3
3
  import { Fn, float, fract, length, mix, positionLocal, smoothstep, step, vec2 } from 'three/tsl';
4
4
  import { MeshLambertNodeMaterial, MeshStandardNodeMaterial } from 'three/webgpu';
5
- import { baseMaterial, createDefaultMaterial, createMaterial, createMaterialFromPresetRef, createSurfaceRoleMaterial, materialPresetRefSignature, resolveMaterialRef, resolveSurfaceColor, } from '../../lib/materials';
5
+ import { baseMaterial, createDefaultMaterial, createMaterial, createMaterialFromPresetRef, createSurfaceRoleMaterial, materialPresetRefSignature, resolveMaterialRef, resolveSlotDefaultRef, resolveSurfaceColor, } from '../../lib/materials';
6
6
  const DEFAULT_WALL_COLOR = '#e9e7e3';
7
7
  const WALL_HIGHLIGHT_PROFILES = {
8
8
  delete: {
@@ -33,112 +33,95 @@ function getSurfaceVisibleMaterial(spec, shading) {
33
33
  }
34
34
  return baseMaterial(shading);
35
35
  }
36
- function hasExplicitMaterial(spec) {
37
- return Boolean(spec.materialPreset || spec.material);
38
- }
39
36
  // Resolve a wall face's declared default — a catalog `library:` finish or a
40
37
  // flat colour — to a renderable material.
41
- function resolveWallSlotDefault(slotDefault, shading) {
38
+ function resolveWallSlotDefault(declaredDefault, shading) {
39
+ const slotDefault = resolveSlotDefaultRef(declaredDefault);
42
40
  if (parseMaterialRef(slotDefault)?.kind === 'library') {
43
41
  return createMaterialFromPresetRef(slotDefault, shading) ?? baseMaterial(shading);
44
42
  }
45
43
  return createDefaultMaterial(slotDefault, 0.9, shading);
46
44
  }
45
+ // The assembly cladding's ref when `face` is the wall's exterior face (a is the
46
+ // +normal face; an unresolved side keeps the cladding on b, as legacy did).
47
+ function wallAssemblyFaceFinishRef(wallNode, face) {
48
+ const exteriorFace = resolveWallExteriorSide(wallNode) === 1 ? 'a' : 'b';
49
+ return face === exteriorFace ? wallAssemblyFinishRef(wallNode) : null;
50
+ }
47
51
  // Slot-first resolution for one wall face, matching every other paintable kind:
48
- // node.slots[side] ref → legacy inline fields → declared slot default.
52
+ // node.slots[face] ref → legacy inline finish → declared slot default.
49
53
  // A dangling `scene:` ref (material deleted / copied across scenes) falls back
50
54
  // to the declared default — it never blocks rendering (the dangling-ref rule).
51
- function resolveWallFaceMaterial(wallNode, side, shading, sceneMaterials) {
52
- const ref = wallNode.slots?.[side];
53
- if (ref) {
54
- return (resolveMaterialRef(ref, sceneMaterials, shading) ??
55
- resolveWallSlotDefault(WALL_SLOT_DEFAULT[side], shading));
55
+ function resolveWallFaceMaterial(wallNode, face, shading, sceneMaterials) {
56
+ const finish = resolveWallFaceChain(wallNode, face);
57
+ if (finish.kind === 'ref') {
58
+ return (resolveMaterialRef(finish.ref, sceneMaterials, shading) ??
59
+ resolveWallSlotDefault(WALL_SLOT_DEFAULT[face], shading));
56
60
  }
57
- const spec = getEffectiveWallSurfaceMaterial(wallNode, side);
58
- if (hasExplicitMaterial(spec)) {
59
- return getSurfaceVisibleMaterial(spec, shading);
60
- }
61
- return resolveWallSlotDefault(WALL_SLOT_DEFAULT[side], shading);
61
+ if (finish.kind === 'legacy')
62
+ return getSurfaceVisibleMaterial(finish.spec, shading);
63
+ // No paint on this face: the wall ASSEMBLY's cladding (WS5) skins the
64
+ // exterior — a "2x6 lap siding" wall reads as lap siding without the user
65
+ // painting it. Painting the face still wins.
66
+ const assemblyRef = wallAssemblyFaceFinishRef(wallNode, face);
67
+ return resolveWallSlotDefault(assemblyRef ?? finish.ref, shading);
62
68
  }
63
- function resolveWallSlotMaterial(wallNode, slotId, shading, sceneMaterials) {
64
- const ref = wallNode.slots?.[slotId];
65
- if (ref) {
66
- return (resolveMaterialRef(ref, sceneMaterials, shading) ??
67
- resolveWallSlotDefault(WALL_SURFACE_SLOT_DEFAULTS[slotId], shading));
68
- }
69
- const side = getWallSurfaceSideFromBandSlot(slotId);
70
- if (side) {
71
- return resolveWallFaceMaterial(wallNode, side, shading, sceneMaterials);
72
- }
73
- return resolveWallSlotDefault(WALL_SURFACE_SLOT_DEFAULTS[slotId], shading);
69
+ // A region or room finish: its ref, else the face default (same dangling rule).
70
+ function resolveWallFinishMaterial(ref, shading, sceneMaterials) {
71
+ return (resolveMaterialRef(ref, sceneMaterials, shading) ??
72
+ resolveWallSlotDefault(WALL_SLOT_DEFAULT.a, shading));
74
73
  }
75
- // Cache-key fragment for one face: the slot ref plus, for a `scene:` ref, the
76
- // referenced material's *content* — so editing a scene material assigned to a
77
- // wall invalidates the cache. A `library:` ref carries its resolution state
78
- // instead: AI-generated materials register asynchronously, and a dangling ref
79
- // resolved to the slot default must not stay cached once the library lands.
80
- // Falls back to the legacy signature when unmigrated.
81
- function wallFaceMaterialSignature(wallNode, side, sceneMaterials) {
82
- const ref = wallNode.slots?.[side];
83
- if (ref) {
84
- const parsed = parseMaterialRef(ref);
85
- if (parsed?.kind === 'scene') {
86
- return JSON.stringify({
87
- ref,
88
- material: sceneMaterials?.[parsed.id]?.material ?? null,
89
- });
90
- }
91
- return JSON.stringify({ ref: materialPresetRefSignature(ref) });
74
+ // Cache-key fragment for a ref: for a `scene:` ref, the referenced material's
75
+ // *content* — so editing a scene material assigned to a wall invalidates the
76
+ // cache. A `library:` ref carries its resolution state instead: AI-generated
77
+ // materials register asynchronously, and a dangling ref resolved to the slot
78
+ // default must not stay cached once the library lands.
79
+ function refMaterialSignature(ref, sceneMaterials) {
80
+ const parsed = parseMaterialRef(ref);
81
+ if (parsed?.kind === 'scene') {
82
+ return JSON.stringify({
83
+ ref,
84
+ material: sceneMaterials?.[parsed.id]?.material ?? null,
85
+ });
92
86
  }
93
- return getWallSurfaceMaterialSignature(getEffectiveWallSurfaceMaterial(wallNode, side));
87
+ return JSON.stringify({ ref: materialPresetRefSignature(ref) });
94
88
  }
95
- function wallSlotMaterialSignature(wallNode, slotId, sceneMaterials) {
96
- const ref = wallNode.slots?.[slotId];
97
- if (ref) {
98
- const parsed = parseMaterialRef(ref);
99
- if (parsed?.kind === 'scene') {
100
- return JSON.stringify({
101
- ref,
102
- material: sceneMaterials?.[parsed.id]?.material ?? null,
103
- });
104
- }
105
- return JSON.stringify({ ref: materialPresetRefSignature(ref) });
106
- }
107
- const side = getWallSurfaceSideFromBandSlot(slotId);
108
- if (side)
109
- return wallFaceMaterialSignature(wallNode, side, sceneMaterials);
110
- return JSON.stringify({ default: WALL_SURFACE_SLOT_DEFAULTS[slotId] });
89
+ // Falls back to the legacy signature when the face has no slot ref.
90
+ function wallFaceMaterialSignature(wallNode, face, sceneMaterials) {
91
+ const ref = wallNode.slots?.[face];
92
+ if (ref)
93
+ return refMaterialSignature(ref, sceneMaterials);
94
+ return JSON.stringify({
95
+ legacy: getWallSurfaceMaterialSignature(getEffectiveWallFaceMaterial(wallNode, face)),
96
+ // Changing the assembly's cladding must re-skin the face.
97
+ assemblyFinish: wallAssemblyFaceFinishRef(wallNode, face),
98
+ });
111
99
  }
112
- // Slot-first tint for the cutaway/invisible wall variant.
113
- function resolveWallFaceColor(wallNode, side, sceneMaterials, fallback) {
114
- const ref = wallNode.slots?.[side];
115
- if (ref) {
116
- const parsed = parseMaterialRef(ref);
117
- if (parsed?.kind === 'library') {
118
- return getMaterialPresetByRef(ref)?.mapProperties?.color ?? fallback;
119
- }
120
- if (parsed?.kind === 'scene') {
121
- const sceneMaterial = sceneMaterials?.[parsed.id];
122
- return sceneMaterial ? resolveMaterial(sceneMaterial.material).color : fallback;
123
- }
124
- return fallback;
100
+ function resolveRefColor(ref, sceneMaterials, fallback) {
101
+ const color = parseMaterialColor(ref);
102
+ if (color)
103
+ return color;
104
+ const parsed = parseMaterialRef(ref);
105
+ if (parsed?.kind === 'library') {
106
+ return getMaterialPresetByRef(ref)?.mapProperties?.color ?? fallback;
125
107
  }
126
- return getSurfaceColor(getEffectiveWallSurfaceMaterial(wallNode, side), fallback);
108
+ if (parsed?.kind === 'scene') {
109
+ const sceneMaterial = sceneMaterials?.[parsed.id];
110
+ return sceneMaterial ? resolveMaterial(sceneMaterial.material).color : fallback;
111
+ }
112
+ return fallback;
127
113
  }
128
- function resolveWallSlotColor(wallNode, slotId, sceneMaterials, fallback) {
129
- const ref = wallNode.slots?.[slotId];
130
- if (ref) {
131
- const parsed = parseMaterialRef(ref);
132
- if (parsed?.kind === 'library') {
133
- return getMaterialPresetByRef(ref)?.mapProperties?.color ?? fallback;
134
- }
135
- if (parsed?.kind === 'scene') {
136
- const sceneMaterial = sceneMaterials?.[parsed.id];
137
- return sceneMaterial ? resolveMaterial(sceneMaterial.material).color : fallback;
138
- }
114
+ // Slot-first tint for the cutaway/invisible wall variant.
115
+ function resolveWallFaceColor(wallNode, face, sceneMaterials, fallback) {
116
+ const ref = wallNode.slots?.[face];
117
+ if (ref)
118
+ return resolveRefColor(ref, sceneMaterials, fallback);
119
+ const spec = getEffectiveWallFaceMaterial(wallNode, face);
120
+ const assemblyRef = wallAssemblyFaceFinishRef(wallNode, face);
121
+ if (assemblyRef && !(spec.materialPreset || spec.material)) {
122
+ return getMaterialPresetByRef(assemblyRef)?.mapProperties?.color ?? fallback;
139
123
  }
140
- const side = getWallSurfaceSideFromBandSlot(slotId);
141
- return side ? resolveWallFaceColor(wallNode, side, sceneMaterials, fallback) : fallback;
124
+ return getSurfaceColor(spec, fallback);
142
125
  }
143
126
  function getSurfaceColor(spec, fallback = DEFAULT_WALL_COLOR) {
144
127
  const preset = getMaterialPresetByRef(spec.materialPreset);
@@ -286,32 +269,32 @@ function disposeOwnedMaterials(materials) {
286
269
  material.dispose();
287
270
  });
288
271
  }
289
- export function getWallMaterialHash(wallNode, shading, sceneMaterials) {
272
+ export function getWallMaterialHash(wallNode, shading, sceneMaterials, finishRefs = [], overrideHash) {
290
273
  return JSON.stringify({
291
274
  shading,
292
- interior: wallFaceMaterialSignature(wallNode, 'interior', sceneMaterials),
293
- exterior: wallFaceMaterialSignature(wallNode, 'exterior', sceneMaterials),
294
- lowerInterior: wallSlotMaterialSignature(wallNode, 'lowerInterior', sceneMaterials),
295
- middleInterior: wallSlotMaterialSignature(wallNode, 'middleInterior', sceneMaterials),
296
- upperInterior: wallSlotMaterialSignature(wallNode, 'upperInterior', sceneMaterials),
297
- topInterior: wallSlotMaterialSignature(wallNode, 'topInterior', sceneMaterials),
298
- lowerExterior: wallSlotMaterialSignature(wallNode, 'lowerExterior', sceneMaterials),
299
- middleExterior: wallSlotMaterialSignature(wallNode, 'middleExterior', sceneMaterials),
300
- upperExterior: wallSlotMaterialSignature(wallNode, 'upperExterior', sceneMaterials),
301
- topExterior: wallSlotMaterialSignature(wallNode, 'topExterior', sceneMaterials),
275
+ overrideHash,
276
+ a: wallFaceMaterialSignature(wallNode, 'a', sceneMaterials),
277
+ b: wallFaceMaterialSignature(wallNode, 'b', sceneMaterials),
278
+ finishes: finishRefs.map((ref) => refMaterialSignature(ref, sceneMaterials)),
302
279
  });
303
280
  }
304
- export function getMaterialsForWall(wallNode, shading = 'rendered', textures = true, colorPreset = 'clay', sceneTheme, sceneMaterials) {
281
+ /**
282
+ * A wall's material palette: 0 caps/edges (themed role), 1 face a, 2 face b,
283
+ * then one entry per `finishRefs` — the region and room finishes the built
284
+ * geometry's groups point at (`getWallFinishRefs(mesh.geometry)`).
285
+ */
286
+ export function getMaterialsForWall(wallNode, shading = 'rendered', textures = true, colorPreset = 'clay', sceneTheme, sceneMaterials, finishRefs = [], override) {
305
287
  const cacheKey = `${wallNode.id}-${shading}-${textures}-${colorPreset}-${sceneTheme ?? 'base'}`;
306
288
  const materialHash = textures
307
- ? getWallMaterialHash(wallNode, shading, sceneMaterials)
308
- : JSON.stringify({ textures, colorPreset, sceneTheme });
289
+ ? getWallMaterialHash(wallNode, shading, sceneMaterials, finishRefs, override?.hash)
290
+ : JSON.stringify({ textures, colorPreset, sceneTheme, finishes: finishRefs.length });
309
291
  const existing = wallMaterialCache.get(cacheKey);
310
292
  if (existing && existing.materialHash === materialHash) {
311
293
  return existing;
312
294
  }
313
295
  if (existing) {
314
296
  disposeOwnedMaterials([
297
+ ...(existing.ownedVisible ? [existing.ownedVisible] : []),
315
298
  existing.invisible,
316
299
  existing.translucent,
317
300
  existing.deleteVisible,
@@ -320,62 +303,32 @@ export function getMaterialsForWall(wallNode, shading = 'rendered', textures = t
320
303
  ]);
321
304
  }
322
305
  const wallRoleMaterial = createSurfaceRoleMaterial('wall', colorPreset, undefined, sceneTheme);
306
+ const resolvedOverride = textures ? override?.create() : undefined;
323
307
  // Colored mode: each face resolves slot-first (node.slots ref → legacy inline
324
308
  // fields → declared slot default, parity with the retired DEFAULT_WALL_MATERIAL).
325
309
  // Textures-off collapses every face to the themed wall role (the guaranteed
326
310
  // escape hatch). The edge/cap slot (index 0) stays role-based.
327
- const visible = textures
328
- ? [
329
- wallRoleMaterial,
330
- resolveWallFaceMaterial(wallNode, 'interior', shading, sceneMaterials),
331
- resolveWallFaceMaterial(wallNode, 'exterior', shading, sceneMaterials),
332
- resolveWallSlotMaterial(wallNode, 'lowerInterior', shading, sceneMaterials),
333
- resolveWallSlotMaterial(wallNode, 'middleInterior', shading, sceneMaterials),
334
- resolveWallSlotMaterial(wallNode, 'upperInterior', shading, sceneMaterials),
335
- resolveWallSlotMaterial(wallNode, 'topInterior', shading, sceneMaterials),
336
- resolveWallSlotMaterial(wallNode, 'lowerExterior', shading, sceneMaterials),
337
- resolveWallSlotMaterial(wallNode, 'middleExterior', shading, sceneMaterials),
338
- resolveWallSlotMaterial(wallNode, 'upperExterior', shading, sceneMaterials),
339
- resolveWallSlotMaterial(wallNode, 'topExterior', shading, sceneMaterials),
340
- ]
341
- : Array.from({ length: 11 }, () => wallRoleMaterial);
311
+ const visible = resolvedOverride
312
+ ? resolvedOverride.visible
313
+ : textures
314
+ ? [
315
+ wallRoleMaterial,
316
+ resolveWallFaceMaterial(wallNode, 'a', shading, sceneMaterials),
317
+ resolveWallFaceMaterial(wallNode, 'b', shading, sceneMaterials),
318
+ ...finishRefs.map((ref) => resolveWallFinishMaterial(ref, shading, sceneMaterials)),
319
+ ]
320
+ : Array.from({ length: 3 + finishRefs.length }, () => wallRoleMaterial);
342
321
  const wallRoleColor = resolveSurfaceColor('wall', colorPreset, sceneTheme);
343
- const invisible = [
344
- createInvisibleWallMaterial(wallRoleColor, textures ? shading : 'solid'),
345
- ...['interior', 'exterior'].map((side) => createInvisibleWallMaterial(textures
346
- ? resolveWallFaceColor(wallNode, side, sceneMaterials, wallRoleColor)
347
- : wallRoleColor, textures ? shading : 'solid')),
348
- ...[
349
- 'lowerInterior',
350
- 'middleInterior',
351
- 'upperInterior',
352
- 'topInterior',
353
- 'lowerExterior',
354
- 'middleExterior',
355
- 'upperExterior',
356
- 'topExterior',
357
- ].map((slotId) => createInvisibleWallMaterial(textures
358
- ? resolveWallSlotColor(wallNode, slotId, sceneMaterials, wallRoleColor)
359
- : wallRoleColor, textures ? shading : 'solid')),
360
- ];
361
- const translucent = [
362
- createTranslucentWallMaterial(wallRoleColor, textures ? shading : 'solid'),
363
- ...['interior', 'exterior'].map((side) => createTranslucentWallMaterial(textures
364
- ? resolveWallFaceColor(wallNode, side, sceneMaterials, wallRoleColor)
365
- : wallRoleColor, textures ? shading : 'solid')),
366
- ...[
367
- 'lowerInterior',
368
- 'middleInterior',
369
- 'upperInterior',
370
- 'topInterior',
371
- 'lowerExterior',
372
- 'middleExterior',
373
- 'upperExterior',
374
- 'topExterior',
375
- ].map((slotId) => createTranslucentWallMaterial(textures
376
- ? resolveWallSlotColor(wallNode, slotId, sceneMaterials, wallRoleColor)
377
- : wallRoleColor, textures ? shading : 'solid')),
322
+ const variantShading = textures ? shading : 'solid';
323
+ const paletteColors = [
324
+ wallRoleColor,
325
+ ...['a', 'b'].map((face) => textures
326
+ ? resolveWallFaceColor(wallNode, face, sceneMaterials, wallRoleColor)
327
+ : wallRoleColor),
328
+ ...finishRefs.map((ref) => textures ? resolveRefColor(ref, sceneMaterials, wallRoleColor) : wallRoleColor),
378
329
  ];
330
+ const invisible = paletteColors.map((color) => createInvisibleWallMaterial(color, variantShading));
331
+ const translucent = paletteColors.map((color) => createTranslucentWallMaterial(color, variantShading));
379
332
  const deleteVisible = mapWallMaterialArray(visible, (material) => createHighlightedWallMaterial(material, 'delete'));
380
333
  const deleteInvisible = mapWallMaterialArray(invisible, (material) => createHighlightedWallMaterial(material, 'delete'));
381
334
  const deleteTranslucent = mapWallMaterialArray(translucent, (material) => createHighlightedWallMaterial(material, 'delete'));
@@ -387,11 +340,11 @@ export function getMaterialsForWall(wallNode, shading = 'rendered', textures = t
387
340
  deleteInvisible,
388
341
  deleteTranslucent,
389
342
  materialHash,
343
+ ownedVisible: resolvedOverride?.owned,
390
344
  };
391
345
  wallMaterialCache.set(cacheKey, result);
392
346
  return result;
393
347
  }
394
- export function getVisibleWallMaterials(wallNode, shading = 'rendered', textures = true, colorPreset = 'clay', sceneTheme, sceneMaterials) {
395
- return getMaterialsForWall(wallNode, shading, textures, colorPreset, sceneTheme, sceneMaterials)
396
- .visible;
348
+ export function getVisibleWallMaterials(wallNode, shading = 'rendered', textures = true, colorPreset = 'clay', sceneTheme, sceneMaterials, finishRefs = []) {
349
+ return getMaterialsForWall(wallNode, shading, textures, colorPreset, sceneTheme, sceneMaterials, finishRefs).visible;
397
350
  }
@@ -1,6 +1,7 @@
1
- import { type AnyNode, type AnyNodeId, type WallMiterData, type WallNode, type WallSlabSupportSegment } from '@pascal-app/core';
1
+ import { type AnyNode, type AnyNodeId, type WallFinishLayout, type WallMiterData, type WallNode, type WallSlabSupportSegment } from '@pascal-app/core';
2
2
  import * as THREE from 'three';
3
3
  import { Brush } from 'three-bvh-csg';
4
+ import { type WallFaceBaseRun } from './wall-finish-data';
4
5
  export { isWallInitialBuildActive } from './wall-build-lifecycle';
5
6
  export { drainRebuiltWalls } from './wall-rebuild-notifications';
6
7
  export declare function mergeWallCutoutBrushes(brushes: readonly Brush[]): {
@@ -8,11 +9,49 @@ export declare function mergeWallCutoutBrushes(brushes: readonly Brush[]): {
8
9
  fallbackBrushes: Brush[];
9
10
  droppedCount: number;
10
11
  };
12
+ /**
13
+ * What decides a face triangle's material: the wall's finish layout (regions and
14
+ * room spans) and, where the faces stand on different floors, each face's base —
15
+ * region heights are measured from the face's own base.
16
+ */
17
+ export type WallFinishContext = {
18
+ layout: WallFinishLayout;
19
+ faceBase: Record<'a' | 'b', WallFaceBaseRun[]> | null;
20
+ /** The palette's finish refs (material indices 3..): the layout's, then the foundation's. */
21
+ refs: readonly string[];
22
+ /** The underpinning's stemwall: triangles below local y `top` take material `index`. */
23
+ foundation: {
24
+ index: number;
25
+ top: number;
26
+ } | null;
27
+ };
11
28
  export declare function shouldDeferWallRebuild(wallId: string, nodes: Record<AnyNodeId, AnyNode>, rebuiltThisFrame: number, elapsedMs: number): boolean;
12
29
  /** Rebuilds this system still owes — neighbours deferred during a drag. */
13
30
  export declare function getPendingWallRebuildCount(): number;
14
- export declare const WallSystem: () => null;
15
- export declare function runWallBuildFrame(): void;
31
+ export type WallGeometryAdapterContext = {
32
+ isLive: (id: AnyNodeId) => boolean;
33
+ };
34
+ export type WallGeometryAdapter = {
35
+ prepareChildren?: (wall: WallNode, children: readonly AnyNode[], context: WallGeometryAdapterContext) => {
36
+ envelopeChildren: AnyNode[];
37
+ renderChildren: AnyNode[];
38
+ };
39
+ buildGeometry?: (wall: WallNode, envelope: THREE.BufferGeometry, children: readonly AnyNode[]) => THREE.BufferGeometry;
40
+ syncAuxiliaryGeometry?: (wall: WallNode, mesh: THREE.Mesh, geometry: THREE.BufferGeometry) => void;
41
+ };
42
+ export declare const WallSystem: ({ geometryAdapter }?: {
43
+ geometryAdapter?: WallGeometryAdapter;
44
+ }) => null;
45
+ export declare function runWallBuildFrame(geometryAdapter?: WallGeometryAdapter): void;
46
+ /**
47
+ * A room's wall finish lives on its zone, so a zone edit must rebuild the walls
48
+ * bounding it — the old boundary and the new one. Zones without a wall finish
49
+ * never touch their walls, so ordinary room edits cost nothing here.
50
+ */
51
+ export declare function markWallsForZoneFinishChanges(): void;
16
52
  type WallTerrainBottomSampler = (x: number, z: number) => number | null;
17
- export declare function generateExtrudedWall(wallNode: WallNode, childrenNodes: AnyNode[], miterData: WallMiterData, slabElevation?: number, baseElevation?: number, baseSegments?: readonly WallSlabSupportSegment[], storeyHeight?: number, terrainBottomAt?: WallTerrainBottomSampler): THREE.BufferGeometry;
53
+ export declare function generateExtrudedWall(wallNode: WallNode, childrenNodes: AnyNode[], miterData: WallMiterData, slabElevation?: number, baseElevation?: number, baseSegments?: readonly WallSlabSupportSegment[], storeyHeight?: number, terrainBottomAt?: WallTerrainBottomSampler, faceBase?: {
54
+ a: readonly WallSlabSupportSegment[];
55
+ b: readonly WallSlabSupportSegment[];
56
+ }, finishLayout?: WallFinishLayout, sceneNodes?: Readonly<Record<string, AnyNode>>): THREE.BufferGeometry;
18
57
  //# sourceMappingURL=wall-system.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"wall-system.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-system.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,SAAS,EAyBd,KAAK,aAAa,EAClB,KAAK,QAAQ,EACb,KAAK,sBAAsB,EAI5B,MAAM,kBAAkB,CAAA;AAGzB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAY,KAAK,EAA0B,MAAM,eAAe,CAAA;AAsBvE,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAA;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAA;AA2DhE,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,SAAS,KAAK,EAAE,GAAG;IACjE,MAAM,EAAE,KAAK,GAAG,IAAI,CAAA;IACpB,eAAe,EAAE,KAAK,EAAE,CAAA;IACxB,YAAY,EAAE,MAAM,CAAA;CACrB,CA4FA;AAmbD,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,EACjC,gBAAgB,EAAE,MAAM,EACxB,SAAS,EAAE,MAAM,GAChB,OAAO,CAET;AAED,2EAA2E;AAC3E,wBAAgB,0BAA0B,IAAI,MAAM,CAEnD;AAID,eAAO,MAAM,UAAU,YAMtB,CAAA;AAED,wBAAgB,iBAAiB,SAahC;AA6bD,KAAK,wBAAwB,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAA;AAgEvE,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,QAAQ,EAClB,aAAa,EAAE,OAAO,EAAE,EACxB,SAAS,EAAE,aAAa,EACxB,aAAa,SAAI,EACjB,aAAa,SAAgB,EAC7B,YAAY,GAAE,SAAS,sBAAsB,EAE5C,EACD,YAAY,SAAuB,EACnC,eAAe,CAAC,EAAE,wBAAwB,GACzC,KAAK,CAAC,cAAc,CAwPtB"}
1
+ {"version":3,"file":"wall-system.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-system.tsx"],"names":[],"mappings":"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"}