@pascal-app/viewer 1.0.3 → 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 +12 -4
- 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 +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 +253 -86
- 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-reference-nodes.js +1 -1
- package/dist/components/viewer/glb-replace-instances.js +1 -1
- 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 +421 -508
- 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 +13 -2
- package/dist/components/viewer/index.d.ts +24 -1
- package/dist/components/viewer/index.d.ts.map +1 -1
- package/dist/components/viewer/index.js +111 -37
- package/dist/components/viewer/lights.d.ts.map +1 -1
- package/dist/components/viewer/lights.js +113 -22
- package/dist/components/viewer/perf-monitor.d.ts.map +1 -1
- package/dist/components/viewer/perf-monitor.js +6 -0
- 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 +104 -36
- package/dist/components/viewer/registered-systems.d.ts.map +1 -1
- package/dist/components/viewer/registered-systems.js +17 -3
- 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-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/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 +25 -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 +4 -0
- package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
- package/dist/components/viewer/viewer-camera.js +12 -2
- 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 +36 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +34 -19
- package/dist/lib/asset-url.d.ts.map +1 -1
- package/dist/lib/asset-url.js +7 -1
- 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/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/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 +13 -3
- 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 +6 -3
- 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/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/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 +19 -7
- package/dist/lib/materials.d.ts.map +1 -1
- package/dist/lib/materials.js +65 -9
- 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/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 +26 -2
- package/dist/lib/pointer-events.d.ts.map +1 -1
- package/dist/lib/pointer-events.js +99 -12
- package/dist/lib/post-processing-resources.d.ts.map +1 -1
- 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/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-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-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 +17 -1
- package/dist/lib/scene-visibility.d.ts.map +1 -1
- package/dist/lib/scene-visibility.js +53 -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/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 +34 -3
- package/dist/lib/snapshot-pipeline.d.ts.map +1 -1
- package/dist/lib/snapshot-pipeline.js +247 -117
- package/dist/lib/terrain-perimeter-fill.d.ts.map +1 -1
- package/dist/lib/terrain-perimeter-fill.js +22 -14
- 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/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 +194 -26
- package/dist/store/use-viewer.d.ts.map +1 -1
- package/dist/store/use-viewer.js +164 -21
- 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 +88 -6
- 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/door/door-system.d.ts.map +1 -1
- package/dist/systems/door/door-system.js +55 -17
- 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/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 +33 -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/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 +52 -17
- 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 +48 -53
- 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 +19 -6
- package/dist/systems/level/level-utils.d.ts +19 -6
- package/dist/systems/level/level-utils.d.ts.map +1 -1
- package/dist/systems/level/level-utils.js +68 -9
- 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/__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 +16 -2
- package/dist/systems/roof/roof-materials.d.ts.map +1 -1
- package/dist/systems/roof/roof-materials.js +64 -31
- package/dist/systems/roof/roof-system.d.ts +21 -4
- package/dist/systems/roof/roof-system.d.ts.map +1 -1
- package/dist/systems/roof/roof-system.js +374 -49
- 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 +39 -3
- package/dist/systems/wall/wall-cutout-cache.d.ts.map +1 -1
- package/dist/systems/wall/wall-cutout-cache.js +141 -19
- package/dist/systems/wall/wall-cutout.d.ts +6 -0
- package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
- package/dist/systems/wall/wall-cutout.js +42 -28
- 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-material-variant.d.ts.map +1 -1
- package/dist/systems/wall/wall-materials.d.ts +9 -4
- package/dist/systems/wall/wall-materials.d.ts.map +1 -1
- package/dist/systems/wall/wall-materials.js +135 -153
- package/dist/systems/wall/wall-placeholder-sweep.d.ts.map +1 -1
- package/dist/systems/wall/wall-system.d.ts +33 -2
- package/dist/systems/wall/wall-system.d.ts.map +1 -1
- package/dist/systems/wall/wall-system.js +621 -159
- 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 +71 -27
- package/package.json +10 -10
- 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
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { InstancedBufferAttribute } from 'three';
|
|
2
|
+
import { MeshBasicNodeMaterial } from 'three/webgpu';
|
|
3
|
+
/**
|
|
4
|
+
* A soft disc on a quad, faded per sprite by `alpha`. It draws into the scene pass so walls in front
|
|
5
|
+
* hide it, and writes nothing to that pass's normals and diffuse colour (alpha 0 under their material
|
|
6
|
+
* blending): the ink reads the normals, and the whole quad, not just its disc, was an edge there.
|
|
7
|
+
* `output` is named too: a pass without MRT draws what this node names and nothing else.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createDustMaterial(alpha: InstancedBufferAttribute): MeshBasicNodeMaterial;
|
|
10
|
+
//# sourceMappingURL=dust-material.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dust-material.d.ts","sourceRoot":"","sources":["../../../src/systems/construction-reveal/dust-material.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,OAAO,CAAA;AAErD,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AAIpD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,wBAAwB,yBAOjE"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { color, float, instancedDynamicBufferAttribute, mrt, output, uv, vec4 } from 'three/tsl';
|
|
2
|
+
import { MeshBasicNodeMaterial } from 'three/webgpu';
|
|
3
|
+
const DUST_COLOR = '#d9cdb8';
|
|
4
|
+
/**
|
|
5
|
+
* A soft disc on a quad, faded per sprite by `alpha`. It draws into the scene pass so walls in front
|
|
6
|
+
* hide it, and writes nothing to that pass's normals and diffuse colour (alpha 0 under their material
|
|
7
|
+
* blending): the ink reads the normals, and the whole quad, not just its disc, was an edge there.
|
|
8
|
+
* `output` is named too: a pass without MRT draws what this node names and nothing else.
|
|
9
|
+
*/
|
|
10
|
+
export function createDustMaterial(alpha) {
|
|
11
|
+
const material = new MeshBasicNodeMaterial({ transparent: true, depthWrite: false });
|
|
12
|
+
material.colorNode = color(DUST_COLOR);
|
|
13
|
+
const edge = float(1).sub(uv().sub(0.5).length().mul(2)).clamp(0, 1);
|
|
14
|
+
material.opacityNode = edge.mul(edge).mul(instancedDynamicBufferAttribute(alpha, 'float'));
|
|
15
|
+
material.mrtNode = mrt({ output, diffuseColor: vec4(0), normal: vec4(0) });
|
|
16
|
+
return material;
|
|
17
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Object3D } from 'three';
|
|
2
|
+
/**
|
|
3
|
+
* The picture a deleted node plays out is a copy of what stood there, and a copy wears the
|
|
4
|
+
* material its source had in that frame. A node the cutaway was hiding wears its stipple, so its
|
|
5
|
+
* deletion would play as faint dots: when its source carries a visible variant (the wall cutout
|
|
6
|
+
* keeps one, `visibleMaterialsOf`), the copy wears that instead, the one a capture draws, for as long
|
|
7
|
+
* as it plays. What the cutaway showed, and any other wall mode, keep what they wear.
|
|
8
|
+
*/
|
|
9
|
+
export declare function dressDeletionGhost(source: Object3D, clone: Object3D): void;
|
|
10
|
+
//# sourceMappingURL=ghost-dress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ghost-dress.d.ts","sourceRoot":"","sources":["../../../src/systems/construction-reveal/ghost-dress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,OAAO,CAAA;AAK3C;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,QAenE"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { hasMaterialsForGroups } from '../../lib/pointer-events';
|
|
2
|
+
import useViewer from '../../store/use-viewer';
|
|
3
|
+
import { visibleMaterialsOf } from '../wall/wall-cutout-cache';
|
|
4
|
+
/**
|
|
5
|
+
* The picture a deleted node plays out is a copy of what stood there, and a copy wears the
|
|
6
|
+
* material its source had in that frame. A node the cutaway was hiding wears its stipple, so its
|
|
7
|
+
* deletion would play as faint dots: when its source carries a visible variant (the wall cutout
|
|
8
|
+
* keeps one, `visibleMaterialsOf`), the copy wears that instead, the one a capture draws, for as long
|
|
9
|
+
* as it plays. What the cutaway showed, and any other wall mode, keep what they wear.
|
|
10
|
+
*/
|
|
11
|
+
export function dressDeletionGhost(source, clone) {
|
|
12
|
+
const visible = visibleMaterialsOf.get(source);
|
|
13
|
+
const mesh = clone;
|
|
14
|
+
if (visible &&
|
|
15
|
+
useViewer.getState().wallMode === 'cutaway' &&
|
|
16
|
+
source.userData.wallHidden === true &&
|
|
17
|
+
mesh.isMesh &&
|
|
18
|
+
mesh.geometry &&
|
|
19
|
+
// A room-wide paint preview can leave the source with more groups than the palette has
|
|
20
|
+
// entries; a copy with a group and no material throws on every raycast over it (see
|
|
21
|
+
// `WallCutoutCache.mayAssign`), so it keeps what it wears then.
|
|
22
|
+
hasMaterialsForGroups(mesh, visible))
|
|
23
|
+
mesh.material = visible;
|
|
24
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"door-system.d.ts","sourceRoot":"","sources":["../../../src/systems/door/door-system.tsx"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,QAAQ,
|
|
1
|
+
{"version":3,"file":"door-system.d.ts","sourceRoot":"","sources":["../../../src/systems/door/door-system.tsx"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,QAAQ,EAcd,MAAM,kBAAkB,CAAA;AAGzB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AA6D9B,eAAO,MAAM,UAAU,YAyHtB,CAAA;AAiqCD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,QAAQ,EACd,IAAI,EAAE,KAAK,CAAC,QAAQ,GAAG,SAAS,EAChC,KAAK,EAAE,MAAM,GACZ,OAAO,CA6GT;AA6wCD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,QAAQ,GAAG,KAAK,CAAC,IAAI,CAI/D"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { clampDoorOperationState, DEFAULT_WALL_THICKNESS, DoorNode as DoorNodeSchema, getDoorRenderOpenAmount, getEffectiveNode, getWallThickness, sceneRegistry, useInteractive, useLiveNodeOverrides, useScene, } from '@pascal-app/core';
|
|
1
|
+
import { clampDoorOperationState, DEFAULT_WALL_THICKNESS, DoorNode as DoorNodeSchema, getDoorRenderOpenAmount, 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';
|
|
5
5
|
import { applyWorldScaleBoxUVs } from '../../lib/box-uv';
|
|
6
|
-
import { createDefaultMaterial, createSurfaceRoleMaterial, glassMaterial as defaultGlassMaterial, baseMaterial as getBaseMaterial, resolveMaterialRef, } from '../../lib/materials';
|
|
6
|
+
import { createDefaultMaterial, createMaterial, createSurfaceRoleMaterial, glassMaterial as defaultGlassMaterial, baseMaterial as getBaseMaterial, resolveMaterialRef, } from '../../lib/materials';
|
|
7
7
|
import { timeSpan } from '../../lib/perf-tracks';
|
|
8
|
+
import { settleScriptedOpening } from '../../lib/scripted-opening';
|
|
8
9
|
import useViewer from '../../store/use-viewer';
|
|
9
10
|
import { getOpeningCutoutProxyDepth } from '../wall/opening-cutout-geometry';
|
|
10
11
|
// Invisible material for root mesh — used as selection hitbox only
|
|
@@ -146,9 +147,12 @@ export const DoorSystem = () => {
|
|
|
146
147
|
// rebuild reflects the in-flight drag without zustand churn. When
|
|
147
148
|
// no override is set this returns the scene node unchanged.
|
|
148
149
|
const effectiveNode = getEffectiveNode(node);
|
|
149
|
-
timeSpan('door', () => updateDoorMesh(effectiveNode, mesh), {
|
|
150
|
+
const built = timeSpan('door', () => updateDoorMesh(effectiveNode, mesh), {
|
|
150
151
|
properties: [['node', id]],
|
|
151
152
|
});
|
|
153
|
+
// A scripted opening stays dirty until its artifact has loaded.
|
|
154
|
+
if (!built)
|
|
155
|
+
continue;
|
|
152
156
|
clearDirty(id);
|
|
153
157
|
rebuiltDoorsThisFrame += 1;
|
|
154
158
|
// Rebuild the parent wall so its cutout reflects the updated door geometry
|
|
@@ -208,16 +212,21 @@ function doorSlotDefault(slotId) {
|
|
|
208
212
|
getBaseMaterial(currentShading));
|
|
209
213
|
}
|
|
210
214
|
// Resolve a door's slot to a material: the `node.slots` override (colored mode
|
|
211
|
-
// only) → the body
|
|
212
|
-
//
|
|
215
|
+
// only) → the door-wide `material` for the body (panel and frame; glass and
|
|
216
|
+
// hardware keep their own finish) → the body/glass/hardware default.
|
|
217
|
+
// Textures-off ignores overrides — the monochrome escape hatch.
|
|
213
218
|
function resolveDoorSlotMaterial(node, slotId) {
|
|
214
219
|
const fallback = doorSlotDefault(slotId);
|
|
215
220
|
if (!currentTextures)
|
|
216
221
|
return fallback;
|
|
217
222
|
const ref = node.slots?.[slotId];
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
223
|
+
const painted = ref ? resolveMaterialRef(ref, currentSceneMaterials, currentShading) : null;
|
|
224
|
+
if (painted)
|
|
225
|
+
return painted;
|
|
226
|
+
if (node.material && (slotId === 'panel' || slotId === 'frame')) {
|
|
227
|
+
return createMaterial(node.material, currentShading);
|
|
228
|
+
}
|
|
229
|
+
return fallback;
|
|
221
230
|
}
|
|
222
231
|
function addBox(parent, material, w, h, d, x, y, z) {
|
|
223
232
|
const geometry = new THREE.BoxGeometry(w, h, d);
|
|
@@ -676,7 +685,9 @@ function addLeafSegmentContent({ addLeafBox, addLeafShape, leafWidth, leafHeight
|
|
|
676
685
|
addLeafShape(baseMaterial, innerPanelShape, effectiveDepth);
|
|
677
686
|
}
|
|
678
687
|
else {
|
|
688
|
+
// A leaf is paneled on both faces, so the person sees the style from either side.
|
|
679
689
|
addLeafBox(baseMaterial, panelW, panelH, effectiveDepth, colX, segCenterY, panelZ);
|
|
690
|
+
addLeafBox(baseMaterial, panelW, panelH, effectiveDepth, colX, segCenterY, -panelZ);
|
|
680
691
|
}
|
|
681
692
|
}
|
|
682
693
|
}
|
|
@@ -1239,8 +1250,19 @@ function updateDoorMesh(rawNode, mesh) {
|
|
|
1239
1250
|
mesh.geometry = new THREE.BoxGeometry(node.width, node.height, node.frameDepth);
|
|
1240
1251
|
mesh.material = hitboxMaterial;
|
|
1241
1252
|
// Sync transform from node (React may lag behind the system by a frame during drag)
|
|
1242
|
-
|
|
1243
|
-
|
|
1253
|
+
const parent = node.parentId ? useScene.getState().nodes[node.parentId] : undefined;
|
|
1254
|
+
const placement = parent?.type === 'wall' && !node.roofSegmentId
|
|
1255
|
+
? getOpeningWallPlacement(getEffectiveNode(parent), node, useScene.getState().nodes)
|
|
1256
|
+
: node;
|
|
1257
|
+
mesh.position.set(...placement.position);
|
|
1258
|
+
mesh.rotation.set(...placement.rotation);
|
|
1259
|
+
// Built from a script: the renderer shows its artifact, not the parametric
|
|
1260
|
+
// frame, and the hit box is what the script built.
|
|
1261
|
+
if (node.source) {
|
|
1262
|
+
mesh.geometry.dispose();
|
|
1263
|
+
mesh.geometry = new THREE.BoxGeometry(...scriptedSize(node.source.manifest));
|
|
1264
|
+
return settleScriptedOpening(mesh);
|
|
1265
|
+
}
|
|
1244
1266
|
// Dispose and remove all old visual children; preserve 'cutout'
|
|
1245
1267
|
for (const child of [...mesh.children]) {
|
|
1246
1268
|
if (child.name === 'cutout')
|
|
@@ -1263,7 +1285,7 @@ function updateDoorMesh(rawNode, mesh) {
|
|
|
1263
1285
|
const clampedSwingAngle = Math.max(0, Math.min(Math.PI / 2, swingAngle));
|
|
1264
1286
|
if (openingKind === 'opening') {
|
|
1265
1287
|
syncDoorCutout(node, mesh);
|
|
1266
|
-
return;
|
|
1288
|
+
return true;
|
|
1267
1289
|
}
|
|
1268
1290
|
const insideWidth = width - 2 * frameThickness;
|
|
1269
1291
|
const leafH = height - frameThickness; // only top frame
|
|
@@ -1505,11 +1527,12 @@ function updateDoorMesh(rawNode, mesh) {
|
|
|
1505
1527
|
// … was not set" on a Draw(0, …)). Hide any empty mesh so it is never
|
|
1506
1528
|
// drawn (it would render nothing anyway).
|
|
1507
1529
|
hideEmptyGeometryMeshes(mesh);
|
|
1530
|
+
return true;
|
|
1508
1531
|
}
|
|
1509
1532
|
function hideEmptyGeometryMeshes(root) {
|
|
1510
1533
|
root.traverse((obj) => {
|
|
1511
1534
|
const child = obj;
|
|
1512
|
-
if (!child.isMesh
|
|
1535
|
+
if (!(child.isMesh && child.geometry))
|
|
1513
1536
|
return;
|
|
1514
1537
|
const position = child.geometry.getAttribute('position');
|
|
1515
1538
|
if (!position || position.count === 0)
|
|
@@ -1531,7 +1554,15 @@ function syncDoorCutout(node, mesh) {
|
|
|
1531
1554
|
mesh.add(cutout);
|
|
1532
1555
|
}
|
|
1533
1556
|
cutout.geometry.dispose();
|
|
1534
|
-
const depth =
|
|
1557
|
+
const { depth, center } = resolveOpeningCutoutProxy(node);
|
|
1558
|
+
cutout.position.set(0, 0, 0);
|
|
1559
|
+
if (center) {
|
|
1560
|
+
// Curved and justified walls place openings off the reference line. Remove
|
|
1561
|
+
// only the visual plane offset, preserving the frame's resolved floor datum.
|
|
1562
|
+
cutout.position
|
|
1563
|
+
.set(center[0] - mesh.position.x, 0, center[2] - mesh.position.z)
|
|
1564
|
+
.applyQuaternion(mesh.quaternion.clone().invert());
|
|
1565
|
+
}
|
|
1535
1566
|
const openingShape = getEffectiveOpeningShape(node);
|
|
1536
1567
|
if (openingShape === 'arch') {
|
|
1537
1568
|
cutout.geometry = new THREE.ExtrudeGeometry(createArchShape(-node.width / 2, node.width / 2, -node.height / 2, node.height / 2, getClampedArchHeight(node.width, node.height, node.archHeight)), {
|
|
@@ -1558,11 +1589,18 @@ function syncDoorCutout(node, mesh) {
|
|
|
1558
1589
|
// the proxy stays proud of both wall faces (front/back selection) without the
|
|
1559
1590
|
// old 1m depth that blanketed the floor. Falls back to the default thickness
|
|
1560
1591
|
// when the parent wall isn't a resolvable wall node.
|
|
1561
|
-
function
|
|
1592
|
+
function resolveOpeningCutoutProxy(node) {
|
|
1562
1593
|
const parentId = node.parentId;
|
|
1563
|
-
const
|
|
1564
|
-
const
|
|
1565
|
-
|
|
1594
|
+
const nodes = useScene.getState().nodes;
|
|
1595
|
+
const parent = parentId ? nodes[parentId] : undefined;
|
|
1596
|
+
const wall = parent?.type === 'wall' ? getEffectiveNode(parent) : undefined;
|
|
1597
|
+
const wallThickness = wall ? getWallThickness(wall) : DEFAULT_WALL_THICKNESS;
|
|
1598
|
+
return {
|
|
1599
|
+
depth: getOpeningCutoutProxyDepth(wallThickness),
|
|
1600
|
+
center: wall && !node.roofSegmentId && node.position[2] !== 0
|
|
1601
|
+
? getOpeningWallPlacement(wall, { ...node, position: [node.position[0], node.position[1], 0] }, nodes).position
|
|
1602
|
+
: undefined,
|
|
1603
|
+
};
|
|
1566
1604
|
}
|
|
1567
1605
|
/**
|
|
1568
1606
|
* Build a fresh door mesh for preview/ghost rendering.
|
|
@@ -1,2 +1,9 @@
|
|
|
1
|
+
import { type Object3D } from 'three';
|
|
2
|
+
/**
|
|
3
|
+
* The roots of every elevator button under `object`. Any button along the ray
|
|
4
|
+
* takes the click, so only they are raycast — never the rest of the scene,
|
|
5
|
+
* whose meshes this capture-phase listener must not trip over.
|
|
6
|
+
*/
|
|
7
|
+
export declare function collectElevatorButtons(object: Object3D, into?: Object3D[]): Object3D[];
|
|
1
8
|
export declare function ElevatorInteractionSystem(): null;
|
|
2
9
|
//# sourceMappingURL=elevator-interaction-system.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"elevator-interaction-system.d.ts","sourceRoot":"","sources":["../../../src/systems/elevator/elevator-interaction-system.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"elevator-interaction-system.d.ts","sourceRoot":"","sources":["../../../src/systems/elevator/elevator-interaction-system.tsx"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,QAAQ,EAAsB,MAAM,OAAO,CAAA;AAwBzD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,GAAE,QAAQ,EAAO,GAAG,QAAQ,EAAE,CAO1F;AAED,wBAAgB,yBAAyB,SA6DxC"}
|
|
@@ -12,6 +12,20 @@ function getElevatorButtonData(object) {
|
|
|
12
12
|
}
|
|
13
13
|
return null;
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* The roots of every elevator button under `object`. Any button along the ray
|
|
17
|
+
* takes the click, so only they are raycast — never the rest of the scene,
|
|
18
|
+
* whose meshes this capture-phase listener must not trip over.
|
|
19
|
+
*/
|
|
20
|
+
export function collectElevatorButtons(object, into = []) {
|
|
21
|
+
if (object.userData.elevatorButton) {
|
|
22
|
+
into.push(object);
|
|
23
|
+
return into;
|
|
24
|
+
}
|
|
25
|
+
for (const child of object.children)
|
|
26
|
+
collectElevatorButtons(child, into);
|
|
27
|
+
return into;
|
|
28
|
+
}
|
|
15
29
|
export function ElevatorInteractionSystem() {
|
|
16
30
|
const camera = useThree((state) => state.camera);
|
|
17
31
|
const gl = useThree((state) => state.gl);
|
|
@@ -23,6 +37,9 @@ export function ElevatorInteractionSystem() {
|
|
|
23
37
|
const handlePointerDown = (event) => {
|
|
24
38
|
if (event.button !== 0)
|
|
25
39
|
return;
|
|
40
|
+
const buttons = collectElevatorButtons(scene);
|
|
41
|
+
if (buttons.length === 0)
|
|
42
|
+
return;
|
|
26
43
|
const rect = canvas.getBoundingClientRect();
|
|
27
44
|
const pointer = pointerRef.current;
|
|
28
45
|
pointer.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
|
|
@@ -30,7 +47,7 @@ export function ElevatorInteractionSystem() {
|
|
|
30
47
|
const raycaster = raycasterRef.current;
|
|
31
48
|
raycaster.setFromCamera(pointer, camera);
|
|
32
49
|
const button = raycaster
|
|
33
|
-
.intersectObjects(
|
|
50
|
+
.intersectObjects(buttons, true)
|
|
34
51
|
.map((intersection) => getElevatorButtonData(intersection.object))
|
|
35
52
|
.find((data) => data !== null);
|
|
36
53
|
if (!button || button.disabled)
|
|
@@ -14,7 +14,9 @@
|
|
|
14
14
|
* Runs at priority 1 — before the priority-2 systems (`GeometrySystem`,
|
|
15
15
|
* `ItemSystem`) so the dirty mark survives long enough for those to do
|
|
16
16
|
* their own work. Kinds with no geometry/system have no downstream dirty
|
|
17
|
-
* consumer, so this system clears their dirty mark after applying the lift
|
|
17
|
+
* consumer, so this system clears their dirty mark after applying the lift,
|
|
18
|
+
* at priority 2 like every other consumer: the node batch snapshots marks at
|
|
19
|
+
* priority 1, and a mark cleared there could run before that snapshot.
|
|
18
20
|
*/
|
|
19
21
|
export declare const FloorElevationSystem: () => null;
|
|
20
22
|
//# sourceMappingURL=floor-elevation-system.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"floor-elevation-system.d.ts","sourceRoot":"","sources":["../../../src/systems/floor-elevation/floor-elevation-system.tsx"],"names":[],"mappings":"AA8DA
|
|
1
|
+
{"version":3,"file":"floor-elevation-system.d.ts","sourceRoot":"","sources":["../../../src/systems/floor-elevation/floor-elevation-system.tsx"],"names":[],"mappings":"AA8DA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,oBAAoB,YAoNhC,CAAA"}
|
|
@@ -43,11 +43,17 @@ function restoreMountedExitPose(saved) {
|
|
|
43
43
|
* Runs at priority 1 — before the priority-2 systems (`GeometrySystem`,
|
|
44
44
|
* `ItemSystem`) so the dirty mark survives long enough for those to do
|
|
45
45
|
* their own work. Kinds with no geometry/system have no downstream dirty
|
|
46
|
-
* consumer, so this system clears their dirty mark after applying the lift
|
|
46
|
+
* consumer, so this system clears their dirty mark after applying the lift,
|
|
47
|
+
* at priority 2 like every other consumer: the node batch snapshots marks at
|
|
48
|
+
* priority 1, and a mark cleared there could run before that snapshot.
|
|
47
49
|
*/
|
|
48
50
|
export const FloorElevationSystem = () => {
|
|
49
51
|
const dirtyNodes = useScene((s) => s.dirtyNodes);
|
|
50
52
|
const clearDirty = useScene((s) => s.clearDirty);
|
|
53
|
+
const consumed = useMemo(() => new Set(), []);
|
|
54
|
+
// Meshes already lifted once. A mesh mounts at its base Y, and a remount (entering Preview
|
|
55
|
+
// builds a fresh viewer tree) raises no dirty mark, so the lift would never be applied.
|
|
56
|
+
const mounted = useMemo(() => ({ revision: -1, meshes: new WeakSet() }), []);
|
|
51
57
|
const preview = useMemo(() => ({
|
|
52
58
|
local: new Matrix4(),
|
|
53
59
|
target: new Matrix4(),
|
|
@@ -107,7 +113,8 @@ export const FloorElevationSystem = () => {
|
|
|
107
113
|
// during group drags over elevated slabs).
|
|
108
114
|
const overrides = useLiveNodeOverrides.getState().overrides;
|
|
109
115
|
const transforms = useLiveTransforms.getState().transforms;
|
|
110
|
-
|
|
116
|
+
const registryChanged = sceneRegistry.revision !== mounted.revision;
|
|
117
|
+
if (dirtyNodes.size === 0 && overrides.size === 0 && transforms.size === 0 && !registryChanged)
|
|
111
118
|
return;
|
|
112
119
|
const nodes = useScene.getState().nodes;
|
|
113
120
|
const applyLift = (id) => {
|
|
@@ -128,9 +135,11 @@ export const FloorElevationSystem = () => {
|
|
|
128
135
|
return;
|
|
129
136
|
if (effectiveNode.parentId !== node.parentId)
|
|
130
137
|
return;
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
138
|
+
// A mounted object still loading (a scripted column's artifact) keeps its mark, so
|
|
139
|
+
// scene-ready and bakes wait for it; settling marks the node dirty again.
|
|
140
|
+
const settled = def.capabilities?.batchable?.settled?.(mesh.userData) ?? true;
|
|
141
|
+
if (!(def.geometry || def.system) && dirtyNodes.has(id) && settled)
|
|
142
|
+
consumed.add(id);
|
|
134
143
|
// `applies === false` means the kind opts OUT of floor stacking for this
|
|
135
144
|
// node: its Y belongs to a host frame (a wall/ceiling-mounted item, a
|
|
136
145
|
// cabinet module inside a run, a wall duct terminal). `getFloorPlacedElevation`
|
|
@@ -162,6 +171,20 @@ export const FloorElevationSystem = () => {
|
|
|
162
171
|
});
|
|
163
172
|
mesh.position.y = visualPosition[1];
|
|
164
173
|
};
|
|
174
|
+
if (registryChanged) {
|
|
175
|
+
mounted.revision = sceneRegistry.revision;
|
|
176
|
+
for (const [kind, ids] of Object.entries(sceneRegistry.byType)) {
|
|
177
|
+
if (!nodeRegistry.get(kind)?.capabilities?.floorPlaced)
|
|
178
|
+
continue;
|
|
179
|
+
for (const id of ids) {
|
|
180
|
+
const mesh = sceneRegistry.nodes.get(id);
|
|
181
|
+
if (!mesh || mounted.meshes.has(mesh))
|
|
182
|
+
continue;
|
|
183
|
+
mounted.meshes.add(mesh);
|
|
184
|
+
applyLift(id);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
165
188
|
dirtyNodes.forEach((id) => {
|
|
166
189
|
applyLift(id);
|
|
167
190
|
});
|
|
@@ -174,6 +197,11 @@ export const FloorElevationSystem = () => {
|
|
|
174
197
|
applyLift(id);
|
|
175
198
|
});
|
|
176
199
|
}, 1);
|
|
200
|
+
useFrame(() => {
|
|
201
|
+
for (const id of consumed)
|
|
202
|
+
clearDirty(id);
|
|
203
|
+
consumed.clear();
|
|
204
|
+
}, 2);
|
|
177
205
|
// PostProcessing draws at priority 1 after this system; later callbacks would show one wrong frame per move.
|
|
178
206
|
useFrame(() => {
|
|
179
207
|
const nodes = useScene.getState().nodes;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"geometry-system.d.ts","sourceRoot":"","sources":["../../../src/systems/geometry/geometry-system.tsx"],"names":[],"mappings":"AAkBA,OAAO,EAAa,KAAK,KAAK,EAA4B,KAAK,QAAQ,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"geometry-system.d.ts","sourceRoot":"","sources":["../../../src/systems/geometry/geometry-system.tsx"],"names":[],"mappings":"AAkBA,OAAO,EAAa,KAAK,KAAK,EAA4B,KAAK,QAAQ,EAAE,MAAM,OAAO,CAAA;AAatF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,cAAc,YA8N1B,CAAA;eA+Kc,cAAc;AAE7B,MAAM,MAAM,uBAAuB,GAAG;IACpC,KAAK,EAAE,KAAK,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;CACZ,CAAA;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAY7D;AAED,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,uBAAuB,CAAC,EAC3C,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,MAAM,GACV,OAAO,CAKT"}
|
|
@@ -4,8 +4,10 @@ import { useFrame } from '@react-three/fiber';
|
|
|
4
4
|
import { useEffect, useRef } from 'react';
|
|
5
5
|
import { FrontSide } from 'three';
|
|
6
6
|
import { disposeObject3DResources } from '../../lib/dispose-object3d';
|
|
7
|
-
import { createSurfaceRoleMaterial, } from '../../lib/materials';
|
|
7
|
+
import { createSurfaceRoleMaterial, materialCastsShadow, } from '../../lib/materials';
|
|
8
|
+
import { createNodeTopSurfaceHeightSampler } from '../../lib/node-top-surface-height';
|
|
8
9
|
import { timeSpan } from '../../lib/perf-tracks';
|
|
10
|
+
import { createSceneSupportHeightSampler } from '../../lib/scene-support-height';
|
|
9
11
|
import useViewer from '../../store/use-viewer';
|
|
10
12
|
/**
|
|
11
13
|
* Generic geometry system.
|
|
@@ -55,6 +57,22 @@ export const GeometrySystem = () => {
|
|
|
55
57
|
// but its geometry inputs are unchanged — e.g. an item reparenting onto a
|
|
56
58
|
// shelf dirties the shelf without altering its boards.
|
|
57
59
|
const builtGeometryKeyRef = useRef(new Map());
|
|
60
|
+
const liveBuildIds = useRef(new Set());
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
const unsubscribe = useLiveNodeOverrides.subscribe((state, previous) => {
|
|
63
|
+
for (const id of new Set([...state.overrides.keys(), ...previous.overrides.keys()])) {
|
|
64
|
+
if (state.overrides.get(id) === previous.overrides.get(id))
|
|
65
|
+
continue;
|
|
66
|
+
const node = useScene.getState().nodes[id];
|
|
67
|
+
if (node && nodeRegistry.get(node.type)?.geometry)
|
|
68
|
+
liveBuildIds.current.add(id);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
return () => {
|
|
72
|
+
unsubscribe();
|
|
73
|
+
liveBuildIds.current.clear();
|
|
74
|
+
};
|
|
75
|
+
}, []);
|
|
58
76
|
// Re-mark every geometry-backed node dirty whenever a viewer appearance
|
|
59
77
|
// value changes, so `def.geometry` builders re-run and pick up the new
|
|
60
78
|
// shading / texture / preset / theme. These four are deliberate re-run
|
|
@@ -92,19 +110,23 @@ export const GeometrySystem = () => {
|
|
|
92
110
|
}
|
|
93
111
|
}, [sceneMaterials]);
|
|
94
112
|
useFrame(() => {
|
|
95
|
-
if (dirtyNodes.size === 0)
|
|
113
|
+
if (dirtyNodes.size === 0 && liveBuildIds.current.size === 0)
|
|
96
114
|
return;
|
|
97
115
|
const nodes = useScene.getState().nodes;
|
|
98
116
|
let rebuiltGeometry = false;
|
|
99
117
|
const batches = new Map();
|
|
100
118
|
const dirtyIds = [];
|
|
101
|
-
dirtyNodes.
|
|
119
|
+
for (const id of new Set([...dirtyNodes, ...liveBuildIds.current])) {
|
|
102
120
|
const node = nodes[id];
|
|
103
|
-
if (!node)
|
|
104
|
-
|
|
121
|
+
if (!node) {
|
|
122
|
+
liveBuildIds.current.delete(id);
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
105
125
|
const def = nodeRegistry.get(node.type);
|
|
106
|
-
if (!def?.geometry)
|
|
107
|
-
|
|
126
|
+
if (!def?.geometry) {
|
|
127
|
+
liveBuildIds.current.delete(id);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
108
130
|
dirtyIds.push(id);
|
|
109
131
|
const parentId = (node.parentId ?? null);
|
|
110
132
|
const key = `${node.type}::${parentId ?? ''}`;
|
|
@@ -113,7 +135,7 @@ export const GeometrySystem = () => {
|
|
|
113
135
|
existing.ids.push(id);
|
|
114
136
|
else
|
|
115
137
|
batches.set(key, { kind: node.type, parentId, ids: [id] });
|
|
116
|
-
}
|
|
138
|
+
}
|
|
117
139
|
// Phase 2 — for each batch whose kind declares `computeLevelData`,
|
|
118
140
|
// collect every sibling in the level + run the precompute once.
|
|
119
141
|
const levelDataByBatch = new Map();
|
|
@@ -170,7 +192,9 @@ export const GeometrySystem = () => {
|
|
|
170
192
|
const childLiveOverrideKey = liveChildOverrideKey(node);
|
|
171
193
|
const builtKey = `${shading}|${textures}|${colorPreset}|${sceneTheme}|${def.geometryKey(effectiveNode)}|${childLiveOverrideKey}`;
|
|
172
194
|
if (shouldReuseGeometryBuild(builtGeometryKeyRef.current, id, group, builtKey)) {
|
|
173
|
-
|
|
195
|
+
liveBuildIds.current.delete(id);
|
|
196
|
+
if (dirtyNodes.has(id))
|
|
197
|
+
clearDirty(id);
|
|
174
198
|
continue;
|
|
175
199
|
}
|
|
176
200
|
}
|
|
@@ -199,7 +223,9 @@ export const GeometrySystem = () => {
|
|
|
199
223
|
// because the renderer doesn't necessarily re-render on the rebuild
|
|
200
224
|
// tick, R3F wouldn't re-apply the props, leaving the group stuck at
|
|
201
225
|
// origin. Geometry builders are expected to emit local-space children.
|
|
202
|
-
|
|
226
|
+
liveBuildIds.current.delete(id);
|
|
227
|
+
if (dirtyNodes.has(id))
|
|
228
|
+
clearDirty(id);
|
|
203
229
|
}
|
|
204
230
|
if (rebuiltGeometry)
|
|
205
231
|
bumpGeometryRevision();
|
|
@@ -281,7 +307,38 @@ function buildGeometryContext(node, nodes, levelData, materials) {
|
|
|
281
307
|
noteLevelBaseConsumer(node.type);
|
|
282
308
|
return levelId ? levelBaseElevationAt(nodes, levelId, x, z) : 0;
|
|
283
309
|
};
|
|
284
|
-
|
|
310
|
+
const surfaceSamplers = new Map();
|
|
311
|
+
const surfaceHeightAt = (hostId, x, z) => {
|
|
312
|
+
if (!levelId)
|
|
313
|
+
return null;
|
|
314
|
+
if (!surfaceSamplers.has(hostId)) {
|
|
315
|
+
surfaceSamplers.set(hostId, createNodeTopSurfaceHeightSampler(hostId, levelId));
|
|
316
|
+
}
|
|
317
|
+
return surfaceSamplers.get(hostId)?.(x, z) ?? null;
|
|
318
|
+
};
|
|
319
|
+
const supportSamplers = new Map();
|
|
320
|
+
const supportHeightAt = (x, z, selectedHostId) => {
|
|
321
|
+
if (!levelId)
|
|
322
|
+
return levelBaseAt(x, z);
|
|
323
|
+
const key = selectedHostId ?? 'auto';
|
|
324
|
+
let sampler = supportSamplers.get(key);
|
|
325
|
+
if (!sampler) {
|
|
326
|
+
sampler = createSceneSupportHeightSampler(nodes, levelId, selectedHostId);
|
|
327
|
+
supportSamplers.set(key, sampler);
|
|
328
|
+
}
|
|
329
|
+
return sampler(x, z);
|
|
330
|
+
};
|
|
331
|
+
return {
|
|
332
|
+
resolve,
|
|
333
|
+
children,
|
|
334
|
+
siblings,
|
|
335
|
+
parent,
|
|
336
|
+
levelBaseAt,
|
|
337
|
+
surfaceHeightAt,
|
|
338
|
+
supportHeightAt,
|
|
339
|
+
levelData,
|
|
340
|
+
materials,
|
|
341
|
+
};
|
|
285
342
|
}
|
|
286
343
|
function disposeChildren(group) {
|
|
287
344
|
// Only dispose meshes the geometry builder produced on the previous
|
|
@@ -330,6 +387,9 @@ export function markGeometryBuildOutput(child) {
|
|
|
330
387
|
// example a cabinet sink faucet handle), while disposal still removes only
|
|
331
388
|
// top-level builder children from the registered group.
|
|
332
389
|
child.traverse((object) => {
|
|
390
|
+
const mesh = object;
|
|
391
|
+
if (mesh.isMesh)
|
|
392
|
+
mesh.castShadow = mesh.castShadow && materialCastsShadow(mesh.material);
|
|
333
393
|
object.userData = {
|
|
334
394
|
...object.userData,
|
|
335
395
|
__fromGeometry: true,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control-widget.d.ts","sourceRoot":"","sources":["../../../src/systems/interactive/control-widget.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAE7D;;8EAE8E;AAC9E,eAAO,MAAM,aAAa,
|
|
1
|
+
{"version":3,"file":"control-widget.d.ts","sourceRoot":"","sources":["../../../src/systems/interactive/control-widget.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAE7D;;8EAE8E;AAC9E,eAAO,MAAM,aAAa,kCAIvB;IACD,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,YAAY,CAAA;IACnB,QAAQ,EAAE,CAAC,CAAC,EAAE,YAAY,KAAK,IAAI,CAAA;CACpC,uCAuEA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interactive-system.d.ts","sourceRoot":"","sources":["../../../src/systems/interactive/interactive-system.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"interactive-system.d.ts","sourceRoot":"","sources":["../../../src/systems/interactive/interactive-system.tsx"],"names":[],"mappings":"AAwCA,eAAO,MAAM,iBAAiB,mCAoC7B,CAAA"}
|