@pascal-app/viewer 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/dist/capture/capture-runtime.js +1 -1
  2. package/dist/components/error-boundary.d.ts.map +1 -1
  3. package/dist/components/renderers/node-renderer.d.ts +1 -1
  4. package/dist/components/renderers/node-renderer.d.ts.map +1 -1
  5. package/dist/components/renderers/node-renderer.js +4 -1
  6. package/dist/components/renderers/parametric-node-renderer.d.ts.map +1 -1
  7. package/dist/components/viewer/bvh-ecctrl.d.ts.map +1 -1
  8. package/dist/components/viewer/frame-limiter.d.ts.map +1 -1
  9. package/dist/components/viewer/glb-interactive.js +1 -1
  10. package/dist/components/viewer/glb-reference-nodes.js +1 -1
  11. package/dist/components/viewer/glb-replace-instances.js +1 -1
  12. package/dist/components/viewer/glb-scene.d.ts.map +1 -1
  13. package/dist/components/viewer/glb-scene.js +2 -3
  14. package/dist/components/viewer/glb-walkthrough-controller.js +1 -1
  15. package/dist/components/viewer/index.d.ts +10 -1
  16. package/dist/components/viewer/index.d.ts.map +1 -1
  17. package/dist/components/viewer/index.js +28 -12
  18. package/dist/components/viewer/lights.d.ts.map +1 -1
  19. package/dist/components/viewer/lights.js +107 -21
  20. package/dist/components/viewer/perf-panel.d.ts.map +1 -1
  21. package/dist/components/viewer/perf-panel.js +1 -1
  22. package/dist/components/viewer/post-processing.d.ts.map +1 -1
  23. package/dist/components/viewer/post-processing.js +96 -33
  24. package/dist/components/viewer/registered-systems.js +1 -1
  25. package/dist/components/viewer/scene-bvh.d.ts.map +1 -1
  26. package/dist/components/viewer/scene-bvh.js +1 -1
  27. package/dist/components/viewer/scene-environment.d.ts.map +1 -1
  28. package/dist/components/viewer/scene-environment.js +31 -11
  29. package/dist/components/viewer/selection-manager.js +15 -7
  30. package/dist/components/viewer/underground-ambiance.d.ts +9 -0
  31. package/dist/components/viewer/underground-ambiance.d.ts.map +1 -0
  32. package/dist/components/viewer/underground-ambiance.js +105 -0
  33. package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
  34. package/dist/components/viewer/viewer-camera.js +1 -1
  35. package/dist/components/viewer/viewer-presentations.d.ts.map +1 -1
  36. package/dist/hooks/use-gltf-ktx2.d.ts.map +1 -1
  37. package/dist/hooks/use-gltf-ktx2.js +2 -0
  38. package/dist/hooks/use-node-events.d.ts.map +1 -1
  39. package/dist/index.d.ts +16 -7
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +16 -7
  42. package/dist/lib/backdrop.d.ts +17 -0
  43. package/dist/lib/backdrop.d.ts.map +1 -1
  44. package/dist/lib/backdrop.js +18 -1
  45. package/dist/lib/construction-dust.d.ts +57 -0
  46. package/dist/lib/construction-dust.d.ts.map +1 -0
  47. package/dist/lib/construction-dust.js +187 -0
  48. package/dist/lib/csg-utils.js +1 -1
  49. package/dist/lib/foundation-materials.d.ts +26 -0
  50. package/dist/lib/foundation-materials.d.ts.map +1 -0
  51. package/dist/lib/foundation-materials.js +65 -0
  52. package/dist/lib/gltf-unlit.d.ts +10 -0
  53. package/dist/lib/gltf-unlit.d.ts.map +1 -0
  54. package/dist/lib/gltf-unlit.js +23 -0
  55. package/dist/lib/ground-window.d.ts +33 -0
  56. package/dist/lib/ground-window.d.ts.map +1 -0
  57. package/dist/lib/ground-window.js +114 -0
  58. package/dist/lib/hero-pose.d.ts +1 -1
  59. package/dist/lib/hero-pose.d.ts.map +1 -1
  60. package/dist/lib/ink-edges.d.ts +2 -1
  61. package/dist/lib/ink-edges.d.ts.map +1 -1
  62. package/dist/lib/ktx2-loader.d.ts +7 -0
  63. package/dist/lib/ktx2-loader.d.ts.map +1 -1
  64. package/dist/lib/ktx2-loader.js +35 -0
  65. package/dist/lib/layer-pass.d.ts.map +1 -1
  66. package/dist/lib/layers.d.ts +11 -7
  67. package/dist/lib/layers.d.ts.map +1 -1
  68. package/dist/lib/layers.js +16 -6
  69. package/dist/lib/materials.d.ts +5 -0
  70. package/dist/lib/materials.d.ts.map +1 -1
  71. package/dist/lib/materials.js +34 -4
  72. package/dist/lib/merged-outline-node.d.ts +5 -0
  73. package/dist/lib/merged-outline-node.d.ts.map +1 -1
  74. package/dist/lib/merged-outline-node.js +80 -2
  75. package/dist/lib/pointer-events.d.ts +20 -4
  76. package/dist/lib/pointer-events.d.ts.map +1 -1
  77. package/dist/lib/pointer-events.js +78 -14
  78. package/dist/lib/post-processing-resources.d.ts.map +1 -1
  79. package/dist/lib/reveal-candidates.d.ts +80 -0
  80. package/dist/lib/reveal-candidates.d.ts.map +1 -0
  81. package/dist/lib/reveal-candidates.js +524 -0
  82. package/dist/lib/reveal-ghost.d.ts +9 -0
  83. package/dist/lib/reveal-ghost.d.ts.map +1 -0
  84. package/dist/lib/reveal-ghost.js +62 -0
  85. package/dist/lib/reveal-pending.d.ts +12 -0
  86. package/dist/lib/reveal-pending.d.ts.map +1 -0
  87. package/dist/lib/reveal-pending.js +12 -0
  88. package/dist/lib/reveal-pose.d.ts +70 -0
  89. package/dist/lib/reveal-pose.d.ts.map +1 -0
  90. package/dist/lib/reveal-pose.js +248 -0
  91. package/dist/lib/reveal-schedule.d.ts +208 -0
  92. package/dist/lib/reveal-schedule.d.ts.map +1 -0
  93. package/dist/lib/reveal-schedule.js +410 -0
  94. package/dist/lib/reveal-texture-scale.d.ts +5 -0
  95. package/dist/lib/reveal-texture-scale.d.ts.map +1 -0
  96. package/dist/lib/reveal-texture-scale.js +113 -0
  97. package/dist/lib/reveal-windows.d.ts +23 -0
  98. package/dist/lib/reveal-windows.d.ts.map +1 -0
  99. package/dist/lib/reveal-windows.js +31 -0
  100. package/dist/lib/reveal-wipe.d.ts +71 -0
  101. package/dist/lib/reveal-wipe.d.ts.map +1 -0
  102. package/dist/lib/reveal-wipe.js +105 -0
  103. package/dist/lib/scene-pass-mrt.d.ts +12 -0
  104. package/dist/lib/scene-pass-mrt.d.ts.map +1 -0
  105. package/dist/lib/scene-pass-mrt.js +23 -0
  106. package/dist/lib/scene-themes.d.ts +30 -8
  107. package/dist/lib/scene-themes.d.ts.map +1 -1
  108. package/dist/lib/scene-themes.js +36 -0
  109. package/dist/lib/scene-visibility.d.ts +11 -1
  110. package/dist/lib/scene-visibility.d.ts.map +1 -1
  111. package/dist/lib/scene-visibility.js +38 -0
  112. package/dist/lib/site-surfaces.d.ts +32 -0
  113. package/dist/lib/site-surfaces.d.ts.map +1 -0
  114. package/dist/lib/site-surfaces.js +70 -0
  115. package/dist/lib/snapshot-pipeline.d.ts +2 -1
  116. package/dist/lib/snapshot-pipeline.d.ts.map +1 -1
  117. package/dist/lib/snapshot-pipeline.js +35 -15
  118. package/dist/lib/tsl-compat.d.ts +2 -2
  119. package/dist/lib/tsl-compat.d.ts.map +1 -1
  120. package/dist/lib/underground-ambiance.d.ts +168 -0
  121. package/dist/lib/underground-ambiance.d.ts.map +1 -0
  122. package/dist/lib/underground-ambiance.js +334 -0
  123. package/dist/store/use-viewer.d.ts +184 -27
  124. package/dist/store/use-viewer.d.ts.map +1 -1
  125. package/dist/store/use-viewer.js +155 -19
  126. package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
  127. package/dist/systems/ceiling/ceiling-system.js +2 -0
  128. package/dist/systems/construction-reveal/construction-dust.d.ts +16 -0
  129. package/dist/systems/construction-reveal/construction-dust.d.ts.map +1 -0
  130. package/dist/systems/construction-reveal/construction-dust.js +92 -0
  131. package/dist/systems/construction-reveal/construction-reveal.d.ts +276 -0
  132. package/dist/systems/construction-reveal/construction-reveal.d.ts.map +1 -0
  133. package/dist/systems/construction-reveal/construction-reveal.js +2383 -0
  134. package/dist/systems/construction-reveal/dust-material.d.ts +10 -0
  135. package/dist/systems/construction-reveal/dust-material.d.ts.map +1 -0
  136. package/dist/systems/construction-reveal/dust-material.js +17 -0
  137. package/dist/systems/construction-reveal/ghost-dress.d.ts +10 -0
  138. package/dist/systems/construction-reveal/ghost-dress.d.ts.map +1 -0
  139. package/dist/systems/construction-reveal/ghost-dress.js +24 -0
  140. package/dist/systems/elevator/elevator-interaction-system.d.ts +7 -0
  141. package/dist/systems/elevator/elevator-interaction-system.d.ts.map +1 -1
  142. package/dist/systems/elevator/elevator-interaction-system.js +18 -1
  143. package/dist/systems/geometry/geometry-system.d.ts.map +1 -1
  144. package/dist/systems/interactive/control-widget.d.ts.map +1 -1
  145. package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
  146. package/dist/systems/interactive/interactive-system.js +16 -6
  147. package/dist/systems/interactive/scripted-clips.js +1 -1
  148. package/dist/systems/item-light/item-light-system.js +1 -1
  149. package/dist/systems/level/level-hold.d.ts +4 -0
  150. package/dist/systems/level/level-hold.d.ts.map +1 -0
  151. package/dist/systems/level/level-hold.js +14 -0
  152. package/dist/systems/level/level-system.d.ts.map +1 -1
  153. package/dist/systems/level/level-system.js +16 -1
  154. package/dist/systems/level/level-utils.d.ts +13 -3
  155. package/dist/systems/level/level-utils.d.ts.map +1 -1
  156. package/dist/systems/level/level-utils.js +56 -8
  157. package/dist/systems/roof/__fixtures__/roof-plane-cases.d.ts +103 -0
  158. package/dist/systems/roof/__fixtures__/roof-plane-cases.d.ts.map +1 -0
  159. package/dist/systems/roof/__fixtures__/roof-plane-cases.js +317 -0
  160. package/dist/systems/roof/roof-materials.d.ts +4 -2
  161. package/dist/systems/roof/roof-materials.d.ts.map +1 -1
  162. package/dist/systems/roof/roof-materials.js +18 -24
  163. package/dist/systems/roof/roof-system.d.ts +8 -1
  164. package/dist/systems/roof/roof-system.d.ts.map +1 -1
  165. package/dist/systems/roof/roof-system.js +195 -45
  166. package/dist/systems/wall/wall-cutout-cache.d.ts +32 -3
  167. package/dist/systems/wall/wall-cutout-cache.d.ts.map +1 -1
  168. package/dist/systems/wall/wall-cutout-cache.js +95 -2
  169. package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
  170. package/dist/systems/wall/wall-material-variant.d.ts.map +1 -1
  171. package/dist/systems/wall/wall-materials.d.ts.map +1 -1
  172. package/dist/systems/wall/wall-materials.js +39 -4
  173. package/dist/systems/wall/wall-placeholder-sweep.d.ts.map +1 -1
  174. package/dist/systems/wall/wall-system.d.ts +5 -0
  175. package/dist/systems/wall/wall-system.d.ts.map +1 -1
  176. package/dist/systems/wall/wall-system.js +42 -19
  177. package/dist/systems/window/corner-frame.d.ts +10 -0
  178. package/dist/systems/window/corner-frame.d.ts.map +1 -0
  179. package/dist/systems/window/corner-frame.js +17 -0
  180. package/dist/systems/window/window-system.d.ts +1 -1
  181. package/dist/systems/window/window-system.d.ts.map +1 -1
  182. package/dist/systems/window/window-system.js +31 -6
  183. package/package.json +8 -8
