@pascal-app/viewer 1.0.6 → 1.1.0
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/README.md +54 -21
- package/dist/components/viewer/daylight-atmosphere.d.ts +9 -0
- package/dist/components/viewer/daylight-atmosphere.d.ts.map +1 -0
- package/dist/components/viewer/daylight-atmosphere.js +315 -0
- package/dist/components/viewer/drone-controller.d.ts +15 -0
- package/dist/components/viewer/drone-controller.d.ts.map +1 -0
- package/dist/components/viewer/drone-controller.js +194 -0
- package/dist/components/viewer/glb-interactive.d.ts +4 -18
- package/dist/components/viewer/glb-interactive.d.ts.map +1 -1
- package/dist/components/viewer/glb-interactive.js +32 -268
- package/dist/components/viewer/glb-replace-instances.d.ts +6 -3
- package/dist/components/viewer/glb-replace-instances.d.ts.map +1 -1
- package/dist/components/viewer/glb-replace-instances.js +5 -4
- package/dist/components/viewer/glb-scene-motion.d.ts +8 -0
- package/dist/components/viewer/glb-scene-motion.d.ts.map +1 -0
- package/dist/components/viewer/glb-scene-motion.js +57 -0
- package/dist/components/viewer/glb-scene.d.ts +5 -1
- package/dist/components/viewer/glb-scene.d.ts.map +1 -1
- package/dist/components/viewer/glb-scene.js +290 -49
- package/dist/components/viewer/glb-selection-callout.d.ts +17 -0
- package/dist/components/viewer/glb-selection-callout.d.ts.map +1 -0
- package/dist/components/viewer/glb-selection-callout.js +54 -0
- package/dist/components/viewer/glb-walkthrough-controller.d.ts.map +1 -1
- package/dist/components/viewer/glb-walkthrough-controller.js +6 -3
- package/dist/components/viewer/index.d.ts +5 -0
- package/dist/components/viewer/index.d.ts.map +1 -1
- package/dist/components/viewer/index.js +38 -10
- package/dist/components/viewer/lights.d.ts.map +1 -1
- package/dist/components/viewer/lights.js +192 -5
- package/dist/components/viewer/post-processing.d.ts.map +1 -1
- package/dist/components/viewer/post-processing.js +150 -94
- package/dist/components/viewer/registered-systems.d.ts.map +1 -1
- package/dist/components/viewer/registered-systems.js +2 -2
- package/dist/components/viewer/scene-environment.js +4 -5
- package/dist/components/viewer/selection-callout.d.ts +51 -0
- package/dist/components/viewer/selection-callout.d.ts.map +1 -0
- package/dist/components/viewer/selection-callout.js +197 -0
- package/dist/components/viewer/unsupported-gpu-fallback.d.ts.map +1 -1
- package/dist/components/viewer/unsupported-gpu-fallback.js +21 -1
- package/dist/components/viewer/use-daylight-rooms.d.ts +13 -0
- package/dist/components/viewer/use-daylight-rooms.d.ts.map +1 -0
- package/dist/components/viewer/use-daylight-rooms.js +278 -0
- package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
- package/dist/components/viewer/viewer-camera.js +12 -1
- package/dist/hooks/use-gltf-ktx2.d.ts.map +1 -1
- package/dist/hooks/use-gltf-ktx2.js +2 -1
- package/dist/index.d.ts +26 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -8
- package/dist/lib/asset-url.d.ts +0 -1
- package/dist/lib/asset-url.d.ts.map +1 -1
- package/dist/lib/asset-url.js +3 -3
- package/dist/lib/daylight/ground.d.ts +21 -0
- package/dist/lib/daylight/ground.d.ts.map +1 -0
- package/dist/lib/daylight/ground.js +53 -0
- package/dist/lib/daylight/location.d.ts +32 -0
- package/dist/lib/daylight/location.d.ts.map +1 -0
- package/dist/lib/daylight/location.js +74 -0
- package/dist/lib/daylight/look.d.ts +33 -0
- package/dist/lib/daylight/look.d.ts.map +1 -0
- package/dist/lib/daylight/look.js +13 -0
- package/dist/lib/daylight/material-hooks.d.ts +2 -0
- package/dist/lib/daylight/material-hooks.d.ts.map +1 -0
- package/dist/lib/daylight/material-hooks.js +150 -0
- package/dist/lib/daylight/room-context.d.ts +12 -0
- package/dist/lib/daylight/room-context.d.ts.map +1 -0
- package/dist/lib/daylight/room-context.js +54 -0
- package/dist/lib/daylight/room-map.d.ts +124 -0
- package/dist/lib/daylight/room-map.d.ts.map +1 -0
- package/dist/lib/daylight/room-map.js +630 -0
- package/dist/lib/daylight/room-scene.d.ts +56 -0
- package/dist/lib/daylight/room-scene.d.ts.map +1 -0
- package/dist/lib/daylight/room-scene.js +391 -0
- package/dist/lib/daylight/rooms.d.ts +292 -0
- package/dist/lib/daylight/rooms.d.ts.map +1 -0
- package/dist/lib/daylight/rooms.js +1268 -0
- package/dist/lib/daylight/runtime.d.ts +33 -0
- package/dist/lib/daylight/runtime.d.ts.map +1 -0
- package/dist/lib/daylight/runtime.js +42 -0
- package/dist/lib/daylight/sky.d.ts +26 -0
- package/dist/lib/daylight/sky.d.ts.map +1 -0
- package/dist/lib/daylight/sky.js +217 -0
- package/dist/lib/daylight/solar.d.ts +46 -0
- package/dist/lib/daylight/solar.d.ts.map +1 -0
- package/dist/lib/daylight/solar.js +156 -0
- package/dist/lib/device-tier.d.ts +29 -0
- package/dist/lib/device-tier.d.ts.map +1 -0
- package/dist/lib/device-tier.js +71 -0
- package/dist/lib/dispose-object3d.d.ts.map +1 -1
- package/dist/lib/dispose-object3d.js +4 -2
- package/dist/lib/drone-flight.d.ts +44 -0
- package/dist/lib/drone-flight.d.ts.map +1 -0
- package/dist/lib/drone-flight.js +121 -0
- package/dist/lib/editor-highlight.d.ts +16 -0
- package/dist/lib/editor-highlight.d.ts.map +1 -0
- package/dist/lib/editor-highlight.js +33 -0
- package/dist/lib/foundation-materials.d.ts.map +1 -1
- package/dist/lib/foundation-materials.js +2 -4
- package/dist/lib/glb-merge.d.ts +23 -0
- package/dist/lib/glb-merge.d.ts.map +1 -0
- package/dist/lib/glb-merge.js +184 -0
- package/dist/lib/hardware-geometry.d.ts +21 -0
- package/dist/lib/hardware-geometry.d.ts.map +1 -0
- package/dist/lib/hardware-geometry.js +283 -0
- package/dist/lib/ktx2-loader.d.ts.map +1 -1
- package/dist/lib/ktx2-loader.js +2 -1
- package/dist/lib/layer-pass.d.ts +2 -0
- package/dist/lib/layer-pass.d.ts.map +1 -1
- package/dist/lib/layer-pass.js +20 -0
- package/dist/lib/materials.d.ts.map +1 -1
- package/dist/lib/materials.js +50 -23
- package/dist/lib/measurement-label.d.ts +4 -0
- package/dist/lib/measurement-label.d.ts.map +1 -0
- package/dist/lib/measurement-label.js +54 -0
- package/dist/lib/measurement-presentation.d.ts +5 -0
- package/dist/lib/measurement-presentation.d.ts.map +1 -0
- package/dist/lib/measurement-presentation.js +8 -0
- package/dist/lib/merged-outline-node.d.ts.map +1 -1
- package/dist/lib/merged-outline-node.js +7 -1
- package/dist/lib/occlusion-culler.d.ts +34 -0
- package/dist/lib/occlusion-culler.d.ts.map +1 -0
- package/dist/lib/occlusion-culler.js +698 -0
- package/dist/lib/pointer-events.d.ts.map +1 -1
- package/dist/lib/pointer-events.js +3 -3
- package/dist/lib/post-processing-resources.d.ts +4 -0
- package/dist/lib/post-processing-resources.d.ts.map +1 -1
- package/dist/lib/post-processing-resources.js +5 -0
- package/dist/lib/readback-rows.d.ts +14 -0
- package/dist/lib/readback-rows.d.ts.map +1 -0
- package/dist/lib/readback-rows.js +31 -0
- package/dist/lib/reveal-pose.d.ts.map +1 -1
- package/dist/lib/reveal-pose.js +3 -0
- package/dist/lib/scene-matrices.d.ts +12 -0
- package/dist/lib/scene-matrices.d.ts.map +1 -0
- package/dist/lib/scene-matrices.js +287 -0
- package/dist/lib/scene-pass-mrt.d.ts +5 -2
- package/dist/lib/scene-pass-mrt.d.ts.map +1 -1
- package/dist/lib/scene-pass-mrt.js +7 -2
- package/dist/lib/scene-themes.d.ts +0 -2
- package/dist/lib/scene-themes.d.ts.map +1 -1
- package/dist/lib/scene-themes.js +0 -2
- package/dist/lib/scene-visibility.d.ts +2 -0
- package/dist/lib/scene-visibility.d.ts.map +1 -1
- package/dist/lib/scene-visibility.js +12 -3
- package/dist/lib/site-surfaces.d.ts +1 -8
- package/dist/lib/site-surfaces.d.ts.map +1 -1
- package/dist/lib/site-surfaces.js +0 -19
- package/dist/lib/snapshot-pipeline.d.ts.map +1 -1
- package/dist/lib/snapshot-pipeline.js +24 -40
- package/dist/lib/texture-reference.d.ts.map +1 -1
- package/dist/lib/texture-reference.js +13 -10
- package/dist/lib/underground-ambiance.d.ts +0 -2
- package/dist/lib/underground-ambiance.d.ts.map +1 -1
- package/dist/lib/underground-ambiance.js +0 -2
- package/dist/lib/viewer-config.d.ts +21 -0
- package/dist/lib/viewer-config.d.ts.map +1 -0
- package/dist/lib/viewer-config.js +21 -0
- package/dist/lib/viewer-preference-storage.d.ts +7 -0
- package/dist/lib/viewer-preference-storage.d.ts.map +1 -0
- package/dist/lib/viewer-preference-storage.js +23 -0
- package/dist/store/use-item-light-pool.d.ts +3 -6
- package/dist/store/use-item-light-pool.d.ts.map +1 -1
- package/dist/store/use-item-light-pool.js +12 -0
- package/dist/store/use-viewer.d.ts +62 -59
- package/dist/store/use-viewer.d.ts.map +1 -1
- package/dist/store/use-viewer.js +106 -19
- package/dist/systems/ceiling/ceiling-system.d.ts +5 -0
- package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
- package/dist/systems/ceiling/ceiling-system.js +11 -1
- package/dist/systems/door/door-system.d.ts.map +1 -1
- package/dist/systems/door/door-system.js +501 -145
- package/dist/systems/door/leaf-profile.d.ts +11 -0
- package/dist/systems/door/leaf-profile.d.ts.map +1 -0
- package/dist/systems/door/leaf-profile.js +140 -0
- package/dist/systems/elevator/elevator-interaction-system.d.ts +16 -2
- package/dist/systems/elevator/elevator-interaction-system.d.ts.map +1 -1
- package/dist/systems/elevator/elevator-interaction-system.js +41 -8
- package/dist/systems/geometry/geometry-status.d.ts +8 -0
- package/dist/systems/geometry/geometry-status.d.ts.map +1 -0
- package/dist/systems/geometry/geometry-status.js +24 -0
- package/dist/systems/interactive/callout-actions.d.ts +72 -0
- package/dist/systems/interactive/callout-actions.d.ts.map +1 -0
- package/dist/systems/interactive/callout-actions.js +212 -0
- package/dist/systems/interactive/interactive-system.d.ts +5 -1
- package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
- package/dist/systems/interactive/interactive-system.js +44 -121
- package/dist/systems/interactive/opening-settled.d.ts +16 -0
- package/dist/systems/interactive/opening-settled.d.ts.map +1 -0
- package/dist/systems/interactive/opening-settled.js +53 -0
- package/dist/systems/item-light/item-light-system.d.ts.map +1 -1
- package/dist/systems/item-light/item-light-system.js +22 -214
- package/dist/systems/item-light/pooled-lights.d.ts +70 -0
- package/dist/systems/item-light/pooled-lights.d.ts.map +1 -0
- package/dist/systems/item-light/pooled-lights.js +449 -0
- package/dist/systems/level/level-system.d.ts.map +1 -1
- package/dist/systems/level/level-system.js +19 -1
- package/dist/systems/roof/roof-material-slots.d.ts +14 -0
- package/dist/systems/roof/roof-material-slots.d.ts.map +1 -0
- package/dist/systems/roof/roof-material-slots.js +13 -0
- package/dist/systems/roof/roof-materials.d.ts +25 -2
- package/dist/systems/roof/roof-materials.d.ts.map +1 -1
- package/dist/systems/roof/roof-materials.js +85 -26
- package/dist/systems/roof/roof-system.d.ts +34 -1
- package/dist/systems/roof/roof-system.d.ts.map +1 -1
- package/dist/systems/roof/roof-system.js +582 -168
- package/dist/systems/wall/opening-casing-geometry.d.ts +14 -0
- package/dist/systems/wall/opening-casing-geometry.d.ts.map +1 -0
- package/dist/systems/wall/opening-casing-geometry.js +102 -0
- package/dist/systems/wall/opening-cutout-geometry.d.ts +9 -0
- package/dist/systems/wall/opening-cutout-geometry.d.ts.map +1 -1
- package/dist/systems/wall/opening-cutout-geometry.js +7 -3
- package/dist/systems/wall/wall-materials.d.ts.map +1 -1
- package/dist/systems/wall/wall-materials.js +28 -35
- package/dist/systems/wall/wall-system.d.ts +11 -0
- package/dist/systems/wall/wall-system.d.ts.map +1 -1
- package/dist/systems/wall/wall-system.js +86 -25
- package/dist/systems/wall/wall-t-junctions.d.ts +15 -0
- package/dist/systems/wall/wall-t-junctions.d.ts.map +1 -0
- package/dist/systems/wall/wall-t-junctions.js +149 -0
- package/dist/systems/window/window-animation-system.d.ts +3 -12
- package/dist/systems/window/window-animation-system.d.ts.map +1 -1
- package/dist/systems/window/window-animation-system.js +37 -122
- package/dist/systems/window/window-pose.d.ts +31 -0
- package/dist/systems/window/window-pose.d.ts.map +1 -0
- package/dist/systems/window/window-pose.js +152 -0
- package/dist/systems/window/window-roller-shutter.d.ts +8 -0
- package/dist/systems/window/window-roller-shutter.d.ts.map +1 -0
- package/dist/systems/window/window-roller-shutter.js +64 -0
- package/dist/systems/window/window-system.d.ts +2 -11
- package/dist/systems/window/window-system.d.ts.map +1 -1
- package/dist/systems/window/window-system.js +470 -303
- package/package.json +6 -6
- package/dist/systems/interactive/control-widget.d.ts +0 -10
- package/dist/systems/interactive/control-widget.d.ts.map +0 -1
- package/dist/systems/interactive/control-widget.js +0 -35
- package/dist/systems/interactive/procedural-controls.d.ts +0 -13
- package/dist/systems/interactive/procedural-controls.d.ts.map +0 -1
- package/dist/systems/interactive/procedural-controls.js +0 -17
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Color } from 'three';
|
|
2
|
+
/**
|
|
3
|
+
* Live controls of the colored appearance's daylight look, shared by the atmosphere (which drives
|
|
4
|
+
* them every frame), the material hooks and the post-processing. Uniforms only: switching time,
|
|
5
|
+
* weather, lamps or quality never recompiles a material.
|
|
6
|
+
*/
|
|
7
|
+
export declare const daylightUniforms: {
|
|
8
|
+
/** EON rough-diffuse amount on matte surfaces (0 = Lambert). Off on the Low tier. */
|
|
9
|
+
eon: import("three/webgpu").UniformNode<"float", number>;
|
|
10
|
+
/** 1 = albedo remapped into sRGB 40–243 (no black holes, no pure whites). */
|
|
11
|
+
albedoClamp: import("three/webgpu").UniformNode<"float", number>;
|
|
12
|
+
/** Time-of-day lamp level: 0 by day … 1 at night (lamps on 'auto'). */
|
|
13
|
+
lampLevel: import("three/webgpu").UniformNode<"float", number>;
|
|
14
|
+
/** Extra emission of lamp shades and screens at night (× lampLevel). */
|
|
15
|
+
emissiveBoost: import("three/webgpu").UniformNode<"float", number>;
|
|
16
|
+
/** 1 / the night exposure lift, so glows don't blow out when the meter brightens the scene. */
|
|
17
|
+
glowComp: import("three/webgpu").UniformNode<"float", number>;
|
|
18
|
+
/** 1 / exposure: lit lamp emitters target a fixed display brightness (always read as lit). */
|
|
19
|
+
emitterExpInv: import("three/webgpu").UniformNode<"float", number>;
|
|
20
|
+
/** Lit lamp emitter brightness on screen, × display white. */
|
|
21
|
+
emitterDisplay: import("three/webgpu").UniformNode<"float", number>;
|
|
22
|
+
/** 1 = the camera stands outside every room (drives the exterior window glow). */
|
|
23
|
+
cameraOutside: import("three/webgpu").UniformNode<"float", number>;
|
|
24
|
+
/** Night window glow, and whether panes emit enough to bloom softly. */
|
|
25
|
+
windowGlow: import("three/webgpu").UniformNode<"float", number>;
|
|
26
|
+
windowEmit: import("three/webgpu").UniformNode<"float", number>;
|
|
27
|
+
/** Tuned glass: base opacity at normal incidence and tint. */
|
|
28
|
+
glassOpacity: import("three/webgpu").UniformNode<"float", number>;
|
|
29
|
+
glassTint: import("three/webgpu").UniformNode<"color", Color>;
|
|
30
|
+
};
|
|
31
|
+
export declare function setDaylightRenderer(renderer: object, active: boolean): void;
|
|
32
|
+
export declare function isDaylightRenderer(renderer: unknown): boolean;
|
|
33
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../src/lib/daylight/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAA;AAG7B;;;;GAIG;AACH,eAAO,MAAM,gBAAgB;IAC3B,qFAAqF;IACrF,GAAG;IACH,6EAA6E;IAC7E,WAAW;IACX,uEAAuE;IACvE,SAAS;IACT,wEAAwE;IACxE,aAAa;IACb,+FAA+F;IAC/F,QAAQ;IACR,8FAA8F;IAC9F,aAAa;IACb,8DAA8D;IAC9D,cAAc;IACd,kFAAkF;IAClF,aAAa;IACb,wEAAwE;IACxE,UAAU;IACV,UAAU;IACV,8DAA8D;IAC9D,YAAY;IACZ,SAAS;CACV,CAAA;AAKD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,CAG3E;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAE7D"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Color } from 'three';
|
|
2
|
+
import { uniform } from 'three/tsl';
|
|
3
|
+
/**
|
|
4
|
+
* Live controls of the colored appearance's daylight look, shared by the atmosphere (which drives
|
|
5
|
+
* them every frame), the material hooks and the post-processing. Uniforms only: switching time,
|
|
6
|
+
* weather, lamps or quality never recompiles a material.
|
|
7
|
+
*/
|
|
8
|
+
export const daylightUniforms = {
|
|
9
|
+
/** EON rough-diffuse amount on matte surfaces (0 = Lambert). Off on the Low tier. */
|
|
10
|
+
eon: uniform(0),
|
|
11
|
+
/** 1 = albedo remapped into sRGB 40–243 (no black holes, no pure whites). */
|
|
12
|
+
albedoClamp: uniform(0),
|
|
13
|
+
/** Time-of-day lamp level: 0 by day … 1 at night (lamps on 'auto'). */
|
|
14
|
+
lampLevel: uniform(0),
|
|
15
|
+
/** Extra emission of lamp shades and screens at night (× lampLevel). */
|
|
16
|
+
emissiveBoost: uniform(5),
|
|
17
|
+
/** 1 / the night exposure lift, so glows don't blow out when the meter brightens the scene. */
|
|
18
|
+
glowComp: uniform(1),
|
|
19
|
+
/** 1 / exposure: lit lamp emitters target a fixed display brightness (always read as lit). */
|
|
20
|
+
emitterExpInv: uniform(1),
|
|
21
|
+
/** Lit lamp emitter brightness on screen, × display white. */
|
|
22
|
+
emitterDisplay: uniform(4),
|
|
23
|
+
/** 1 = the camera stands outside every room (drives the exterior window glow). */
|
|
24
|
+
cameraOutside: uniform(1),
|
|
25
|
+
/** Night window glow, and whether panes emit enough to bloom softly. */
|
|
26
|
+
windowGlow: uniform(1.12),
|
|
27
|
+
windowEmit: uniform(1),
|
|
28
|
+
/** Tuned glass: base opacity at normal incidence and tint. */
|
|
29
|
+
glassOpacity: uniform(0.1),
|
|
30
|
+
glassTint: uniform(new Color(0.86, 0.95, 0.93)),
|
|
31
|
+
};
|
|
32
|
+
/** Renderers currently showing the daylight look; material hooks only attach for these. */
|
|
33
|
+
const activeRenderers = new WeakSet();
|
|
34
|
+
export function setDaylightRenderer(renderer, active) {
|
|
35
|
+
if (active)
|
|
36
|
+
activeRenderers.add(renderer);
|
|
37
|
+
else
|
|
38
|
+
activeRenderers.delete(renderer);
|
|
39
|
+
}
|
|
40
|
+
export function isDaylightRenderer(renderer) {
|
|
41
|
+
return !!renderer && typeof renderer === 'object' && activeRenderers.has(renderer);
|
|
42
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Color, Vector3 } from 'three';
|
|
2
|
+
import type { SolarLight } from './solar';
|
|
3
|
+
/** Cloud cover per weather preset (0 clear … 1 overcast). */
|
|
4
|
+
export declare const WEATHER_COVER: {
|
|
5
|
+
readonly clear: 0;
|
|
6
|
+
readonly fair: 0.45;
|
|
7
|
+
readonly cloudy: 0.7;
|
|
8
|
+
readonly overcast: 0.9;
|
|
9
|
+
};
|
|
10
|
+
export type Weather = keyof typeof WEATHER_COVER;
|
|
11
|
+
export declare function createDaylightSky(): {
|
|
12
|
+
skyRadiance: (rawDir: any, { disc: withDisc }?: {
|
|
13
|
+
disc?: boolean;
|
|
14
|
+
}) => any;
|
|
15
|
+
horizonRadiance: (rawDir: any) => any;
|
|
16
|
+
lightRadiance: (rawDir: any) => any;
|
|
17
|
+
radianceCpu: (dir: Vector3, out?: Color) => Color;
|
|
18
|
+
update: (sun: SolarLight, coverValue: number) => void;
|
|
19
|
+
cloudTime: import("three/webgpu").UniformNode<"float", number>;
|
|
20
|
+
cover: import("three/webgpu").UniformNode<"float", number>;
|
|
21
|
+
horizon: import("three/webgpu").UniformNode<"color", Color>;
|
|
22
|
+
top: import("three/webgpu").UniformNode<"color", Color>;
|
|
23
|
+
ground: import("three/webgpu").UniformNode<"color", Color>;
|
|
24
|
+
};
|
|
25
|
+
export type DaylightSky = ReturnType<typeof createDaylightSky>;
|
|
26
|
+
//# sourceMappingURL=sky.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sky.d.ts","sourceRoot":"","sources":["../../../src/lib/daylight/sky.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAa,OAAO,EAAE,MAAM,OAAO,CAAA;AAoBjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAsBzC,6DAA6D;AAC7D,eAAO,MAAM,aAAa;aAAK,KAAK,EAAE,CAAC;aAAE,IAAI,EAAE,IAAI;aAAE,MAAM,EAAE,GAAG;aAAE,QAAQ,EAAE,GAAG;CAAW,CAAA;AAC1F,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,aAAa,CAAA;AAqBhD,wBAAgB,iBAAiB;0BAqEF,GAAG,uBAA6B;QAAE,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,KAAQ,GAAG;8BA8CzD,GAAG,KAAG,GAAG;4BAeX,GAAG,KAAG,GAAG;uBAiBd,OAAO;kBAkCZ,UAAU,cAAc,MAAM;;;;;;EAwBpD;AAED,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAA"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { Color, MathUtils, Vector3 } from 'three';
|
|
2
|
+
import { abs, clamp, dot, exp, float, floor, hash, max, mix, mx_fractal_noise_float, normalize, pow, select, smoothstep, uniform, vec2, vec3, } from 'three/tsl';
|
|
3
|
+
const STOPS = [
|
|
4
|
+
{ el: -18, top: '#050b18', horizon: '#1b2232', ground: '#0c1016', glow: '#1b2232' },
|
|
5
|
+
{ el: -8, top: '#0b1731', horizon: '#424b64', ground: '#17202a', glow: '#4a4f6a' },
|
|
6
|
+
{ el: -3, top: '#2f4470', horizon: '#9a8aa0', ground: '#3e434f', glow: '#d98c6c' },
|
|
7
|
+
{ el: 1, top: '#4e638b', horizon: '#e5a38b', ground: '#5d626e', glow: '#f08a52' },
|
|
8
|
+
{ el: 7, top: '#5d7fae', horizon: '#ecc6a4', ground: '#8d8c7e', glow: '#f3b070' },
|
|
9
|
+
{ el: 18, top: '#5b88b8', horizon: '#d8e2e3', ground: '#aeb3a0', glow: '#efd9b8' },
|
|
10
|
+
{ el: 45, top: '#4f82b8', horizon: '#d4e1e6', ground: '#b4b9a6', glow: '#e8e6dc' },
|
|
11
|
+
];
|
|
12
|
+
/** Cloud cover per weather preset (0 clear … 1 overcast). */
|
|
13
|
+
export const WEATHER_COVER = { clear: 0, fair: 0.45, cloudy: 0.7, overcast: 0.9 };
|
|
14
|
+
const scratchA = new Color();
|
|
15
|
+
const scratchB = new Color();
|
|
16
|
+
function lerpStops(el, key, out) {
|
|
17
|
+
const first = STOPS[0];
|
|
18
|
+
const last = STOPS[STOPS.length - 1];
|
|
19
|
+
if (el <= first.el)
|
|
20
|
+
return out.set(first[key]);
|
|
21
|
+
for (let i = 0; i < STOPS.length - 1; i++) {
|
|
22
|
+
const a = STOPS[i];
|
|
23
|
+
const b = STOPS[i + 1];
|
|
24
|
+
if (el <= b.el) {
|
|
25
|
+
return out
|
|
26
|
+
.copy(scratchA.set(a[key]))
|
|
27
|
+
.lerp(scratchB.set(b[key]), MathUtils.smoothstep(el, a.el, b.el));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return out.set(last[key]);
|
|
31
|
+
}
|
|
32
|
+
export function createDaylightSky() {
|
|
33
|
+
const top = uniform(new Color());
|
|
34
|
+
const horizon = uniform(new Color());
|
|
35
|
+
const ground = uniform(new Color());
|
|
36
|
+
const glow = uniform(new Color());
|
|
37
|
+
const sunDir = uniform(new Vector3(0, 1, 0));
|
|
38
|
+
const sunColor = uniform(new Color(1, 1, 1));
|
|
39
|
+
const sunVisible = uniform(1);
|
|
40
|
+
const night = uniform(0);
|
|
41
|
+
const cover = uniform(WEATHER_COVER.fair);
|
|
42
|
+
const cloudTime = uniform(0);
|
|
43
|
+
/** Gradient + warm band towards the sun + overcast greying (shared by every sampler). */
|
|
44
|
+
const base = (dir) => {
|
|
45
|
+
const y = dir.y;
|
|
46
|
+
const gradient = mix(horizon, select(y.greaterThanEqual(0), top, ground), pow(abs(y), 0.55));
|
|
47
|
+
const sunFlat = normalize(vec3(sunDir.x, 0.0001, sunDir.z));
|
|
48
|
+
const toward = max(dot(normalize(vec3(dir.x, 0, dir.z)), sunFlat), 0);
|
|
49
|
+
const banded = mix(gradient, glow, exp(abs(y).mul(-6)).mul(pow(toward, 3)).mul(0.75));
|
|
50
|
+
const lum = dot(banded, vec3(0.2126, 0.7152, 0.0722));
|
|
51
|
+
const grey = vec3(lum).mul(1.08).add(sunColor.mul(sunVisible).mul(0.06));
|
|
52
|
+
return mix(banded, grey, cover
|
|
53
|
+
.mul(cover)
|
|
54
|
+
.mul(0.75)
|
|
55
|
+
.mul(smoothstep(-0.2, 0.05, y)));
|
|
56
|
+
};
|
|
57
|
+
const cloudColor = () => {
|
|
58
|
+
const ambient = mix(horizon, top, 0.35).mul(0.95);
|
|
59
|
+
const dusk = glow.mul(float(1).sub(sunVisible)).mul(0.5).mul(float(1).sub(night));
|
|
60
|
+
return { ambient, dusk };
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Stars: one in a few hundred cells of a fine grid on the sphere, each a small round dot at a
|
|
64
|
+
* jittered spot in its cell. The cell index is offset positive before hashing (the hash reads
|
|
65
|
+
* its seed as an unsigned int, so negative cells all hashed alike and half the sky had none)
|
|
66
|
+
* and kept under 2^24 so the float seed stays exact. Most stars sit below the bloom threshold
|
|
67
|
+
* at night exposure; only the brightest few cross it, for a faint glow.
|
|
68
|
+
*/
|
|
69
|
+
const STAR_GRID = 450;
|
|
70
|
+
const stars = (dir) => {
|
|
71
|
+
const grid = dir.mul(STAR_GRID);
|
|
72
|
+
const cell = floor(grid).add(STAR_GRID + 1);
|
|
73
|
+
const seed = cell.x.add(cell.y.mul(311)).add(cell.z.mul(17123));
|
|
74
|
+
const presence = smoothstep(0.9968, 1, hash(seed));
|
|
75
|
+
const magnitude = presence.mul(presence);
|
|
76
|
+
const jitter = vec3(hash(seed.add(101)), hash(seed.add(211)), hash(seed.add(307)))
|
|
77
|
+
.sub(0.5)
|
|
78
|
+
.mul(0.5);
|
|
79
|
+
const offset = grid.sub(floor(grid)).sub(0.5).sub(jitter);
|
|
80
|
+
const radius = mix(float(0.2), float(0.4), magnitude);
|
|
81
|
+
const dot = float(1).sub(smoothstep(radius.mul(0.35), radius, offset.length()));
|
|
82
|
+
return dot
|
|
83
|
+
.mul(presence)
|
|
84
|
+
.mul(mix(float(0.12), float(0.6), magnitude))
|
|
85
|
+
.mul(night)
|
|
86
|
+
.mul(smoothstep(0.05, 0.3, dir.y));
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Visible sky: fbm clouds (self-shadowed towards the sun), aureole, AA'd sun disc, stars.
|
|
90
|
+
* `disc: false` is the environment capture: aureole kept, disc and stars off (the sun is the
|
|
91
|
+
* directional light — no double specular sun).
|
|
92
|
+
*/
|
|
93
|
+
const skyRadiance = (rawDir, { disc: withDisc = true } = {}) => {
|
|
94
|
+
const dir = normalize(rawDir);
|
|
95
|
+
const y = dir.y;
|
|
96
|
+
const proj = vec2(dir.x, dir.z)
|
|
97
|
+
.div(max(y, 0).add(0.12))
|
|
98
|
+
.mul(0.6)
|
|
99
|
+
.add(vec2(cloudTime.mul(0.006), cloudTime.mul(0.0025)));
|
|
100
|
+
const noiseAt = (q) => mx_fractal_noise_float(vec3(q.x, q.y, 0.37), 5, 2, 0.5)
|
|
101
|
+
.mul(0.5)
|
|
102
|
+
.add(0.5);
|
|
103
|
+
const thresh = float(0.8).sub(cover.mul(0.56));
|
|
104
|
+
const dens = smoothstep(thresh.sub(0.06), thresh.add(0.2), noiseAt(proj));
|
|
105
|
+
const toSun = normalize(vec2(sunDir.x, sunDir.z).add(vec2(1e-4, 0)));
|
|
106
|
+
const occl = smoothstep(thresh.sub(0.06), thresh.add(0.2), noiseAt(proj.add(toSun.mul(0.08))));
|
|
107
|
+
const { ambient, dusk } = cloudColor();
|
|
108
|
+
const lit = sunColor.mul(sunVisible).mul(clamp(float(1.15).sub(occl.mul(0.75)), 0.25, 1.15));
|
|
109
|
+
const cloud = ambient
|
|
110
|
+
.mul(0.55)
|
|
111
|
+
.add(lit.mul(0.85))
|
|
112
|
+
.add(dusk)
|
|
113
|
+
.mul(mix(float(1), float(0.72), cover.mul(cover)));
|
|
114
|
+
const cloudA = dens.mul(smoothstep(0.015, 0.16, y)).mul(0.96);
|
|
115
|
+
const col = mix(base(dir), cloud, cloudA);
|
|
116
|
+
const align = dot(dir, sunDir);
|
|
117
|
+
const aureole = exp(align.sub(1).mul(110))
|
|
118
|
+
.mul(0.2)
|
|
119
|
+
.add(exp(align.sub(1).mul(8)).mul(0.08));
|
|
120
|
+
const disc = withDisc
|
|
121
|
+
? smoothstep(0.99996, 0.999985, align).mul(40).mul(float(1).sub(cloudA))
|
|
122
|
+
: float(0);
|
|
123
|
+
const sun = sunColor
|
|
124
|
+
.mul(aureole.add(disc))
|
|
125
|
+
.mul(sunVisible)
|
|
126
|
+
.mul(smoothstep(-0.01, 0.02, y))
|
|
127
|
+
.mul(float(1).sub(cover.mul(0.6)));
|
|
128
|
+
if (!withDisc)
|
|
129
|
+
return col.add(sun);
|
|
130
|
+
return col.add(sun).add(vec3(stars(dir).mul(float(1).sub(cloudA))));
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* The sky just above the horizon, where the fog and the backdrop meet: `skyRadiance` without
|
|
134
|
+
* its disc, minus the clouds, which fade out below 0.015 of elevation anyway. Every fogged
|
|
135
|
+
* material samples it, so leaving out the cloud noise saves two 5-octave fbm calls per fragment
|
|
136
|
+
* and keeps `cloudTime` (which moves every frame) out of every material's uniforms.
|
|
137
|
+
*/
|
|
138
|
+
const horizonRadiance = (rawDir) => {
|
|
139
|
+
const dir = normalize(rawDir);
|
|
140
|
+
const align = dot(dir, sunDir);
|
|
141
|
+
const aureole = exp(align.sub(1).mul(110))
|
|
142
|
+
.mul(0.2)
|
|
143
|
+
.add(exp(align.sub(1).mul(8)).mul(0.08));
|
|
144
|
+
const sun = sunColor
|
|
145
|
+
.mul(aureole)
|
|
146
|
+
.mul(sunVisible)
|
|
147
|
+
.mul(smoothstep(-0.01, 0.02, dir.y))
|
|
148
|
+
.mul(float(1).sub(cover.mul(0.6)));
|
|
149
|
+
return base(dir).add(sun);
|
|
150
|
+
};
|
|
151
|
+
/** Lighting sky: clouds as their coverage-weighted average (smooth, cheap, sun-free). */
|
|
152
|
+
const lightRadiance = (rawDir) => {
|
|
153
|
+
const dir = normalize(rawDir);
|
|
154
|
+
const { ambient, dusk } = cloudColor();
|
|
155
|
+
const cloud = ambient
|
|
156
|
+
.mul(0.55)
|
|
157
|
+
.add(sunColor.mul(sunVisible).mul(0.68))
|
|
158
|
+
.add(dusk)
|
|
159
|
+
.mul(float(1).sub(cover.mul(cover).mul(0.28)));
|
|
160
|
+
const cloudA = cover
|
|
161
|
+
.mul(0.85)
|
|
162
|
+
.min(1)
|
|
163
|
+
.mul(smoothstep(0.015, 0.16, dir.y))
|
|
164
|
+
.mul(0.7);
|
|
165
|
+
return mix(base(dir), cloud, cloudA);
|
|
166
|
+
};
|
|
167
|
+
/** CPU mirror of the sky (statistical clouds, aureole, no disc) for meters and window portals. */
|
|
168
|
+
const radianceCpu = (dir, out = new Color()) => {
|
|
169
|
+
const d = dir.clone().normalize();
|
|
170
|
+
const t = top.value;
|
|
171
|
+
const h = horizon.value;
|
|
172
|
+
out.copy(h).lerp(d.y >= 0 ? t : ground.value, Math.abs(d.y) ** 0.55);
|
|
173
|
+
const sd = sunDir.value;
|
|
174
|
+
const flat = new Vector3(sd.x, 0.0001, sd.z).normalize();
|
|
175
|
+
const toward = Math.max(new Vector3(d.x, 0, d.z).normalize().dot(flat), 0);
|
|
176
|
+
out.lerp(glow.value, Math.exp(-6 * Math.abs(d.y)) * toward ** 3 * 0.75);
|
|
177
|
+
const c = cover.value;
|
|
178
|
+
const sv = sunVisible.value;
|
|
179
|
+
const sc = sunColor.value;
|
|
180
|
+
const lum = 0.2126 * out.r + 0.7152 * out.g + 0.0722 * out.b;
|
|
181
|
+
const above = MathUtils.smoothstep(d.y, -0.2, 0.05);
|
|
182
|
+
out.lerp(new Color(lum * 1.08 + sc.r * sv * 0.06, lum * 1.08 + sc.g * sv * 0.06, lum * 1.08 + sc.b * sv * 0.06), c * c * 0.75 * above);
|
|
183
|
+
const amb = h.clone().lerp(t, 0.35).multiplyScalar(0.95);
|
|
184
|
+
const cloud = amb
|
|
185
|
+
.multiplyScalar(0.55)
|
|
186
|
+
.add(sc.clone().multiplyScalar(sv * 0.68))
|
|
187
|
+
.multiplyScalar(1 - 0.28 * c * c);
|
|
188
|
+
out.lerp(cloud, Math.min(1, c * 0.85) * MathUtils.smoothstep(d.y, 0.015, 0.16) * 0.7);
|
|
189
|
+
const align = d.dot(sd);
|
|
190
|
+
const aureole = Math.exp((align - 1) * 110) * 0.2 + Math.exp((align - 1) * 8) * 0.08;
|
|
191
|
+
out.add(sc.clone().multiplyScalar(aureole * sv));
|
|
192
|
+
return out;
|
|
193
|
+
};
|
|
194
|
+
const update = (sun, coverValue) => {
|
|
195
|
+
lerpStops(sun.elevation, 'top', top.value);
|
|
196
|
+
lerpStops(sun.elevation, 'horizon', horizon.value);
|
|
197
|
+
lerpStops(sun.elevation, 'ground', ground.value);
|
|
198
|
+
lerpStops(sun.elevation, 'glow', glow.value);
|
|
199
|
+
sunDir.value.copy(sun.direction);
|
|
200
|
+
sunColor.value.copy(sun.color);
|
|
201
|
+
sunVisible.value = MathUtils.smoothstep(sun.elevation, -1, 1);
|
|
202
|
+
night.value = sun.night;
|
|
203
|
+
cover.value = coverValue;
|
|
204
|
+
};
|
|
205
|
+
return {
|
|
206
|
+
skyRadiance,
|
|
207
|
+
horizonRadiance,
|
|
208
|
+
lightRadiance,
|
|
209
|
+
radianceCpu,
|
|
210
|
+
update,
|
|
211
|
+
cloudTime,
|
|
212
|
+
cover,
|
|
213
|
+
horizon,
|
|
214
|
+
top,
|
|
215
|
+
ground,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Color, Vector3 } from 'three';
|
|
2
|
+
export declare function solarPosition(date: Date, lat: number, lng: number): {
|
|
3
|
+
azimuth: number;
|
|
4
|
+
elevation: number;
|
|
5
|
+
};
|
|
6
|
+
export type SolarLight = {
|
|
7
|
+
azimuth: number;
|
|
8
|
+
elevation: number;
|
|
9
|
+
/** Unit vector towards the sun, site frame. */
|
|
10
|
+
direction: Vector3;
|
|
11
|
+
/** Linear RGB, max channel 1: white at noon, gold low, orange at sunset. */
|
|
12
|
+
color: Color;
|
|
13
|
+
/** 0…1 relative strength of the disc (horizon fade × air-mass extinction). */
|
|
14
|
+
strength: number;
|
|
15
|
+
/** 0 day … 1 night (sun below −7°). */
|
|
16
|
+
night: number;
|
|
17
|
+
};
|
|
18
|
+
export declare function solarLight(date: Date, lat: number, lng: number, northRotation: number, out?: SolarLight): SolarLight;
|
|
19
|
+
/** Sun elevation each preset aims for (degrees); midday takes the day's highest sun. */
|
|
20
|
+
declare const PRESET_ELEVATION: {
|
|
21
|
+
readonly golden: 6;
|
|
22
|
+
readonly midday: number;
|
|
23
|
+
readonly blue: -4;
|
|
24
|
+
readonly night: -14;
|
|
25
|
+
};
|
|
26
|
+
export type TimeOfDay = keyof typeof PRESET_ELEVATION;
|
|
27
|
+
/**
|
|
28
|
+
* The instant on `day` (local calendar day at the site's longitude) when the sun reaches the
|
|
29
|
+
* preset's elevation on the afternoon/evening side. Defined by the sun, not the clock, so no
|
|
30
|
+
* timezone is needed. Falls back to the closest elevation reached (polar summer/winter).
|
|
31
|
+
*/
|
|
32
|
+
export declare function presetInstant(day: Date, lat: number, lng: number, preset: TimeOfDay): Date;
|
|
33
|
+
/** The instant `minutes` after local mean midnight on `day` at longitude `lng`. */
|
|
34
|
+
export declare function solarClockInstant(day: Date, lng: number, minutes: number): Date;
|
|
35
|
+
/** Minutes after local mean midnight of `date` at longitude `lng`, 0…1440. */
|
|
36
|
+
export declare function solarClockMinutes(date: Date, lng: number): number;
|
|
37
|
+
/** Sunrise and sunset on `day` (local mean minutes), or null where the sun never crosses. */
|
|
38
|
+
export declare function sunEvents(day: Date, lat: number, lng: number): {
|
|
39
|
+
sunrise: number;
|
|
40
|
+
sunset: number;
|
|
41
|
+
} | null;
|
|
42
|
+
/** The phase a moment of the day reads as, from the sun's elevation and the side of noon. */
|
|
43
|
+
export type SkyPhase = 'night' | 'blue' | 'golden' | 'morning' | 'midday' | 'afternoon';
|
|
44
|
+
export declare function skyPhase(elevation: number, minutes: number): SkyPhase;
|
|
45
|
+
export {};
|
|
46
|
+
//# sourceMappingURL=solar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"solar.d.ts","sourceRoot":"","sources":["../../../src/lib/daylight/solar.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAa,OAAO,EAAE,MAAM,OAAO,CAAA;AAWjD,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM;;;EA4CjE;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,+CAA+C;IAC/C,SAAS,EAAE,OAAO,CAAA;IAClB,4EAA4E;IAC5E,KAAK,EAAE,KAAK,CAAA;IACZ,8EAA8E;IAC9E,QAAQ,EAAE,MAAM,CAAA;IAChB,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAUD,wBAAgB,UAAU,CACxB,IAAI,EAAE,IAAI,EACV,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,aAAa,EAAE,MAAM,EACrB,GAAG,CAAC,EAAE,UAAU,GACf,UAAU,CA4BZ;AAED,wFAAwF;AACxF,QAAA,MAAM,gBAAgB;aACpB,MAAM,EAAE,CAAC;aACT,MAAM;aACN,IAAI,EAAE,CAAC,CAAC;aACR,KAAK,EAAE,CAAC,EAAE;CACF,CAAA;AACV,MAAM,MAAM,SAAS,GAAG,MAAM,OAAO,gBAAgB,CAAA;AAErD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,IAAI,CAiB1F;AAUD,mFAAmF;AACnF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAE/E;AAED,8EAA8E;AAC9E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAGjE;AAED,6FAA6F;AAC7F,wBAAgB,SAAS,CACvB,GAAG,EAAE,IAAI,EACT,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAa5C;AAED,6FAA6F;AAC7F,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,WAAW,CAAA;AAEvF,wBAAgB,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,QAAQ,CAOrE"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { Color, MathUtils, Vector3 } from 'three';
|
|
2
|
+
/**
|
|
3
|
+
* Sun position (NOAA / Meeus low-precision, ~0.01° for 1950–2050) and a small art-directed
|
|
4
|
+
* model of the light it gives. Angles in degrees unless noted. The site frame is x east,
|
|
5
|
+
* y up, z south; `northRotation` (radians, clockwise) is true north's offset from plan −z,
|
|
6
|
+
* so a compass bearing `az` points along plan angle `az + northRotation`.
|
|
7
|
+
*/
|
|
8
|
+
const RAD = Math.PI / 180;
|
|
9
|
+
export function solarPosition(date, lat, lng) {
|
|
10
|
+
const jd = date.getTime() / 86_400_000 + 2_440_587.5;
|
|
11
|
+
const t = (jd - 2_451_545) / 36_525;
|
|
12
|
+
const l0 = (280.46646 + t * (36_000.76983 + t * 0.0003032)) % 360;
|
|
13
|
+
const m = 357.52911 + t * (35_999.05029 - 0.0001537 * t);
|
|
14
|
+
const e = 0.016708634 - t * (0.000042037 + 0.0000001267 * t);
|
|
15
|
+
const c = Math.sin(m * RAD) * (1.914602 - t * (0.004817 + 0.000014 * t)) +
|
|
16
|
+
Math.sin(2 * m * RAD) * (0.019993 - 0.000101 * t) +
|
|
17
|
+
Math.sin(3 * m * RAD) * 0.000289;
|
|
18
|
+
const omega = 125.04 - 1934.136 * t;
|
|
19
|
+
const lambda = l0 + c - 0.00569 - 0.00478 * Math.sin(omega * RAD);
|
|
20
|
+
const eps0 = 23 + (26 + (21.448 - t * (46.815 + t * (0.00059 - t * 0.001813))) / 60) / 60;
|
|
21
|
+
const eps = eps0 + 0.00256 * Math.cos(omega * RAD);
|
|
22
|
+
const decl = Math.asin(Math.sin(eps * RAD) * Math.sin(lambda * RAD)) / RAD;
|
|
23
|
+
const y = Math.tan((eps / 2) * RAD) ** 2;
|
|
24
|
+
const eqTime = (4 / RAD) *
|
|
25
|
+
(y * Math.sin(2 * l0 * RAD) -
|
|
26
|
+
2 * e * Math.sin(m * RAD) +
|
|
27
|
+
4 * e * y * Math.sin(m * RAD) * Math.cos(2 * l0 * RAD) -
|
|
28
|
+
0.5 * y * y * Math.sin(4 * l0 * RAD) -
|
|
29
|
+
1.25 * e * e * Math.sin(2 * m * RAD));
|
|
30
|
+
const minutes = date.getUTCHours() * 60 + date.getUTCMinutes() + date.getUTCSeconds() / 60;
|
|
31
|
+
const solarTime = (((minutes + eqTime + 4 * lng) % 1440) + 1440) % 1440;
|
|
32
|
+
let hourAngle = solarTime / 4 - 180;
|
|
33
|
+
if (hourAngle < -180)
|
|
34
|
+
hourAngle += 360;
|
|
35
|
+
const cosZenith = Math.sin(lat * RAD) * Math.sin(decl * RAD) +
|
|
36
|
+
Math.cos(lat * RAD) * Math.cos(decl * RAD) * Math.cos(hourAngle * RAD);
|
|
37
|
+
const zenith = Math.acos(MathUtils.clamp(cosZenith, -1, 1)) / RAD;
|
|
38
|
+
const azimuth = (Math.atan2(Math.sin(hourAngle * RAD), Math.cos(hourAngle * RAD) * Math.sin(lat * RAD) - Math.tan(decl * RAD) * Math.cos(lat * RAD)) /
|
|
39
|
+
RAD +
|
|
40
|
+
180) %
|
|
41
|
+
360;
|
|
42
|
+
let elevation = 90 - zenith;
|
|
43
|
+
// Atmospheric refraction only matters near the horizon.
|
|
44
|
+
if (elevation > -1)
|
|
45
|
+
elevation += 1.02 / Math.tan((elevation + 10.3 / (elevation + 5.11)) * RAD) / 60;
|
|
46
|
+
return { azimuth, elevation };
|
|
47
|
+
}
|
|
48
|
+
// Rayleigh (680/550/440 nm) plus a light aerosol term.
|
|
49
|
+
const TAU = [0.043 + 0.07, 0.098 + 0.08, 0.236 + 0.095];
|
|
50
|
+
function airMass(elevation) {
|
|
51
|
+
const z = 90 - Math.max(elevation, -0.5);
|
|
52
|
+
return 1 / (Math.cos(z * RAD) + 0.50572 * (96.07995 - z) ** -1.6364);
|
|
53
|
+
}
|
|
54
|
+
export function solarLight(date, lat, lng, northRotation, out) {
|
|
55
|
+
const { azimuth, elevation } = solarPosition(date, lat, lng);
|
|
56
|
+
const s = out ?? {
|
|
57
|
+
azimuth: 0,
|
|
58
|
+
elevation: 0,
|
|
59
|
+
direction: new Vector3(),
|
|
60
|
+
color: new Color(),
|
|
61
|
+
strength: 0,
|
|
62
|
+
night: 0,
|
|
63
|
+
};
|
|
64
|
+
s.azimuth = azimuth;
|
|
65
|
+
s.elevation = elevation;
|
|
66
|
+
const bearing = azimuth * RAD + northRotation;
|
|
67
|
+
const el = elevation * RAD;
|
|
68
|
+
s.direction
|
|
69
|
+
.set(Math.sin(bearing) * Math.cos(el), Math.sin(el), -Math.cos(bearing) * Math.cos(el))
|
|
70
|
+
.normalize();
|
|
71
|
+
const m = airMass(elevation);
|
|
72
|
+
const ratio = TAU.map((tau) => Math.exp(-tau * m) / Math.exp(-tau));
|
|
73
|
+
const peak = Math.max(...ratio, 1e-6);
|
|
74
|
+
// pow 0.6: golden rather than raw-physical orange.
|
|
75
|
+
s.color.setRGB((ratio[0] / peak) ** 0.6, (ratio[1] / peak) ** 0.6, (ratio[2] / peak) ** 0.6);
|
|
76
|
+
s.strength =
|
|
77
|
+
MathUtils.smoothstep(elevation, -1, 1.5) *
|
|
78
|
+
peak *
|
|
79
|
+
Math.min(1, Math.sin(Math.max(el, 0)) * 0.5 + 0.55);
|
|
80
|
+
s.night = 1 - MathUtils.smoothstep(elevation, -7, 2);
|
|
81
|
+
return s;
|
|
82
|
+
}
|
|
83
|
+
/** Sun elevation each preset aims for (degrees); midday takes the day's highest sun. */
|
|
84
|
+
const PRESET_ELEVATION = {
|
|
85
|
+
golden: 6,
|
|
86
|
+
midday: Number.POSITIVE_INFINITY,
|
|
87
|
+
blue: -4,
|
|
88
|
+
night: -14,
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* The instant on `day` (local calendar day at the site's longitude) when the sun reaches the
|
|
92
|
+
* preset's elevation on the afternoon/evening side. Defined by the sun, not the clock, so no
|
|
93
|
+
* timezone is needed. Falls back to the closest elevation reached (polar summer/winter).
|
|
94
|
+
*/
|
|
95
|
+
export function presetInstant(day, lat, lng, preset) {
|
|
96
|
+
// Solar noon in UTC for this longitude: 12:00 local mean time.
|
|
97
|
+
const noonUtc = Date.UTC(day.getFullYear(), day.getMonth(), day.getDate(), 12) - (lng / 15) * 3_600_000;
|
|
98
|
+
const target = PRESET_ELEVATION[preset];
|
|
99
|
+
let best = noonUtc;
|
|
100
|
+
let bestScore = Number.POSITIVE_INFINITY;
|
|
101
|
+
for (let minutes = -120; minutes <= 14 * 60; minutes += 5) {
|
|
102
|
+
const t = noonUtc + minutes * 60_000;
|
|
103
|
+
const { elevation } = solarPosition(new Date(t), lat, lng);
|
|
104
|
+
const score = Number.isFinite(target) ? Math.abs(elevation - target) : -elevation;
|
|
105
|
+
if (score < bestScore) {
|
|
106
|
+
bestScore = score;
|
|
107
|
+
best = t;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return new Date(best);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Times of day as minutes after local mean midnight at the site's longitude (sun time, no
|
|
114
|
+
* timezone): 720 is roughly when the sun is highest, give or take the equation of time.
|
|
115
|
+
*/
|
|
116
|
+
function meanMidnightUtc(day, lng) {
|
|
117
|
+
return Date.UTC(day.getFullYear(), day.getMonth(), day.getDate()) - (lng / 15) * 3_600_000;
|
|
118
|
+
}
|
|
119
|
+
/** The instant `minutes` after local mean midnight on `day` at longitude `lng`. */
|
|
120
|
+
export function solarClockInstant(day, lng, minutes) {
|
|
121
|
+
return new Date(meanMidnightUtc(day, lng) + minutes * 60_000);
|
|
122
|
+
}
|
|
123
|
+
/** Minutes after local mean midnight of `date` at longitude `lng`, 0…1440. */
|
|
124
|
+
export function solarClockMinutes(date, lng) {
|
|
125
|
+
const minutes = (date.getTime() - meanMidnightUtc(date, lng)) / 60_000;
|
|
126
|
+
return ((minutes % 1440) + 1440) % 1440;
|
|
127
|
+
}
|
|
128
|
+
/** Sunrise and sunset on `day` (local mean minutes), or null where the sun never crosses. */
|
|
129
|
+
export function sunEvents(day, lat, lng) {
|
|
130
|
+
const above = (minutes) => solarPosition(solarClockInstant(day, lng, minutes), lat, lng).elevation > -0.833;
|
|
131
|
+
let sunrise = null;
|
|
132
|
+
let sunset = null;
|
|
133
|
+
let previous = above(0);
|
|
134
|
+
for (let minutes = 5; minutes <= 1440; minutes += 5) {
|
|
135
|
+
const now = above(minutes);
|
|
136
|
+
if (now && !previous && sunrise === null)
|
|
137
|
+
sunrise = minutes;
|
|
138
|
+
if (!now && previous && sunrise !== null)
|
|
139
|
+
sunset = minutes;
|
|
140
|
+
previous = now;
|
|
141
|
+
}
|
|
142
|
+
return sunrise !== null && sunset !== null ? { sunrise, sunset } : null;
|
|
143
|
+
}
|
|
144
|
+
export function skyPhase(elevation, minutes) {
|
|
145
|
+
if (elevation < -6)
|
|
146
|
+
return 'night';
|
|
147
|
+
if (elevation < 0)
|
|
148
|
+
return 'blue';
|
|
149
|
+
if (elevation < 10)
|
|
150
|
+
return 'golden';
|
|
151
|
+
if (minutes < 11 * 60)
|
|
152
|
+
return 'morning';
|
|
153
|
+
if (minutes > 14.5 * 60)
|
|
154
|
+
return 'afternoon';
|
|
155
|
+
return 'midday';
|
|
156
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { RenderQuality } from '../store/use-viewer';
|
|
2
|
+
/** What the browser tells us about the device: capabilities, never measured frame rate. */
|
|
3
|
+
export type DeviceSignals = {
|
|
4
|
+
backend: 'webgpu' | 'webgl';
|
|
5
|
+
/** GPU identity: WebGPU adapter info plus the WebGL renderer string, lower-cased. */
|
|
6
|
+
gpu: string;
|
|
7
|
+
/** WebGPU fell back to a software adapter. */
|
|
8
|
+
fallbackAdapter: boolean;
|
|
9
|
+
/** `navigator.deviceMemory` in GB (Chromium only, capped at 8). */
|
|
10
|
+
memoryGb?: number;
|
|
11
|
+
cores?: number;
|
|
12
|
+
/** A phone or tablet: coarse primary pointer, or an iPad passing as a Mac. */
|
|
13
|
+
touch: boolean;
|
|
14
|
+
/** The screen's short side in CSS px. */
|
|
15
|
+
screenShortSide: number;
|
|
16
|
+
mac: boolean;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* The quality tier a device starts on, from what it is rather than how fast it ran a frame:
|
|
20
|
+
* - Low: no WebGPU, software rendering, 4 GB or less, phones, older Intel integrated graphics.
|
|
21
|
+
* - High: discrete GPUs, Apple Pro/Max/Ultra (Safari hides the model: Macs with 10+ cores).
|
|
22
|
+
* - Medium: tablets, other integrated GPUs, base Apple M chips, and anything unrecognized.
|
|
23
|
+
*/
|
|
24
|
+
export declare function classifyDevice(signals: DeviceSignals): RenderQuality;
|
|
25
|
+
/** Rendering on the CPU (a software adapter or rasterizer): every frame competes with the app. */
|
|
26
|
+
export declare function isSoftwareRenderer(signals: DeviceSignals): boolean;
|
|
27
|
+
/** The device's signals, read once the renderer is up (its backend and WebGPU device). */
|
|
28
|
+
export declare function readDeviceSignals(backend: 'webgpu' | 'webgl', device?: unknown): DeviceSignals;
|
|
29
|
+
//# sourceMappingURL=device-tier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-tier.d.ts","sourceRoot":"","sources":["../../src/lib/device-tier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAExD,2FAA2F;AAC3F,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAA;IAC3B,qFAAqF;IACrF,GAAG,EAAE,MAAM,CAAA;IACX,8CAA8C;IAC9C,eAAe,EAAE,OAAO,CAAA;IACxB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,8EAA8E;IAC9E,KAAK,EAAE,OAAO,CAAA;IACd,yCAAyC;IACzC,eAAe,EAAE,MAAM,CAAA;IACvB,GAAG,EAAE,OAAO,CAAA;CACb,CAAA;AASD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,aAAa,GAAG,aAAa,CAWpE;AAED,kGAAkG;AAClG,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAElE;AAID,0FAA0F;AAC1F,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,aAAa,CAoB9F"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const SOFTWARE = /swiftshader|llvmpipe|softpipe|software|basic render/;
|
|
2
|
+
const DISCRETE = /nvidia|geforce|quadro|radeon\s*(rx|pro|r9|vii)|intel.*arc\s*a\d|intel.*arc\(tm\)\s*a\d/;
|
|
3
|
+
const OLD_INTEL = /intel.*(hd graphics|uhd graphics)|gen-(7|8|9)\b/;
|
|
4
|
+
const APPLE_BIG = /apple m\d+\s*(pro|max|ultra)/;
|
|
5
|
+
const APPLE_BASE = /apple m\d+/;
|
|
6
|
+
/**
|
|
7
|
+
* The quality tier a device starts on, from what it is rather than how fast it ran a frame:
|
|
8
|
+
* - Low: no WebGPU, software rendering, 4 GB or less, phones, older Intel integrated graphics.
|
|
9
|
+
* - High: discrete GPUs, Apple Pro/Max/Ultra (Safari hides the model: Macs with 10+ cores).
|
|
10
|
+
* - Medium: tablets, other integrated GPUs, base Apple M chips, and anything unrecognized.
|
|
11
|
+
*/
|
|
12
|
+
export function classifyDevice(signals) {
|
|
13
|
+
const gpu = signals.gpu;
|
|
14
|
+
if (signals.backend === 'webgl' || signals.fallbackAdapter || SOFTWARE.test(gpu))
|
|
15
|
+
return 'low';
|
|
16
|
+
if (signals.memoryGb !== undefined && signals.memoryGb <= 4)
|
|
17
|
+
return 'low';
|
|
18
|
+
if (signals.touch)
|
|
19
|
+
return signals.screenShortSide < 600 ? 'low' : 'medium';
|
|
20
|
+
if (DISCRETE.test(gpu))
|
|
21
|
+
return 'high';
|
|
22
|
+
if (OLD_INTEL.test(gpu) && !/iris|arc/.test(gpu))
|
|
23
|
+
return 'low';
|
|
24
|
+
if (APPLE_BIG.test(gpu))
|
|
25
|
+
return 'high';
|
|
26
|
+
if (APPLE_BASE.test(gpu))
|
|
27
|
+
return 'medium';
|
|
28
|
+
if (signals.mac && /apple/.test(gpu) && (signals.cores ?? 0) >= 10)
|
|
29
|
+
return 'high';
|
|
30
|
+
return 'medium';
|
|
31
|
+
}
|
|
32
|
+
/** Rendering on the CPU (a software adapter or rasterizer): every frame competes with the app. */
|
|
33
|
+
export function isSoftwareRenderer(signals) {
|
|
34
|
+
return signals.fallbackAdapter || SOFTWARE.test(signals.gpu);
|
|
35
|
+
}
|
|
36
|
+
/** The device's signals, read once the renderer is up (its backend and WebGPU device). */
|
|
37
|
+
export function readDeviceSignals(backend, device) {
|
|
38
|
+
const info = device
|
|
39
|
+
?.adapterInfo;
|
|
40
|
+
const nav = navigator;
|
|
41
|
+
const mac = /mac/i.test(nav.platform || nav.userAgent);
|
|
42
|
+
const touch = window.matchMedia?.('(pointer: coarse)').matches === true || (mac && nav.maxTouchPoints > 1);
|
|
43
|
+
return {
|
|
44
|
+
backend,
|
|
45
|
+
gpu: [info?.vendor, info?.architecture, info?.device, info?.description, webglRenderer()]
|
|
46
|
+
.filter(Boolean)
|
|
47
|
+
.join(' ')
|
|
48
|
+
.toLowerCase(),
|
|
49
|
+
fallbackAdapter: info?.isFallbackAdapter === true,
|
|
50
|
+
memoryGb: typeof nav.deviceMemory === 'number' ? nav.deviceMemory : undefined,
|
|
51
|
+
cores: nav.hardwareConcurrency || undefined,
|
|
52
|
+
touch,
|
|
53
|
+
screenShortSide: Math.min(window.screen?.width ?? 0, window.screen?.height ?? 0),
|
|
54
|
+
mac,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/** The WebGL renderer string (the GPU model on Chromium; masked on Safari and Firefox). */
|
|
58
|
+
function webglRenderer() {
|
|
59
|
+
try {
|
|
60
|
+
const gl = document.createElement('canvas').getContext('webgl2');
|
|
61
|
+
if (!gl)
|
|
62
|
+
return '';
|
|
63
|
+
const debug = gl.getExtension('WEBGL_debug_renderer_info');
|
|
64
|
+
const name = String(gl.getParameter(debug ? debug.UNMASKED_RENDERER_WEBGL : gl.RENDERER));
|
|
65
|
+
gl.getExtension('WEBGL_lose_context')?.loseContext();
|
|
66
|
+
return name;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return '';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispose-object3d.d.ts","sourceRoot":"","sources":["../../src/lib/dispose-object3d.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA4B,QAAQ,EAAE,MAAM,OAAO,CAAA;AAM/D,8EAA8E;AAC9E,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"dispose-object3d.d.ts","sourceRoot":"","sources":["../../src/lib/dispose-object3d.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA4B,QAAQ,EAAE,MAAM,OAAO,CAAA;AAM/D,8EAA8E;AAC9E,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,CAwB7D"}
|
|
@@ -18,8 +18,10 @@ export function disposeObject3DResources(root) {
|
|
|
18
18
|
materials.add(objectMaterials);
|
|
19
19
|
}
|
|
20
20
|
});
|
|
21
|
-
for (const geometry of geometries)
|
|
22
|
-
geometry.
|
|
21
|
+
for (const geometry of geometries) {
|
|
22
|
+
if (!geometry.userData.shared)
|
|
23
|
+
geometry.dispose();
|
|
24
|
+
}
|
|
23
25
|
for (const material of materials) {
|
|
24
26
|
if (!isCachedMaterial(material))
|
|
25
27
|
material.dispose();
|