fix #176/#181 (A7 fix #2): stationary server-weenie fixtures take retail's STATIC light curve - isDynamic decided by motion, not origin

The former #143 flag forced every server-object light onto the D3D dynamic path (1/d attenuation, range x1.5); retail's stationary fixtures use the static calc_point_light curve (0x0059c8b0, Ghidra-verified: f=(1-d/range)*intensity*wrap/d^3 beyond 1m, range=falloff*1.3, per-channel colour clamp) - the dynamic path burned every Facility Hub lamp ~10x hotter than retail at 3m, producing the saturated magenta wash that zebra-striped normal wall geometry into the #176 'stripes/triangles' pattern (VSync-on test + clean captures characterized it as STATIC over-bright content, not flicker/tearing/camera). The shader's static branch already implements the verified curve faithfully (mesh_modern.vert pointContribution - its wrap constants pin LIGHT_POINT_RANGE=0.75), so the whole fix is the registration decision. Site-A lights are all stationary today (AP-44); genuinely moving lights re-earn isDynamic when they exist. Suites green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-07-06 22:45:50 +02:00
parent cace430299
commit 233b469b01

View file

@ -3714,7 +3714,18 @@ public sealed class GameWindow : IDisposable
ownerId: entity.Id,
entityPosition: entity.Position,
entityRotation: entity.Rotation,
isDynamic: true, // #143: server-object lights take the D3D dynamic path (1/d att, range×1.5)
// A7 fix #2 (#176/#181): isDynamic is decided by whether the light
// MOVES, not by weenie-vs-dat origin. Server-spawned FIXTURES
// (lanterns, braziers) are stationary — they take retail's STATIC
// curve (calc_point_light 0x0059c8b0: 1/d³ beyond 1 m, range×1.3,
// per-channel colour clamp), not the D3D dynamic path (1/d,
// range×1.5) the former #143 flag forced — which burned every
// fixture ~10× hotter than retail at 3 m: the magenta wash that
// zebra-striped the Facility Hub walls. Site-A lights are all
// stationary today (AP-44: they don't follow bearers); genuinely
// moving lights (projectiles, portal swirls) re-earn isDynamic
// when they exist.
isDynamic: false,
cellId: entity.ParentCellId ?? 0u); // A7 #176/#177: scope to the owning cell's visibility
foreach (var ls in loaded)
_lightingSink.RegisterOwnedLight(ls);