@pascal-app/viewer 1.0.4 → 1.0.5
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.
- package/dist/capture/capture-runtime.js +1 -1
- package/dist/components/error-boundary.d.ts.map +1 -1
- package/dist/components/renderers/node-renderer.d.ts +1 -1
- package/dist/components/renderers/node-renderer.d.ts.map +1 -1
- package/dist/components/renderers/node-renderer.js +4 -1
- package/dist/components/renderers/parametric-node-renderer.d.ts.map +1 -1
- package/dist/components/viewer/bvh-ecctrl.d.ts.map +1 -1
- package/dist/components/viewer/frame-limiter.d.ts.map +1 -1
- package/dist/components/viewer/glb-interactive.js +1 -1
- package/dist/components/viewer/glb-reference-nodes.js +1 -1
- package/dist/components/viewer/glb-replace-instances.js +1 -1
- package/dist/components/viewer/glb-scene.d.ts.map +1 -1
- package/dist/components/viewer/glb-scene.js +2 -3
- package/dist/components/viewer/glb-walkthrough-controller.js +1 -1
- package/dist/components/viewer/index.d.ts +10 -1
- package/dist/components/viewer/index.d.ts.map +1 -1
- package/dist/components/viewer/index.js +28 -12
- package/dist/components/viewer/lights.d.ts.map +1 -1
- package/dist/components/viewer/lights.js +107 -21
- package/dist/components/viewer/perf-panel.d.ts.map +1 -1
- package/dist/components/viewer/perf-panel.js +1 -1
- package/dist/components/viewer/post-processing.d.ts.map +1 -1
- package/dist/components/viewer/post-processing.js +96 -33
- package/dist/components/viewer/registered-systems.js +1 -1
- package/dist/components/viewer/scene-bvh.d.ts.map +1 -1
- package/dist/components/viewer/scene-bvh.js +1 -1
- package/dist/components/viewer/scene-environment.d.ts.map +1 -1
- package/dist/components/viewer/scene-environment.js +31 -11
- package/dist/components/viewer/selection-manager.js +15 -7
- package/dist/components/viewer/underground-ambiance.d.ts +9 -0
- package/dist/components/viewer/underground-ambiance.d.ts.map +1 -0
- package/dist/components/viewer/underground-ambiance.js +105 -0
- package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
- package/dist/components/viewer/viewer-camera.js +1 -1
- package/dist/components/viewer/viewer-presentations.d.ts.map +1 -1
- package/dist/hooks/use-gltf-ktx2.d.ts.map +1 -1
- package/dist/hooks/use-gltf-ktx2.js +2 -0
- package/dist/hooks/use-node-events.d.ts.map +1 -1
- package/dist/index.d.ts +16 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -7
- package/dist/lib/backdrop.d.ts +17 -0
- package/dist/lib/backdrop.d.ts.map +1 -1
- package/dist/lib/backdrop.js +18 -1
- package/dist/lib/construction-dust.d.ts +57 -0
- package/dist/lib/construction-dust.d.ts.map +1 -0
- package/dist/lib/construction-dust.js +187 -0
- package/dist/lib/csg-utils.js +1 -1
- package/dist/lib/foundation-materials.d.ts +26 -0
- package/dist/lib/foundation-materials.d.ts.map +1 -0
- package/dist/lib/foundation-materials.js +65 -0
- package/dist/lib/gltf-unlit.d.ts +10 -0
- package/dist/lib/gltf-unlit.d.ts.map +1 -0
- package/dist/lib/gltf-unlit.js +23 -0
- package/dist/lib/ground-window.d.ts +33 -0
- package/dist/lib/ground-window.d.ts.map +1 -0
- package/dist/lib/ground-window.js +114 -0
- package/dist/lib/hero-pose.d.ts +1 -1
- package/dist/lib/hero-pose.d.ts.map +1 -1
- package/dist/lib/ink-edges.d.ts +2 -1
- package/dist/lib/ink-edges.d.ts.map +1 -1
- package/dist/lib/ktx2-loader.d.ts +7 -0
- package/dist/lib/ktx2-loader.d.ts.map +1 -1
- package/dist/lib/ktx2-loader.js +35 -0
- package/dist/lib/layer-pass.d.ts.map +1 -1
- package/dist/lib/layers.d.ts +11 -7
- package/dist/lib/layers.d.ts.map +1 -1
- package/dist/lib/layers.js +16 -6
- package/dist/lib/materials.d.ts +5 -0
- package/dist/lib/materials.d.ts.map +1 -1
- package/dist/lib/materials.js +34 -4
- package/dist/lib/merged-outline-node.d.ts +5 -0
- package/dist/lib/merged-outline-node.d.ts.map +1 -1
- package/dist/lib/merged-outline-node.js +80 -2
- package/dist/lib/pointer-events.d.ts +20 -4
- package/dist/lib/pointer-events.d.ts.map +1 -1
- package/dist/lib/pointer-events.js +78 -14
- package/dist/lib/post-processing-resources.d.ts.map +1 -1
- package/dist/lib/reveal-candidates.d.ts +80 -0
- package/dist/lib/reveal-candidates.d.ts.map +1 -0
- package/dist/lib/reveal-candidates.js +524 -0
- package/dist/lib/reveal-ghost.d.ts +9 -0
- package/dist/lib/reveal-ghost.d.ts.map +1 -0
- package/dist/lib/reveal-ghost.js +62 -0
- package/dist/lib/reveal-pending.d.ts +12 -0
- package/dist/lib/reveal-pending.d.ts.map +1 -0
- package/dist/lib/reveal-pending.js +12 -0
- package/dist/lib/reveal-pose.d.ts +70 -0
- package/dist/lib/reveal-pose.d.ts.map +1 -0
- package/dist/lib/reveal-pose.js +248 -0
- package/dist/lib/reveal-schedule.d.ts +208 -0
- package/dist/lib/reveal-schedule.d.ts.map +1 -0
- package/dist/lib/reveal-schedule.js +410 -0
- package/dist/lib/reveal-texture-scale.d.ts +5 -0
- package/dist/lib/reveal-texture-scale.d.ts.map +1 -0
- package/dist/lib/reveal-texture-scale.js +113 -0
- package/dist/lib/reveal-windows.d.ts +23 -0
- package/dist/lib/reveal-windows.d.ts.map +1 -0
- package/dist/lib/reveal-windows.js +31 -0
- package/dist/lib/reveal-wipe.d.ts +71 -0
- package/dist/lib/reveal-wipe.d.ts.map +1 -0
- package/dist/lib/reveal-wipe.js +105 -0
- package/dist/lib/scene-pass-mrt.d.ts +12 -0
- package/dist/lib/scene-pass-mrt.d.ts.map +1 -0
- package/dist/lib/scene-pass-mrt.js +23 -0
- package/dist/lib/scene-themes.d.ts +30 -8
- package/dist/lib/scene-themes.d.ts.map +1 -1
- package/dist/lib/scene-themes.js +36 -0
- package/dist/lib/scene-visibility.d.ts +11 -1
- package/dist/lib/scene-visibility.d.ts.map +1 -1
- package/dist/lib/scene-visibility.js +38 -0
- package/dist/lib/site-surfaces.d.ts +32 -0
- package/dist/lib/site-surfaces.d.ts.map +1 -0
- package/dist/lib/site-surfaces.js +70 -0
- package/dist/lib/snapshot-pipeline.d.ts +2 -1
- package/dist/lib/snapshot-pipeline.d.ts.map +1 -1
- package/dist/lib/snapshot-pipeline.js +35 -15
- package/dist/lib/tsl-compat.d.ts +2 -2
- package/dist/lib/tsl-compat.d.ts.map +1 -1
- package/dist/lib/underground-ambiance.d.ts +168 -0
- package/dist/lib/underground-ambiance.d.ts.map +1 -0
- package/dist/lib/underground-ambiance.js +334 -0
- package/dist/store/use-viewer.d.ts +184 -27
- package/dist/store/use-viewer.d.ts.map +1 -1
- package/dist/store/use-viewer.js +155 -19
- package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
- package/dist/systems/ceiling/ceiling-system.js +2 -0
- package/dist/systems/construction-reveal/construction-dust.d.ts +16 -0
- package/dist/systems/construction-reveal/construction-dust.d.ts.map +1 -0
- package/dist/systems/construction-reveal/construction-dust.js +92 -0
- package/dist/systems/construction-reveal/construction-reveal.d.ts +276 -0
- package/dist/systems/construction-reveal/construction-reveal.d.ts.map +1 -0
- package/dist/systems/construction-reveal/construction-reveal.js +2383 -0
- package/dist/systems/construction-reveal/dust-material.d.ts +10 -0
- package/dist/systems/construction-reveal/dust-material.d.ts.map +1 -0
- package/dist/systems/construction-reveal/dust-material.js +17 -0
- package/dist/systems/construction-reveal/ghost-dress.d.ts +10 -0
- package/dist/systems/construction-reveal/ghost-dress.d.ts.map +1 -0
- package/dist/systems/construction-reveal/ghost-dress.js +24 -0
- package/dist/systems/elevator/elevator-interaction-system.d.ts +7 -0
- package/dist/systems/elevator/elevator-interaction-system.d.ts.map +1 -1
- package/dist/systems/elevator/elevator-interaction-system.js +18 -1
- package/dist/systems/geometry/geometry-system.d.ts.map +1 -1
- package/dist/systems/interactive/control-widget.d.ts.map +1 -1
- package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
- package/dist/systems/interactive/interactive-system.js +16 -6
- package/dist/systems/interactive/scripted-clips.js +1 -1
- package/dist/systems/item-light/item-light-system.js +1 -1
- package/dist/systems/level/level-hold.d.ts +4 -0
- package/dist/systems/level/level-hold.d.ts.map +1 -0
- package/dist/systems/level/level-hold.js +14 -0
- package/dist/systems/level/level-system.d.ts.map +1 -1
- package/dist/systems/level/level-system.js +16 -1
- package/dist/systems/level/level-utils.d.ts +13 -3
- package/dist/systems/level/level-utils.d.ts.map +1 -1
- package/dist/systems/level/level-utils.js +56 -8
- package/dist/systems/roof/__fixtures__/roof-plane-cases.d.ts +103 -0
- package/dist/systems/roof/__fixtures__/roof-plane-cases.d.ts.map +1 -0
- package/dist/systems/roof/__fixtures__/roof-plane-cases.js +317 -0
- package/dist/systems/roof/roof-materials.d.ts +4 -2
- package/dist/systems/roof/roof-materials.d.ts.map +1 -1
- package/dist/systems/roof/roof-materials.js +18 -24
- package/dist/systems/roof/roof-system.d.ts +8 -1
- package/dist/systems/roof/roof-system.d.ts.map +1 -1
- package/dist/systems/roof/roof-system.js +195 -45
- package/dist/systems/wall/wall-cutout-cache.d.ts +32 -3
- package/dist/systems/wall/wall-cutout-cache.d.ts.map +1 -1
- package/dist/systems/wall/wall-cutout-cache.js +95 -2
- package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
- package/dist/systems/wall/wall-material-variant.d.ts.map +1 -1
- package/dist/systems/wall/wall-materials.d.ts.map +1 -1
- package/dist/systems/wall/wall-materials.js +39 -4
- package/dist/systems/wall/wall-placeholder-sweep.d.ts.map +1 -1
- package/dist/systems/wall/wall-system.d.ts +5 -0
- package/dist/systems/wall/wall-system.d.ts.map +1 -1
- package/dist/systems/wall/wall-system.js +42 -19
- package/dist/systems/window/corner-frame.d.ts +10 -0
- package/dist/systems/window/corner-frame.d.ts.map +1 -0
- package/dist/systems/window/corner-frame.js +17 -0
- package/dist/systems/window/window-system.d.ts +1 -1
- package/dist/systems/window/window-system.d.ts.map +1 -1
- package/dist/systems/window/window-system.js +31 -6
- package/package.json +8 -8
|
@@ -62,11 +62,51 @@ export function wallFacingNegative(dot, previous) {
|
|
|
62
62
|
return dot < 0;
|
|
63
63
|
return previous ? dot < WALL_FACING_HYSTERESIS : dot < -WALL_FACING_HYSTERESIS;
|
|
64
64
|
}
|
|
65
|
+
const NOT_REVEALING = [];
|
|
66
|
+
/**
|
|
67
|
+
* The walls the cutaway must leave in view because something on them is playing: a wall that is
|
|
68
|
+
* moving in, going out or being edited, a wall that hosts an opening that is, and the walls under a
|
|
69
|
+
* node that is (a facade part). The person's wall mode is not touched: these are stamped visible
|
|
70
|
+
* for as long as they play, and the next frame after hides them again as the view wants.
|
|
71
|
+
*/
|
|
72
|
+
export function wallsInPlay(nodes, revealing) {
|
|
73
|
+
const walls = new Set();
|
|
74
|
+
if (revealing.length === 0)
|
|
75
|
+
return walls;
|
|
76
|
+
const down = (id, depth) => {
|
|
77
|
+
const node = nodes[id];
|
|
78
|
+
if (!node)
|
|
79
|
+
return;
|
|
80
|
+
if (node.type === 'wall')
|
|
81
|
+
walls.add(id);
|
|
82
|
+
if (depth >= 4)
|
|
83
|
+
return;
|
|
84
|
+
for (const child of node.children ?? [])
|
|
85
|
+
down(child, depth + 1);
|
|
86
|
+
};
|
|
87
|
+
for (const id of revealing) {
|
|
88
|
+
// The wall itself, or an opening in it: the host stands visible while the opening plays.
|
|
89
|
+
for (let node = nodes[id], guard = 0; node && guard < 16; node = node.parentId ? nodes[node.parentId] : undefined, guard += 1)
|
|
90
|
+
if (node.type === 'wall')
|
|
91
|
+
walls.add(node.id);
|
|
92
|
+
// What stands under a node that plays rides with it.
|
|
93
|
+
down(id, 0);
|
|
94
|
+
}
|
|
95
|
+
return walls;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The visible variant of a mesh's materials, kept on the object by the system that cuts it away, for
|
|
99
|
+
* whoever must draw a copy of the mesh while the cutaway hides it (a deleted wall's ghost). Not in
|
|
100
|
+
* `userData`: `Object3D.clone` copies that through JSON, which would serialize every material.
|
|
101
|
+
*/
|
|
102
|
+
export const visibleMaterialsOf = new WeakMap();
|
|
65
103
|
export class WallCutoutCache {
|
|
66
104
|
viewerStore;
|
|
67
105
|
materialResolver;
|
|
68
106
|
walls = new Map();
|
|
69
107
|
rebuilt = new Set();
|
|
108
|
+
/** Walls whose palette waits for a preview to give back their groups (see `mayAssign`). */
|
|
109
|
+
deferred = new Set();
|
|
70
110
|
viewer;
|
|
71
111
|
nodes;
|
|
72
112
|
materials;
|
|
@@ -83,6 +123,9 @@ export class WallCutoutCache {
|
|
|
83
123
|
highlightKey = '';
|
|
84
124
|
transformed = new Set();
|
|
85
125
|
overrides = useLiveNodeOverrides.getState().overrides;
|
|
126
|
+
/** The reveal's list the walls in play were read from, and the walls it named. */
|
|
127
|
+
revealing = NOT_REVEALING;
|
|
128
|
+
inPlay = new Set();
|
|
86
129
|
constructor(viewerStore = useViewer, materialResolver = getMaterialsForWall) {
|
|
87
130
|
this.viewerStore = viewerStore;
|
|
88
131
|
this.materialResolver = materialResolver;
|
|
@@ -138,13 +181,35 @@ export class WallCutoutCache {
|
|
|
138
181
|
: null;
|
|
139
182
|
const releasedPreview = previewId(previous) === previewId(viewer) ? null : previewId(previous);
|
|
140
183
|
this.viewer = viewer;
|
|
184
|
+
// A wall whose play starts or ends, or a node that moves under one, changes only its own stamp.
|
|
185
|
+
const revealing = viewer.nodesRevealing ?? NOT_REVEALING;
|
|
186
|
+
const playChanged = new Set();
|
|
187
|
+
if (this.revealing !== revealing || (nodesChanged && revealing.length > 0)) {
|
|
188
|
+
const next = wallsInPlay(scene.nodes, revealing);
|
|
189
|
+
for (const id of next)
|
|
190
|
+
if (!this.inPlay.has(id))
|
|
191
|
+
playChanged.add(id);
|
|
192
|
+
for (const id of this.inPlay)
|
|
193
|
+
if (!next.has(id))
|
|
194
|
+
playChanged.add(id);
|
|
195
|
+
this.inPlay = next;
|
|
196
|
+
this.revealing = revealing;
|
|
197
|
+
}
|
|
198
|
+
// After the stamps are current: a deferred wall is applied with the walls in play it stands among.
|
|
199
|
+
for (const id of this.deferred.size > 0 ? Array.from(this.deferred) : []) {
|
|
200
|
+
this.deferred.delete(id);
|
|
201
|
+
const wall = this.walls.get(id);
|
|
202
|
+
if (wall)
|
|
203
|
+
this.apply(wall, viewer.wallMode, false);
|
|
204
|
+
}
|
|
141
205
|
const invalidated = overridesChanged ||
|
|
142
206
|
appearanceChanged ||
|
|
143
207
|
nodesChanged ||
|
|
144
208
|
registryChanged ||
|
|
145
209
|
this.rebuilt.size > 0 ||
|
|
146
210
|
this.transformed.size > 0 ||
|
|
147
|
-
releasedPreview !== null
|
|
211
|
+
releasedPreview !== null ||
|
|
212
|
+
playChanged.size > 0;
|
|
148
213
|
const cutaway = viewer.wallMode === 'cutaway';
|
|
149
214
|
if (!invalidated && !cutaway)
|
|
150
215
|
return;
|
|
@@ -220,6 +285,7 @@ export class WallCutoutCache {
|
|
|
220
285
|
changed ||
|
|
221
286
|
rebuilt ||
|
|
222
287
|
releasedPreview === id ||
|
|
288
|
+
playChanged.has(id) ||
|
|
223
289
|
cameraChanged) {
|
|
224
290
|
this.apply(wall, viewer.wallMode, refresh);
|
|
225
291
|
}
|
|
@@ -285,6 +351,8 @@ export class WallCutoutCache {
|
|
|
285
351
|
};
|
|
286
352
|
wall.visibleVariant = variant(false);
|
|
287
353
|
wall.hiddenVariant = variant(true);
|
|
354
|
+
// The plain visible palette (no selection or hover tint): what the wall wears when a capture sees it.
|
|
355
|
+
visibleMaterialsOf.set(wall.mesh, materials.visible);
|
|
288
356
|
}
|
|
289
357
|
refreshNormal(wall, visited) {
|
|
290
358
|
const update = (object) => {
|
|
@@ -306,7 +374,10 @@ export class WallCutoutCache {
|
|
|
306
374
|
if (mode === 'cutaway') {
|
|
307
375
|
wall.negativeFacing = wallFacingNegative(wall.normal.dot(this.cameraDirection), wall.negativeFacing);
|
|
308
376
|
}
|
|
309
|
-
|
|
377
|
+
// A wall in play is seen whatever the cutaway says (walls down is the person's own choice, and
|
|
378
|
+
// stays); `mode` itself is the person's, untouched.
|
|
379
|
+
const hidden = wallHiddenFromFacing(wall.node, mode, wall.negativeFacing ?? false) &&
|
|
380
|
+
!(mode === 'cutaway' && this.inPlay.has(wall.node.id));
|
|
310
381
|
wall.hidden = hidden;
|
|
311
382
|
// The wall batch and pointer handlers consume this boolean, including
|
|
312
383
|
// false on stamp lift; translucent walls must continue receiving events.
|
|
@@ -330,13 +401,35 @@ export class WallCutoutCache {
|
|
|
330
401
|
if (wall.variantKey !== variant.key || refresh) {
|
|
331
402
|
if (wall.mesh.material !== variant.materials &&
|
|
332
403
|
!sameMaterialArray(wall.mesh.material, variant.materials)) {
|
|
404
|
+
if (!this.mayAssign(wall, variant.materials)) {
|
|
405
|
+
wall.variantKey = undefined;
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
333
408
|
wall.mesh.material = variant.materials;
|
|
334
409
|
}
|
|
335
410
|
wall.variantKey = variant.key;
|
|
336
411
|
wall.assignedMaterials = wall.mesh.material;
|
|
337
412
|
}
|
|
338
413
|
else if (wall.mesh.material !== wall.assignedMaterials) {
|
|
414
|
+
if (!this.mayAssign(wall, wall.assignedMaterials))
|
|
415
|
+
return;
|
|
339
416
|
wall.mesh.material = wall.assignedMaterials;
|
|
340
417
|
}
|
|
341
418
|
}
|
|
419
|
+
/**
|
|
420
|
+
* A paint preview spread over several walls (a room, all matching) regroups
|
|
421
|
+
* each wall's faces onto its own longer array, but only the hovered wall is
|
|
422
|
+
* known here as a preview's owner. A palette that cannot draw the groups the
|
|
423
|
+
* mesh wears now waits for the preview's restore to put them back: the mesh
|
|
424
|
+
* would otherwise keep groups with no material, and every raycast over it
|
|
425
|
+
* throws on `material.side`.
|
|
426
|
+
*/
|
|
427
|
+
mayAssign(wall, materials) {
|
|
428
|
+
if (hasMaterialsForGroups(wall.mesh, materials) || !hasMaterialsForGroups(wall.mesh)) {
|
|
429
|
+
this.deferred.delete(wall.node.id);
|
|
430
|
+
return true;
|
|
431
|
+
}
|
|
432
|
+
this.deferred.add(wall.node.id);
|
|
433
|
+
return false;
|
|
434
|
+
}
|
|
342
435
|
}
|
|
@@ -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;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,
|
|
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,uCAGpB;IACD,WAAW,CAAC,EAAE,qBAAqB,CAAA;IACnC,gBAAgB,CAAC,EAAE,qBAAqB,CAAA;CACzC,SAiCA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wall-material-variant.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-material-variant.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,mBAAmB,GAC3B,SAAS,GACT,WAAW,GACX,aAAa,GACb,gBAAgB,GAChB,kBAAkB,GAClB,oBAAoB,GACpB,mBAAmB,GACnB,qBAAqB,GACrB,uBAAuB,GACvB,iBAAiB,CAAA;AAErB,eAAO,MAAM,0BAA0B,
|
|
1
|
+
{"version":3,"file":"wall-material-variant.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-material-variant.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,mBAAmB,GAC3B,SAAS,GACT,WAAW,GACX,aAAa,GACb,gBAAgB,GAChB,kBAAkB,GAClB,oBAAoB,GACpB,mBAAmB,GACnB,qBAAqB,GACrB,uBAAuB,GACvB,iBAAiB,CAAA;AAErB,eAAO,MAAM,0BAA0B,4FAMpC;IACD,6DAA6D;IAC7D,eAAe,EAAE,OAAO,CAAA;IACxB,2EAA2E;IAC3E,MAAM,EAAE,OAAO,CAAA;IACf,6DAA6D;IAC7D,iBAAiB,EAAE,OAAO,CAAA;IAC1B,uEAAuE;IACvE,oBAAoB,EAAE,OAAO,CAAA;IAC7B,4DAA4D;IAC5D,gBAAgB,EAAE,OAAO,CAAA;CAC1B,KAAG,mBAMH,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wall-materials.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-materials.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"wall-materials.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-materials.ts"],"names":[],"mappings":"AAAA,OAAO,EAUL,KAAK,aAAa,EAClB,KAAK,eAAe,EAGpB,KAAK,QAAQ,EAId,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAS,KAAK,QAAQ,EAAE,MAAM,OAAO,CAAA;AAS5C,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;AA4SlB,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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { getEffectiveWallFaceMaterial, getMaterialPresetByRef, getWallSurfaceMaterialSignature, parseMaterialColor, parseMaterialRef, resolveMaterial, resolveWallExteriorSide, resolveWallFaceChain, WALL_SLOT_DEFAULT, wallAssemblyFinishRef, } from '@pascal-app/core';
|
|
1
|
+
import { getEffectiveWallFaceMaterial, getMaterialPresetByRef, getWallSurfaceMaterialSignature, parseMaterialColor, parseMaterialRef, resolveMaterial, resolveWallExteriorSide, resolveWallFaceChain, WALL_SLOT_DEFAULT, wallAssemblyExposedStructure, wallAssemblyFinishRef, } from '@pascal-app/core';
|
|
2
2
|
import { Color } from 'three';
|
|
3
|
-
import { Fn, float, fract, length, mix, positionLocal, smoothstep, step, vec2 } from 'three/tsl';
|
|
3
|
+
import { Fn, float, fract, length, mix, positionLocal, smoothstep, step, uv, vec2 } from 'three/tsl';
|
|
4
4
|
import { MeshLambertNodeMaterial, MeshStandardNodeMaterial } from 'three/webgpu';
|
|
5
|
+
import { BLOCK_MASONRY, concreteMaterial, masonryMaterial, STRUCTURE_COLORS, } from '../../lib/foundation-materials';
|
|
5
6
|
import { baseMaterial, createDefaultMaterial, createMaterial, createMaterialFromPresetRef, createSurfaceRoleMaterial, materialPresetRefSignature, resolveMaterialRef, resolveSlotDefaultRef, resolveSurfaceColor, } from '../../lib/materials';
|
|
6
7
|
const DEFAULT_WALL_COLOR = '#e9e7e3';
|
|
7
8
|
const WALL_HIGHLIGHT_PROFILES = {
|
|
@@ -48,6 +49,29 @@ function wallAssemblyFaceFinishRef(wallNode, face) {
|
|
|
48
49
|
const exteriorFace = resolveWallExteriorSide(wallNode) === 1 ? 'a' : 'b';
|
|
49
50
|
return face === exteriorFace ? wallAssemblyFinishRef(wallNode) : null;
|
|
50
51
|
}
|
|
52
|
+
// What `face` shows when it is the exterior face of a wall with no cladding over a bare masonry or
|
|
53
|
+
// concrete core: the structure itself (null otherwise, and for any face with a cladding).
|
|
54
|
+
function wallFaceExposedStructure(wallNode, face) {
|
|
55
|
+
if (wallAssemblyFaceFinishRef(wallNode, face))
|
|
56
|
+
return null;
|
|
57
|
+
const exteriorFace = resolveWallExteriorSide(wallNode) === 1 ? 'a' : 'b';
|
|
58
|
+
return face === exteriorFace ? wallAssemblyExposedStructure(wallNode) : null;
|
|
59
|
+
}
|
|
60
|
+
// The structure as the generic foundation draws it (same courses, joints and colours), made once:
|
|
61
|
+
// the block's bond runs along the face's world-planar U, the wall's own metres. Solid shading
|
|
62
|
+
// keeps to a flat colour, like every other wall finish.
|
|
63
|
+
const structureMaterials = new Map();
|
|
64
|
+
function exposedStructureMaterial(structure, shading) {
|
|
65
|
+
if (shading !== 'rendered')
|
|
66
|
+
return createDefaultMaterial(STRUCTURE_COLORS[structure], 0.9, shading);
|
|
67
|
+
let material = structureMaterials.get(structure);
|
|
68
|
+
if (!material) {
|
|
69
|
+
material = structure === 'cmu' ? masonryMaterial(BLOCK_MASONRY, uv().x) : concreteMaterial();
|
|
70
|
+
material.userData.__pascalCachedMaterial = true;
|
|
71
|
+
structureMaterials.set(structure, material);
|
|
72
|
+
}
|
|
73
|
+
return material;
|
|
74
|
+
}
|
|
51
75
|
// Slot-first resolution for one wall face, matching every other paintable kind:
|
|
52
76
|
// node.slots[face] ref → legacy inline finish → declared slot default.
|
|
53
77
|
// A dangling `scene:` ref (material deleted / copied across scenes) falls back
|
|
@@ -64,6 +88,9 @@ function resolveWallFaceMaterial(wallNode, face, shading, sceneMaterials) {
|
|
|
64
88
|
// exterior — a "2x6 lap siding" wall reads as lap siding without the user
|
|
65
89
|
// painting it. Painting the face still wins.
|
|
66
90
|
const assemblyRef = wallAssemblyFaceFinishRef(wallNode, face);
|
|
91
|
+
const exposed = assemblyRef ? null : wallFaceExposedStructure(wallNode, face);
|
|
92
|
+
if (exposed)
|
|
93
|
+
return exposedStructureMaterial(exposed, shading);
|
|
67
94
|
return resolveWallSlotDefault(assemblyRef ?? finish.ref, shading);
|
|
68
95
|
}
|
|
69
96
|
// A region or room finish: its ref, else the face default (same dangling rule).
|
|
@@ -91,10 +118,15 @@ function wallFaceMaterialSignature(wallNode, face, sceneMaterials) {
|
|
|
91
118
|
const ref = wallNode.slots?.[face];
|
|
92
119
|
if (ref)
|
|
93
120
|
return refMaterialSignature(ref, sceneMaterials);
|
|
121
|
+
const spec = getEffectiveWallFaceMaterial(wallNode, face);
|
|
122
|
+
const assemblyRef = wallAssemblyFaceFinishRef(wallNode, face);
|
|
94
123
|
return JSON.stringify({
|
|
95
|
-
legacy: getWallSurfaceMaterialSignature(
|
|
124
|
+
legacy: getWallSurfaceMaterialSignature(spec),
|
|
125
|
+
preset: spec.materialPreset ? materialPresetRefSignature(spec.materialPreset) : null,
|
|
96
126
|
// Changing the assembly's cladding must re-skin the face.
|
|
97
|
-
assemblyFinish:
|
|
127
|
+
assemblyFinish: assemblyRef ? materialPresetRefSignature(assemblyRef) : null,
|
|
128
|
+
// So must baring or covering its structure.
|
|
129
|
+
structure: assemblyRef ? null : wallFaceExposedStructure(wallNode, face),
|
|
98
130
|
});
|
|
99
131
|
}
|
|
100
132
|
function resolveRefColor(ref, sceneMaterials, fallback) {
|
|
@@ -121,6 +153,9 @@ function resolveWallFaceColor(wallNode, face, sceneMaterials, fallback) {
|
|
|
121
153
|
if (assemblyRef && !(spec.materialPreset || spec.material)) {
|
|
122
154
|
return getMaterialPresetByRef(assemblyRef)?.mapProperties?.color ?? fallback;
|
|
123
155
|
}
|
|
156
|
+
const exposed = assemblyRef ? null : wallFaceExposedStructure(wallNode, face);
|
|
157
|
+
if (exposed && !(spec.materialPreset || spec.material))
|
|
158
|
+
return STRUCTURE_COLORS[exposed];
|
|
124
159
|
return getSurfaceColor(spec, fallback);
|
|
125
160
|
}
|
|
126
161
|
function getSurfaceColor(spec, fallback = DEFAULT_WALL_COLOR) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wall-placeholder-sweep.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-placeholder-sweep.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,eAAO,MAAM,+BAA+B,KAAK,CAAA;AAEjD,KAAK,YAAY,GAAG;IAClB,QAAQ,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;IACrD,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAA;CAC/D,GAAG,IAAI,CAAA;AAER,4EAA4E;AAC5E,eAAO,MAAM,yBAAyB,
|
|
1
|
+
{"version":3,"file":"wall-placeholder-sweep.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-placeholder-sweep.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,eAAO,MAAM,+BAA+B,KAAK,CAAA;AAEjD,KAAK,YAAY,GAAG;IAClB,QAAQ,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;IACrD,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAA;CAC/D,GAAG,IAAI,CAAA;AAER,4EAA4E;AAC5E,eAAO,MAAM,yBAAyB,aAAc,YAAY,KAAG,OAMlE,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB,iDAK3B;IACD,4DAA4D;IAC5D,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAA;IACzB,qEAAqE;IACrE,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,YAAY,CAAA;IAC5C,iEAAiE;IACjE,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAA;IACpC,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;CACpC,KAAG,MAAM,EAST,CAAA"}
|
|
@@ -49,6 +49,11 @@ export declare function runWallBuildFrame(geometryAdapter?: WallGeometryAdapter)
|
|
|
49
49
|
* never touch their walls, so ordinary room edits cost nothing here.
|
|
50
50
|
*/
|
|
51
51
|
export declare function markWallsForZoneFinishChanges(): void;
|
|
52
|
+
/**
|
|
53
|
+
* The children a wall builds around, as they stand live: an opening still waiting for its
|
|
54
|
+
* construction reveal is left out, so the wall rises whole and is cut at the opening's turn.
|
|
55
|
+
*/
|
|
56
|
+
export declare function wallGeometryChildren(node: WallNode, nodes: Readonly<Record<string, AnyNode>>): AnyNode[];
|
|
52
57
|
type WallTerrainBottomSampler = (x: number, z: number) => number | null;
|
|
53
58
|
export declare function generateExtrudedWall(wallNode: WallNode, childrenNodes: AnyNode[], miterData: WallMiterData, slabElevation?: number, baseElevation?: number, baseSegments?: readonly WallSlabSupportSegment[], storeyHeight?: number, terrainBottomAt?: WallTerrainBottomSampler, faceBase?: {
|
|
54
59
|
a: readonly WallSlabSupportSegment[];
|
|
@@ -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,
|
|
1
|
+
{"version":3,"file":"wall-system.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-system.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buildWallFinishLayout, containsPoint, DEFAULT_LEVEL_HEIGHT, getAdjacentWallIds, getEffectiveNode, getOpeningWallCut, getWallBodyCenterOffset, getWallCurveFrameAt, getWallCurveLength, getWallFaceOffsets, getWallLevelZones, getWallMiterBoundaryPoints, getWallPlaneTop, getWallPlanFootprint, getWallSurfacePolygon, getWallThickness, isCurvedWall, isCutterName, pointToKey, resolveCutterHost, resolveLevelId, resolveWallFaceBottom, resolveWallFinish, resolveWallTop, sceneRegistry, spatialGridManager, terrainSupportLift, useLiveNodeOverrides, useLiveTransforms, useScene, WALL_SURFACE_SLOT_DEFAULTS, wallFinishMaterialIndex, zoneHasWallFinish, } from '@pascal-app/core';
|
|
1
|
+
import { buildWallFinishLayout, containsPoint, cornerPair, DEFAULT_LEVEL_HEIGHT, getAdjacentWallIds, getEffectiveNode, getOpeningWallCut, getWallBodyCenterOffset, getWallCurveFrameAt, getWallCurveLength, getWallFaceOffsets, getWallLevelZones, getWallMiterBoundaryPoints, getWallPlaneTop, getWallPlanFootprint, getWallSurfacePolygon, getWallThickness, isCurvedWall, isCutterName, pointToKey, resolveCutterHost, resolveLevelId, resolveWallFaceBottom, resolveWallFinish, resolveWallTop, sceneRegistry, spatialGridManager, terrainSupportLift, useLiveNodeOverrides, useLiveTransforms, useScene, WALL_SURFACE_SLOT_DEFAULTS, wallFinishMaterialIndex, zoneHasWallFinish, } from '@pascal-app/core';
|
|
2
2
|
import { useFrame } from '@react-three/fiber';
|
|
3
3
|
import { useEffect } from 'react';
|
|
4
4
|
import * as THREE from 'three';
|
|
@@ -8,7 +8,9 @@ import { computeBoundsTree } from 'three-mesh-bvh';
|
|
|
8
8
|
import { ensureRenderableGeometryAttributes, prepareBrushForCSG } from '../../lib/csg-utils';
|
|
9
9
|
import { setGroupsSortedByMaterial } from '../../lib/geometry-groups';
|
|
10
10
|
import { timeSpan } from '../../lib/perf-tracks';
|
|
11
|
+
import { isRevealWaiting } from '../../lib/reveal-pending';
|
|
11
12
|
import { buildTerrainPerimeterFillGeometry } from '../../lib/terrain-perimeter-fill';
|
|
13
|
+
import { cornerFrameStale } from '../window/corner-frame';
|
|
12
14
|
import { clearLevelMiterCache, getCachedLevelMiters } from './level-miter-cache';
|
|
13
15
|
import { buildOpeningCutoutGeometry, getOpeningCutoutBottomPadding, } from './opening-cutout-geometry';
|
|
14
16
|
import { drainStats, endInitialBuild, initiallyBuiltWalls, isWallInitialBuildActive, pendingAdjacentByLevel, publishWallDrainStats, } from './wall-build-lifecycle';
|
|
@@ -27,7 +29,7 @@ const WALL_BAND_SPLIT_EPSILON = 1e-5;
|
|
|
27
29
|
function computeGeometryBoundsTree(geometry) {
|
|
28
30
|
;
|
|
29
31
|
geometry.computeBoundsTree = computeBoundsTree;
|
|
30
|
-
geometry.computeBoundsTree({
|
|
32
|
+
geometry.computeBoundsTree({ targetLeafSize: 10 });
|
|
31
33
|
}
|
|
32
34
|
function csgGeometry(brush) {
|
|
33
35
|
return brush.geometry;
|
|
@@ -880,21 +882,11 @@ function updateWallGeometry(wallId, miterData, geometryAdapter) {
|
|
|
880
882
|
return;
|
|
881
883
|
const levelId = resolveLevelId(node, nodes);
|
|
882
884
|
// Covering-clamped plane: a flush/thick slab on the level above shortens
|
|
883
|
-
// the plane-bound walls below it
|
|
885
|
+
// the plane-bound walls below it.
|
|
884
886
|
const planeTop = getWallPlaneTop(node, levelId, nodes);
|
|
885
887
|
const slabSupport = spatialGridManager.getSlabSupportForWall(levelId, node.start, node.end, node.curveOffset ?? 0, node.thickness, node.supportSlabId, undefined, node.supportOffset, node.justification);
|
|
886
888
|
const slabElevation = slabSupport.elevation;
|
|
887
|
-
const
|
|
888
|
-
const childrenNodes = childrenIds
|
|
889
|
-
.map((childId) => nodes[childId])
|
|
890
|
-
.filter((n) => n !== undefined)
|
|
891
|
-
.map((child) => {
|
|
892
|
-
if (child.type !== 'door' && child.type !== 'window')
|
|
893
|
-
return child;
|
|
894
|
-
const effective = getEffectiveNode(child);
|
|
895
|
-
const live = useLiveTransforms.getState().get(child.id);
|
|
896
|
-
return live?.position ? { ...effective, position: live.position } : effective;
|
|
897
|
-
});
|
|
889
|
+
const childrenNodes = wallGeometryChildren(node, nodes);
|
|
898
890
|
const prepared = geometryAdapter?.prepareChildren?.(node, childrenNodes, {
|
|
899
891
|
isLive: (id) => useLiveNodeOverrides.getState().get(id) !== undefined ||
|
|
900
892
|
useLiveTransforms.getState().get(id) !== undefined,
|
|
@@ -948,6 +940,27 @@ function updateWallGeometry(wallId, miterData, geometryAdapter) {
|
|
|
948
940
|
useScene.getState().markDirty(child.id);
|
|
949
941
|
}
|
|
950
942
|
mesh.userData.wallFrameKey = frameKey;
|
|
943
|
+
// A wall moved apart from the other wall of a corner window, or back, makes or breaks the pair:
|
|
944
|
+
// its windows rebuild their frames once (they read the pair only then).
|
|
945
|
+
for (const child of childrenNodes)
|
|
946
|
+
if (cornerFrameStale(child, nodes))
|
|
947
|
+
useScene.getState().markDirty(child.id);
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* The children a wall builds around, as they stand live: an opening still waiting for its
|
|
951
|
+
* construction reveal is left out, so the wall rises whole and is cut at the opening's turn.
|
|
952
|
+
*/
|
|
953
|
+
export function wallGeometryChildren(node, nodes) {
|
|
954
|
+
return (node.children ?? [])
|
|
955
|
+
.map((childId) => nodes[childId])
|
|
956
|
+
.filter((child) => child !== undefined && !isRevealWaiting(child.id))
|
|
957
|
+
.map((child) => {
|
|
958
|
+
if (child.type !== 'door' && child.type !== 'window')
|
|
959
|
+
return child;
|
|
960
|
+
const effective = getEffectiveNode(child);
|
|
961
|
+
const live = useLiveTransforms.getState().get(child.id);
|
|
962
|
+
return live?.position ? { ...effective, position: live.position } : effective;
|
|
963
|
+
});
|
|
951
964
|
}
|
|
952
965
|
const WALL_UV_Y_AXIS = new THREE.Vector3(0, 1, 0);
|
|
953
966
|
const WALL_UV_UNIT_SCALE = new THREE.Vector3(1, 1, 1);
|
|
@@ -1200,7 +1213,10 @@ export function generateExtrudedWall(wallNode, childrenNodes, miterData, slabEle
|
|
|
1200
1213
|
], storeyHeight = DEFAULT_LEVEL_HEIGHT, terrainBottomAt, faceBase, finishLayout = buildWallFinishLayout(wallNode, []), sceneNodes = useScene.getState().nodes) {
|
|
1201
1214
|
const wallStart = { x: wallNode.start[0], y: wallNode.start[1] };
|
|
1202
1215
|
const wallEnd = { x: wallNode.end[0], y: wallNode.end[1] };
|
|
1203
|
-
const
|
|
1216
|
+
const levelId = resolveLevelId(wallNode, sceneNodes);
|
|
1217
|
+
const level = sceneNodes[levelId];
|
|
1218
|
+
const authoredStoreyHeight = level?.type === 'level' ? (level.height ?? null) : null;
|
|
1219
|
+
const topElevation = resolveWallTop(wallNode, storeyHeight, slabElevation, authoredStoreyHeight);
|
|
1204
1220
|
const faceDatum = faceBase;
|
|
1205
1221
|
if (faceBase)
|
|
1206
1222
|
faceBase = {
|
|
@@ -1555,7 +1571,7 @@ function collectCutoutBrushes(wallNode, childrenNodes, wallThickness, faceBase,
|
|
|
1555
1571
|
child.position[1] + cut.datum - elevation,
|
|
1556
1572
|
child.position[2],
|
|
1557
1573
|
],
|
|
1558
|
-
}, wallThickness, getWallBodyCenterOffset(wallNode), cut.bottom - elevation));
|
|
1574
|
+
}, wallThickness, getWallBodyCenterOffset(wallNode), cut.bottom - elevation, child.type === 'window' ? cornerPair(sceneNodes, child)?.end : undefined));
|
|
1559
1575
|
continue;
|
|
1560
1576
|
}
|
|
1561
1577
|
if (cut.band.length < 3)
|
|
@@ -1646,13 +1662,20 @@ function collectCutoutBrushes(wallNode, childrenNodes, wallThickness, faceBase,
|
|
|
1646
1662
|
}
|
|
1647
1663
|
return brushes;
|
|
1648
1664
|
}
|
|
1649
|
-
|
|
1665
|
+
/**
|
|
1666
|
+
* How far a corner window's cut runs past its wall's end: past this wall's half of the corner at any
|
|
1667
|
+
* angle (the miter stops at five thicknesses), so the two sides open the corner between them. The
|
|
1668
|
+
* cut only takes this wall's own geometry, so running past it touches nothing else.
|
|
1669
|
+
*/
|
|
1670
|
+
const CORNER_CUT_REACH = 6;
|
|
1671
|
+
function createOpeningCutoutBrush(opening, wallThickness, centerOffset, cutBottom, cornerEnd) {
|
|
1650
1672
|
const halfWidth = opening.width / 2;
|
|
1651
1673
|
const bottom = cutBottom ?? opening.position[1] - opening.height / 2;
|
|
1652
1674
|
const bottomPadding = getOpeningCutoutBottomPadding(opening, bottom);
|
|
1675
|
+
const reach = CORNER_CUT_REACH * wallThickness;
|
|
1653
1676
|
const geometry = buildOpeningCutoutGeometry(opening, {
|
|
1654
|
-
left: opening.position[0] - halfWidth,
|
|
1655
|
-
right: opening.position[0] + halfWidth,
|
|
1677
|
+
left: opening.position[0] - halfWidth - (cornerEnd === 'start' ? reach : 0),
|
|
1678
|
+
right: opening.position[0] + halfWidth + (cornerEnd === 'end' ? reach : 0),
|
|
1656
1679
|
bottom: bottom - bottomPadding,
|
|
1657
1680
|
top: opening.position[1] + opening.height / 2,
|
|
1658
1681
|
}, wallThickness * 2, wallThickness);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type AnyNode } from '@pascal-app/core';
|
|
2
|
+
/** Where a window mesh keeps the corner side its frame was built fused on (-1, 0 or 1). */
|
|
3
|
+
export declare const FUSED_CORNER_SIDE = "fusedCornerSide";
|
|
4
|
+
/**
|
|
5
|
+
* Whether a window's frame was built for a corner pair that has since been made or broken without
|
|
6
|
+
* the window changing (its partner deleted or moved, a wall moved apart): a build reads the pair
|
|
7
|
+
* once, so the window rebuilds.
|
|
8
|
+
*/
|
|
9
|
+
export declare function cornerFrameStale(node: AnyNode, nodes: Readonly<Record<string, AnyNode>>): boolean;
|
|
10
|
+
//# sourceMappingURL=corner-frame.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"corner-frame.d.ts","sourceRoot":"","sources":["../../../src/systems/window/corner-frame.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAA4C,MAAM,kBAAkB,CAAA;AAEzF,2FAA2F;AAC3F,eAAO,MAAM,iBAAiB,oBAAoB,CAAA;AAElD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,OAAO,CAMjG"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { cornerPair, fusesAtCorner, sceneRegistry } from '@pascal-app/core';
|
|
2
|
+
/** Where a window mesh keeps the corner side its frame was built fused on (-1, 0 or 1). */
|
|
3
|
+
export const FUSED_CORNER_SIDE = 'fusedCornerSide';
|
|
4
|
+
/**
|
|
5
|
+
* Whether a window's frame was built for a corner pair that has since been made or broken without
|
|
6
|
+
* the window changing (its partner deleted or moved, a wall moved apart): a build reads the pair
|
|
7
|
+
* once, so the window rebuilds.
|
|
8
|
+
*/
|
|
9
|
+
export function cornerFrameStale(node, nodes) {
|
|
10
|
+
if (node.type !== 'window')
|
|
11
|
+
return false;
|
|
12
|
+
const mesh = sceneRegistry.nodes.get(node.id);
|
|
13
|
+
if (!mesh)
|
|
14
|
+
return false;
|
|
15
|
+
const fused = cornerPair(nodes, node)?.post === 'none' && fusesAtCorner(node);
|
|
16
|
+
return Boolean(mesh.userData[FUSED_CORNER_SIDE]) !== fused;
|
|
17
|
+
}
|
|
@@ -10,7 +10,7 @@ export declare const DOUBLE_HUNG_BOTTOM_SASH_NAME = "double-hung-bottom-sash";
|
|
|
10
10
|
export declare const LOUVERED_WINDOW_SLATS_NAME = "louvered-window-slats";
|
|
11
11
|
export declare const AWNING_WINDOW_SASH_NAME = "awning-window-sash";
|
|
12
12
|
export declare const HOPPER_WINDOW_SASH_NAME = "hopper-window-sash";
|
|
13
|
-
export declare const pendingWindowAnimationRebuilds: Set<`
|
|
13
|
+
export declare const pendingWindowAnimationRebuilds: Set<`block_${string}` | `building_${string}` | `bvent_${string}` | `cabinet-module_${string}` | `cabinet_${string}` | `ceiling_${string}` | `chimney_${string}` | `column_${string}` | `construction-dimension_${string}` | `cupola_${string}` | `door_${string}` | `dormer_${string}` | `downspout_${string}` | `duct-fitting_${string}` | `duct-segment_${string}` | `duct-terminal_${string}` | `elevator_${string}` | `eyebrow-vent_${string}` | `fence-gate_${string}` | `fence-opening_${string}` | `fence_${string}` | `floor-opening_${string}` | `guide_${string}` | `gutter_${string}` | `hvac-equipment_${string}` | `imesh_${string}` | `item_${string}` | `leanto_${string}` | `level_${string}` | `lineset_${string}` | `liquid-line_${string}` | `measurement_${string}` | `pipe-fitting_${string}` | `pipe-segment_${string}` | `pipe-trap_${string}` | `procedural-item_${string}` | `roof_${string}` | `rseg_${string}` | `rvent_${string}` | `scan_${string}` | `separator_${string}` | `shelf_${string}` | `site_${string}` | `skylight_${string}` | `slab_${string}` | `solarpanel_${string}` | `spawn_${string}` | `sseg_${string}` | `stair_${string}` | `structural-grid_${string}` | `tvent_${string}` | `unit_${string}` | `wall_${string}` | `window_${string}` | `zone_${string}`>;
|
|
14
14
|
export declare const WindowSystem: () => null;
|
|
15
15
|
/**
|
|
16
16
|
* Build a fresh window mesh for preview/ghost rendering.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"window-system.d.ts","sourceRoot":"","sources":["../../../src/systems/window/window-system.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"window-system.d.ts","sourceRoot":"","sources":["../../../src/systems/window/window-system.tsx"],"names":[],"mappings":"AAAA,OAAO,EAeL,KAAK,UAAU,EAChB,MAAM,kBAAkB,CAAA;AAGzB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AA4B9B,eAAO,MAAM,yBAAyB,yBAAyB,CAAA;AAC/D,eAAO,MAAM,8BAA8B,8BAA8B,CAAA;AACzE,eAAO,MAAM,+BAA+B,+BAA+B,CAAA;AAC3E,eAAO,MAAM,gCAAgC,gCAAgC,CAAA;AAC7E,eAAO,MAAM,4BAA4B,4BAA4B,CAAA;AACrE,eAAO,MAAM,yBAAyB,yBAAyB,CAAA;AAC/D,eAAO,MAAM,4BAA4B,4BAA4B,CAAA;AACrE,eAAO,MAAM,0BAA0B,0BAA0B,CAAA;AACjE,eAAO,MAAM,uBAAuB,uBAAuB,CAAA;AAC3D,eAAO,MAAM,uBAAuB,uBAAuB,CAAA;AAS3D,eAAO,MAAM,8BAA8B,gvCAAuB,CAAA;AAElE,eAAO,MAAM,YAAY,YA2HxB,CAAA;AA8+GD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,UAAU,GAAG,KAAK,CAAC,IAAI,CAInE"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_WALL_THICKNESS, getEffectiveNode, getOpeningWallPlacement, getWallThickness, sceneRegistry, scriptedSize, useInteractive, useLiveNodeOverrides, useScene, } from '@pascal-app/core';
|
|
1
|
+
import { cornerPair, DEFAULT_WALL_THICKNESS, getEffectiveNode, getOpeningWallPlacement, getWallThickness, sceneRegistry, scriptedSize, useInteractive, useLiveNodeOverrides, useScene, } from '@pascal-app/core';
|
|
2
2
|
import { useFrame } from '@react-three/fiber';
|
|
3
3
|
import { useEffect, useRef } from 'react';
|
|
4
4
|
import * as THREE from 'three';
|
|
@@ -8,6 +8,7 @@ import { timeSpan } from '../../lib/perf-tracks';
|
|
|
8
8
|
import { settleScriptedOpening } from '../../lib/scripted-opening';
|
|
9
9
|
import useViewer from '../../store/use-viewer';
|
|
10
10
|
import { getOpeningCutoutProxyDepth } from '../wall/opening-cutout-geometry';
|
|
11
|
+
import { cornerFrameStale, FUSED_CORNER_SIDE } from './corner-frame';
|
|
11
12
|
// Invisible material for root mesh — used as selection hitbox only
|
|
12
13
|
const hitboxMaterial = new THREE.MeshBasicMaterial({ visible: false });
|
|
13
14
|
let baseMaterial = getBaseMaterial();
|
|
@@ -137,6 +138,11 @@ export const WindowSystem = () => {
|
|
|
137
138
|
continue;
|
|
138
139
|
clearDirty(id);
|
|
139
140
|
rebuiltWindowsThisFrame += 1;
|
|
141
|
+
// This window's change can make or break its pair: the partner's frame follows.
|
|
142
|
+
const partnerId = node.corner?.partnerId;
|
|
143
|
+
const partner = partnerId ? nodes[partnerId] : undefined;
|
|
144
|
+
if (partner && cornerFrameStale(partner, nodes))
|
|
145
|
+
useScene.getState().markDirty(partner.id);
|
|
140
146
|
// Rebuild the parent wall so its cutout reflects the updated window geometry
|
|
141
147
|
// Avoid triggering expensive wall CSG rebuilds while the window is being interactively moved/duplicated.
|
|
142
148
|
// The editor tools will request a final wall rebuild on commit.
|
|
@@ -1526,8 +1532,21 @@ function addShapedLouveredWindowVisuals(node, mesh) {
|
|
|
1526
1532
|
addBox(mesh, baseMaterial, sillW, sillThickness, sillDepth, 0, -height / 2 - sillThickness / 2, sillZ);
|
|
1527
1533
|
}
|
|
1528
1534
|
}
|
|
1535
|
+
/**
|
|
1536
|
+
* The side of a corner window fused at the corner, in the window's own frame: 1 its +x side, -1 its
|
|
1537
|
+
* -x side, 0 none. A window facing its wall's back is turned half round, so its +x then points to
|
|
1538
|
+
* the wall's start.
|
|
1539
|
+
*/
|
|
1540
|
+
function fusedCornerSide(node, rotationY) {
|
|
1541
|
+
const pair = cornerPair(useScene.getState().nodes, node);
|
|
1542
|
+
if (pair?.post !== 'none')
|
|
1543
|
+
return 0;
|
|
1544
|
+
const flipped = Math.abs(rotationY) > Math.PI / 2;
|
|
1545
|
+
return (pair.end === 'end') !== flipped ? 1 : -1;
|
|
1546
|
+
}
|
|
1529
1547
|
function updateWindowMesh(node, mesh) {
|
|
1530
1548
|
currentWindowSlot = undefined;
|
|
1549
|
+
mesh.userData[FUSED_CORNER_SIDE] = 0;
|
|
1531
1550
|
// Root mesh is an invisible hitbox; all visuals live in child meshes
|
|
1532
1551
|
mesh.geometry.dispose();
|
|
1533
1552
|
mesh.geometry = new THREE.BoxGeometry(node.width, node.height, node.frameDepth);
|
|
@@ -1618,7 +1637,11 @@ function updateWindowMesh(node, mesh) {
|
|
|
1618
1637
|
syncWindowCutout(node, mesh);
|
|
1619
1638
|
return true;
|
|
1620
1639
|
}
|
|
1621
|
-
|
|
1640
|
+
// A corner window fused at the corner: no jamb on that side, the glass runs to the corner.
|
|
1641
|
+
const fused = fusedCornerSide(node, placement.rotation[1]);
|
|
1642
|
+
mesh.userData[FUSED_CORNER_SIDE] = fused;
|
|
1643
|
+
const innerW = width - (fused ? 1 : 2) * frameThickness;
|
|
1644
|
+
const innerX = (fused * frameThickness) / 2;
|
|
1622
1645
|
const innerH = height - 2 * frameThickness;
|
|
1623
1646
|
// ── Frame members ──
|
|
1624
1647
|
// Top / bottom — full width
|
|
@@ -1626,8 +1649,10 @@ function updateWindowMesh(node, mesh) {
|
|
|
1626
1649
|
addBox(mesh, baseMaterial, width, frameThickness, frameDepth, 0, height / 2 - frameThickness / 2, 0);
|
|
1627
1650
|
addBox(mesh, baseMaterial, width, frameThickness, frameDepth, 0, -height / 2 + frameThickness / 2, 0);
|
|
1628
1651
|
// Left / right — inner height to avoid corner overlap
|
|
1629
|
-
|
|
1630
|
-
|
|
1652
|
+
if (fused !== -1)
|
|
1653
|
+
addBox(mesh, baseMaterial, frameThickness, innerH, frameDepth, -width / 2 + frameThickness / 2, 0, 0);
|
|
1654
|
+
if (fused !== 1)
|
|
1655
|
+
addBox(mesh, baseMaterial, frameThickness, innerH, frameDepth, width / 2 - frameThickness / 2, 0, 0);
|
|
1631
1656
|
// ── Pane grid ──
|
|
1632
1657
|
const numCols = columnRatios.length;
|
|
1633
1658
|
const numRows = rowRatios.length;
|
|
@@ -1639,7 +1664,7 @@ function updateWindowMesh(node, mesh) {
|
|
|
1639
1664
|
const rowHeights = rowRatios.map((r) => (r / rowSum) * usableH);
|
|
1640
1665
|
// Compute column x-centers starting from left edge of inner area
|
|
1641
1666
|
const colXCenters = [];
|
|
1642
|
-
let cx = -innerW / 2;
|
|
1667
|
+
let cx = innerX - innerW / 2;
|
|
1643
1668
|
for (let c = 0; c < numCols; c++) {
|
|
1644
1669
|
colXCenters.push(cx + colWidths[c] / 2);
|
|
1645
1670
|
cx += colWidths[c];
|
|
@@ -1656,7 +1681,7 @@ function updateWindowMesh(node, mesh) {
|
|
|
1656
1681
|
cy -= rowDividerThickness;
|
|
1657
1682
|
}
|
|
1658
1683
|
// Column dividers — full inner height
|
|
1659
|
-
cx = -innerW / 2;
|
|
1684
|
+
cx = innerX - innerW / 2;
|
|
1660
1685
|
currentWindowSlot = 'frame';
|
|
1661
1686
|
for (let c = 0; c < numCols - 1; c++) {
|
|
1662
1687
|
cx += colWidths[c];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pascal-app/viewer",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "3D viewer component for Pascal building editor",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"prepublishOnly": "npm run build"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
|
-
"@pascal-app/core": "^1.0.
|
|
36
|
+
"@pascal-app/core": "^1.0.5",
|
|
37
37
|
"@react-three/drei": "^10",
|
|
38
38
|
"@react-three/fiber": "^9",
|
|
39
39
|
"react": "^18 || ^19",
|
|
@@ -42,18 +42,18 @@
|
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"three-bvh-csg": "^0.0.18",
|
|
45
|
-
"three-mesh-bvh": "
|
|
45
|
+
"three-mesh-bvh": "~0.9.16",
|
|
46
46
|
"zustand": "^5"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@pascal-app/core": "^1.0.
|
|
49
|
+
"@pascal-app/core": "^1.0.5",
|
|
50
50
|
"@pascal/typescript-config": "*",
|
|
51
|
-
"@react-three/test-renderer": "^9.1.
|
|
52
|
-
"@types/node": "^
|
|
51
|
+
"@react-three/test-renderer": "^9.1.1",
|
|
52
|
+
"@types/node": "^26",
|
|
53
53
|
"@types/react": "19.3.0",
|
|
54
54
|
"@types/react-dom": "19.3.0",
|
|
55
|
-
"@types/three": "^0.
|
|
56
|
-
"typescript": "
|
|
55
|
+
"@types/three": "^0.186.0",
|
|
56
|
+
"typescript": "7.0.2"
|
|
57
57
|
},
|
|
58
58
|
"keywords": [
|
|
59
59
|
"3d",
|