fix(G.3 A7): dungeon lighting — select 8 NEAREST lights, not viewer-in-range (#133)

The active-light selection dropped any point light whose range didn't reach the
VIEWER (DistSq > Range^2*slack -> skip). Retail's D3D-style fixed pipeline picks
the 8 NEAREST lights and applies the hard range cutoff PER SURFACE in the shader
(mesh_modern.frag: if (d < range)). The viewer-range candidacy filter suppressed
a torch whenever the player stood outside its range, so a dungeon room with 2227
registered torches lit only the ~1 the player was standing in (activeLights ~= 1,
rest of the room at flat 0.2 ambient = the "lighting off" report). Drop the filter;
take the nearest 8 regardless of viewer range. Removed the now-unused RangeSlack
const; updated the two tests that codified the old filter. Core lighting suite green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-13 20:35:01 +02:00
parent d6fb788c96
commit a80061b0c2
2 changed files with 23 additions and 8 deletions

View file

@ -37,7 +37,6 @@ namespace AcDream.Core.Lighting;
public sealed class LightManager
{
public const int MaxActiveLights = 8; // D3D parity
private const float RangeSlack = 1.1f; // 10% hysteresis around hard cutoff
private readonly List<LightSource> _all = new();
private readonly LightSource?[] _active = new LightSource?[MaxActiveLights];
@ -109,8 +108,16 @@ public sealed class LightManager
Vector3 delta = light.WorldPosition - viewerWorldPos;
light.DistSq = delta.LengthSquared();
float rangeSq = light.Range * light.Range * RangeSlack * RangeSlack;
if (light.DistSq > rangeSq) continue;
// Retail D3D-style fixed-pipeline lighting picks the 8 NEAREST point
// lights and applies each light's hard range-cutoff PER SURFACE in the
// shader (mesh_modern.frag: `if (d < range && range > 1e-3)`). The
// previous viewer-range candidacy filter (skip when DistSq > Range²·slack²)
// was wrong — it dropped a torch whenever the VIEWER stood outside that
// torch's range, so a dungeon room with 2227 registered torches lit only
// the ~1 the player was standing inside (activeLights≈1, the rest of the
// room at flat 0.2 ambient — the "dungeon lighting off" report). Take the
// nearest 8 regardless of viewer range; the shader's per-fragment
// `d < range` does the actual hard cutoff.
candidates.Add(light);
}