@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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getConicalRoofCoverage, getDutchEndSlopeFaces, getDutchRoofShapeMetrics, getEffectiveNode, getRoofModuleFaces, getRoofPlanBounds, getRoofSegmentSurfaceY, getRoofShapeInsets, getRoofShapeRatios, getSegmentSlopeFrame, hasSegmentMaterialOverride, isBandedShedSegment, nodeRegistry, normalizeRoofSegmentTrim, pointInPolygon2D, ROOF_SHAPE_DEFAULTS, roofOverlapEntryOwns, roofPlanBoundsOverlap, sceneRegistry, unionPolygons, useLiveNodeOverrides, useScene, } from '@pascal-app/core';
|
|
1
|
+
import { getBarrelRoofFaces, getBarrelRoofSolidPieces, getConicalRoofCoverage, getDutchEndSlopeFaces, getDutchRoofShapeMetrics, getEffectiveNode, getGlazedRoofFaces, getGlazedRoofPieceFaces, getGlazedRoofPieces, getRoofModuleFaces, getRoofPlanBounds, getRoofSegmentDeckReach, getRoofSegmentSurfaceY, getRoofShapeInsets, getRoofShapeRatios, getSegmentSlopeFrame, hasSegmentMaterialOverride, isBandedShedSegment, isGlazedRoofSegment, isIndependentRoofSegment, nodeRegistry, normalizeRoofSegmentTrim, pointInPolygon2D, ROOF_SHAPE_DEFAULTS, roofOverlapEntryOwns, roofPlanBoundsOverlap, sceneRegistry, unionPolygons, useLiveNodeOverrides, useScene, } from '@pascal-app/core';
|
|
2
2
|
import { useFrame } from '@react-three/fiber';
|
|
3
3
|
import * as THREE from 'three';
|
|
4
4
|
import { mergeGeometries, mergeVertices } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
|
|
@@ -6,6 +6,7 @@ import { ADDITION, Brush, Evaluator, SUBTRACTION } from 'three-bvh-csg';
|
|
|
6
6
|
import { computeBoundsTree } from 'three-mesh-bvh';
|
|
7
7
|
import { applyWorldScaleBoxUVs } from '../../lib/box-uv';
|
|
8
8
|
import { ensureRenderableGeometryAttributes, subtractCsgBrush } from '../../lib/csg-utils';
|
|
9
|
+
import { isRevealAssembling } from '../construction-reveal/construction-reveal';
|
|
9
10
|
function csgGeometry(brush) {
|
|
10
11
|
return brush.geometry;
|
|
11
12
|
}
|
|
@@ -20,7 +21,7 @@ csgEvaluator.attributes = ['position', 'normal', 'uv', 'uv2'];
|
|
|
20
21
|
function computeGeometryBoundsTree(geometry) {
|
|
21
22
|
;
|
|
22
23
|
geometry.computeBoundsTree = computeBoundsTree;
|
|
23
|
-
geometry.computeBoundsTree({
|
|
24
|
+
geometry.computeBoundsTree({ targetLeafSize: 10 });
|
|
24
25
|
}
|
|
25
26
|
function prepareBrushForCSG(brush) {
|
|
26
27
|
ensureRenderableGeometryAttributes(brush.geometry);
|
|
@@ -108,10 +109,31 @@ function createDegenerateRoofPlaceholder() {
|
|
|
108
109
|
}
|
|
109
110
|
// Pending merged-roof updates carried across frames (for throttling)
|
|
110
111
|
const pendingRoofUpdates = new Set();
|
|
112
|
+
const pendingIndependentSegments = new Set();
|
|
111
113
|
const previousRoofPlanBounds = new Map();
|
|
112
114
|
const warnedMergedRoofNaNIds = new Set();
|
|
113
115
|
const MAX_ROOFS_PER_FRAME = 1;
|
|
114
116
|
const MAX_SEGMENTS_PER_FRAME = 3;
|
|
117
|
+
/** What each roof's shell was last merged from: its node and its segments, as the store held them. */
|
|
118
|
+
const mergedFrom = new Map();
|
|
119
|
+
function rememberMergedInputs(roof, nodes) {
|
|
120
|
+
const inputs = new Map();
|
|
121
|
+
for (const id of [roof.id, ...(roof.children ?? [])]) {
|
|
122
|
+
const node = nodes[id];
|
|
123
|
+
if (node)
|
|
124
|
+
inputs.set(id, node);
|
|
125
|
+
}
|
|
126
|
+
mergedFrom.set(roof.id, inputs);
|
|
127
|
+
}
|
|
128
|
+
/** Whether nothing a roof's shell is made of has changed since it was merged. */
|
|
129
|
+
function mergedFromStillHolds(roofId, nodes) {
|
|
130
|
+
const inputs = mergedFrom.get(roofId);
|
|
131
|
+
const roof = nodes[roofId];
|
|
132
|
+
if (!inputs || roof?.type !== 'roof')
|
|
133
|
+
return false;
|
|
134
|
+
const ids = [roofId, ...(roof.children ?? [])];
|
|
135
|
+
return ids.length === inputs.size && ids.every((id) => inputs.get(id) === nodes[id]);
|
|
136
|
+
}
|
|
115
137
|
/**
|
|
116
138
|
* Roofs whose merged shell is still to build: until it is, the roof shows its segments apart (or
|
|
117
139
|
* an old shell), and a capture would show that. Only roofs on screen count, a first merge (a
|
|
@@ -119,7 +141,7 @@ const MAX_SEGMENTS_PER_FRAME = 3;
|
|
|
119
141
|
* capture.
|
|
120
142
|
*/
|
|
121
143
|
export function getPendingRoofMergeCount() {
|
|
122
|
-
return countPendingRoofShells(pendingRoofUpdates, (id) => sceneRegistry.nodes.get(id));
|
|
144
|
+
return countPendingRoofShells([...pendingRoofUpdates, ...pendingIndependentSegments], (id) => sceneRegistry.nodes.get(id));
|
|
123
145
|
}
|
|
124
146
|
export function countPendingRoofShells(pending, mounted) {
|
|
125
147
|
let count = 0;
|
|
@@ -128,7 +150,19 @@ export function countPendingRoofShells(pending, mounted) {
|
|
|
128
150
|
count++;
|
|
129
151
|
return count;
|
|
130
152
|
}
|
|
131
|
-
function queueSiblingRoofUpdates(roofId, nodes) {
|
|
153
|
+
function queueSiblingRoofUpdates(roofId, nodes, changedSegmentId) {
|
|
154
|
+
const queueIndependent = (roof) => {
|
|
155
|
+
for (const id of roof.children) {
|
|
156
|
+
const segment = nodes[id];
|
|
157
|
+
if (segment?.type === 'roof-segment' &&
|
|
158
|
+
segment.id !== changedSegmentId &&
|
|
159
|
+
isIndependentRoofSegment(getEffectiveNode(segment)))
|
|
160
|
+
pendingIndependentSegments.add(segment.id);
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
const currentRoof = nodes[roofId];
|
|
164
|
+
if (currentRoof?.type === 'roof')
|
|
165
|
+
queueIndependent(currentRoof);
|
|
132
166
|
pendingRoofUpdates.add(roofId);
|
|
133
167
|
const roof = nodes[roofId]?.type === 'roof' ? getEffectiveNode(nodes[roofId]) : undefined;
|
|
134
168
|
if (roof?.type !== 'roof' || !roof.parentId)
|
|
@@ -186,13 +220,11 @@ function queueSiblingRoofUpdates(roofId, nodes) {
|
|
|
186
220
|
if ((currentBounds && roofPlanBoundsOverlap(currentBounds, siblingBounds)) ||
|
|
187
221
|
(oldBounds && roofPlanBoundsOverlap(oldBounds, siblingBounds))) {
|
|
188
222
|
pendingRoofUpdates.add(sibling.id);
|
|
223
|
+
queueIndependent(sibling);
|
|
189
224
|
}
|
|
190
225
|
}
|
|
191
226
|
}
|
|
192
|
-
|
|
193
|
-
// ROOF SYSTEM
|
|
194
|
-
// ============================================================================
|
|
195
|
-
export const RoofSystem = () => {
|
|
227
|
+
export const RoofSystem = ({ geometryAdapter } = {}) => {
|
|
196
228
|
const dirtyNodes = useScene((state) => state.dirtyNodes);
|
|
197
229
|
const clearDirty = useScene((state) => state.clearDirty);
|
|
198
230
|
const rootNodeIds = useScene((state) => state.rootNodeIds);
|
|
@@ -205,23 +237,30 @@ export const RoofSystem = () => {
|
|
|
205
237
|
// Clear stale pending updates when the scene is unloaded
|
|
206
238
|
if (rootNodeIds.length === 0) {
|
|
207
239
|
pendingRoofUpdates.clear();
|
|
240
|
+
pendingIndependentSegments.clear();
|
|
208
241
|
previousRoofPlanBounds.clear();
|
|
209
242
|
warnedMergedRoofNaNIds.clear();
|
|
243
|
+
mergedFrom.clear();
|
|
210
244
|
for (const cached of mergedRoofSegmentGeometryCache.values()) {
|
|
211
245
|
disposeCachedMergedRoofSegmentGeometrySet(cached);
|
|
212
246
|
}
|
|
213
247
|
mergedRoofSegmentGeometryCache.clear();
|
|
214
248
|
return;
|
|
215
249
|
}
|
|
216
|
-
if (dirtyNodes.size === 0 &&
|
|
250
|
+
if (dirtyNodes.size === 0 &&
|
|
251
|
+
pendingRoofUpdates.size === 0 &&
|
|
252
|
+
pendingIndependentSegments.size === 0)
|
|
217
253
|
return;
|
|
218
254
|
const nodes = useScene.getState().nodes;
|
|
219
255
|
// --- Pass 1: Process dirty roof-segments (throttled) ---
|
|
220
256
|
let segmentsProcessed = 0;
|
|
221
|
-
dirtyNodes.forEach((id) => {
|
|
257
|
+
new Set([...dirtyNodes, ...pendingIndependentSegments]).forEach((id) => {
|
|
258
|
+
const independentCascade = pendingIndependentSegments.has(id) && !dirtyNodes.has(id);
|
|
222
259
|
const node = nodes[id];
|
|
223
|
-
if (!node)
|
|
260
|
+
if (!node) {
|
|
261
|
+
pendingIndependentSegments.delete(id);
|
|
224
262
|
return;
|
|
263
|
+
}
|
|
225
264
|
// Cutting roof accessories cascade their dirty mark to the host
|
|
226
265
|
// segment's parent roof so the merged shell re-CSGs with the new
|
|
227
266
|
// cut. Non-cutting accessories (vents, panels, gutters, etc.) sit on
|
|
@@ -236,6 +275,8 @@ export const RoofSystem = () => {
|
|
|
236
275
|
const seg = segId ? nodes[segId] : undefined;
|
|
237
276
|
if (roofAccessory.buildCut && seg?.parentId) {
|
|
238
277
|
pendingRoofUpdates.add(seg.parentId);
|
|
278
|
+
if (isIndependentRoofSegment(seg))
|
|
279
|
+
useScene.getState().markDirty(seg.id);
|
|
239
280
|
}
|
|
240
281
|
clearDirty(id);
|
|
241
282
|
return;
|
|
@@ -261,7 +302,7 @@ export const RoofSystem = () => {
|
|
|
261
302
|
const revealOnly = mesh.parent?.name === 'segments-wrapper' &&
|
|
262
303
|
mesh.parent?.parent?.getObjectByName('merged-roof')?.visible === true;
|
|
263
304
|
if (isVisible && !revealOnly && segmentsProcessed < MAX_SEGMENTS_PER_FRAME) {
|
|
264
|
-
updateRoofSegmentGeometry(effectiveSegment, mesh, nodes);
|
|
305
|
+
updateRoofSegmentGeometry(effectiveSegment, mesh, nodes, geometryAdapter);
|
|
265
306
|
segmentsProcessed++;
|
|
266
307
|
}
|
|
267
308
|
else if (isVisible && !revealOnly) {
|
|
@@ -284,9 +325,12 @@ export const RoofSystem = () => {
|
|
|
284
325
|
else {
|
|
285
326
|
clearDirty(id);
|
|
286
327
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
328
|
+
pendingIndependentSegments.delete(id);
|
|
329
|
+
const parentId = effectiveSegment.parentId;
|
|
330
|
+
if (parentId &&
|
|
331
|
+
!independentCascade &&
|
|
332
|
+
!(isRevealAssembling(parentId) && mergedFromStillHolds(parentId, nodes))) {
|
|
333
|
+
queueSiblingRoofUpdates(parentId, nodes, id);
|
|
290
334
|
}
|
|
291
335
|
}
|
|
292
336
|
else if (node.type === 'roof') {
|
|
@@ -302,6 +346,7 @@ export const RoofSystem = () => {
|
|
|
302
346
|
const node = nodes[id];
|
|
303
347
|
if (node?.type !== 'roof') {
|
|
304
348
|
pendingRoofUpdates.delete(id);
|
|
349
|
+
mergedFrom.delete(id);
|
|
305
350
|
continue;
|
|
306
351
|
}
|
|
307
352
|
const group = sceneRegistry.nodes.get(id);
|
|
@@ -310,11 +355,11 @@ export const RoofSystem = () => {
|
|
|
310
355
|
const mergedMesh = group.getObjectByName('merged-roof');
|
|
311
356
|
if (!mergedMesh)
|
|
312
357
|
continue;
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
358
|
+
// Built even while hidden: a roof assembling from its segments (construction
|
|
359
|
+
// reveal) hides its shell, and must show it whole the moment it is captured.
|
|
360
|
+
rememberMergedInputs(node, nodes);
|
|
361
|
+
updateMergedRoofGeometry(node, group, nodes);
|
|
362
|
+
roofsProcessed++;
|
|
318
363
|
pendingRoofUpdates.delete(id);
|
|
319
364
|
}
|
|
320
365
|
}, 5); // Priority 5: run after all other systems have settled
|
|
@@ -323,13 +368,14 @@ export const RoofSystem = () => {
|
|
|
323
368
|
// ============================================================================
|
|
324
369
|
// GEOMETRY GENERATION
|
|
325
370
|
// ============================================================================
|
|
326
|
-
function updateRoofSegmentGeometry(node, mesh, nodes) {
|
|
371
|
+
function updateRoofSegmentGeometry(node, mesh, nodes, adapter) {
|
|
327
372
|
const newGeo = generateRoofSegmentGeometry(node, nodes);
|
|
328
373
|
mesh.geometry.dispose();
|
|
329
374
|
mesh.geometry = newGeo;
|
|
330
375
|
computeGeometryBoundsTree(newGeo);
|
|
331
376
|
mesh.position.set(node.position[0], node.position[1], node.position[2]);
|
|
332
377
|
mesh.rotation.y = node.rotation;
|
|
378
|
+
adapter?.syncGeometry(node, mesh);
|
|
333
379
|
}
|
|
334
380
|
/**
|
|
335
381
|
* Subtract every hosted accessory cut (`capabilities.roofAccessory.
|
|
@@ -477,7 +523,7 @@ function updateMergedRoofGeometry(roofNode, group, nodes) {
|
|
|
477
523
|
const scn = nodes[id];
|
|
478
524
|
return scn ? getEffectiveNode(scn) : undefined;
|
|
479
525
|
})
|
|
480
|
-
.filter((n) => n !== undefined && !hasSegmentMaterialOverride(n));
|
|
526
|
+
.filter((n) => n !== undefined && !hasSegmentMaterialOverride(n) && !isIndependentRoofSegment(n));
|
|
481
527
|
if (children.length === 0) {
|
|
482
528
|
mergedMesh.geometry.dispose();
|
|
483
529
|
// Not BoxGeometry: its 6 groups against the merged mesh's 4-material array
|
|
@@ -1015,7 +1061,12 @@ function buildSegmentTrimCutBrushes(segment) {
|
|
|
1015
1061
|
return [];
|
|
1016
1062
|
}
|
|
1017
1063
|
const { activeRh } = getSegmentSlopeFrame(segment);
|
|
1018
|
-
const extra = segment.wallThickness +
|
|
1064
|
+
const extra = segment.wallThickness +
|
|
1065
|
+
segment.overhang +
|
|
1066
|
+
(segment.gableOverhang ?? 0) +
|
|
1067
|
+
segment.deckThickness +
|
|
1068
|
+
segment.shingleThickness +
|
|
1069
|
+
2;
|
|
1019
1070
|
const minX = -segment.width / 2 - extra;
|
|
1020
1071
|
const maxX = segment.width / 2 + extra;
|
|
1021
1072
|
const minZ = -segment.depth / 2 - extra;
|
|
@@ -1109,6 +1160,9 @@ export function clipGeometryBySegmentTrim(geometry, segment) {
|
|
|
1109
1160
|
prepareBrushForCSG(brush);
|
|
1110
1161
|
const next = csgEvaluator.evaluate(brush, cut, SUBTRACTION);
|
|
1111
1162
|
const trimmed = csgGeometry(next).clone();
|
|
1163
|
+
const materialIndices = new Map(dummyMats.map((material, index) => [material, index]));
|
|
1164
|
+
for (const group of trimmed.groups)
|
|
1165
|
+
group.materialIndex = mapRoofGroupMaterialIndex(group.materialIndex, csgMaterials(next), materialIndices);
|
|
1112
1166
|
currentGeometry.dispose();
|
|
1113
1167
|
next.geometry.dispose();
|
|
1114
1168
|
ensureRenderableGeometryAttributes(trimmed);
|
|
@@ -1145,13 +1199,16 @@ const FASCIA_MATERIAL_INDEX = 2;
|
|
|
1145
1199
|
* trims, cuts and sibling roofs clip them like the deck. Gable, hip, shed
|
|
1146
1200
|
* and flat; null for the other forms.
|
|
1147
1201
|
*/
|
|
1148
|
-
function buildFasciaBoards(node,
|
|
1202
|
+
function buildFasciaBoards(node, reach, eaveTopY, tanTheta) {
|
|
1149
1203
|
const { roofType, width, depth } = node;
|
|
1150
|
-
const
|
|
1204
|
+
const bt = node.fasciaDepth ?? FASCIA_BOARD.thickness;
|
|
1205
|
+
const bh = node.fasciaHeight ?? FASCIA_BOARD.height;
|
|
1206
|
+
// A flat roof's boards sized on purpose reach its top, over the membrane's edge: a coping band.
|
|
1207
|
+
const flatTopY = node.fasciaHeight !== undefined ? eaveTopY + node.shingleThickness : eaveTopY;
|
|
1151
1208
|
const out = bt / 2 - FASCIA_EMBED;
|
|
1152
|
-
const halfD = depth / 2 +
|
|
1209
|
+
const halfD = depth / 2 + reach.z;
|
|
1153
1210
|
const zE = halfD + out;
|
|
1154
|
-
const xE = width / 2 +
|
|
1211
|
+
const xE = width / 2 + reach.x + out;
|
|
1155
1212
|
// the eave boards run through the corners to the rake / side boards'
|
|
1156
1213
|
// outer faces (a lapped return, no notch at the corner)
|
|
1157
1214
|
const xL = xE + bt / 2;
|
|
@@ -1201,11 +1258,12 @@ function buildFasciaBoards(node, deckExt, eaveTopY, tanTheta) {
|
|
|
1201
1258
|
}
|
|
1202
1259
|
}
|
|
1203
1260
|
else if (roofType === 'hip' || roofType === 'flat') {
|
|
1261
|
+
const y = roofType === 'flat' ? flatTopY : eaveTopY;
|
|
1204
1262
|
for (const sz of [-1, 1])
|
|
1205
|
-
board([-xL,
|
|
1263
|
+
board([-xL, y, sz * zE], [xL, y, sz * zE], [0, 1]);
|
|
1206
1264
|
// the side boards butt the eave boards' backs
|
|
1207
1265
|
for (const sx of [-1, 1])
|
|
1208
|
-
board([sx * xE,
|
|
1266
|
+
board([sx * xE, y, -zE + bt / 2], [sx * xE, y, zE - bt / 2], [1, 0]);
|
|
1209
1267
|
}
|
|
1210
1268
|
else if (roofType === 'shed') {
|
|
1211
1269
|
// the low eave at +z, the slope rising to −z
|
|
@@ -1240,7 +1298,7 @@ function wearsFascia(node, parentRoof) {
|
|
|
1240
1298
|
* renderer asks; a dormer, a chimney or a sibling's clip takes the bare shell.
|
|
1241
1299
|
*/
|
|
1242
1300
|
export function getRoofSegmentBrushes(node, options = {}) {
|
|
1243
|
-
const { roofType, width, depth, wallHeight, wallThickness, deckThickness,
|
|
1301
|
+
const { roofType, width, depth, wallHeight, wallThickness, deckThickness, shingleThickness } = node;
|
|
1244
1302
|
const conicalCoverage = getConicalRoofCoverage(node);
|
|
1245
1303
|
const { activeRh, tanTheta, cosTheta, sinTheta } = getSegmentSlopeFrame(node);
|
|
1246
1304
|
const shapeRatios = getRoofShapeRatios({
|
|
@@ -1257,8 +1315,9 @@ export function getRoofSegmentBrushes(node, options = {}) {
|
|
|
1257
1315
|
// same ratio). A hardcoded 0.25 desyncs the gablet from the parameter.
|
|
1258
1316
|
const baseI = Math.min(width, depth) * node.dutchHipWidthRatio;
|
|
1259
1317
|
const plateSeated = wallHeight <= 0;
|
|
1260
|
-
|
|
1261
|
-
|
|
1318
|
+
// `xExt`: how far the volume reaches along x, where a gable's rakes are; the eaves' `wExt` else.
|
|
1319
|
+
const getVol = (wExt, vOffset, baseY, matIndex, isVoid, materialRule, onPlate = false, xExt = wExt) => {
|
|
1320
|
+
const wV = Math.max(0.01, width + 2 * xExt);
|
|
1262
1321
|
const dV = Math.max(0.01, depth + 2 * wExt);
|
|
1263
1322
|
const autoDrop = wExt * tanTheta;
|
|
1264
1323
|
// Floor every prism at 5 cm so CSG never sees a degenerate volume — by
|
|
@@ -1301,25 +1360,25 @@ export function getRoofSegmentBrushes(node, options = {}) {
|
|
|
1301
1360
|
};
|
|
1302
1361
|
const wallGeo = getVol(wallThickness / 2, 0, 0, 0, false, undefined, true);
|
|
1303
1362
|
const innerGeo = getVol(-wallThickness / 2, 0, -5, 2, false, undefined, true);
|
|
1304
|
-
const
|
|
1305
|
-
const deckExt =
|
|
1363
|
+
const reach = getRoofSegmentDeckReach(node);
|
|
1364
|
+
const deckExt = reach.z;
|
|
1306
1365
|
// A shed's deck and shingle edges take the wall/trim slot, unless it wears
|
|
1307
1366
|
// fascia boards: then they are fascia like a gable's, in the deck slot.
|
|
1308
1367
|
const shedEdgesInTrimSlot = roofType === 'shed' && !options.fascia;
|
|
1309
1368
|
const shedRoofSideMaterialRule = shedEdgesInTrimSlot
|
|
1310
1369
|
? (normal) => (normal.y > SHINGLE_SURFACE_EPSILON ? 3 : ROOF_EDGE_MATERIAL_INDEX)
|
|
1311
1370
|
: undefined;
|
|
1312
|
-
const deckTopGeo = getVol(deckExt, verticalRt, 0, 1, false, shedRoofSideMaterialRule);
|
|
1313
|
-
const deckBotGeo = getVol(deckExt, 0, -5, 0, true);
|
|
1371
|
+
const deckTopGeo = getVol(deckExt, verticalRt, 0, 1, false, shedRoofSideMaterialRule, false, reach.x);
|
|
1372
|
+
const deckBotGeo = getVol(deckExt, 0, -5, 0, true, undefined, false, reach.x);
|
|
1314
1373
|
// the deck's top edge at its outer plane, as `getVol` puts it
|
|
1315
1374
|
const deckEaveDrop = deckExt * tanTheta;
|
|
1316
1375
|
const deckEaveTopY = plateSeated && deckEaveDrop > 0
|
|
1317
1376
|
? wallHeight - deckEaveDrop + verticalRt
|
|
1318
1377
|
: Math.max(0.05, wallHeight - deckEaveDrop + verticalRt);
|
|
1319
|
-
const fasciaGeo = options.fascia ? buildFasciaBoards(node,
|
|
1378
|
+
const fasciaGeo = options.fascia ? buildFasciaBoards(node, reach, deckEaveTopY, tanTheta) : null;
|
|
1320
1379
|
const stSin = shingleThickness * sinTheta;
|
|
1321
1380
|
const stCos = shingleThickness * cosTheta;
|
|
1322
|
-
const shinBotW = Math.max(0.01, width + 2 *
|
|
1381
|
+
const shinBotW = Math.max(0.01, width + 2 * reach.x);
|
|
1323
1382
|
const shinBotD = Math.max(0.01, depth + 2 * deckExt);
|
|
1324
1383
|
const deckDrop = deckExt * tanTheta;
|
|
1325
1384
|
const shinBotWh = wallHeight - deckDrop + verticalRt;
|
|
@@ -1453,7 +1512,7 @@ export function getRoofSegmentBrushes(node, options = {}) {
|
|
|
1453
1512
|
const deckTopBrush = toBrush(deckTopGeo);
|
|
1454
1513
|
const deckBotBrush = toBrush(deckBotGeo);
|
|
1455
1514
|
if (deckBotBrush) {
|
|
1456
|
-
const wV = Math.max(0.01, width + 2 *
|
|
1515
|
+
const wV = Math.max(0.01, width + 2 * reach.x);
|
|
1457
1516
|
const dV = Math.max(0.01, depth + 2 * deckExt);
|
|
1458
1517
|
deckBotBrush.scale.set(1 + eps / wV, 1, 1 + eps / dV);
|
|
1459
1518
|
deckBotBrush.updateMatrixWorld();
|
|
@@ -1532,13 +1591,90 @@ export function getRoofSegmentBrushes(node, options = {}) {
|
|
|
1532
1591
|
rakeBoards?.dispose();
|
|
1533
1592
|
return null;
|
|
1534
1593
|
}
|
|
1594
|
+
function buildIndependentRoofSegmentGeometry(node, nodes) {
|
|
1595
|
+
if (!isIndependentRoofSegment(node))
|
|
1596
|
+
return null;
|
|
1597
|
+
const pieces = isGlazedRoofSegment(node)
|
|
1598
|
+
? getGlazedRoofPieces(node)
|
|
1599
|
+
: getBarrelRoofSolidPieces(node);
|
|
1600
|
+
const build = (pieces) => {
|
|
1601
|
+
const parts = pieces.map((piece) => createGeometryFromFaces(getGlazedRoofPieceFaces(piece).map((face) => face.map(({ x, y, z }) => new THREE.Vector3(x, y, z))), piece.slot));
|
|
1602
|
+
const geometry = (parts.length ? mergeGeometries(parts, true) : null) ?? new THREE.BufferGeometry();
|
|
1603
|
+
geometry.groups.forEach((group, index) => {
|
|
1604
|
+
group.materialIndex = parts[index]?.groups[0]?.materialIndex ?? 0;
|
|
1605
|
+
});
|
|
1606
|
+
for (const part of parts)
|
|
1607
|
+
part.dispose();
|
|
1608
|
+
ensureRenderableGeometryAttributes(geometry);
|
|
1609
|
+
return geometry;
|
|
1610
|
+
};
|
|
1611
|
+
let covering = build(pieces.filter((piece) => piece.face.normal[1] > 0));
|
|
1612
|
+
let walls = build(pieces.filter((piece) => piece.face.normal[1] <= 0));
|
|
1613
|
+
const subtract = (geometry, cutter) => {
|
|
1614
|
+
if (!geometry.getAttribute('position')?.count)
|
|
1615
|
+
return geometry;
|
|
1616
|
+
const brush = new Brush(geometry, dummyMats);
|
|
1617
|
+
prepareBrushForCSG(brush);
|
|
1618
|
+
const result = csgEvaluator.evaluate(brush, cutter, SUBTRACTION);
|
|
1619
|
+
const next = result.geometry.clone();
|
|
1620
|
+
const indices = new Map(dummyMats.map((material, index) => [material, index]));
|
|
1621
|
+
for (const group of next.groups)
|
|
1622
|
+
group.materialIndex = mapRoofGroupMaterialIndex(group.materialIndex, csgMaterials(result), indices);
|
|
1623
|
+
result.geometry.dispose();
|
|
1624
|
+
geometry.dispose();
|
|
1625
|
+
return next;
|
|
1626
|
+
};
|
|
1627
|
+
for (const id of node.children ?? []) {
|
|
1628
|
+
const stored = nodes?.[id];
|
|
1629
|
+
if (!stored)
|
|
1630
|
+
continue;
|
|
1631
|
+
const child = getEffectiveNode(stored);
|
|
1632
|
+
if (child.metadata?.isTransient)
|
|
1633
|
+
continue;
|
|
1634
|
+
const capability = nodeRegistry.get(child.type)?.capabilities.roofAccessory;
|
|
1635
|
+
const cutGeometry = capability?.buildCut?.(child, node);
|
|
1636
|
+
if (!cutGeometry)
|
|
1637
|
+
continue;
|
|
1638
|
+
const cutter = new Brush(cutGeometry, dummyMats[0]);
|
|
1639
|
+
try {
|
|
1640
|
+
prepareBrushForCSG(cutter);
|
|
1641
|
+
if (capability?.cutScope !== 'wall')
|
|
1642
|
+
covering = subtract(covering, cutter);
|
|
1643
|
+
walls = subtract(walls, cutter);
|
|
1644
|
+
}
|
|
1645
|
+
finally {
|
|
1646
|
+
cutGeometry.dispose();
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
const parts = [covering, walls]
|
|
1650
|
+
.filter((part) => part.getAttribute('position')?.count)
|
|
1651
|
+
.map((part) => (part.index ? part.toNonIndexed() : part.clone()));
|
|
1652
|
+
let geometry = (parts.length ? mergeGeometries(parts, false) : null) ?? new THREE.BufferGeometry();
|
|
1653
|
+
let offset = 0;
|
|
1654
|
+
for (const part of parts) {
|
|
1655
|
+
for (const group of part.groups)
|
|
1656
|
+
geometry.addGroup(offset + group.start, group.count, group.materialIndex);
|
|
1657
|
+
offset += part.getAttribute('position').count;
|
|
1658
|
+
part.dispose();
|
|
1659
|
+
}
|
|
1660
|
+
covering.dispose();
|
|
1661
|
+
walls.dispose();
|
|
1662
|
+
geometry = clipGeometryBySegmentTrim(geometry, node) ?? geometry;
|
|
1663
|
+
ensureRenderableGeometryAttributes(geometry);
|
|
1664
|
+
return geometry;
|
|
1665
|
+
}
|
|
1535
1666
|
export function generateRoofSegmentGeometry(node, nodes) {
|
|
1536
1667
|
const parentNode = node.parentId ? nodes?.[node.parentId] : undefined;
|
|
1537
1668
|
const parentRoof = parentNode?.type === 'roof' ? parentNode : undefined;
|
|
1538
1669
|
const parentRoofPosition = parentRoof?.position;
|
|
1539
1670
|
const parentRoofRotation = parentRoof?.rotation ?? 0;
|
|
1540
1671
|
const segmentWorldMatrix = composeSegmentWorldMatrix(parentRoofPosition, parentRoofRotation, node.position, node.rotation ?? 0);
|
|
1541
|
-
const directSegmentGeometry = withSegmentUvMatrix(segmentWorldMatrix, () =>
|
|
1672
|
+
const directSegmentGeometry = withSegmentUvMatrix(segmentWorldMatrix, () => {
|
|
1673
|
+
const independent = buildIndependentRoofSegmentGeometry(node, nodes);
|
|
1674
|
+
if (independent)
|
|
1675
|
+
return independent;
|
|
1676
|
+
return buildCustomShedGeometry(node, nodes) ?? buildDirectConicalSectorGeometry(node);
|
|
1677
|
+
});
|
|
1542
1678
|
if (directSegmentGeometry) {
|
|
1543
1679
|
let result = addShedInsetEndPanels(directSegmentGeometry, [node], false);
|
|
1544
1680
|
if (nodes) {
|
|
@@ -1617,7 +1753,7 @@ export function generateRoofSegmentGeometry(node, nodes) {
|
|
|
1617
1753
|
ensureRenderableGeometryAttributes(resultGeo);
|
|
1618
1754
|
return resultGeo;
|
|
1619
1755
|
}
|
|
1620
|
-
function clipDirectRoofGeometryAgainstSiblings(geometry, node, nodes, space) {
|
|
1756
|
+
export function clipDirectRoofGeometryAgainstSiblings(geometry, node, nodes, space) {
|
|
1621
1757
|
const siblingInterior = buildOccludingRoofInterior(node, nodes, space);
|
|
1622
1758
|
if (!siblingInterior)
|
|
1623
1759
|
return geometry;
|
|
@@ -3171,7 +3307,21 @@ function pushRoofUv(uvs, point, normal) {
|
|
|
3171
3307
|
* to the true roof surface even on multi-slope roofs (gambrel/mansard/dutch).
|
|
3172
3308
|
*/
|
|
3173
3309
|
export function getRoofOuterSurfaceFrameAtPoint(segment, lx, lz) {
|
|
3174
|
-
|
|
3310
|
+
if (isGlazedRoofSegment(segment) || segment.roofType === 'barrel') {
|
|
3311
|
+
const face = (isGlazedRoofSegment(segment) ? getGlazedRoofFaces(segment) : getBarrelRoofFaces(segment)).find((face) => face.normal[1] > 0 &&
|
|
3312
|
+
lx >= face.origin[0] &&
|
|
3313
|
+
lx <= face.origin[0] + face.width &&
|
|
3314
|
+
(lz - face.origin[2]) / face.v[2] >= -1e-6 &&
|
|
3315
|
+
(lz - face.origin[2]) / face.v[2] <= face.length + 1e-6);
|
|
3316
|
+
if (face) {
|
|
3317
|
+
const v = (lz - face.origin[2]) / face.v[2];
|
|
3318
|
+
return {
|
|
3319
|
+
point: new THREE.Vector3(lx, face.origin[1] + face.v[1] * v, lz),
|
|
3320
|
+
normal: new THREE.Vector3(...face.normal),
|
|
3321
|
+
};
|
|
3322
|
+
}
|
|
3323
|
+
}
|
|
3324
|
+
const { roofType, width, depth, wallHeight, deckThickness, shingleThickness } = segment;
|
|
3175
3325
|
const { activeRh, tanTheta, cosTheta, sinTheta } = getSegmentSlopeFrame(segment);
|
|
3176
3326
|
if (roofType === 'flat' || activeRh === 0) {
|
|
3177
3327
|
return {
|
|
@@ -3180,11 +3330,11 @@ export function getRoofOuterSurfaceFrameAtPoint(segment, lx, lz) {
|
|
|
3180
3330
|
};
|
|
3181
3331
|
}
|
|
3182
3332
|
const verticalRt = deckThickness / cosTheta;
|
|
3183
|
-
const
|
|
3184
|
-
const deckExt =
|
|
3333
|
+
const reach = getRoofSegmentDeckReach(segment);
|
|
3334
|
+
const deckExt = reach.z;
|
|
3185
3335
|
const stSin = shingleThickness * sinTheta;
|
|
3186
3336
|
const stCos = shingleThickness * cosTheta;
|
|
3187
|
-
const shinBotW = Math.max(0.01, width + 2 *
|
|
3337
|
+
const shinBotW = Math.max(0.01, width + 2 * reach.x);
|
|
3188
3338
|
const shinBotD = Math.max(0.01, depth + 2 * deckExt);
|
|
3189
3339
|
const deckDrop = deckExt * tanTheta;
|
|
3190
3340
|
const shinBotWh = wallHeight - deckDrop + verticalRt;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type WallNode } from '@pascal-app/core';
|
|
2
|
-
import { type Camera, type Material, Matrix4, type Mesh, Vector3 } from 'three';
|
|
1
|
+
import { useScene, type WallNode } from '@pascal-app/core';
|
|
2
|
+
import { type Camera, type Material, Matrix4, type Mesh, type Object3D, Vector3 } from 'three';
|
|
3
3
|
import useViewer, { type WallMode } from '../../store/use-viewer';
|
|
4
4
|
import { type WallMaterialVariant } from './wall-material-variant';
|
|
5
5
|
import { type WallMaterialsResolver } from './wall-materials';
|
|
@@ -26,7 +26,22 @@ type CachedWall = {
|
|
|
26
26
|
export declare const WALL_FACING_HYSTERESIS = 0.001;
|
|
27
27
|
export declare function wallHiddenFromFacing(node: Pick<WallNode, 'frontSide' | 'backSide'>, mode: WallMode, negativeFacing: boolean): boolean;
|
|
28
28
|
export declare function wallFacingNegative(dot: number, previous: boolean | undefined): boolean;
|
|
29
|
-
export type WallCutoutViewerState = Pick<ReturnType<typeof useViewer.getState>, 'wallMode' | 'shading' | 'textures' | 'colorPreset' | 'sceneTheme' | 'selection' | 'previewSelectedIds' | 'hoveredId' | 'hoverHighlightMode'
|
|
29
|
+
export type WallCutoutViewerState = Pick<ReturnType<typeof useViewer.getState>, 'wallMode' | 'shading' | 'textures' | 'colorPreset' | 'sceneTheme' | 'selection' | 'previewSelectedIds' | 'hoveredId' | 'hoverHighlightMode'> & {
|
|
30
|
+
nodesRevealing?: readonly string[];
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* The walls the cutaway must leave in view because something on them is playing: a wall that is
|
|
34
|
+
* moving in, going out or being edited, a wall that hosts an opening that is, and the walls under a
|
|
35
|
+
* node that is (a facade part). The person's wall mode is not touched: these are stamped visible
|
|
36
|
+
* for as long as they play, and the next frame after hides them again as the view wants.
|
|
37
|
+
*/
|
|
38
|
+
export declare function wallsInPlay(nodes: ReturnType<typeof useScene.getState>['nodes'], revealing: readonly string[]): Set<string>;
|
|
39
|
+
/**
|
|
40
|
+
* The visible variant of a mesh's materials, kept on the object by the system that cuts it away, for
|
|
41
|
+
* whoever must draw a copy of the mesh while the cutaway hides it (a deleted wall's ghost). Not in
|
|
42
|
+
* `userData`: `Object3D.clone` copies that through JSON, which would serialize every material.
|
|
43
|
+
*/
|
|
44
|
+
export declare const visibleMaterialsOf: WeakMap<Object3D<import("three").Object3DEventMap>, Material<import("three").MaterialEventMap>[]>;
|
|
30
45
|
export type WallCutoutViewerStore = {
|
|
31
46
|
getState: () => WallCutoutViewerState;
|
|
32
47
|
};
|
|
@@ -35,6 +50,8 @@ export declare class WallCutoutCache {
|
|
|
35
50
|
private readonly materialResolver;
|
|
36
51
|
readonly walls: Map<string, CachedWall>;
|
|
37
52
|
readonly rebuilt: Set<string>;
|
|
53
|
+
/** Walls whose palette waits for a preview to give back their groups (see `mayAssign`). */
|
|
54
|
+
private readonly deferred;
|
|
38
55
|
private viewer;
|
|
39
56
|
private nodes;
|
|
40
57
|
private materials;
|
|
@@ -51,6 +68,9 @@ export declare class WallCutoutCache {
|
|
|
51
68
|
private highlightKey;
|
|
52
69
|
private transformed;
|
|
53
70
|
private overrides;
|
|
71
|
+
/** The reveal's list the walls in play were read from, and the walls it named. */
|
|
72
|
+
private revealing;
|
|
73
|
+
private inPlay;
|
|
54
74
|
constructor(viewerStore?: WallCutoutViewerStore, materialResolver?: WallMaterialsResolver);
|
|
55
75
|
subscribeLiveTransforms(): () => void;
|
|
56
76
|
update(camera: Camera, time: number): void;
|
|
@@ -62,6 +82,15 @@ export declare class WallCutoutCache {
|
|
|
62
82
|
private refreshAppearance;
|
|
63
83
|
private refreshNormal;
|
|
64
84
|
private apply;
|
|
85
|
+
/**
|
|
86
|
+
* A paint preview spread over several walls (a room, all matching) regroups
|
|
87
|
+
* each wall's faces onto its own longer array, but only the hovered wall is
|
|
88
|
+
* known here as a preview's owner. A palette that cannot draw the groups the
|
|
89
|
+
* mesh wears now waits for the preview's restore to put them back: the mesh
|
|
90
|
+
* would otherwise keep groups with no material, and every raycast over it
|
|
91
|
+
* throws on `material.side`.
|
|
92
|
+
*/
|
|
93
|
+
private mayAssign;
|
|
65
94
|
}
|
|
66
95
|
export {};
|
|
67
96
|
//# sourceMappingURL=wall-cutout-cache.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wall-cutout-cache.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-cutout-cache.ts"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"wall-cutout-cache.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-cutout-cache.ts"],"names":[],"mappings":"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"}
|