@@ -0,0 +1,168 @@
1
+ import type { AmbientLight, DirectionalLight, HemisphereLight } from 'three';
2
+ import type { GroundMode } from '../store/use-viewer';
3
+ /**
4
+ * Diving under the ground is the scene's own dark mode, as going under water is in a game: the
5
+ * moment the eye crosses the ground surface the backdrop, the far site, the screen's edges and
6
+ * the sky's light go to the dark theme's slate, and crossing back up brings the day back. Above
7
+ * the ground nothing changes, whatever level is shown (a veiled basement seen from above is the
8
+ * veil as it was). The shown level is not touched: its materials keep their colours and the
9
+ * eye's own light keeps its rooms as readable as ever. Dark around, not dark inside.
10
+ *
11
+ * One factor drives all of it: `0` is the world as it is, `1` is the dark mode. The factor eases
12
+ * between the two over {@link AMBIANCE_SECONDS}, never pops, and is instant when the person's
13
+ * animation setting is Off. It lives here as a shader uniform (the pipeline and the veil read
14
+ * it with no rebuild); the store keeps only whether it is above 0 (`ambianceActive`).
15
+ */
16
+ /** How long the crossing takes, in and out. */
17
+ export declare const AMBIANCE_SECONDS = 0.7;
18
+ /** The simple fade's crossing (`UndergroundTransition` `fade`), in and out. */
19
+ export declare const FADE_SECONDS = 0.4;
20
+ /** The thin rim along the cut of the hole in the ground, from above. */
21
+ export declare const UNDERGROUND_RIM: "#3c3c3c";
22
+ /** The grade ring, where the ground meets the level below grade: a light rim in the dark mode. */
23
+ export declare const UNDERGROUND_RING: "#b4b4b4";
24
+ /** The corners the vignette closes in to. */
25
+ export declare const UNDERGROUND_VIGNETTE: "#0c0c0c";
26
+ /** The sky's ambient and hemisphere light, as a share of what the theme gives. */
27
+ export declare const SKY_LIGHT_SCALE = 0.65;
28
+ /**
29
+ * The eye's own light at full ambiance: the veil's own fill (1) and as much again as the sky's
30
+ * comes down, so the rooms keep their light. It rides the factor, from the veil's own fill (or
31
+ * from nothing where no veil stands), so it neither steps on nor off.
32
+ */
33
+ export declare const EYE_FILL_INTENSITY = 2.4;
34
+ /** The sky light's tint pulls this much toward the palette's neutral light grey. */
35
+ export declare const SKY_LIGHT_TINT = 0.15;
36
+ /** Fog begins this far past the shown level's box and has taken the site by the second. */
37
+ export declare const FOG_START = 3;
38
+ export declare const FOG_END = 24;
39
+ /** The screen's corners darken by this much at most. */
40
+ export declare const VIGNETTE_STRENGTH = 0.42;
41
+ /**
42
+ * How far the eye goes under (or back over) the ground before the flag turns, in metres: it dives
43
+ * 0.10 m below the ground and surfaces 0.10 m above it, a 0.20 m band in all, so an orbit
44
+ * grazing the surface does not flicker.
45
+ */
46
+ export declare const EYE_BAND = 0.1;
47
+ /**
48
+ * Whether the world beyond the walls is the earth's: only the eye under the ground, and never on
49
+ * Solid (the person's own override). What level is shown, or whether the ground is veiled, is not
50
+ * in it. The backdrop and, outside the walkthrough, the whole dark mode follow this edge.
51
+ */
52
+ export declare function earthTarget(state: {
53
+ eyeUnderGround: boolean;
54
+ groundMode: GroundMode;
55
+ }): 0 | 1;
56
+ /**
57
+ * The edge the dark mode (fog, vignette, the sky's light and the eye's own) is wanted by: the
58
+ * earth's edge, except in the walkthrough, where the rooms keep their normal light as the person
59
+ * walks down a stair, and only what is seen outside (the backdrop) turns to earth.
60
+ */
61
+ export declare function ambianceTarget(state: {
62
+ eyeUnderGround: boolean;
63
+ groundMode: GroundMode;
64
+ walkthroughMode?: boolean;
65
+ }): 0 | 1;
66
+ /**
67
+ * Whether the eye lies under the ground, with a small band so a camera resting on the ground
68
+ * plane does not chatter: it goes under {@link EYE_BAND} below the ground and comes back
69
+ * {@link EYE_BAND} above it.
70
+ */
71
+ export declare function eyeUnderTheGround(previous: boolean, eyeY: number, groundY: number): boolean;
72
+ /**
73
+ * One step of the crossing: `progress` moves toward `target` (0 or 1) in a straight line over
74
+ * `seconds`, and is exactly the target once it has arrived. `seconds <= 0` is instant.
75
+ */
76
+ export declare function stepAmbianceProgress(progress: number, target: number, dt: number, seconds: number): number;
77
+ /** The eased factor of a straight-line progress: slow in, slow out. */
78
+ export declare function easeAmbiance(progress: number): number;
79
+ /** The factor as shaders read it: zero while a capture holds the ambiance off. */
80
+ export declare function undergroundAmbianceNode(): import("three/webgpu").UniformNode<"float", number>;
81
+ /** How far the backdrop is the earth's, as shaders read it: zero while a capture holds the ambiance off. */
82
+ export declare function undergroundEarthNode(): import("three/webgpu").UniformNode<"float", number>;
83
+ /** How far the ground's hole is open, as shaders read it: 0 while a capture holds the world plain. */
84
+ export declare function groundHoleOpenNode(): import("three/webgpu").UniformNode<"float", number>;
85
+ /** How far the ground's hole is open now (0 while a capture holds the world plain). */
86
+ export declare function groundHoleOpenNow(): number;
87
+ /** Sets how much the crossing is the noise mask (1) rather than a uniform fade (0); the system's word. */
88
+ export declare function setDiveGrainWeight(next: number): void;
89
+ /** Sets the live opening of the ground's hole (the system's word). */
90
+ export declare function setGroundHoleOpen(next: number): void;
91
+ /** The factor the shown frame wears now: the eased value, or `0` while a capture holds it off. */
92
+ export declare function ambianceNow(): number;
93
+ /** How far the backdrop is the earth's now: the eased value, or `0` while a capture holds it off. */
94
+ export declare function earthNow(): number;
95
+ /** Sets the live factor (the system's word); shaders and lights follow the held value. */
96
+ export declare function setAmbianceFactor(next: number): void;
97
+ /** Sets the live earth factor of the backdrop (the system's word). */
98
+ export declare function setEarthFactor(next: number): void;
99
+ /**
100
+ * The shown frame without the ambiance, for one capture: an agent's picture of a level below
101
+ * grade is read for its rooms, so it is taken in the world's own light, and the person's hole in
102
+ * the ground is not in it (a capture sets its own ground: the plain veil over a level below
103
+ * grade, solid for an exterior). The returned function gives the person's view back.
104
+ */
105
+ export declare function temporarilyWithoutAmbiance(): () => void;
106
+ /** The box the fog keeps clear: the shown level's, or a cube about the eye when none is shown. */
107
+ export declare function setAmbianceFocus(box: {
108
+ min: readonly number[];
109
+ max: readonly number[];
110
+ } | null, eye: {
111
+ x: number;
112
+ y: number;
113
+ z: number;
114
+ }): void;
115
+ /** The box the fog keeps clear now (what {@link setAmbianceFocus} last set). */
116
+ export declare function ambianceFocus(): {
117
+ min: number[];
118
+ max: number[];
119
+ };
120
+ /**
121
+ * The dark mode's backdrop for a view direction: the theme backdrop's own gradient, in the dark
122
+ * theme's colours. The fog dissolves into this same colour per pixel, so what is far leaves no seam.
123
+ */
124
+ export declare function undergroundBackdrop(dirY: any): any;
125
+ /**
126
+ * How far a world position stands in the dark mode's fog, 0 to 1, from its distance outside the
127
+ * shown box (the box itself, and what stands close about it, stays clear). Not scaled by the
128
+ * factor: the pipeline weighs it with {@link undergroundDissolve}.
129
+ */
130
+ export declare function undergroundFogAmount(worldPosition: any): any;
131
+ /**
132
+ * The noise the crossing's mask is cut from: two screen-space perlin octaves, 0 to 1. Computed once
133
+ * a pixel and handed to every {@link undergroundDissolve} that needs it.
134
+ */
135
+ export declare function undergroundDissolveGrain(uv?: any): any;
136
+ /**
137
+ * The crossing as a noise mask closing over the screen: a per-pixel weight, 0 to 1, that goes
138
+ * from 0 everywhere at `amount` 0 (the factor, by default) to 1 everywhere at 1, through soft
139
+ * blobs of the dark world spreading (a fine perlin mask in screen space, not a cross-fade). Weighs
140
+ * the backdrop, the fog and the vignette; above and below the crossing it is exactly 0 and 1.
141
+ * With the simple fade chosen (`grainWeightNode` 0) it is `amount` itself, the same everywhere.
142
+ */
143
+ export declare function undergroundDissolve(uv?: any, amount?: any, grain?: any): any;
144
+ /** The world position of the pixel at `uv` from the scene pass's raw depth. */
145
+ export declare function worldPositionFromDepth(rawDepth: any, projectionInverse: any, cameraWorld: any, uv?: any): any;
146
+ /** The screen's edges closing in: 0 at the centre, 1 at the corners (weigh it with the dissolve). */
147
+ export declare function undergroundVignette(uv?: any): any;
148
+ type SkyLights = {
149
+ ambient: AmbientLight | null;
150
+ hemisphere: HemisphereLight | null;
151
+ /** The light from the eye (`UndergroundFill`), when it is mounted. */
152
+ fill: DirectionalLight | null;
153
+ /** What the fill shines at with no ambiance: the veil's own 1 over a veiled ground, else 0. */
154
+ fillRest: number;
155
+ };
156
+ /** The lights the ambiance moves: `Lights` offers the sky's ambient and hemisphere and the eye's fill. */
157
+ export declare function registerSkyLights(source: () => SkyLights): () => void;
158
+ /**
159
+ * Brings the sky's lights down and tints them by `amount`, and brings the eye's fill up, for the
160
+ * frame about to be drawn. Their neutral state is kept and given back by
161
+ * {@link restoreSkyLights} before the next frame lerps them, so the theme's own smoothing never
162
+ * fights the ambiance. With no ambiance (or none registered) the fill is simply set to its rest.
163
+ */
164
+ export declare function applyAmbianceToSkyLights(amount: number): void;
165
+ /** Gives the sky's lights back as the theme set them, and the eye's fill its rest. */
166
+ export declare function restoreSkyLights(): void;
167
+ export {};
168
+ //# sourceMappingURL=underground-ambiance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"underground-ambiance.d.ts","sourceRoot":"","sources":["../../src/lib/underground-ambiance.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,334 @@
1
+ import { Color, Vector3 } from 'three';
2
+ import { abs, clamp, color, float, getViewPosition, max, mix, mx_noise_float, screenUV, smoothstep, uniform, vec2, vec3, vec4, } from 'three/tsl';
3
+ import { backdropGradient } from './backdrop';
4
+ import { UNDERGROUND_PALETTE } from './scene-themes';
5
+ /**
6
+ * Diving under the ground is the scene's own dark mode, as going under water is in a game: the
7
+ * moment the eye crosses the ground surface the backdrop, the far site, the screen's edges and
8
+ * the sky's light go to the dark theme's slate, and crossing back up brings the day back. Above
9
+ * the ground nothing changes, whatever level is shown (a veiled basement seen from above is the
10
+ * veil as it was). The shown level is not touched: its materials keep their colours and the
11
+ * eye's own light keeps its rooms as readable as ever. Dark around, not dark inside.
12
+ *
13
+ * One factor drives all of it: `0` is the world as it is, `1` is the dark mode. The factor eases
14
+ * between the two over {@link AMBIANCE_SECONDS}, never pops, and is instant when the person's
15
+ * animation setting is Off. It lives here as a shader uniform (the pipeline and the veil read
16
+ * it with no rebuild); the store keeps only whether it is above 0 (`ambianceActive`).
17
+ */
18
+ /** How long the crossing takes, in and out. */
19
+ export const AMBIANCE_SECONDS = 0.7;
20
+ /** The simple fade's crossing (`UndergroundTransition` `fade`), in and out. */
21
+ export const FADE_SECONDS = 0.4;
22
+ /**
23
+ * The dark mode's palette is one place, beside the scene theme tokens
24
+ * (`UNDERGROUND_PALETTE` in `lib/scene-themes.ts`): neutral anthracite, no blue cast. The backdrop
25
+ * is the theme backdrop's own gradient (`lib/backdrop.ts`) in its stops.
26
+ */
27
+ const BACKDROP = {
28
+ background: UNDERGROUND_PALETTE.background,
29
+ haze: UNDERGROUND_PALETTE.haze,
30
+ sky: UNDERGROUND_PALETTE.sky,
31
+ skyDeep: UNDERGROUND_PALETTE.skyDeep,
32
+ };
33
+ /** The thin rim along the cut of the hole in the ground, from above. */
34
+ export const UNDERGROUND_RIM = UNDERGROUND_PALETTE.rim;
35
+ /** The grade ring, where the ground meets the level below grade: a light rim in the dark mode. */
36
+ export const UNDERGROUND_RING = UNDERGROUND_PALETTE.ring;
37
+ /** The corners the vignette closes in to. */
38
+ export const UNDERGROUND_VIGNETTE = UNDERGROUND_PALETTE.vignette;
39
+ /** The sky's ambient and hemisphere light, as a share of what the theme gives. */
40
+ export const SKY_LIGHT_SCALE = 0.65;
41
+ /**
42
+ * The eye's own light at full ambiance: the veil's own fill (1) and as much again as the sky's
43
+ * comes down, so the rooms keep their light. It rides the factor, from the veil's own fill (or
44
+ * from nothing where no veil stands), so it neither steps on nor off.
45
+ */
46
+ export const EYE_FILL_INTENSITY = 2.4;
47
+ /** The sky light's tint pulls this much toward the palette's neutral light grey. */
48
+ export const SKY_LIGHT_TINT = 0.15;
49
+ const TINT = UNDERGROUND_PALETTE.tint;
50
+ /** Fog begins this far past the shown level's box and has taken the site by the second. */
51
+ export const FOG_START = 3;
52
+ export const FOG_END = 24;
53
+ /** What remains of the far site when the fog is complete. */
54
+ const FOG_CAP = 0.97;
55
+ /** The screen's corners darken by this much at most. */
56
+ export const VIGNETTE_STRENGTH = 0.42;
57
+ /**
58
+ * How far the eye goes under (or back over) the ground before the flag turns, in metres: it dives
59
+ * 0.10 m below the ground and surfaces 0.10 m above it, a 0.20 m band in all, so an orbit
60
+ * grazing the surface does not flicker.
61
+ */
62
+ export const EYE_BAND = 0.1;
63
+ /**
64
+ * Whether the world beyond the walls is the earth's: only the eye under the ground, and never on
65
+ * Solid (the person's own override). What level is shown, or whether the ground is veiled, is not
66
+ * in it. The backdrop and, outside the walkthrough, the whole dark mode follow this edge.
67
+ */
68
+ export function earthTarget(state) {
69
+ if (state.groundMode !== 'auto')
70
+ return 0;
71
+ return state.eyeUnderGround ? 1 : 0;
72
+ }
73
+ /**
74
+ * The edge the dark mode (fog, vignette, the sky's light and the eye's own) is wanted by: the
75
+ * earth's edge, except in the walkthrough, where the rooms keep their normal light as the person
76
+ * walks down a stair, and only what is seen outside (the backdrop) turns to earth.
77
+ */
78
+ export function ambianceTarget(state) {
79
+ return state.walkthroughMode === true ? 0 : earthTarget(state);
80
+ }
81
+ /**
82
+ * Whether the eye lies under the ground, with a small band so a camera resting on the ground
83
+ * plane does not chatter: it goes under {@link EYE_BAND} below the ground and comes back
84
+ * {@link EYE_BAND} above it.
85
+ */
86
+ export function eyeUnderTheGround(previous, eyeY, groundY) {
87
+ return previous ? eyeY < groundY + EYE_BAND : eyeY < groundY - EYE_BAND;
88
+ }
89
+ /**
90
+ * One step of the crossing: `progress` moves toward `target` (0 or 1) in a straight line over
91
+ * `seconds`, and is exactly the target once it has arrived. `seconds <= 0` is instant.
92
+ */
93
+ export function stepAmbianceProgress(progress, target, dt, seconds) {
94
+ if (seconds <= 0 || dt >= seconds)
95
+ return target;
96
+ const step = dt / seconds;
97
+ return target > progress ? Math.min(target, progress + step) : Math.max(target, progress - step);
98
+ }
99
+ /** The eased factor of a straight-line progress: slow in, slow out. */
100
+ export function easeAmbiance(progress) {
101
+ const p = Math.min(1, Math.max(0, progress));
102
+ return p * p * (3 - 2 * p);
103
+ }
104
+ // ── The shared factor ────────────────────────────────────────────────────────────────────────
105
+ const factorNode = uniform(0);
106
+ /**
107
+ * How far what is seen beyond the walls (the sky) has turned to earth, 0 to 1. The same as the
108
+ * factor in the overview; in the walkthrough the factor stays 0 (no fog, no vignette, the rooms'
109
+ * own light) and this one alone follows the eye under the ground.
110
+ */
111
+ const earthNode = uniform(0);
112
+ /**
113
+ * How far the hole in the ground is open (0 closed, 1 open): over a level below grade seen from
114
+ * above, the ground stays solid and a hole with a noise-eroded edge opens over the below-grade
115
+ * footprint. Eased like the factor; 0 in a capture, whose own ground it keeps (see
116
+ * {@link temporarilyWithoutAmbiance}).
117
+ */
118
+ const holeNode = uniform(0);
119
+ /**
120
+ * How much of the crossing is the noise mask: 1 the blobs of the dark world spreading, 0 the whole
121
+ * frame fading at once (`UndergroundTransition`). A uniform, so the two share one pipeline.
122
+ */
123
+ const grainWeightNode = uniform(1);
124
+ let hole = 0;
125
+ let factor = 0;
126
+ let earth = 0;
127
+ let held = false;
128
+ /** The factor as shaders read it: zero while a capture holds the ambiance off. */
129
+ export function undergroundAmbianceNode() {
130
+ return factorNode;
131
+ }
132
+ /** How far the backdrop is the earth's, as shaders read it: zero while a capture holds the ambiance off. */
133
+ export function undergroundEarthNode() {
134
+ return earthNode;
135
+ }
136
+ /** How far the ground's hole is open, as shaders read it: 0 while a capture holds the world plain. */
137
+ export function groundHoleOpenNode() {
138
+ return holeNode;
139
+ }
140
+ /** How far the ground's hole is open now (0 while a capture holds the world plain). */
141
+ export function groundHoleOpenNow() {
142
+ return holeNode.value;
143
+ }
144
+ /** Sets how much the crossing is the noise mask (1) rather than a uniform fade (0); the system's word. */
145
+ export function setDiveGrainWeight(next) {
146
+ grainWeightNode.value = next;
147
+ }
148
+ /** Sets the live opening of the ground's hole (the system's word). */
149
+ export function setGroundHoleOpen(next) {
150
+ hole = next;
151
+ holeNode.value = held ? 0 : next;
152
+ }
153
+ /** The factor the shown frame wears now: the eased value, or `0` while a capture holds it off. */
154
+ export function ambianceNow() {
155
+ return held ? 0 : factor;
156
+ }
157
+ /** How far the backdrop is the earth's now: the eased value, or `0` while a capture holds it off. */
158
+ export function earthNow() {
159
+ return held ? 0 : earth;
160
+ }
161
+ /** Sets the live factor (the system's word); shaders and lights follow the held value. */
162
+ export function setAmbianceFactor(next) {
163
+ factor = next;
164
+ factorNode.value = held ? 0 : next;
165
+ }
166
+ /** Sets the live earth factor of the backdrop (the system's word). */
167
+ export function setEarthFactor(next) {
168
+ earth = next;
169
+ earthNode.value = held ? 0 : next;
170
+ }
171
+ /**
172
+ * The shown frame without the ambiance, for one capture: an agent's picture of a level below
173
+ * grade is read for its rooms, so it is taken in the world's own light, and the person's hole in
174
+ * the ground is not in it (a capture sets its own ground: the plain veil over a level below
175
+ * grade, solid for an exterior). The returned function gives the person's view back.
176
+ */
177
+ export function temporarilyWithoutAmbiance() {
178
+ const wasHeld = held;
179
+ held = true;
180
+ factorNode.value = 0;
181
+ earthNode.value = 0;
182
+ holeNode.value = 0;
183
+ restoreSkyLights();
184
+ return () => {
185
+ held = wasHeld;
186
+ factorNode.value = held ? 0 : factor;
187
+ earthNode.value = held ? 0 : earth;
188
+ holeNode.value = held ? 0 : hole;
189
+ };
190
+ }
191
+ // ── The shown level's box: where the world stays clear ──────────────────────────────────────
192
+ const focusMin = uniform(new Vector3(-20, -20, -20));
193
+ const focusMax = uniform(new Vector3(20, 20, 20));
194
+ /** The box the fog keeps clear: the shown level's, or a cube about the eye when none is shown. */
195
+ export function setAmbianceFocus(box, eye) {
196
+ if (box) {
197
+ focusMin.value.set(box.min[0], box.min[1], box.min[2]);
198
+ focusMax.value.set(box.max[0], box.max[1], box.max[2]);
199
+ return;
200
+ }
201
+ const half = 20;
202
+ focusMin.value.set(eye.x - half, eye.y - half, eye.z - half);
203
+ focusMax.value.set(eye.x + half, eye.y + half, eye.z + half);
204
+ }
205
+ /** The box the fog keeps clear now (what {@link setAmbianceFocus} last set). */
206
+ export function ambianceFocus() {
207
+ return { min: focusMin.value.toArray(), max: focusMax.value.toArray() };
208
+ }
209
+ // ── The pipeline's pieces (TSL) ──────────────────────────────────────────────────────────────
210
+ /**
211
+ * The dark mode's backdrop for a view direction: the theme backdrop's own gradient, in the dark
212
+ * theme's colours. The fog dissolves into this same colour per pixel, so what is far leaves no seam.
213
+ */
214
+ export function undergroundBackdrop(dirY) {
215
+ return backdropGradient({
216
+ dirY,
217
+ background: color(BACKDROP.background),
218
+ haze: color(BACKDROP.haze),
219
+ sky: color(BACKDROP.sky),
220
+ skyDeep: color(BACKDROP.skyDeep),
221
+ });
222
+ }
223
+ /**
224
+ * How far a world position stands in the dark mode's fog, 0 to 1, from its distance outside the
225
+ * shown box (the box itself, and what stands close about it, stays clear). Not scaled by the
226
+ * factor: the pipeline weighs it with {@link undergroundDissolve}.
227
+ */
228
+ export function undergroundFogAmount(worldPosition) {
229
+ const centre = focusMin.add(focusMax).mul(0.5);
230
+ const half = focusMax.sub(focusMin).mul(0.5);
231
+ const outside = max(abs(worldPosition.sub(centre)).sub(half), vec3(0)).length();
232
+ return smoothstep(float(FOG_START), float(FOG_END), outside).mul(FOG_CAP);
233
+ }
234
+ /**
235
+ * The noise the crossing's mask is cut from: two screen-space perlin octaves, 0 to 1. Computed once
236
+ * a pixel and handed to every {@link undergroundDissolve} that needs it.
237
+ */
238
+ export function undergroundDissolveGrain(uv = screenUV) {
239
+ // Perlin is centred on 0 with a short spread: stretch it so the mask covers 0..1.
240
+ const grain = mx_noise_float(vec3(uv.mul(vec2(9, 5.8)), 0.37))
241
+ .mul(0.62)
242
+ .add(mx_noise_float(vec3(uv.mul(vec2(21, 13.5)), 1.91)).mul(0.22));
243
+ return clamp(grain.add(0.5), 0, 1);
244
+ }
245
+ /**
246
+ * The crossing as a noise mask closing over the screen: a per-pixel weight, 0 to 1, that goes
247
+ * from 0 everywhere at `amount` 0 (the factor, by default) to 1 everywhere at 1, through soft
248
+ * blobs of the dark world spreading (a fine perlin mask in screen space, not a cross-fade). Weighs
249
+ * the backdrop, the fog and the vignette; above and below the crossing it is exactly 0 and 1.
250
+ * With the simple fade chosen (`grainWeightNode` 0) it is `amount` itself, the same everywhere.
251
+ */
252
+ export function undergroundDissolve(uv = screenUV, amount = factorNode, grain = undergroundDissolveGrain(uv)) {
253
+ const softness = 0.32;
254
+ const t = amount.mul(1 + 2 * softness).sub(softness);
255
+ return mix(amount, smoothstep(grain.sub(softness), grain.add(softness), t), grainWeightNode);
256
+ }
257
+ /** The world position of the pixel at `uv` from the scene pass's raw depth. */
258
+ export function worldPositionFromDepth(rawDepth, projectionInverse, cameraWorld, uv = screenUV) {
259
+ const viewPosition = getViewPosition(uv, rawDepth, projectionInverse);
260
+ return cameraWorld.mul(vec4(viewPosition, 1)).xyz;
261
+ }
262
+ /** The screen's edges closing in: 0 at the centre, 1 at the corners (weigh it with the dissolve). */
263
+ export function undergroundVignette(uv = screenUV) {
264
+ const radial = uv.sub(0.5).length().mul(2);
265
+ return smoothstep(float(0.55), float(1.45), radial).mul(VIGNETTE_STRENGTH);
266
+ }
267
+ let skyLightSource = null;
268
+ let saved = null;
269
+ const tint = new Color(TINT);
270
+ /** The lights the ambiance moves: `Lights` offers the sky's ambient and hemisphere and the eye's fill. */
271
+ export function registerSkyLights(source) {
272
+ skyLightSource = source;
273
+ return () => {
274
+ if (skyLightSource === source) {
275
+ restoreSkyLights();
276
+ skyLightSource = null;
277
+ }
278
+ };
279
+ }
280
+ /**
281
+ * Brings the sky's lights down and tints them by `amount`, and brings the eye's fill up, for the
282
+ * frame about to be drawn. Their neutral state is kept and given back by
283
+ * {@link restoreSkyLights} before the next frame lerps them, so the theme's own smoothing never
284
+ * fights the ambiance. With no ambiance (or none registered) the fill is simply set to its rest.
285
+ */
286
+ export function applyAmbianceToSkyLights(amount) {
287
+ restoreSkyLights();
288
+ if (!skyLightSource)
289
+ return;
290
+ const { ambient, hemisphere, fill, fillRest } = skyLightSource();
291
+ saved = { fill: null, ambient: null, hemisphere: null };
292
+ if (fill) {
293
+ saved.fill = { light: fill, intensity: fillRest };
294
+ fill.intensity = fillRest + (EYE_FILL_INTENSITY - fillRest) * Math.max(0, amount);
295
+ }
296
+ if (amount <= 0)
297
+ return;
298
+ const scale = 1 + (SKY_LIGHT_SCALE - 1) * amount;
299
+ const warmth = SKY_LIGHT_TINT * amount;
300
+ if (ambient) {
301
+ saved.ambient = { light: ambient, intensity: ambient.intensity, color: ambient.color.clone() };
302
+ ambient.intensity *= scale;
303
+ ambient.color.lerp(tint, warmth);
304
+ }
305
+ if (hemisphere) {
306
+ saved.hemisphere = {
307
+ light: hemisphere,
308
+ intensity: hemisphere.intensity,
309
+ color: hemisphere.color.clone(),
310
+ groundColor: hemisphere.groundColor.clone(),
311
+ };
312
+ hemisphere.intensity *= scale;
313
+ hemisphere.color.lerp(tint, warmth);
314
+ hemisphere.groundColor.lerp(tint, warmth);
315
+ }
316
+ }
317
+ /** Gives the sky's lights back as the theme set them, and the eye's fill its rest. */
318
+ export function restoreSkyLights() {
319
+ if (!saved)
320
+ return;
321
+ const { fill, ambient, hemisphere } = saved;
322
+ saved = null;
323
+ if (fill)
324
+ fill.light.intensity = fill.intensity;
325
+ if (ambient) {
326
+ ambient.light.intensity = ambient.intensity;
327
+ ambient.light.color.copy(ambient.color);
328
+ }
329
+ if (hemisphere) {
330
+ hemisphere.light.intensity = hemisphere.intensity;
331
+ hemisphere.light.color.copy(hemisphere.color);
332
+ hemisphere.light.groundColor.copy(hemisphere.groundColor);
333
+ }
334
+ }