@pascal-app/viewer 1.0.1 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/renderers/node-renderer.d.ts.map +1 -1
- package/dist/components/renderers/node-renderer.js +8 -3
- package/dist/components/viewer/frame-limiter.d.ts +1 -0
- package/dist/components/viewer/frame-limiter.d.ts.map +1 -1
- package/dist/components/viewer/frame-limiter.js +22 -3
- package/dist/components/viewer/glb-interactive.d.ts +25 -1
- package/dist/components/viewer/glb-interactive.d.ts.map +1 -1
- package/dist/components/viewer/glb-interactive.js +252 -85
- package/dist/components/viewer/glb-mechanisms.d.ts +10 -0
- package/dist/components/viewer/glb-mechanisms.d.ts.map +1 -0
- package/dist/components/viewer/glb-mechanisms.js +19 -0
- package/dist/components/viewer/glb-scene.d.ts +45 -27
- package/dist/components/viewer/glb-scene.d.ts.map +1 -1
- package/dist/components/viewer/glb-scene.js +420 -506
- package/dist/components/viewer/glb-walkthrough-controller.d.ts +1 -1
- package/dist/components/viewer/glb-walkthrough-controller.d.ts.map +1 -1
- package/dist/components/viewer/glb-walkthrough-controller.js +12 -1
- package/dist/components/viewer/index.d.ts +26 -0
- package/dist/components/viewer/index.d.ts.map +1 -1
- package/dist/components/viewer/index.js +108 -29
- package/dist/components/viewer/lights.d.ts.map +1 -1
- package/dist/components/viewer/lights.js +6 -1
- package/dist/components/viewer/perf-monitor.d.ts.map +1 -1
- package/dist/components/viewer/perf-monitor.js +6 -0
- package/dist/components/viewer/post-processing.d.ts.map +1 -1
- package/dist/components/viewer/post-processing.js +8 -3
- package/dist/components/viewer/registered-systems.d.ts.map +1 -1
- package/dist/components/viewer/registered-systems.js +16 -2
- package/dist/components/viewer/render-error.d.ts +18 -0
- package/dist/components/viewer/render-error.d.ts.map +1 -0
- package/dist/components/viewer/render-error.js +29 -0
- package/dist/components/viewer/scene-ground-replacement.d.ts +6 -0
- package/dist/components/viewer/scene-ground-replacement.d.ts.map +1 -1
- package/dist/components/viewer/scene-ground-replacement.js +15 -0
- package/dist/components/viewer/selection-manager.d.ts.map +1 -1
- package/dist/components/viewer/selection-manager.js +10 -0
- package/dist/components/viewer/viewer-camera.d.ts +19 -1
- package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
- package/dist/components/viewer/viewer-camera.js +53 -5
- package/dist/index.d.ts +28 -15
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -18
- package/dist/lib/asset-url.d.ts.map +1 -1
- package/dist/lib/asset-url.js +7 -1
- package/dist/lib/csg-utils.d.ts +1 -1
- package/dist/lib/csg-utils.d.ts.map +1 -1
- package/dist/lib/csg-utils.js +1 -1
- package/dist/lib/display-state.d.ts +25 -0
- package/dist/lib/display-state.d.ts.map +1 -0
- package/dist/lib/display-state.js +1 -0
- package/dist/lib/hero-pose.d.ts +12 -2
- package/dist/lib/hero-pose.d.ts.map +1 -1
- package/dist/lib/hero-pose.js +72 -5
- package/dist/lib/ink-edges.d.ts +4 -2
- package/dist/lib/ink-edges.d.ts.map +1 -1
- package/dist/lib/ink-edges.js +17 -7
- package/dist/lib/key-light-override.d.ts +11 -0
- package/dist/lib/key-light-override.d.ts.map +1 -0
- package/dist/lib/key-light-override.js +14 -0
- package/dist/lib/live-frame-hold.d.ts +14 -0
- package/dist/lib/live-frame-hold.d.ts.map +1 -0
- package/dist/lib/live-frame-hold.js +28 -0
- package/dist/lib/materials.d.ts +14 -7
- package/dist/lib/materials.d.ts.map +1 -1
- package/dist/lib/materials.js +31 -5
- package/dist/lib/node-top-surface-height.d.ts +6 -0
- package/dist/lib/node-top-surface-height.d.ts.map +1 -0
- package/dist/lib/node-top-surface-height.js +74 -0
- package/dist/lib/perf-probe-state.d.ts +39 -0
- package/dist/lib/perf-probe-state.d.ts.map +1 -0
- package/dist/lib/perf-probe-state.js +17 -0
- package/dist/lib/pointer-events.d.ts +8 -0
- package/dist/lib/pointer-events.d.ts.map +1 -1
- package/dist/lib/pointer-events.js +27 -4
- package/dist/lib/procedural-emission.d.ts +7 -0
- package/dist/lib/procedural-emission.d.ts.map +1 -0
- package/dist/lib/procedural-emission.js +54 -0
- package/dist/lib/renderer-capability.d.ts +2 -1
- package/dist/lib/renderer-capability.d.ts.map +1 -1
- package/dist/lib/renderer-capability.js +2 -2
- package/dist/lib/scene-support-height.d.ts +4 -0
- package/dist/lib/scene-support-height.d.ts.map +1 -0
- package/dist/lib/scene-support-height.js +34 -0
- package/dist/lib/scene-visibility.d.ts +6 -0
- package/dist/lib/scene-visibility.d.ts.map +1 -1
- package/dist/lib/scene-visibility.js +15 -3
- package/dist/lib/scripted-opening.d.ts +11 -0
- package/dist/lib/scripted-opening.d.ts.map +1 -0
- package/dist/lib/scripted-opening.js +20 -0
- package/dist/lib/snapshot-pipeline.d.ts +32 -2
- package/dist/lib/snapshot-pipeline.d.ts.map +1 -1
- package/dist/lib/snapshot-pipeline.js +215 -105
- package/dist/lib/terrain-perimeter-fill.d.ts.map +1 -1
- package/dist/lib/terrain-perimeter-fill.js +22 -14
- package/dist/lib/zone-geometry.d.ts +11 -0
- package/dist/lib/zone-geometry.d.ts.map +1 -0
- package/dist/lib/zone-geometry.js +46 -0
- package/dist/store/use-item-light-pool.d.ts +14 -10
- package/dist/store/use-item-light-pool.d.ts.map +1 -1
- package/dist/store/use-item-light-pool.js +52 -26
- package/dist/store/use-viewer.d.ts +11 -0
- package/dist/store/use-viewer.d.ts.map +1 -1
- package/dist/store/use-viewer.js +13 -6
- package/dist/systems/ceiling/ceiling-system.d.ts +15 -1
- package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
- package/dist/systems/ceiling/ceiling-system.js +86 -6
- package/dist/systems/door/door-system.d.ts.map +1 -1
- package/dist/systems/door/door-system.js +73 -22
- package/dist/systems/floor-elevation/floor-elevation-system.d.ts +3 -1
- package/dist/systems/floor-elevation/floor-elevation-system.d.ts.map +1 -1
- package/dist/systems/floor-elevation/floor-elevation-system.js +148 -5
- package/dist/systems/geometry/geometry-system.d.ts.map +1 -1
- package/dist/systems/geometry/geometry-system.js +71 -11
- package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
- package/dist/systems/interactive/interactive-system.js +40 -15
- package/dist/systems/interactive/procedural-controls.d.ts +13 -0
- package/dist/systems/interactive/procedural-controls.d.ts.map +1 -0
- package/dist/systems/interactive/procedural-controls.js +17 -0
- package/dist/systems/interactive/scripted-clips.d.ts +19 -0
- package/dist/systems/interactive/scripted-clips.d.ts.map +1 -0
- package/dist/systems/interactive/scripted-clips.js +134 -0
- package/dist/systems/item/item-system.d.ts.map +1 -1
- package/dist/systems/item/item-system.js +9 -3
- package/dist/systems/item/object-cut-invalidation.d.ts +3 -0
- package/dist/systems/item/object-cut-invalidation.d.ts.map +1 -0
- package/dist/systems/item/object-cut-invalidation.js +68 -0
- package/dist/systems/item-light/item-light-system.d.ts +1 -1
- package/dist/systems/item-light/item-light-system.d.ts.map +1 -1
- package/dist/systems/item-light/item-light-system.js +47 -52
- package/dist/systems/level/level-system.d.ts.map +1 -1
- package/dist/systems/level/level-system.js +17 -19
- package/dist/systems/level/level-utils.d.ts +28 -1
- package/dist/systems/level/level-utils.d.ts.map +1 -1
- package/dist/systems/level/level-utils.js +36 -4
- package/dist/systems/level/shadow-only-levels.d.ts +9 -0
- package/dist/systems/level/shadow-only-levels.d.ts.map +1 -0
- package/dist/systems/level/shadow-only-levels.js +7 -0
- package/dist/systems/roof/roof-materials.d.ts +14 -2
- package/dist/systems/roof/roof-materials.d.ts.map +1 -1
- package/dist/systems/roof/roof-materials.js +54 -15
- package/dist/systems/roof/roof-system.d.ts +13 -3
- package/dist/systems/roof/roof-system.d.ts.map +1 -1
- package/dist/systems/roof/roof-system.js +188 -13
- package/dist/systems/slab/slab-system.d.ts +1 -1
- package/dist/systems/slab/slab-system.d.ts.map +1 -1
- package/dist/systems/slab/slab-system.js +49 -19
- package/dist/systems/stair/stair-system.d.ts.map +1 -1
- package/dist/systems/stair/stair-system.js +133 -354
- package/dist/systems/wall/wall-cutout-cache.d.ts +11 -1
- package/dist/systems/wall/wall-cutout-cache.d.ts.map +1 -1
- package/dist/systems/wall/wall-cutout-cache.js +58 -20
- package/dist/systems/wall/wall-cutout.d.ts +9 -1
- package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
- package/dist/systems/wall/wall-cutout.js +46 -32
- package/dist/systems/wall/wall-finish-data.d.ts +22 -0
- package/dist/systems/wall/wall-finish-data.d.ts.map +1 -0
- package/dist/systems/wall/wall-finish-data.js +17 -0
- package/dist/systems/wall/wall-materials.d.ts +17 -3
- package/dist/systems/wall/wall-materials.d.ts.map +1 -1
- package/dist/systems/wall/wall-materials.js +109 -156
- package/dist/systems/wall/wall-system.d.ts +43 -4
- package/dist/systems/wall/wall-system.d.ts.map +1 -1
- package/dist/systems/wall/wall-system.js +601 -163
- 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 +44 -24
- package/dist/xr/presentation-context.d.ts +7 -0
- package/dist/xr/presentation-context.d.ts.map +1 -0
- package/dist/xr/presentation-context.js +10 -0
- package/package.json +6 -6
- package/dist/systems/fence/fence-system.d.ts +0 -22
- package/dist/systems/fence/fence-system.d.ts.map +0 -1
- package/dist/systems/fence/fence-system.js +0 -357
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createStairFlightFromStair, getEffectiveNode, getFloorStackedPosition, sceneRegistry, useScene, } from '@pascal-app/core';
|
|
1
|
+
import { computeSegmentTransforms, createStairFlightFromStair, getEffectiveNode, getFloorStackedPosition, measureStairDetail, resolveStraightStairConstruction, resolveWinderStairConstruction, sceneRegistry, stairSegmentConstructionError, stairSegmentDetailError, useScene, } from '@pascal-app/core';
|
|
2
2
|
import { useFrame } from '@react-three/fiber';
|
|
3
3
|
import * as THREE from 'three';
|
|
4
4
|
import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
|
|
@@ -115,6 +115,19 @@ export const StairSystem = () => {
|
|
|
115
115
|
}, 5);
|
|
116
116
|
return null;
|
|
117
117
|
};
|
|
118
|
+
function mergeConstructionGeometries(geometries) {
|
|
119
|
+
const result = mergeGeometries(geometries, true) ?? createEmptyGeometry();
|
|
120
|
+
result.clearGroups();
|
|
121
|
+
let offset = 0;
|
|
122
|
+
for (const geometry of geometries) {
|
|
123
|
+
for (const group of geometry.groups)
|
|
124
|
+
result.addGroup(offset + group.start, group.count, group.materialIndex);
|
|
125
|
+
offset += geometry.getAttribute('position').count;
|
|
126
|
+
geometry.dispose();
|
|
127
|
+
}
|
|
128
|
+
result.userData.stairConstructionGroups = true;
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
118
131
|
// ============================================================================
|
|
119
132
|
// SEGMENT GEOMETRY
|
|
120
133
|
// ============================================================================
|
|
@@ -122,7 +135,91 @@ export const StairSystem = () => {
|
|
|
122
135
|
* Generates the step/landing profile as a THREE.Shape (in the XY plane),
|
|
123
136
|
* then extrudes along Z for the segment width.
|
|
124
137
|
*/
|
|
125
|
-
function generateStairSegmentGeometry(segment, absoluteHeight) {
|
|
138
|
+
function generateStairSegmentGeometry(segment, absoluteHeight, parent) {
|
|
139
|
+
if (stairSegmentDetailError(segment) || stairSegmentConstructionError(segment, parent))
|
|
140
|
+
return createEmptyGeometry();
|
|
141
|
+
const winderPieces = resolveWinderStairConstruction(segment, absoluteHeight, parent);
|
|
142
|
+
if (winderPieces) {
|
|
143
|
+
const geometries = winderPieces.map((piece) => {
|
|
144
|
+
const polygon = piece.polygon;
|
|
145
|
+
const faces = THREE.ShapeUtils.triangulateShape(polygon.map(([x, z]) => new THREE.Vector2(x, z)), []);
|
|
146
|
+
const positions = [], materials = [];
|
|
147
|
+
const vertex = (i, top) => [
|
|
148
|
+
polygon[i][0],
|
|
149
|
+
top ? piece.top : piece.bottom[i],
|
|
150
|
+
polygon[i][1],
|
|
151
|
+
];
|
|
152
|
+
const triangle = (a, b, c, material) => {
|
|
153
|
+
positions.push(...a, ...b, ...c);
|
|
154
|
+
materials.push(material);
|
|
155
|
+
};
|
|
156
|
+
for (const face of faces) {
|
|
157
|
+
let [a, b, c] = face;
|
|
158
|
+
const pa = polygon[a], pb = polygon[b], pc = polygon[c];
|
|
159
|
+
if ((pb[0] - pa[0]) * (pc[1] - pa[1]) - (pb[1] - pa[1]) * (pc[0] - pa[0]) < 0)
|
|
160
|
+
[b, c] = [c, b];
|
|
161
|
+
triangle(vertex(a, true), vertex(c, true), vertex(b, true), piece.role === 'tread' || piece.walkingTop ? 0 : 1);
|
|
162
|
+
triangle(vertex(a, false), vertex(b, false), vertex(c, false), piece.role === 'tread' ? 0 : 1);
|
|
163
|
+
}
|
|
164
|
+
const signedArea = polygon.reduce((sum, a, i) => {
|
|
165
|
+
const b = polygon[(i + 1) % polygon.length];
|
|
166
|
+
return sum + a[0] * b[1] - a[1] * b[0];
|
|
167
|
+
}, 0);
|
|
168
|
+
const ring = signedArea > 0 ? polygon.map((_, i) => i) : polygon.map((_, i) => polygon.length - 1 - i);
|
|
169
|
+
for (let k = 0; k < ring.length; k++) {
|
|
170
|
+
const i = ring[k], j = ring[(k + 1) % ring.length];
|
|
171
|
+
triangle(vertex(i, true), vertex(j, true), vertex(j, false), piece.role === 'tread' ? 0 : 1);
|
|
172
|
+
triangle(vertex(i, true), vertex(j, false), vertex(i, false), piece.role === 'tread' ? 0 : 1);
|
|
173
|
+
}
|
|
174
|
+
const geometry = new THREE.BufferGeometry();
|
|
175
|
+
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
|
|
176
|
+
geometry.computeVertexNormals();
|
|
177
|
+
const uvs = [];
|
|
178
|
+
const a = new THREE.Vector3(), b = new THREE.Vector3(), c = new THREE.Vector3();
|
|
179
|
+
const u = new THREE.Vector3(), normal = new THREE.Vector3(), v = new THREE.Vector3();
|
|
180
|
+
for (let i = 0; i < positions.length; i += 9) {
|
|
181
|
+
a.fromArray(positions, i);
|
|
182
|
+
b.fromArray(positions, i + 3);
|
|
183
|
+
c.fromArray(positions, i + 6);
|
|
184
|
+
u.subVectors(b, a).normalize();
|
|
185
|
+
normal.subVectors(b, a).cross(v.subVectors(c, a)).normalize();
|
|
186
|
+
v.crossVectors(normal, u);
|
|
187
|
+
uvs.push(a.dot(u), a.dot(v), b.dot(u), b.dot(v), c.dot(u), c.dot(v));
|
|
188
|
+
}
|
|
189
|
+
geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2));
|
|
190
|
+
ensureUv2Attribute(geometry);
|
|
191
|
+
materials.forEach((material, i) => {
|
|
192
|
+
geometry.addGroup(i * 3, 3, material);
|
|
193
|
+
});
|
|
194
|
+
return geometry;
|
|
195
|
+
});
|
|
196
|
+
return mergeConstructionGeometries(geometries);
|
|
197
|
+
}
|
|
198
|
+
const pieces = resolveStraightStairConstruction(segment, absoluteHeight, parent);
|
|
199
|
+
if (pieces) {
|
|
200
|
+
const geometries = pieces.map((piece) => {
|
|
201
|
+
const shape = new THREE.Shape(piece.profile.map(([z, y]) => new THREE.Vector2(z, y)));
|
|
202
|
+
const geometry = new THREE.ExtrudeGeometry(shape, {
|
|
203
|
+
steps: 1,
|
|
204
|
+
depth: piece.x1 - piece.x0,
|
|
205
|
+
bevelEnabled: false,
|
|
206
|
+
});
|
|
207
|
+
const matrix = new THREE.Matrix4().makeRotationY(-Math.PI / 2);
|
|
208
|
+
matrix.setPosition(piece.x1, 0, 0);
|
|
209
|
+
geometry.applyMatrix4(matrix);
|
|
210
|
+
geometry.computeVertexNormals();
|
|
211
|
+
applyStairSegmentUvs(geometry);
|
|
212
|
+
ensureUv2Attribute(geometry);
|
|
213
|
+
if (piece.role === 'tread' || !piece.walkingTop) {
|
|
214
|
+
geometry.clearGroups();
|
|
215
|
+
geometry.addGroup(0, geometry.getAttribute('position').count, piece.role === 'tread' ? STAIR_TREAD_MATERIAL_INDEX : STAIR_SIDE_MATERIAL_INDEX);
|
|
216
|
+
}
|
|
217
|
+
else
|
|
218
|
+
applyStraightStairMaterialGroups(geometry);
|
|
219
|
+
return geometry;
|
|
220
|
+
});
|
|
221
|
+
return mergeConstructionGeometries(geometries);
|
|
222
|
+
}
|
|
126
223
|
const { width, length, height, stepCount, segmentType, fillToFloor, thickness } = segment;
|
|
127
224
|
const shape = new THREE.Shape();
|
|
128
225
|
if (segmentType === 'landing') {
|
|
@@ -194,7 +291,9 @@ function generateStairSegmentGeometry(segment, absoluteHeight) {
|
|
|
194
291
|
function updateStairSegmentGeometry(node, mesh) {
|
|
195
292
|
// Compute absolute height from parent chain
|
|
196
293
|
const absoluteHeight = computeAbsoluteHeight(node);
|
|
197
|
-
const
|
|
294
|
+
const parent = node.parentId ? useScene.getState().nodes[node.parentId] : undefined;
|
|
295
|
+
const effectiveParent = parent?.type === 'stair' ? getEffectiveNode(parent) : undefined;
|
|
296
|
+
const newGeometry = generateStairSegmentGeometry(node, absoluteHeight, effectiveParent);
|
|
198
297
|
applyStraightStairMaterialGroups(newGeometry);
|
|
199
298
|
mesh.geometry.dispose();
|
|
200
299
|
mesh.geometry = newGeometry;
|
|
@@ -223,6 +322,8 @@ function syncSegmentMeshTransforms(stairNode, nodes) {
|
|
|
223
322
|
const transform = transforms[i];
|
|
224
323
|
const mesh = sceneRegistry.nodes.get(segment.id);
|
|
225
324
|
if (mesh) {
|
|
325
|
+
if (mesh.position.y !== transform.position[1])
|
|
326
|
+
useScene.getState().markDirty(segment.id);
|
|
226
327
|
mesh.position.set(transform.position[0], transform.position[1], transform.position[2]);
|
|
227
328
|
mesh.rotation.y = transform.rotation;
|
|
228
329
|
}
|
|
@@ -274,14 +375,23 @@ function updateMergedStairGeometry(stairNode, group, nodes) {
|
|
|
274
375
|
// it is switched to straight. Draw the flight its own fields describe
|
|
275
376
|
// instead of vanishing; it is the same flight the panel materializes.
|
|
276
377
|
const bodySegments = segments.length > 0 ? segments : [createStairFlightFromStair(stairNode, nodes)];
|
|
378
|
+
if (measureStairDetail(stairNode, bodySegments).error) {
|
|
379
|
+
replaceMeshGeometry(mergedMesh, createEmptyGeometry());
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
277
382
|
// Compute chained transforms for segments
|
|
278
383
|
const transforms = computeSegmentTransforms(bodySegments);
|
|
279
384
|
const geometries = [];
|
|
385
|
+
const materialOffsets = [];
|
|
280
386
|
for (let i = 0; i < bodySegments.length; i++) {
|
|
281
387
|
const segment = bodySegments[i];
|
|
388
|
+
if (segment.visible === false)
|
|
389
|
+
continue;
|
|
390
|
+
materialOffsets.push(i * 2);
|
|
282
391
|
const transform = transforms[i];
|
|
283
392
|
const absoluteHeight = transform.position[1];
|
|
284
|
-
const geo = generateStairSegmentGeometry(segment, absoluteHeight);
|
|
393
|
+
const geo = generateStairSegmentGeometry(segment, absoluteHeight, stairNode);
|
|
394
|
+
applyStraightStairMaterialGroups(geo);
|
|
285
395
|
// Apply segment transform (position + rotation) relative to parent stair
|
|
286
396
|
_position.set(transform.position[0], transform.position[1], transform.position[2]);
|
|
287
397
|
_quaternion.setFromAxisAngle(_yAxis, transform.rotation);
|
|
@@ -289,8 +399,17 @@ function updateMergedStairGeometry(stairNode, group, nodes) {
|
|
|
289
399
|
geo.applyMatrix4(_matrix);
|
|
290
400
|
geometries.push(geo);
|
|
291
401
|
}
|
|
292
|
-
const merged = mergeGeometries(geometries, false) ?? createEmptyGeometry();
|
|
293
|
-
|
|
402
|
+
const merged = (geometries.length ? mergeGeometries(geometries, false) : null) ?? createEmptyGeometry();
|
|
403
|
+
merged.clearGroups();
|
|
404
|
+
let vertexOffset = 0;
|
|
405
|
+
for (const [index, geometry] of geometries.entries()) {
|
|
406
|
+
for (const group of geometry.groups)
|
|
407
|
+
merged.addGroup(vertexOffset + group.start, group.count, materialOffsets[index] + (group.materialIndex ?? 0));
|
|
408
|
+
vertexOffset += geometry.getAttribute('position').count;
|
|
409
|
+
}
|
|
410
|
+
mergedMesh.userData.slotIds = bodySegments.flatMap(() => ['treads', 'body']);
|
|
411
|
+
mergedMesh.userData.surfaceNodeIds = bodySegments.flatMap((segment) => [segment.id, segment.id]);
|
|
412
|
+
mergedMesh.userData.segmentIds = bodySegments.flatMap((segment) => [segment.id, segment.id]);
|
|
294
413
|
replaceMeshGeometry(mergedMesh, merged);
|
|
295
414
|
// Dispose individual geometries
|
|
296
415
|
for (const geo of geometries) {
|
|
@@ -298,6 +417,8 @@ function updateMergedStairGeometry(stairNode, group, nodes) {
|
|
|
298
417
|
}
|
|
299
418
|
}
|
|
300
419
|
function applyStraightStairMaterialGroups(geometry) {
|
|
420
|
+
if (geometry.userData.stairConstructionGroups)
|
|
421
|
+
return;
|
|
301
422
|
const position = geometry.getAttribute('position');
|
|
302
423
|
if (!position || position.count < 3) {
|
|
303
424
|
geometry.clearGroups();
|
|
@@ -377,57 +498,9 @@ function ensureUv2Attribute(geometry) {
|
|
|
377
498
|
return;
|
|
378
499
|
geometry.setAttribute('uv2', new THREE.Float32BufferAttribute(Array.from(uv.array), 2));
|
|
379
500
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
*/
|
|
384
|
-
function computeSegmentTransforms(segments) {
|
|
385
|
-
const transforms = [];
|
|
386
|
-
let currentPos = new THREE.Vector3(0, 0, 0);
|
|
387
|
-
let currentRot = 0;
|
|
388
|
-
for (let i = 0; i < segments.length; i++) {
|
|
389
|
-
const segment = segments[i];
|
|
390
|
-
if (i === 0) {
|
|
391
|
-
transforms.push({
|
|
392
|
-
position: [currentPos.x, currentPos.y, currentPos.z],
|
|
393
|
-
rotation: currentRot,
|
|
394
|
-
});
|
|
395
|
-
}
|
|
396
|
-
else {
|
|
397
|
-
const prev = segments[i - 1];
|
|
398
|
-
const localAttachPos = new THREE.Vector3();
|
|
399
|
-
let rotChange = 0;
|
|
400
|
-
switch (segment.attachmentSide) {
|
|
401
|
-
case 'front':
|
|
402
|
-
localAttachPos.set(0, prev.height, prev.length);
|
|
403
|
-
rotChange = 0;
|
|
404
|
-
break;
|
|
405
|
-
case 'left':
|
|
406
|
-
localAttachPos.set(prev.width / 2, prev.height, prev.length / 2);
|
|
407
|
-
rotChange = Math.PI / 2;
|
|
408
|
-
break;
|
|
409
|
-
case 'right':
|
|
410
|
-
localAttachPos.set(-prev.width / 2, prev.height, prev.length / 2);
|
|
411
|
-
rotChange = -Math.PI / 2;
|
|
412
|
-
break;
|
|
413
|
-
}
|
|
414
|
-
// Rotate local attachment point by previous global rotation
|
|
415
|
-
localAttachPos.applyAxisAngle(new THREE.Vector3(0, 1, 0), currentRot);
|
|
416
|
-
currentPos = currentPos.clone().add(localAttachPos);
|
|
417
|
-
currentRot += rotChange;
|
|
418
|
-
transforms.push({
|
|
419
|
-
position: [currentPos.x, currentPos.y, currentPos.z],
|
|
420
|
-
rotation: currentRot,
|
|
421
|
-
});
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
return transforms;
|
|
425
|
-
}
|
|
426
|
-
function rotateXZ(x, z, angle) {
|
|
427
|
-
const cos = Math.cos(angle);
|
|
428
|
-
const sin = Math.sin(angle);
|
|
429
|
-
return [x * cos + z * sin, -x * sin + z * cos];
|
|
430
|
-
}
|
|
501
|
+
// ============================================================================
|
|
502
|
+
// SEGMENT CHAINING
|
|
503
|
+
// ============================================================================
|
|
431
504
|
function createEmptyGeometry() {
|
|
432
505
|
const geometry = new THREE.BufferGeometry();
|
|
433
506
|
// Three zero-vertices (one degenerate, invisible triangle), not an empty
|
|
@@ -446,301 +519,6 @@ function replaceMeshGeometry(mesh, geometry) {
|
|
|
446
519
|
mesh.geometry.dispose();
|
|
447
520
|
mesh.geometry = geometry;
|
|
448
521
|
}
|
|
449
|
-
function generateStairRailingGeometry(stairNode, segments, transforms) {
|
|
450
|
-
const railingMode = stairNode.railingMode ?? 'none';
|
|
451
|
-
if (railingMode === 'none') {
|
|
452
|
-
return createEmptyGeometry();
|
|
453
|
-
}
|
|
454
|
-
const railHeight = Math.max(0.5, stairNode.railingHeight ?? 0.92);
|
|
455
|
-
const midRailHeight = Math.max(railHeight * 0.45, 0.35);
|
|
456
|
-
const railRadius = 0.022;
|
|
457
|
-
const postRadius = 0.018;
|
|
458
|
-
const inset = 0.06;
|
|
459
|
-
const landingInset = 0.08;
|
|
460
|
-
const geometries = [];
|
|
461
|
-
const segmentRailPaths = buildStairRailPaths(segments, transforms, railingMode, inset, landingInset);
|
|
462
|
-
for (const segmentRailPath of segmentRailPaths) {
|
|
463
|
-
for (const sidePath of segmentRailPath.sidePaths) {
|
|
464
|
-
const points = sidePath.points;
|
|
465
|
-
if (points.length === 0)
|
|
466
|
-
continue;
|
|
467
|
-
geometries.push(...buildBalusterGeometries(points, railHeight, postRadius));
|
|
468
|
-
geometries.push(...buildOffsetRailSegmentGeometries(points, railHeight, railRadius));
|
|
469
|
-
geometries.push(...buildOffsetRailSegmentGeometries(points, midRailHeight, railRadius * 0.8));
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
for (let index = 1; index < segmentRailPaths.length; index++) {
|
|
473
|
-
const previousPath = segmentRailPaths[index - 1];
|
|
474
|
-
const currentPath = segmentRailPaths[index];
|
|
475
|
-
if (!(previousPath && currentPath?.connectFromPrevious))
|
|
476
|
-
continue;
|
|
477
|
-
if (previousPath.segment.segmentType === 'landing')
|
|
478
|
-
continue;
|
|
479
|
-
for (const sidePath of currentPath.sidePaths) {
|
|
480
|
-
if (currentPath.segment.segmentType === 'landing')
|
|
481
|
-
continue;
|
|
482
|
-
const currentPoint = sidePath.points[0];
|
|
483
|
-
if (!currentPoint)
|
|
484
|
-
continue;
|
|
485
|
-
const previousSidePath = [...previousPath.sidePaths]
|
|
486
|
-
.map((entry) => ({
|
|
487
|
-
entry,
|
|
488
|
-
distance: entry.points.length
|
|
489
|
-
? entry.points[entry.points.length - 1].distanceTo(currentPoint)
|
|
490
|
-
: Number.POSITIVE_INFINITY,
|
|
491
|
-
}))
|
|
492
|
-
.sort((left, right) => left.distance - right.distance)[0]?.entry;
|
|
493
|
-
const previousPoint = previousSidePath && previousSidePath.points.length > 0
|
|
494
|
-
? previousSidePath.points[previousSidePath.points.length - 1]
|
|
495
|
-
: null;
|
|
496
|
-
if (!(previousPoint && currentPoint))
|
|
497
|
-
continue;
|
|
498
|
-
const connectorPoints = [previousPoint, currentPoint];
|
|
499
|
-
geometries.push(...buildOffsetRailSegmentGeometries(connectorPoints, railHeight, railRadius));
|
|
500
|
-
geometries.push(...buildOffsetRailSegmentGeometries(connectorPoints, midRailHeight, railRadius * 0.8));
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
const merged = mergeGeometries(geometries, false) ?? createEmptyGeometry();
|
|
504
|
-
for (const geometry of geometries) {
|
|
505
|
-
geometry.dispose();
|
|
506
|
-
}
|
|
507
|
-
return merged;
|
|
508
|
-
}
|
|
509
|
-
function buildStairRailPaths(segments, transforms, railingMode, inset, landingInset) {
|
|
510
|
-
const layouts = computeStairRailLayouts(segments, transforms);
|
|
511
|
-
if (railingMode === 'both') {
|
|
512
|
-
const isStraightLineDoubleLandingLayout = segments.length === 4 &&
|
|
513
|
-
segments[0]?.segmentType === 'stair' &&
|
|
514
|
-
segments[1]?.segmentType === 'landing' &&
|
|
515
|
-
segments[2]?.segmentType === 'stair' &&
|
|
516
|
-
segments[2]?.attachmentSide === 'front' &&
|
|
517
|
-
segments[3]?.segmentType === 'landing' &&
|
|
518
|
-
segments[3]?.attachmentSide === 'front';
|
|
519
|
-
return layouts.map((layout, index) => {
|
|
520
|
-
const segment = layout.segment;
|
|
521
|
-
const previousSegment = index > 0 ? segments[index - 1] : undefined;
|
|
522
|
-
const nextSegment = index < segments.length - 1 ? segments[index + 1] : undefined;
|
|
523
|
-
const hideLandingRailing = segment.segmentType === 'landing' &&
|
|
524
|
-
previousSegment?.segmentType === 'stair' &&
|
|
525
|
-
nextSegment?.segmentType === 'stair';
|
|
526
|
-
const visualTurnSide = nextSegment?.attachmentSide;
|
|
527
|
-
const sideCandidates = hideLandingRailing
|
|
528
|
-
? visualTurnSide === 'left'
|
|
529
|
-
? ['front', 'right']
|
|
530
|
-
: visualTurnSide === 'right'
|
|
531
|
-
? ['front', 'left']
|
|
532
|
-
: ['left', 'right']
|
|
533
|
-
: segment.segmentType === 'landing'
|
|
534
|
-
? nextSegment?.segmentType === 'landing' && visualTurnSide === 'left'
|
|
535
|
-
? ['front', 'right']
|
|
536
|
-
: nextSegment?.segmentType === 'landing' && visualTurnSide === 'right'
|
|
537
|
-
? ['front', 'left']
|
|
538
|
-
: visualTurnSide === 'left'
|
|
539
|
-
? ['right']
|
|
540
|
-
: visualTurnSide === 'right'
|
|
541
|
-
? ['left']
|
|
542
|
-
: ['left', 'right']
|
|
543
|
-
: ['left', 'right'];
|
|
544
|
-
const sidePaths = sideCandidates
|
|
545
|
-
.map((side) => buildSegmentRailPath(layout, side, previousSegment, nextSegment, inset, landingInset))
|
|
546
|
-
.filter((entry) => entry !== null);
|
|
547
|
-
return {
|
|
548
|
-
segment,
|
|
549
|
-
sidePaths: isStraightLineDoubleLandingLayout && index === 1
|
|
550
|
-
? ['left', 'right']
|
|
551
|
-
.map((side) => buildSegmentRailPath(layout, side, previousSegment, nextSegment, inset, landingInset))
|
|
552
|
-
.filter((entry) => entry !== null)
|
|
553
|
-
: sidePaths,
|
|
554
|
-
connectFromPrevious: index > 0 &&
|
|
555
|
-
!(previousSegment?.segmentType === 'landing' && segment.segmentType === 'landing'),
|
|
556
|
-
};
|
|
557
|
-
});
|
|
558
|
-
}
|
|
559
|
-
const isStraightLineDoubleLandingLayout = segments.length === 4 &&
|
|
560
|
-
segments[0]?.segmentType === 'stair' &&
|
|
561
|
-
segments[1]?.segmentType === 'landing' &&
|
|
562
|
-
segments[2]?.segmentType === 'stair' &&
|
|
563
|
-
segments[2]?.attachmentSide === 'front' &&
|
|
564
|
-
segments[3]?.segmentType === 'landing' &&
|
|
565
|
-
segments[3]?.attachmentSide === 'front';
|
|
566
|
-
const resolved = [];
|
|
567
|
-
layouts.forEach((layout, index) => {
|
|
568
|
-
const segment = layout.segment;
|
|
569
|
-
const previousSegment = index > 0 ? segments[index - 1] : undefined;
|
|
570
|
-
const nextSegment = index < segments.length - 1 ? segments[index + 1] : undefined;
|
|
571
|
-
const nextAttachmentSide = nextSegment?.attachmentSide;
|
|
572
|
-
const isMiddleLandingBetweenFlights = segment.segmentType === 'landing' &&
|
|
573
|
-
previousSegment?.segmentType === 'stair' &&
|
|
574
|
-
nextSegment?.segmentType === 'stair';
|
|
575
|
-
const suppressLandingRailing = segment.segmentType === 'landing' &&
|
|
576
|
-
nextSegment?.segmentType === 'landing' &&
|
|
577
|
-
nextAttachmentSide === railingMode;
|
|
578
|
-
const landingContinuesOnPreferredSide = segment.segmentType === 'landing'
|
|
579
|
-
? nextAttachmentSide == null ||
|
|
580
|
-
nextAttachmentSide === 'front' ||
|
|
581
|
-
nextAttachmentSide === railingMode
|
|
582
|
-
: true;
|
|
583
|
-
const sidePaths = suppressLandingRailing
|
|
584
|
-
? []
|
|
585
|
-
: segment.segmentType !== 'landing'
|
|
586
|
-
? [
|
|
587
|
-
buildSegmentRailPath(layout, railingMode, previousSegment, nextSegment, inset, landingInset),
|
|
588
|
-
]
|
|
589
|
-
: isStraightLineDoubleLandingLayout
|
|
590
|
-
? [
|
|
591
|
-
buildSegmentRailPath(layout, railingMode, previousSegment, nextSegment, inset, landingInset),
|
|
592
|
-
]
|
|
593
|
-
: isMiddleLandingBetweenFlights && railingMode === 'left'
|
|
594
|
-
? nextAttachmentSide === 'right'
|
|
595
|
-
? [
|
|
596
|
-
buildSegmentRailPath(layout, 'front', previousSegment, nextSegment, inset, landingInset),
|
|
597
|
-
buildSegmentRailPath(layout, 'left', previousSegment, nextSegment, inset, landingInset),
|
|
598
|
-
]
|
|
599
|
-
: []
|
|
600
|
-
: isMiddleLandingBetweenFlights && railingMode === 'right'
|
|
601
|
-
? nextAttachmentSide === 'left'
|
|
602
|
-
? [
|
|
603
|
-
buildSegmentRailPath(layout, 'front', previousSegment, nextSegment, inset, landingInset),
|
|
604
|
-
buildSegmentRailPath(layout, 'right', previousSegment, nextSegment, inset, landingInset),
|
|
605
|
-
]
|
|
606
|
-
: []
|
|
607
|
-
: nextSegment?.segmentType === 'landing' &&
|
|
608
|
-
nextAttachmentSide != null &&
|
|
609
|
-
nextAttachmentSide !== 'front' &&
|
|
610
|
-
nextAttachmentSide !== railingMode
|
|
611
|
-
? [
|
|
612
|
-
buildSegmentRailPath(layout, 'front', previousSegment, nextSegment, inset, landingInset),
|
|
613
|
-
buildSegmentRailPath(layout, railingMode, previousSegment, nextSegment, inset, landingInset),
|
|
614
|
-
]
|
|
615
|
-
: [
|
|
616
|
-
buildSegmentRailPath(layout, railingMode, previousSegment, nextSegment, inset, landingInset),
|
|
617
|
-
];
|
|
618
|
-
resolved.push({
|
|
619
|
-
segment,
|
|
620
|
-
sidePaths: sidePaths.filter((entry) => entry !== null),
|
|
621
|
-
connectFromPrevious: index > 0 &&
|
|
622
|
-
!suppressLandingRailing &&
|
|
623
|
-
sidePaths.length > 0 &&
|
|
624
|
-
(segment.segmentType === 'landing' ? landingContinuesOnPreferredSide : true),
|
|
625
|
-
});
|
|
626
|
-
});
|
|
627
|
-
return resolved;
|
|
628
|
-
}
|
|
629
|
-
function computeStairRailLayouts(segments, transforms) {
|
|
630
|
-
return segments.map((segment, index) => {
|
|
631
|
-
const transform = transforms[index];
|
|
632
|
-
const [centerOffsetX, centerOffsetZ] = rotateXZ(0, segment.length / 2, transform.rotation);
|
|
633
|
-
return {
|
|
634
|
-
center: [transform.position[0] + centerOffsetX, transform.position[2] + centerOffsetZ],
|
|
635
|
-
elevation: transform.position[1],
|
|
636
|
-
rotation: transform.rotation,
|
|
637
|
-
segment,
|
|
638
|
-
};
|
|
639
|
-
});
|
|
640
|
-
}
|
|
641
|
-
function buildSegmentRailPath(layout, side, previousSegment, nextSegment, inset, landingInset) {
|
|
642
|
-
const segment = layout.segment;
|
|
643
|
-
const segmentSteps = Math.max(1, segment.segmentType === 'landing' ? 1 : segment.stepCount);
|
|
644
|
-
const segmentStepDepth = segment.length / segmentSteps;
|
|
645
|
-
const segmentStepHeight = segment.segmentType === 'landing' ? 0 : segment.height / segmentSteps;
|
|
646
|
-
const segmentTopThickness = getSegmentTopThickness(segment);
|
|
647
|
-
const flightSideOffset = side === 'left' ? segment.width / 2 - 0.045 : -segment.width / 2 + 0.045;
|
|
648
|
-
const flightStartX = previousSegment?.segmentType === 'landing'
|
|
649
|
-
? -segment.length / 2 + landingInset
|
|
650
|
-
: -segment.length / 2;
|
|
651
|
-
const flightEndX = nextSegment?.segmentType === 'landing' ? segment.length / 2 - landingInset : segment.length / 2;
|
|
652
|
-
if (segment.segmentType === 'landing') {
|
|
653
|
-
return buildLandingRailPathFromScratch(layout, side, previousSegment, nextSegment, segmentTopThickness, landingInset);
|
|
654
|
-
}
|
|
655
|
-
return {
|
|
656
|
-
side,
|
|
657
|
-
points: [
|
|
658
|
-
...(previousSegment?.segmentType === 'landing'
|
|
659
|
-
? []
|
|
660
|
-
: [toRailLayoutWorldPoint(layout, flightStartX, segmentTopThickness, flightSideOffset)]),
|
|
661
|
-
...Array.from({ length: segmentSteps }).map((_, index) => toRailLayoutWorldPoint(layout, -segment.length / 2 + segmentStepDepth * index + segmentStepDepth / 2, segmentStepHeight * (index + 1), flightSideOffset)),
|
|
662
|
-
...(nextSegment?.segmentType === 'landing'
|
|
663
|
-
? []
|
|
664
|
-
: [toRailLayoutWorldPoint(layout, flightEndX, segment.height, flightSideOffset)]),
|
|
665
|
-
],
|
|
666
|
-
};
|
|
667
|
-
}
|
|
668
|
-
function buildLandingRailPathFromScratch(layout, side, previousSegment, nextSegment, topY, inset) {
|
|
669
|
-
const segment = layout.segment;
|
|
670
|
-
const backX = -segment.length / 2 + inset;
|
|
671
|
-
const frontX = segment.length / 2 - inset;
|
|
672
|
-
const leftZ = segment.width / 2 - inset;
|
|
673
|
-
const rightZ = -segment.width / 2 + inset;
|
|
674
|
-
const edgePoints = side === 'left'
|
|
675
|
-
? [
|
|
676
|
-
toRailLayoutWorldPoint(layout, backX, topY, leftZ),
|
|
677
|
-
toRailLayoutWorldPoint(layout, frontX, topY, leftZ),
|
|
678
|
-
]
|
|
679
|
-
: side === 'right'
|
|
680
|
-
? [
|
|
681
|
-
toRailLayoutWorldPoint(layout, backX, topY, rightZ),
|
|
682
|
-
toRailLayoutWorldPoint(layout, frontX, topY, rightZ),
|
|
683
|
-
]
|
|
684
|
-
: [
|
|
685
|
-
// When the next flight turns, rail the visible leading edge nearest the turn opening.
|
|
686
|
-
toRailLayoutWorldPoint(layout, previousSegment?.segmentType === 'stair' &&
|
|
687
|
-
nextSegment?.attachmentSide &&
|
|
688
|
-
nextSegment.attachmentSide !== 'front'
|
|
689
|
-
? backX
|
|
690
|
-
: frontX, topY, leftZ),
|
|
691
|
-
toRailLayoutWorldPoint(layout, previousSegment?.segmentType === 'stair' &&
|
|
692
|
-
nextSegment?.attachmentSide &&
|
|
693
|
-
nextSegment.attachmentSide !== 'front'
|
|
694
|
-
? backX
|
|
695
|
-
: frontX, topY, rightZ),
|
|
696
|
-
];
|
|
697
|
-
return {
|
|
698
|
-
side,
|
|
699
|
-
points: edgePoints,
|
|
700
|
-
};
|
|
701
|
-
}
|
|
702
|
-
function toRailLayoutWorldPoint(layout, localX, localY, localZ) {
|
|
703
|
-
const [offsetX, offsetZ] = rotateXZ(localZ, localX, layout.rotation);
|
|
704
|
-
return new THREE.Vector3(layout.center[0] + offsetX, layout.elevation + localY, layout.center[1] + offsetZ);
|
|
705
|
-
}
|
|
706
|
-
function buildOffsetRailSegmentGeometries(points, heightOffset, radius) {
|
|
707
|
-
const geometries = [];
|
|
708
|
-
for (let index = 0; index < points.length - 1; index++) {
|
|
709
|
-
const start = points[index];
|
|
710
|
-
const end = points[index + 1];
|
|
711
|
-
if (!(start && end))
|
|
712
|
-
continue;
|
|
713
|
-
const segmentGeometry = createCylinderBetweenPoints(start.clone().add(new THREE.Vector3(0, heightOffset, 0)), end.clone().add(new THREE.Vector3(0, heightOffset, 0)), radius, 8);
|
|
714
|
-
if (segmentGeometry) {
|
|
715
|
-
geometries.push(segmentGeometry);
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
return geometries;
|
|
719
|
-
}
|
|
720
|
-
function buildBalusterGeometries(points, height, radius) {
|
|
721
|
-
const geometries = [];
|
|
722
|
-
for (const point of points) {
|
|
723
|
-
const geometry = new THREE.CylinderGeometry(radius, radius, Math.max(height, 0.05), 8);
|
|
724
|
-
geometry.translate(point.x, point.y + height / 2, point.z);
|
|
725
|
-
geometries.push(geometry);
|
|
726
|
-
}
|
|
727
|
-
return geometries;
|
|
728
|
-
}
|
|
729
|
-
function getSegmentTopThickness(segment) {
|
|
730
|
-
return Math.max(segment.thickness ?? 0.25, 0.02);
|
|
731
|
-
}
|
|
732
|
-
function createCylinderBetweenPoints(start, end, radius, radialSegments) {
|
|
733
|
-
const direction = new THREE.Vector3().subVectors(end, start);
|
|
734
|
-
const length = direction.length();
|
|
735
|
-
if (length <= 1e-5)
|
|
736
|
-
return null;
|
|
737
|
-
const midpoint = new THREE.Vector3().addVectors(start, end).multiplyScalar(0.5);
|
|
738
|
-
const quaternion = new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0, 1, 0), direction.clone().normalize());
|
|
739
|
-
const geometry = new THREE.CylinderGeometry(radius, radius, length, radialSegments);
|
|
740
|
-
geometry.applyQuaternion(quaternion);
|
|
741
|
-
geometry.translate(midpoint.x, midpoint.y, midpoint.z);
|
|
742
|
-
return geometry;
|
|
743
|
-
}
|
|
744
522
|
/**
|
|
745
523
|
* Computes the absolute Y height of a segment by traversing the stair's segment chain.
|
|
746
524
|
*/
|
|
@@ -751,10 +529,11 @@ function computeAbsoluteHeight(node) {
|
|
|
751
529
|
const parent = nodes[node.parentId];
|
|
752
530
|
if (parent?.type !== 'stair')
|
|
753
531
|
return 0;
|
|
754
|
-
const stair = parent;
|
|
532
|
+
const stair = getEffectiveNode(parent);
|
|
755
533
|
const segments = (stair.children ?? [])
|
|
756
534
|
.map((childId) => nodes[childId])
|
|
757
|
-
.filter((n) => n?.type === 'stair-segment')
|
|
535
|
+
.filter((n) => n?.type === 'stair-segment')
|
|
536
|
+
.map((segment) => getEffectiveNode(segment));
|
|
758
537
|
const transforms = computeSegmentTransforms(segments);
|
|
759
538
|
const index = segments.findIndex((s) => s.id === node.id);
|
|
760
539
|
if (index < 0)
|
|
@@ -2,6 +2,7 @@ import { type WallNode } from '@pascal-app/core';
|
|
|
2
2
|
import { type Camera, type Material, Matrix4, type Mesh, Vector3 } from 'three';
|
|
3
3
|
import useViewer, { type WallMode } from '../../store/use-viewer';
|
|
4
4
|
import { type WallMaterialVariant } from './wall-material-variant';
|
|
5
|
+
import { type WallMaterialsResolver } from './wall-materials';
|
|
5
6
|
export declare function sameMaterialArray(a: Material | Material[], b: Material[]): boolean;
|
|
6
7
|
type Variant = {
|
|
7
8
|
key: WallMaterialVariant;
|
|
@@ -19,6 +20,8 @@ type CachedWall = {
|
|
|
19
20
|
assignedMaterials: Material | Material[] | undefined;
|
|
20
21
|
visibleVariant: Variant;
|
|
21
22
|
hiddenVariant: Variant;
|
|
23
|
+
/** Finish refs the material palette was built for; the geometry's groups index them. */
|
|
24
|
+
paletteKey: string;
|
|
22
25
|
};
|
|
23
26
|
export declare const WALL_FACING_HYSTERESIS = 0.001;
|
|
24
27
|
export declare function wallHiddenFromFacing(node: Pick<WallNode, 'frontSide' | 'backSide'>, mode: WallMode, negativeFacing: boolean): boolean;
|
|
@@ -29,6 +32,7 @@ export type WallCutoutViewerStore = {
|
|
|
29
32
|
};
|
|
30
33
|
export declare class WallCutoutCache {
|
|
31
34
|
private readonly viewerStore;
|
|
35
|
+
private readonly materialResolver;
|
|
32
36
|
readonly walls: Map<string, CachedWall>;
|
|
33
37
|
readonly rebuilt: Set<string>;
|
|
34
38
|
private viewer;
|
|
@@ -46,9 +50,15 @@ export declare class WallCutoutCache {
|
|
|
46
50
|
private selected;
|
|
47
51
|
private highlightKey;
|
|
48
52
|
private transformed;
|
|
49
|
-
|
|
53
|
+
private overrides;
|
|
54
|
+
constructor(viewerStore?: WallCutoutViewerStore, materialResolver?: WallMaterialsResolver);
|
|
50
55
|
subscribeLiveTransforms(): () => void;
|
|
51
56
|
update(camera: Camera, time: number): void;
|
|
57
|
+
/**
|
|
58
|
+
* A rebuilt wall whose finish palette changed must swap materials in the same
|
|
59
|
+
* frame: its new groups already index the new palette.
|
|
60
|
+
*/
|
|
61
|
+
handleRebuilt(id: string): void;
|
|
52
62
|
private refreshAppearance;
|
|
53
63
|
private refreshNormal;
|
|
54
64
|
private apply;
|
|
@@ -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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"}
|