feat(A.5 T22): fog wired from N₁/N₂ + ACDREAM_FOG_*_MULT env vars

Per Phase A.5 spec §4.8: fog ramp is tuned to mask the N₁ scenery
boundary. FogStart = N₁ × 192m × 0.7 ≈ 538m at default radii (4/12).
FogEnd = N₂ × 192m × 0.95 ≈ 2188m. Multipliers exposed as env vars for
fast iteration during visual gate.

Override is injected into the UBO after SceneLightingUbo.Build() so fog
color, lightning flash and mode still come from the sky keyframe. Adds
ParseEnvFloat helper (InvariantCulture) for float env-var parsing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-10 08:27:55 +02:00
parent 1488ec62b7
commit 3b684db0f1

View file

@ -6344,6 +6344,28 @@ public sealed class GameWindow : IDisposable
Lighting.Tick(camPos); Lighting.Tick(camPos);
var ubo = AcDream.Core.Lighting.SceneLightingUbo.Build( var ubo = AcDream.Core.Lighting.SceneLightingUbo.Build(
Lighting, in atmo, camPos, (float)WorldTime.DayFraction); Lighting, in atmo, camPos, (float)WorldTime.DayFraction);
// A.5 T22: override fog ramp with N₁/N₂-derived distances so the
// horizon fog masks the N₁ scenery boundary. Sky keyframe fog is
// retail-accurate at normal view distances but far too short for
// the extended N₂=12 (25×25 LB) streaming window.
// FogStart = N₁ × 192m × 0.7 ≈ 538m at defaults (4/12).
// FogEnd = N₂ × 192m × 0.95 ≈ 2188m at defaults.
// Multipliers exposed as env vars for fast iteration at visual gate.
{
const float LandblockSize = 192.0f;
float startMult = ParseEnvFloat("ACDREAM_FOG_START_MULT", 0.7f);
float endMult = ParseEnvFloat("ACDREAM_FOG_END_MULT", 0.95f);
float fogStart = _nearRadius * LandblockSize * startMult;
float fogEnd = _farRadius * LandblockSize * endMult;
// Preserve fog color (xyz), lightning flash (z), and mode (w).
ubo.FogParams = new System.Numerics.Vector4(
fogStart,
fogEnd,
ubo.FogParams.Z, // lightning flash — unchanged
ubo.FogParams.W); // fog mode — unchanged
}
_sceneLightingUbo?.Upload(ubo); _sceneLightingUbo?.Upload(ubo);
// Never cull the landblock the player is currently on. // Never cull the landblock the player is currently on.
@ -8862,6 +8884,17 @@ public sealed class GameWindow : IDisposable
return copy[copy.Length - 1 - offset]; return copy[copy.Length - 1 - offset];
} }
/// <summary>A.5 T22: parse a float environment variable, returning
/// <paramref name="defaultValue"/> when the variable is absent or unparseable.</summary>
private static float ParseEnvFloat(string name, float defaultValue)
{
var s = System.Environment.GetEnvironmentVariable(name);
if (s is not null && float.TryParse(s, System.Globalization.NumberStyles.Float,
System.Globalization.CultureInfo.InvariantCulture, out var v))
return v;
return defaultValue;
}
private void OnClosing() private void OnClosing()
{ {
// Phase A.1: join the streamer worker thread before tearing down GL // Phase A.1: join the streamer worker thread before tearing down GL