From 756def5cebf3dc8f464a21b9bffa0f1b0437d64e Mon Sep 17 00:00:00 2001 From: Erik Date: Sun, 19 Apr 2026 10:52:54 +0200 Subject: [PATCH] =?UTF-8?q?feat(world):=20Phase=20G.1=20=E2=80=94=20debug-?= =?UTF-8?q?time=20override=20tests=20+=20clear-color=20clamp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Small polish commit: - Clamp ClearColor inputs to [0, 1] because retail keyframes store sun/fog colors pre-multiplied by their brightness scalars, which can exceed 1.0; some drivers treat ClearColor > 1 as a saturate-bright hint and produce visible color shifts at the edges. - 4 new tests cover WorldTimeService.SetDebugTime / ClearDebugTime / SyncFromServer-clears-override / SetProvider hot-swap. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/AcDream.App/Rendering/GameWindow.cs | 10 +++- .../World/WorldTimeDebugTests.cs | 58 +++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 tests/AcDream.Core.Tests/World/WorldTimeDebugTests.cs diff --git a/src/AcDream.App/Rendering/GameWindow.cs b/src/AcDream.App/Rendering/GameWindow.cs index e87aec8..a318ad9 100644 --- a/src/AcDream.App/Rendering/GameWindow.cs +++ b/src/AcDream.App/Rendering/GameWindow.cs @@ -3040,7 +3040,15 @@ public sealed class GameWindow : IDisposable var kf = WorldTime.CurrentSky; var atmo = Weather.Snapshot(in kf); var fogColor = atmo.FogColor; - _gl!.ClearColor(fogColor.X, fogColor.Y, fogColor.Z, 1f); + // Clamp to 0..1 — keyframes may store over-1 values (retail uses the + // dir-bright scalar pre-multiplied into color) and GL's ClearColor + // will silently accept them, but some drivers interpret > 1 as + // "bright clamp", producing ugly pink/green frames. + _gl!.ClearColor( + System.Math.Clamp(fogColor.X, 0f, 1f), + System.Math.Clamp(fogColor.Y, 0f, 1f), + System.Math.Clamp(fogColor.Z, 0f, 1f), + 1f); _gl!.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); diff --git a/tests/AcDream.Core.Tests/World/WorldTimeDebugTests.cs b/tests/AcDream.Core.Tests/World/WorldTimeDebugTests.cs new file mode 100644 index 0000000..c0d5df7 --- /dev/null +++ b/tests/AcDream.Core.Tests/World/WorldTimeDebugTests.cs @@ -0,0 +1,58 @@ +using System; +using AcDream.Core.World; +using Xunit; + +namespace AcDream.Core.Tests.World; + +public sealed class WorldTimeDebugTests +{ + [Fact] + public void SetDebugTime_OverridesDayFraction() + { + var service = new WorldTimeService(SkyStateProvider.Default()); + service.SyncFromServer(0); // midnight + + service.SetDebugTime(0.5f); // force noon + Assert.InRange(service.DayFraction, 0.499, 0.501); + } + + [Fact] + public void ClearDebugTime_RestoresServerTime() + { + var service = new WorldTimeService(SkyStateProvider.Default()); + service.SyncFromServer(DerethDateTime.DayTicks * 0.25); // dawn + service.SetDebugTime(0.5f); + service.ClearDebugTime(); + + Assert.InRange(service.DayFraction, 0.24, 0.26); + } + + [Fact] + public void SyncFromServer_ClearsDebugOverride() + { + var service = new WorldTimeService(SkyStateProvider.Default()); + service.SetDebugTime(0.75f); + service.SyncFromServer(0); // midnight — this should clear the override + + Assert.InRange(service.DayFraction, 0.0, 0.01); + } + + [Fact] + public void SetProvider_AcceptsNewKeyframes() + { + var service = new WorldTimeService(SkyStateProvider.Default()); + var custom = new SkyStateProvider(new[] + { + new SkyKeyframe( + Begin: 0f, + SunHeadingDeg: 0f, + SunPitchDeg: 90f, + SunColor: System.Numerics.Vector3.One, + AmbientColor: System.Numerics.Vector3.One, + FogColor: System.Numerics.Vector3.Zero, + FogDensity: 0f), + }); + service.SetProvider(custom); + Assert.Equal(1, custom.KeyframeCount); + } +}