@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
package/dist/lib/backdrop.d.ts
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
import { type ToneMapping } from 'three';
|
|
2
|
+
import type { SceneTheme } from './scene-themes';
|
|
3
|
+
/** Null explicitly removes the gradient; an omitted sky keeps the theme's derived atmosphere. */
|
|
4
|
+
export declare function backdropColors(theme: Pick<SceneTheme, 'background' | 'backgroundSky' | 'appearance'>): {
|
|
5
|
+
background: string;
|
|
6
|
+
sky: string;
|
|
7
|
+
haze: string;
|
|
8
|
+
skyDeep: string;
|
|
9
|
+
};
|
|
10
|
+
/** Flat authored colours are display colours; only the foreground is exposed and tone mapped. */
|
|
11
|
+
export declare function flatBackdropOutput({ background, foreground, alpha, toneMapping, outputColorSpace, }: {
|
|
12
|
+
background: any;
|
|
13
|
+
foreground: any;
|
|
14
|
+
alpha: any;
|
|
15
|
+
toneMapping: ToneMapping;
|
|
16
|
+
outputColorSpace: string;
|
|
17
|
+
}): any;
|
|
1
18
|
/**
|
|
2
19
|
* Shared backdrop gradient: flat background looking down, a warm haze band
|
|
3
20
|
* hugging the horizon, the theme sky arriving just above it and deepening
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backdrop.d.ts","sourceRoot":"","sources":["../../src/lib/backdrop.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"backdrop.d.ts","sourceRoot":"","sources":["../../src/lib/backdrop.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,WAAW,EAAE,MAAM,OAAO,CAAA;AAEvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAEhD,iGAAiG;AACjG,wBAAgB,cAAc,CAC5B,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,YAAY,GAAG,eAAe,GAAG,YAAY,CAAC;;;;;EASvE;AAED,iGAAiG;AACjG,wBAAgB,kBAAkB,CAAC,EACjC,UAAU,EACV,UAAU,EACV,KAAK,EACL,WAAW,EACX,gBAAgB,GACjB,EAAE;IACD,UAAU,EAAE,GAAG,CAAA;IACf,UAAU,EAAE,GAAG,CAAA;IACf,KAAK,EAAE,GAAG,CAAA;IACV,WAAW,EAAE,WAAW,CAAA;IACxB,gBAAgB,EAAE,MAAM,CAAA;CACzB,GAAG,GAAG,CAIN;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,UAAU,EACV,IAAI,EACJ,GAAG,EACH,OAAO,GACR,EAAE;IACD,IAAI,EAAE,GAAG,CAAA;IACT,UAAU,EAAE,GAAG,CAAA;IACf,IAAI,EAAE,GAAG,CAAA;IACT,GAAG,EAAE,GAAG,CAAA;IACR,OAAO,EAAE,GAAG,CAAA;CACb,GAAG,GAAG,CAUN;AAMD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAWlF;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAyChD"}
|
package/dist/lib/backdrop.js
CHANGED
|
@@ -1,4 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { NoToneMapping } from 'three';
|
|
2
|
+
import { abs, exp, mix, renderOutput, smoothstep, vec4 } from 'three/tsl';
|
|
3
|
+
/** Null explicitly removes the gradient; an omitted sky keeps the theme's derived atmosphere. */
|
|
4
|
+
export function backdropColors(theme) {
|
|
5
|
+
const sky = theme.backgroundSky ?? theme.background;
|
|
6
|
+
return {
|
|
7
|
+
background: theme.background,
|
|
8
|
+
sky,
|
|
9
|
+
haze: theme.backgroundSky === null ? theme.background : horizonHazeColor(sky, theme.appearance),
|
|
10
|
+
skyDeep: theme.backgroundSky === null ? theme.background : deepSkyColor(sky),
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/** Flat authored colours are display colours; only the foreground is exposed and tone mapped. */
|
|
14
|
+
export function flatBackdropOutput({ background, foreground, alpha, toneMapping, outputColorSpace, }) {
|
|
15
|
+
const backdrop = renderOutput(vec4(background, 1), NoToneMapping, outputColorSpace).rgb;
|
|
16
|
+
const scene = renderOutput(vec4(foreground, 1), toneMapping, outputColorSpace).rgb;
|
|
17
|
+
return vec4(mix(backdrop, scene, alpha), 1);
|
|
18
|
+
}
|
|
2
19
|
/**
|
|
3
20
|
* Shared backdrop gradient: flat background looking down, a warm haze band
|
|
4
21
|
* hugging the horizon, the theme sky arriving just above it and deepening
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { Camera, Vector3 } from 'three';
|
|
2
|
+
/**
|
|
3
|
+
* One pool for every puff on screen; a full pool reuses the oldest sprite. A big agent build
|
|
4
|
+
* (Victor run 11: 1,545 walls in 6 s) puffs about 2,000 sprites a lifetime.
|
|
5
|
+
*/
|
|
6
|
+
export declare const DUST_CAPACITY = 2048;
|
|
7
|
+
/** A sprite never draws smaller than this on screen, so a whole-building view still shows its dust. */
|
|
8
|
+
export declare const DUST_MIN_PX = 12;
|
|
9
|
+
/** How long a sprite lives, in milliseconds. */
|
|
10
|
+
export declare const DUST_LIFE_MS = 800;
|
|
11
|
+
/**
|
|
12
|
+
* Construction dust as plain numbers: a fixed ring of sprites, each with its
|
|
13
|
+
* birth time, start point, velocity and size. Nothing allocates after
|
|
14
|
+
* construction. The ring order is the birth order, so the slot after the
|
|
15
|
+
* newest is always the oldest — the one a full pool gives up first.
|
|
16
|
+
*/
|
|
17
|
+
export declare class DustPool {
|
|
18
|
+
readonly capacity: number;
|
|
19
|
+
private readonly lifeMs;
|
|
20
|
+
private readonly born;
|
|
21
|
+
private readonly data;
|
|
22
|
+
private next;
|
|
23
|
+
private warmFrames;
|
|
24
|
+
private latestMs;
|
|
25
|
+
private lastEmitMs;
|
|
26
|
+
constructor(capacity?: number, lifeMs?: number);
|
|
27
|
+
/** Sprites alive at the latest time the pool has seen. */
|
|
28
|
+
get live(): number;
|
|
29
|
+
/** True when nothing emitted within a lifetime of `nowMs` can still be alive: a cheap idle check. */
|
|
30
|
+
quietAt(nowMs: number): boolean;
|
|
31
|
+
emit(x: number, y: number, z: number, vx: number, vy: number, vz: number, size: number, nowMs: number): void;
|
|
32
|
+
/**
|
|
33
|
+
* Advances to `nowMs`: expired sprites are dropped, and `write` receives
|
|
34
|
+
* every live one with its current position, size and opacity. Returns how
|
|
35
|
+
* many are alive.
|
|
36
|
+
*/
|
|
37
|
+
step(nowMs: number, write: (slot: number, x: number, y: number, z: number, size: number, alpha: number) => void): number;
|
|
38
|
+
/**
|
|
39
|
+
* Asks the drawing side for a few frames of one invisible sprite: the first puff of a build would
|
|
40
|
+
* otherwise pay for the sprite material's first draw in the middle of the walls rising.
|
|
41
|
+
*/
|
|
42
|
+
requestWarm(frames?: number): void;
|
|
43
|
+
/** One warm frame, if any is asked for. */
|
|
44
|
+
takeWarm(): boolean;
|
|
45
|
+
clear(): void;
|
|
46
|
+
}
|
|
47
|
+
/** A sprite's size in metres at `point`: its own, or DUST_MIN_PX on a screen `heightPx` tall. */
|
|
48
|
+
export declare function dustSpriteSize(size: number, camera: Camera, heightPx: number, point: Vector3): number;
|
|
49
|
+
/** A rising wall's puff: sprites along its base line, pushed out to either side. */
|
|
50
|
+
export declare function emitAlongBase(pool: DustPool, from: Vector3, to: Vector3, nowMs: number): void;
|
|
51
|
+
/**
|
|
52
|
+
* A landing's puff: sprites around the footprint `corners` (four points of
|
|
53
|
+
* its base, in order), pushed outwards. `strength` in [0, 1] scales the
|
|
54
|
+
* cloud, so a crate makes less dust than a column.
|
|
55
|
+
*/
|
|
56
|
+
export declare function emitAroundFoot(pool: DustPool, corners: readonly Vector3[], strength: number, nowMs: number): void;
|
|
57
|
+
//# sourceMappingURL=construction-dust.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"construction-dust.d.ts","sourceRoot":"","sources":["../../src/lib/construction-dust.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAyC,OAAO,EAAE,MAAM,OAAO,CAAA;AAEnF;;;GAGG;AACH,eAAO,MAAM,aAAa,OAAO,CAAA;AACjC,uGAAuG;AACvG,eAAO,MAAM,WAAW,KAAK,CAAA;AAC7B,gDAAgD;AAChD,eAAO,MAAM,YAAY,MAAM,CAAA;AAQ/B;;;;;GAKG;AACH,qBAAa,QAAQ;IACnB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAc;IACnC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAc;IACnC,OAAO,CAAC,IAAI,CAAI;IAChB,OAAO,CAAC,UAAU,CAAI;IACtB,OAAO,CAAC,QAAQ,CAA2B;IAC3C,OAAO,CAAC,UAAU,CAA2B;IAE7C,YAAY,QAAQ,SAAgB,EAAE,MAAM,SAAe,EAK1D;IAED,0DAA0D;IAC1D,IAAI,IAAI,IAAI,MAAM,CAOjB;IAED,qGAAqG;IACrG,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE9B;IAED,IAAI,CACF,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,QAed;IAED;;;;OAIG;IACH,IAAI,CACF,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,GAC1F,MAAM,CAsBR;IAED;;;OAGG;IACH,WAAW,CAAC,MAAM,SAAI,QAErB;IAED,2CAA2C;IAC3C,QAAQ,IAAI,OAAO,CAIlB;IAED,KAAK,SAIJ;CACF;AAED,iGAAiG;AACjG,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,UAc5F;AAYD,oFAAoF;AACpF,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,QAsBtF;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,SAAS,OAAO,EAAE,EAC3B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,QA+Cd"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One pool for every puff on screen; a full pool reuses the oldest sprite. A big agent build
|
|
3
|
+
* (Victor run 11: 1,545 walls in 6 s) puffs about 2,000 sprites a lifetime.
|
|
4
|
+
*/
|
|
5
|
+
export const DUST_CAPACITY = 2048;
|
|
6
|
+
/** A sprite never draws smaller than this on screen, so a whole-building view still shows its dust. */
|
|
7
|
+
export const DUST_MIN_PX = 12;
|
|
8
|
+
/** How long a sprite lives, in milliseconds. */
|
|
9
|
+
export const DUST_LIFE_MS = 800;
|
|
10
|
+
/** Sprites slow down as the cloud spreads, rise a little, and grow as they thin out. */
|
|
11
|
+
const DRAG_PER_S = 2.2;
|
|
12
|
+
const RISE_M_PER_S = 0.25;
|
|
13
|
+
const GROWTH = 1.8;
|
|
14
|
+
const ALPHA = 0.42;
|
|
15
|
+
/**
|
|
16
|
+
* Construction dust as plain numbers: a fixed ring of sprites, each with its
|
|
17
|
+
* birth time, start point, velocity and size. Nothing allocates after
|
|
18
|
+
* construction. The ring order is the birth order, so the slot after the
|
|
19
|
+
* newest is always the oldest — the one a full pool gives up first.
|
|
20
|
+
*/
|
|
21
|
+
export class DustPool {
|
|
22
|
+
capacity;
|
|
23
|
+
lifeMs;
|
|
24
|
+
born;
|
|
25
|
+
data;
|
|
26
|
+
next = 0;
|
|
27
|
+
warmFrames = 0;
|
|
28
|
+
latestMs = Number.NEGATIVE_INFINITY;
|
|
29
|
+
lastEmitMs = Number.NEGATIVE_INFINITY;
|
|
30
|
+
constructor(capacity = DUST_CAPACITY, lifeMs = DUST_LIFE_MS) {
|
|
31
|
+
this.capacity = capacity;
|
|
32
|
+
this.lifeMs = lifeMs;
|
|
33
|
+
this.born = new Float64Array(capacity).fill(Number.NEGATIVE_INFINITY);
|
|
34
|
+
this.data = new Float32Array(capacity * 7);
|
|
35
|
+
}
|
|
36
|
+
/** Sprites alive at the latest time the pool has seen. */
|
|
37
|
+
get live() {
|
|
38
|
+
let alive = 0;
|
|
39
|
+
for (let slot = 0; slot < this.capacity; slot += 1) {
|
|
40
|
+
const age = this.latestMs - this.born[slot];
|
|
41
|
+
if (age >= 0 && age < this.lifeMs)
|
|
42
|
+
alive += 1;
|
|
43
|
+
}
|
|
44
|
+
return alive;
|
|
45
|
+
}
|
|
46
|
+
/** True when nothing emitted within a lifetime of `nowMs` can still be alive: a cheap idle check. */
|
|
47
|
+
quietAt(nowMs) {
|
|
48
|
+
return nowMs - this.lastEmitMs >= this.lifeMs;
|
|
49
|
+
}
|
|
50
|
+
emit(x, y, z, vx, vy, vz, size, nowMs) {
|
|
51
|
+
const slot = this.next;
|
|
52
|
+
this.next = (slot + 1) % this.capacity;
|
|
53
|
+
this.latestMs = Math.max(this.latestMs, nowMs);
|
|
54
|
+
this.lastEmitMs = Math.max(this.lastEmitMs, nowMs);
|
|
55
|
+
this.born[slot] = nowMs;
|
|
56
|
+
const offset = slot * 7;
|
|
57
|
+
this.data[offset] = x;
|
|
58
|
+
this.data[offset + 1] = y;
|
|
59
|
+
this.data[offset + 2] = z;
|
|
60
|
+
this.data[offset + 3] = vx;
|
|
61
|
+
this.data[offset + 4] = vy;
|
|
62
|
+
this.data[offset + 5] = vz;
|
|
63
|
+
this.data[offset + 6] = size;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Advances to `nowMs`: expired sprites are dropped, and `write` receives
|
|
67
|
+
* every live one with its current position, size and opacity. Returns how
|
|
68
|
+
* many are alive.
|
|
69
|
+
*/
|
|
70
|
+
step(nowMs, write) {
|
|
71
|
+
this.latestMs = Math.max(this.latestMs, nowMs);
|
|
72
|
+
let alive = 0;
|
|
73
|
+
for (let slot = 0; slot < this.capacity; slot += 1) {
|
|
74
|
+
const age = nowMs - this.born[slot];
|
|
75
|
+
if (!(age >= 0 && age < this.lifeMs))
|
|
76
|
+
continue;
|
|
77
|
+
const life = age / this.lifeMs;
|
|
78
|
+
const seconds = age / 1000;
|
|
79
|
+
// Distance travelled under linear drag: v·(1 − e^(−k·t))/k.
|
|
80
|
+
const travel = (1 - Math.exp(-DRAG_PER_S * seconds)) / DRAG_PER_S;
|
|
81
|
+
const offset = slot * 7;
|
|
82
|
+
write(alive, this.data[offset] + this.data[offset + 3] * travel, this.data[offset + 1] + this.data[offset + 4] * travel + RISE_M_PER_S * seconds, this.data[offset + 2] + this.data[offset + 5] * travel, this.data[offset + 6] * (1 + GROWTH * Math.sqrt(life)), ALPHA * (1 - life) ** 2);
|
|
83
|
+
alive += 1;
|
|
84
|
+
}
|
|
85
|
+
return alive;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Asks the drawing side for a few frames of one invisible sprite: the first puff of a build would
|
|
89
|
+
* otherwise pay for the sprite material's first draw in the middle of the walls rising.
|
|
90
|
+
*/
|
|
91
|
+
requestWarm(frames = 3) {
|
|
92
|
+
this.warmFrames = Math.max(this.warmFrames, frames);
|
|
93
|
+
}
|
|
94
|
+
/** One warm frame, if any is asked for. */
|
|
95
|
+
takeWarm() {
|
|
96
|
+
if (this.warmFrames <= 0)
|
|
97
|
+
return false;
|
|
98
|
+
this.warmFrames -= 1;
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
clear() {
|
|
102
|
+
this.born.fill(Number.NEGATIVE_INFINITY);
|
|
103
|
+
this.next = 0;
|
|
104
|
+
this.lastEmitMs = Number.NEGATIVE_INFINITY;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/** A sprite's size in metres at `point`: its own, or DUST_MIN_PX on a screen `heightPx` tall. */
|
|
108
|
+
export function dustSpriteSize(size, camera, heightPx, point) {
|
|
109
|
+
const metresPerPx = 'isOrthographicCamera' in camera
|
|
110
|
+
? (camera.top - camera.bottom) /
|
|
111
|
+
camera.zoom /
|
|
112
|
+
heightPx
|
|
113
|
+
: 'isPerspectiveCamera' in camera
|
|
114
|
+
? (2 *
|
|
115
|
+
camera.position.distanceTo(point) *
|
|
116
|
+
Math.tan((camera.fov * Math.PI) / 360)) /
|
|
117
|
+
camera.zoom /
|
|
118
|
+
heightPx
|
|
119
|
+
: 0;
|
|
120
|
+
return Math.max(size, DUST_MIN_PX * metresPerPx);
|
|
121
|
+
}
|
|
122
|
+
/** A fixed pseudo-random sequence: the same build puffs the same way, and tests stay stable. */
|
|
123
|
+
let seed = 0x2f6b9d1;
|
|
124
|
+
function random() {
|
|
125
|
+
seed = (Math.imul(seed ^ (seed >>> 15), 0x2c1b3c6d) + 0x6d2b79f5) | 0;
|
|
126
|
+
return ((seed >>> 0) % 100_000) / 100_000;
|
|
127
|
+
}
|
|
128
|
+
const PUFF_SPACING_M = 0.35;
|
|
129
|
+
const MAX_PUFFS = 16;
|
|
130
|
+
/** A rising wall's puff: sprites along its base line, pushed out to either side. */
|
|
131
|
+
export function emitAlongBase(pool, from, to, nowMs) {
|
|
132
|
+
const dx = to.x - from.x;
|
|
133
|
+
const dz = to.z - from.z;
|
|
134
|
+
const length = Math.hypot(dx, dz);
|
|
135
|
+
const count = Math.max(3, Math.min(MAX_PUFFS, Math.round(length / PUFF_SPACING_M) + 1));
|
|
136
|
+
const nx = length > 1e-6 ? -dz / length : 1;
|
|
137
|
+
const nz = length > 1e-6 ? dx / length : 0;
|
|
138
|
+
for (let index = 0; index < count; index += 1) {
|
|
139
|
+
const along = (index + 0.25 + random() * 0.5) / count;
|
|
140
|
+
const side = random() < 0.5 ? -1 : 1;
|
|
141
|
+
const speed = 0.35 + random() * 0.45;
|
|
142
|
+
pool.emit(from.x + dx * along, from.y + (to.y - from.y) * along + 0.05, from.z + dz * along, side * nx * speed + (random() - 0.5) * 0.2, 0.08 + random() * 0.12, side * nz * speed + (random() - 0.5) * 0.2, 0.22 + random() * 0.14, nowMs);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* A landing's puff: sprites around the footprint `corners` (four points of
|
|
147
|
+
* its base, in order), pushed outwards. `strength` in [0, 1] scales the
|
|
148
|
+
* cloud, so a crate makes less dust than a column.
|
|
149
|
+
*/
|
|
150
|
+
export function emitAroundFoot(pool, corners, strength, nowMs) {
|
|
151
|
+
let cx = 0;
|
|
152
|
+
let cy = 0;
|
|
153
|
+
let cz = 0;
|
|
154
|
+
let perimeter = 0;
|
|
155
|
+
for (let index = 0; index < corners.length; index += 1) {
|
|
156
|
+
const a = corners[index];
|
|
157
|
+
const b = corners[(index + 1) % corners.length];
|
|
158
|
+
cx += a.x / corners.length;
|
|
159
|
+
cy += a.y / corners.length;
|
|
160
|
+
cz += a.z / corners.length;
|
|
161
|
+
perimeter += Math.hypot(b.x - a.x, b.z - a.z);
|
|
162
|
+
}
|
|
163
|
+
const count = Math.max(4, Math.min(MAX_PUFFS, Math.round((perimeter / 0.5) * strength) + 4));
|
|
164
|
+
for (let index = 0; index < count; index += 1) {
|
|
165
|
+
// A point on the perimeter, at an even share of its length.
|
|
166
|
+
let along = ((index + random() * 0.6) / count) * perimeter;
|
|
167
|
+
let x = cx;
|
|
168
|
+
let z = cz;
|
|
169
|
+
for (let edge = 0; edge < corners.length; edge += 1) {
|
|
170
|
+
const a = corners[edge];
|
|
171
|
+
const b = corners[(edge + 1) % corners.length];
|
|
172
|
+
const span = Math.hypot(b.x - a.x, b.z - a.z);
|
|
173
|
+
if (along <= span || edge === corners.length - 1) {
|
|
174
|
+
const f = span > 1e-6 ? Math.min(1, along / span) : 0;
|
|
175
|
+
x = a.x + (b.x - a.x) * f;
|
|
176
|
+
z = a.z + (b.z - a.z) * f;
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
along -= span;
|
|
180
|
+
}
|
|
181
|
+
const ox = x - cx;
|
|
182
|
+
const oz = z - cz;
|
|
183
|
+
const reach = Math.hypot(ox, oz) || 1;
|
|
184
|
+
const speed = (0.3 + random() * 0.4) * (0.5 + strength / 2);
|
|
185
|
+
pool.emit(x, cy + 0.04, z, (ox / reach) * speed, 0.05 + random() * 0.1, (oz / reach) * speed, (0.14 + random() * 0.12) * (0.6 + strength * 0.4), nowMs);
|
|
186
|
+
}
|
|
187
|
+
}
|
package/dist/lib/csg-utils.js
CHANGED
|
@@ -81,7 +81,7 @@ export function computeGeometryBoundsTree(geometry) {
|
|
|
81
81
|
;
|
|
82
82
|
geometry.computeBoundsTree =
|
|
83
83
|
computeBoundsTree;
|
|
84
|
-
geometry.computeBoundsTree({
|
|
84
|
+
geometry.computeBoundsTree({ targetLeafSize: 10 });
|
|
85
85
|
}
|
|
86
86
|
export function prepareBrushForCSG(brush) {
|
|
87
87
|
ensureRenderableGeometryAttributes(brush.geometry);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Discipline, DisplayFamily, DisplayMode } from '@pascal-app/core';
|
|
2
|
+
/**
|
|
3
|
+
* Personal display state (F4, owner decision O3, frozen): kept per project in
|
|
4
|
+
* this browser like `showScans`, never shared with collaborators and never a
|
|
5
|
+
* geometry edit. Additive to `levelMode` / `wallMode`, which keep their
|
|
6
|
+
* meaning. It composes through a `display` layer-hold reason and never
|
|
7
|
+
* touches `object.visible`. Inside it `isolate` wins over `families`, which
|
|
8
|
+
* win over the mode default. "Save look" writes the project default to
|
|
9
|
+
* `site.presentation` (core `SitePresentation`); the canonical bake is
|
|
10
|
+
* independent of both. Not wired into the viewer store yet.
|
|
11
|
+
*/
|
|
12
|
+
export type DisplayState = {
|
|
13
|
+
mode: DisplayMode;
|
|
14
|
+
families?: Partial<Record<DisplayFamily, boolean>>;
|
|
15
|
+
disciplines: Record<Discipline, boolean>;
|
|
16
|
+
xray: boolean;
|
|
17
|
+
/** 0..1 presentation offset. */
|
|
18
|
+
separation: number;
|
|
19
|
+
colorBy: 'material' | 'service';
|
|
20
|
+
isolate?: {
|
|
21
|
+
kind: 'system' | 'owner';
|
|
22
|
+
id: string;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=display-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"display-state.d.ts","sourceRoot":"","sources":["../../src/lib/display-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAE9E;;;;;;;;;GASG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,WAAW,CAAA;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,CAAA;IAClD,WAAW,EAAE,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAAA;IACxC,IAAI,EAAE,OAAO,CAAA;IACb,gCAAgC;IAChC,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,UAAU,GAAG,SAAS,CAAA;IAC/B,OAAO,CAAC,EAAE;QAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAA;CACnD,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { MeshLambertNodeMaterial } from 'three/webgpu';
|
|
2
|
+
/** The flat colour each structure reads as where only a colour is wanted (the cutaway palette). */
|
|
3
|
+
export declare const STRUCTURE_COLORS: {
|
|
4
|
+
readonly cmu: "#97978f";
|
|
5
|
+
readonly concrete: "#8a8a86";
|
|
6
|
+
};
|
|
7
|
+
export type Masonry = {
|
|
8
|
+
course: number;
|
|
9
|
+
length: number;
|
|
10
|
+
joint: number;
|
|
11
|
+
colors: {
|
|
12
|
+
light: string;
|
|
13
|
+
dark: string;
|
|
14
|
+
};
|
|
15
|
+
mortar: string;
|
|
16
|
+
};
|
|
17
|
+
export declare const BLOCK_MASONRY: Masonry;
|
|
18
|
+
/**
|
|
19
|
+
* Courses of running bond: each course half a unit over the one under it, mortar between. The
|
|
20
|
+
* courses are the world's (`positionWorld.y`), so a wall above a foundation bonds on its courses;
|
|
21
|
+
* `along` is the metres along the run, the foundation's own attribute by default, a wall's
|
|
22
|
+
* world-planar U where the geometry has none.
|
|
23
|
+
*/
|
|
24
|
+
export declare function masonryMaterial({ course: courseHeight, length, joint, colors, mortar }: Masonry, along?: any): MeshLambertNodeMaterial;
|
|
25
|
+
export declare function concreteMaterial(): MeshLambertNodeMaterial;
|
|
26
|
+
//# sourceMappingURL=foundation-materials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"foundation-materials.d.ts","sourceRoot":"","sources":["../../src/lib/foundation-materials.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AAkBtD,mGAAmG;AACnG,eAAO,MAAM,gBAAgB;aAC3B,GAAG;aACH,QAAQ;CACA,CAAA;AAUV,MAAM,MAAM,OAAO,GAAG;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IACvC,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAED,eAAO,MAAM,aAAa,EAAE,OAM3B,CAAA;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,OAAO,EAChE,KAAK,GAAE,GAA0C,2BAkBlD;AAED,wBAAgB,gBAAgB,4BAO/B"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { FrontSide } from 'three';
|
|
2
|
+
import { attribute, color, float, floor, fract, mix, mod, mx_noise_float, normalLocal, positionWorld, smoothstep, step, vec3, } from 'three/tsl';
|
|
3
|
+
import { MeshLambertNodeMaterial } from 'three/webgpu';
|
|
4
|
+
/**
|
|
5
|
+
* How bare structure looks: courses of block, and cast concrete. The generic foundation under a
|
|
6
|
+
* building draws them (nodes `generic-foundation.tsx`), and a wall whose assembly has no cladding
|
|
7
|
+
* over a masonry or concrete core shows the same on its exterior face (`wall-materials.ts`), so a
|
|
8
|
+
* basement's walls above grade read as the foundation under them.
|
|
9
|
+
*/
|
|
10
|
+
const INCH = 0.0254;
|
|
11
|
+
/** A nominal 8 × 8 × 16 in block: the course is 8 in with its joint, a block 16 in long. */
|
|
12
|
+
const COURSE = 8 * INCH;
|
|
13
|
+
const BLOCK_LENGTH = 16 * INCH;
|
|
14
|
+
const JOINT = (3 / 8) * INCH;
|
|
15
|
+
const JOINT_SOFTNESS = 0.05;
|
|
16
|
+
const BLOCK_COLORS = { light: '#97978f', dark: '#827f78' };
|
|
17
|
+
const MORTAR_COLOR = '#b5b3aa';
|
|
18
|
+
const CONCRETE_COLORS = { light: '#8a8a86', dark: '#6f6e6a' };
|
|
19
|
+
/** The flat colour each structure reads as where only a colour is wanted (the cutaway palette). */
|
|
20
|
+
export const STRUCTURE_COLORS = {
|
|
21
|
+
cmu: BLOCK_COLORS.light,
|
|
22
|
+
concrete: CONCRETE_COLORS.light,
|
|
23
|
+
};
|
|
24
|
+
// TSL's own typings widen a float to a vec4 here, so the pieces are untyped.
|
|
25
|
+
function jointMask(inside, joint) {
|
|
26
|
+
const edge = joint / 2;
|
|
27
|
+
return smoothstep(float(edge), float(edge + JOINT_SOFTNESS), inside).mul(smoothstep(float(edge), float(edge + JOINT_SOFTNESS), float(1).sub(inside)));
|
|
28
|
+
}
|
|
29
|
+
export const BLOCK_MASONRY = {
|
|
30
|
+
course: COURSE,
|
|
31
|
+
length: BLOCK_LENGTH,
|
|
32
|
+
joint: JOINT,
|
|
33
|
+
colors: BLOCK_COLORS,
|
|
34
|
+
mortar: MORTAR_COLOR,
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Courses of running bond: each course half a unit over the one under it, mortar between. The
|
|
38
|
+
* courses are the world's (`positionWorld.y`), so a wall above a foundation bonds on its courses;
|
|
39
|
+
* `along` is the metres along the run, the foundation's own attribute by default, a wall's
|
|
40
|
+
* world-planar U where the geometry has none.
|
|
41
|
+
*/
|
|
42
|
+
export function masonryMaterial({ course: courseHeight, length, joint, colors, mortar }, along = attribute('along', 'float')) {
|
|
43
|
+
const material = new MeshLambertNodeMaterial({ side: FrontSide });
|
|
44
|
+
const rows = positionWorld.y.div(courseHeight);
|
|
45
|
+
const course = floor(rows);
|
|
46
|
+
const columns = along.div(length).add(mod(course, 2).mul(0.5));
|
|
47
|
+
const face = jointMask(fract(rows), joint / courseHeight).mul(jointMask(fract(columns), joint / length));
|
|
48
|
+
const shade = mx_noise_float(vec3(floor(columns), course, 0.5))
|
|
49
|
+
.mul(0.5)
|
|
50
|
+
.add(0.5);
|
|
51
|
+
const block = mix(color(colors.dark), color(colors.light), shade);
|
|
52
|
+
const patterned = mix(color(mortar), block, face);
|
|
53
|
+
// The top of a run is the plain block, whatever course the shader finds there.
|
|
54
|
+
const flat = step(float(0.5), normalLocal.y.abs());
|
|
55
|
+
material.colorNode = mix(patterned, block, flat);
|
|
56
|
+
return material;
|
|
57
|
+
}
|
|
58
|
+
export function concreteMaterial() {
|
|
59
|
+
const material = new MeshLambertNodeMaterial({ side: FrontSide });
|
|
60
|
+
const grain = mx_noise_float(positionWorld.mul(vec3(2.5, 2.5, 2.5)))
|
|
61
|
+
.mul(0.5)
|
|
62
|
+
.add(0.5);
|
|
63
|
+
material.colorNode = mix(color(CONCRETE_COLORS.dark), color(CONCRETE_COLORS.light), grain);
|
|
64
|
+
return material;
|
|
65
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Material, Object3D } from 'three';
|
|
2
|
+
export declare function importedUnlitMaterial(material: Material | null): boolean;
|
|
3
|
+
/** Native basic materials still use AO; imported unlit assets already own their lighting. */
|
|
4
|
+
export declare function preserveGltfUnlit(): {
|
|
5
|
+
name: string;
|
|
6
|
+
afterRoot({ scenes }: {
|
|
7
|
+
scenes: Object3D[];
|
|
8
|
+
}): null;
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=gltf-unlit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gltf-unlit.d.ts","sourceRoot":"","sources":["../../src/lib/gltf-unlit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAQ,QAAQ,EAAE,MAAM,OAAO,CAAA;AAErD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,GAAG,OAAO,CAExE;AAED,6FAA6F;AAC7F,wBAAgB,iBAAiB;IAE7B,IAAI;IACJ,SAAS,aAAa;QAAE,MAAM,EAAE,QAAQ,EAAE,CAAA;KAAE;EAa/C"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export function importedUnlitMaterial(material) {
|
|
2
|
+
return material?.userData.pascalImportedUnlit === true;
|
|
3
|
+
}
|
|
4
|
+
/** Native basic materials still use AO; imported unlit assets already own their lighting. */
|
|
5
|
+
export function preserveGltfUnlit() {
|
|
6
|
+
return {
|
|
7
|
+
name: 'PASCAL_imported_unlit',
|
|
8
|
+
afterRoot({ scenes }) {
|
|
9
|
+
for (const scene of scenes) {
|
|
10
|
+
scene.traverse((object) => {
|
|
11
|
+
const mesh = object;
|
|
12
|
+
if (!mesh.isMesh)
|
|
13
|
+
return;
|
|
14
|
+
for (const material of [mesh.material].flat()) {
|
|
15
|
+
if ('isMeshBasicMaterial' in material)
|
|
16
|
+
material.userData.pascalImportedUnlit = true;
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type AnyNode, type AnyNodeId, type GroundBox } from '@pascal-app/core';
|
|
2
|
+
import type { Camera } from 'three';
|
|
3
|
+
import { Vector3 } from 'three';
|
|
4
|
+
/** Opens the window for `box` (kept with its margin), or closes it with `null`. */
|
|
5
|
+
export declare function setGroundWindow(box: GroundBox | null): void;
|
|
6
|
+
/** The box the window is currently open for, without its margin. */
|
|
7
|
+
export declare function groundWindowBoxNow(): GroundBox | null;
|
|
8
|
+
/** True while the window is open at all (the veil's word for "only in the window"). */
|
|
9
|
+
export declare function groundWindowEnabledNode(): import("three/webgpu").Node<"bool">;
|
|
10
|
+
/**
|
|
11
|
+
* True for a fragment the window veils: the ray from the eye through it goes on to meet the
|
|
12
|
+
* box (the fragment stands in front of the box, or inside it with box still beyond). The eye
|
|
13
|
+
* inside the box never veils: there the box's near planes are behind it.
|
|
14
|
+
*/
|
|
15
|
+
export declare function inGroundWindowNode(): import("three/webgpu").Node<"bool">;
|
|
16
|
+
/** The mask solid ground draws with: everywhere but in the window. */
|
|
17
|
+
export declare function groundWindowMaskNode(): import("three/webgpu").Node<"bool">;
|
|
18
|
+
/**
|
|
19
|
+
* The CPU twin of {@link inGroundWindowNode} for one pick: whether `point`, seen from
|
|
20
|
+
* `origin`, is ground the window veils — so a click there falls through to what the window
|
|
21
|
+
* reveals, as a click on the wholesale veil does. `false` while the window is closed.
|
|
22
|
+
*/
|
|
23
|
+
export declare function groundWindowContains(origin: Vector3, point: Vector3): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* The window as one capture's own camera sees it, whatever the person's view holds: opened
|
|
26
|
+
* for the interior's floor, or the whole building, when ground stands between that camera
|
|
27
|
+
* and it — and closed when it does not, so a window open for the person's eye never leaks
|
|
28
|
+
* into a picture taken from the other side. The veil surfaces are stood up for the frame
|
|
29
|
+
* (the solid ground stays, its mask cut by the same window). The returned function gives
|
|
30
|
+
* the person's view back.
|
|
31
|
+
*/
|
|
32
|
+
export declare function temporarilyGroundWindowForCapture(camera: Camera, nodes: Record<AnyNodeId, AnyNode>, levelId?: string): () => void;
|
|
33
|
+
//# sourceMappingURL=ground-window.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ground-window.d.ts","sourceRoot":"","sources":["../../src/lib/ground-window.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,SAAS,EAEd,KAAK,SAAS,EAGf,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AACnC,OAAO,EAAa,OAAO,EAAE,MAAM,OAAO,CAAA;AAwB1C,mFAAmF;AACnF,wBAAgB,eAAe,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,GAAG,IAAI,CAiB3D;AAED,oEAAoE;AACpE,wBAAgB,kBAAkB,IAAI,SAAS,GAAG,IAAI,CAErD;AAED,uFAAuF;AACvF,wBAAgB,uBAAuB,wCAEtC;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,wCAiBjC;AAED,sEAAsE;AACtE,wBAAgB,oBAAoB,wCAEnC;AAMD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAkB7E;AAED;;;;;;;GAOG;AACH,wBAAgB,iCAAiC,CAC/C,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,EACjC,OAAO,CAAC,EAAE,MAAM,GACf,MAAM,IAAI,CAaZ"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { buildingGroundBox, levelGroundBox, terrainBetweenEyeAndBox, } from '@pascal-app/core';
|
|
2
|
+
import { Box3, Ray, Vector3 } from 'three';
|
|
3
|
+
import { cameraPosition, max, min, positionWorld, uniform, vec3 } from 'three/tsl';
|
|
4
|
+
import { siteGround } from './site-surfaces';
|
|
5
|
+
/**
|
|
6
|
+
* The ground window: where sculpted ground stands between the eye and the box that is shown
|
|
7
|
+
* (a level, or the whole building), that patch of ground is veiled and the rest stays solid.
|
|
8
|
+
* Decided per fragment here — a ray from the eye through the fragment that goes on to meet
|
|
9
|
+
* the box — because the patch's shape is exactly the box's silhouette against the hillside,
|
|
10
|
+
* which no CPU mesh rebuild could follow as cheaply. Whether the window opens at all is the
|
|
11
|
+
* CPU's word (`terrainBetweenEyeAndBox`, a handful of sight lines), read by the site renderer
|
|
12
|
+
* for the live view and by {@link temporarilyGroundWindowForCapture} for a picture, so both
|
|
13
|
+
* agree from their own cameras.
|
|
14
|
+
*
|
|
15
|
+
* One set of uniforms for the whole scene: there is one shown box at a time.
|
|
16
|
+
*/
|
|
17
|
+
const windowEnabled = uniform(0);
|
|
18
|
+
const windowBoxMin = uniform(new Vector3());
|
|
19
|
+
const windowBoxMax = uniform(new Vector3());
|
|
20
|
+
let currentBox = null;
|
|
21
|
+
/** The margin the window keeps around the box, so the house is not seen through a letterbox. */
|
|
22
|
+
const WINDOW_MARGIN = 0.6;
|
|
23
|
+
/** Opens the window for `box` (kept with its margin), or closes it with `null`. */
|
|
24
|
+
export function setGroundWindow(box) {
|
|
25
|
+
currentBox = box;
|
|
26
|
+
if (!box) {
|
|
27
|
+
windowEnabled.value = 0;
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
windowBoxMin.value.set(box.min[0] - WINDOW_MARGIN, box.min[1] - WINDOW_MARGIN, box.min[2] - WINDOW_MARGIN);
|
|
31
|
+
windowBoxMax.value.set(box.max[0] + WINDOW_MARGIN, box.max[1] + WINDOW_MARGIN, box.max[2] + WINDOW_MARGIN);
|
|
32
|
+
windowEnabled.value = 1;
|
|
33
|
+
}
|
|
34
|
+
/** The box the window is currently open for, without its margin. */
|
|
35
|
+
export function groundWindowBoxNow() {
|
|
36
|
+
return currentBox;
|
|
37
|
+
}
|
|
38
|
+
/** True while the window is open at all (the veil's word for "only in the window"). */
|
|
39
|
+
export function groundWindowEnabledNode() {
|
|
40
|
+
return windowEnabled.greaterThan(0.5);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* True for a fragment the window veils: the ray from the eye through it goes on to meet the
|
|
44
|
+
* box (the fragment stands in front of the box, or inside it with box still beyond). The eye
|
|
45
|
+
* inside the box never veils: there the box's near planes are behind it.
|
|
46
|
+
*/
|
|
47
|
+
export function inGroundWindowNode() {
|
|
48
|
+
const toFragment = positionWorld.sub(cameraPosition);
|
|
49
|
+
const fragmentT = toFragment.length();
|
|
50
|
+
// The epsilon keeps an axis-aligned ray's reciprocal finite where a component is exactly 0.
|
|
51
|
+
const dir = toFragment.normalize().add(vec3(1e-6, 1e-6, 1e-6));
|
|
52
|
+
const invDir = vec3(1).div(dir);
|
|
53
|
+
const near = windowBoxMin.sub(cameraPosition).mul(invDir);
|
|
54
|
+
const far = windowBoxMax.sub(cameraPosition).mul(invDir);
|
|
55
|
+
const tMin = min(near, far);
|
|
56
|
+
const tMax = max(near, far);
|
|
57
|
+
const enter = max(max(tMin.x, tMin.y), tMin.z);
|
|
58
|
+
const exit = min(min(tMax.x, tMax.y), tMax.z);
|
|
59
|
+
return windowEnabled
|
|
60
|
+
.greaterThan(0.5)
|
|
61
|
+
.and(enter.greaterThan(0))
|
|
62
|
+
.and(enter.lessThanEqual(exit))
|
|
63
|
+
.and(exit.greaterThan(fragmentT));
|
|
64
|
+
}
|
|
65
|
+
/** The mask solid ground draws with: everywhere but in the window. */
|
|
66
|
+
export function groundWindowMaskNode() {
|
|
67
|
+
return inGroundWindowNode().not();
|
|
68
|
+
}
|
|
69
|
+
const inflatedBox = new Box3();
|
|
70
|
+
const scratchRay = new Ray();
|
|
71
|
+
const scratchPoint = new Vector3();
|
|
72
|
+
/**
|
|
73
|
+
* The CPU twin of {@link inGroundWindowNode} for one pick: whether `point`, seen from
|
|
74
|
+
* `origin`, is ground the window veils — so a click there falls through to what the window
|
|
75
|
+
* reveals, as a click on the wholesale veil does. `false` while the window is closed.
|
|
76
|
+
*/
|
|
77
|
+
export function groundWindowContains(origin, point) {
|
|
78
|
+
if (!currentBox)
|
|
79
|
+
return false;
|
|
80
|
+
inflatedBox.min.set(currentBox.min[0] - WINDOW_MARGIN, currentBox.min[1] - WINDOW_MARGIN, currentBox.min[2] - WINDOW_MARGIN);
|
|
81
|
+
inflatedBox.max.set(currentBox.max[0] + WINDOW_MARGIN, currentBox.max[1] + WINDOW_MARGIN, currentBox.max[2] + WINDOW_MARGIN);
|
|
82
|
+
if (inflatedBox.containsPoint(origin))
|
|
83
|
+
return false;
|
|
84
|
+
if (inflatedBox.containsPoint(point))
|
|
85
|
+
return true;
|
|
86
|
+
scratchRay.origin.copy(origin);
|
|
87
|
+
scratchRay.direction.copy(point).sub(origin).normalize();
|
|
88
|
+
const entry = scratchRay.intersectBox(inflatedBox, scratchPoint);
|
|
89
|
+
return entry !== null && entry.distanceTo(origin) >= point.distanceTo(origin) - 1e-4;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* The window as one capture's own camera sees it, whatever the person's view holds: opened
|
|
93
|
+
* for the interior's floor, or the whole building, when ground stands between that camera
|
|
94
|
+
* and it — and closed when it does not, so a window open for the person's eye never leaks
|
|
95
|
+
* into a picture taken from the other side. The veil surfaces are stood up for the frame
|
|
96
|
+
* (the solid ground stays, its mask cut by the same window). The returned function gives
|
|
97
|
+
* the person's view back.
|
|
98
|
+
*/
|
|
99
|
+
export function temporarilyGroundWindowForCapture(camera, nodes, levelId) {
|
|
100
|
+
const previous = currentBox;
|
|
101
|
+
const box = levelId ? levelGroundBox(nodes, levelId) : buildingGroundBox(nodes);
|
|
102
|
+
const eye = [camera.position.x, camera.position.y, camera.position.z];
|
|
103
|
+
const open = box !== null && terrainBetweenEyeAndBox(nodes, eye, box);
|
|
104
|
+
setGroundWindow(open ? box : null);
|
|
105
|
+
const { veil } = siteGround();
|
|
106
|
+
const flipped = open ? veil.filter((surface) => !surface.visible) : [];
|
|
107
|
+
for (const surface of flipped)
|
|
108
|
+
surface.visible = true;
|
|
109
|
+
return () => {
|
|
110
|
+
for (const surface of flipped)
|
|
111
|
+
surface.visible = false;
|
|
112
|
+
setGroundWindow(previous);
|
|
113
|
+
};
|
|
114
|
+
}
|