fix(lighting): #143 — dynamic lights use retail's D3D 1/d attenuation (portal + viewer spread)
The portal swirl's magenta light (and the viewer fill) read as a tight, concentrated pool vs retail's soft, room-wide tint. Cause: acdream applied its STATIC dat-bake falloff (1/d^3 distance-cube + range x1.3) to ALL point lights, including dynamic ones. Retail draws dynamic lights through the D3D hardware path (config_hardware_light 0x0059ad30): a point light gets Attenuation1=1 => att = 1/d (inverse-linear), plain Lambert, range x1.5 (rangeAdjust 0x00820cc4). Split the two paths by a per-light IsDynamic flag: - LightSource.IsDynamic; packed into GlobalLight.coneAngleEtc.y (binding=4). - LightInfoLoader.Load(isDynamic) => range x1.5 + flag (server-object/portal lights via the live spawn path); dat-static lights keep x1.3 (default). - Viewer fill + weenie/portal lights = dynamic; dat torches = static. - mesh_modern.vert pointContribution: dynamic branch = 1/d att, plain Lambert, hard cutoff, no per-light cap (D3D accumulates then saturates via the existing min(pointAcc,1)); static branch = the unchanged wrap/norm bake. This is the portal half of #143 (the magenta light itself now registers + reaches the walls via the prior weenie-light + landblock-key fix). Refines AP-35: point lights now split static-bake (1/d^3) vs dynamic-hardware (1/d) by path. Verified: portal light now range=9 (6x1.5), magenta spreads softly; shader compiles clean; static torches unchanged (range 5.2/6.5/7.8). User-confirmed the portal matches retail and the torch-lit interior did not over-brighten. Core lighting 44/44, App 476 green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0d8b827721
commit
57c2ab735d
6 changed files with 41 additions and 17 deletions
|
|
@ -47,8 +47,9 @@ public static class GlobalLightPacker
|
|||
buffer[o + 9] = L.ColorLinear.Y;
|
||||
buffer[o + 10] = L.ColorLinear.Z;
|
||||
buffer[o + 11] = L.Intensity;
|
||||
// coneAngleEtc (x cone radians; yzw reserved)
|
||||
// coneAngleEtc (x cone radians; y = #143 dynamic flag; zw reserved)
|
||||
buffer[o + 12] = L.ConeAngle;
|
||||
buffer[o + 13] = L.IsDynamic ? 1f : 0f; // shader: 1/d D3D attenuation vs static 1/d³
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@ public static class LightInfoLoader
|
|||
Setup setup,
|
||||
uint ownerId,
|
||||
Vector3 entityPosition,
|
||||
Quaternion entityRotation)
|
||||
Quaternion entityRotation,
|
||||
bool isDynamic = false)
|
||||
{
|
||||
var results = new List<LightSource>();
|
||||
if (setup?.Lights is null || setup.Lights.Count == 0) return results;
|
||||
|
|
@ -79,18 +80,17 @@ public static class LightInfoLoader
|
|||
(info.Color?.Green ?? 255) / 255f,
|
||||
(info.Color?.Blue ?? 255) / 255f),
|
||||
Intensity = info.Intensity,
|
||||
// falloff_eff for the per-vertex point-light burn-in (calc_point_light
|
||||
// 0x0059c8b0) is Falloff * static_light_factor, where static_light_factor
|
||||
// is the fixed global 1.3 (0x00820e24). That is the path that lights
|
||||
// STATIC walls — what the dungeon/house "spotlight" report (#133 A7) is
|
||||
// about — so we match it, not the D3D-dynamic config_hardware_light
|
||||
// rangeAdjust (1.5, a different path for moving objects). The shader ramp
|
||||
// (1 - dist/Range) fades to exactly 0 at this Range, eliminating the hard
|
||||
// disc edge that read as a spotlight.
|
||||
Range = info.Falloff * 1.3f,
|
||||
// Range factor by light path (#143). STATIC dat-baked lights use
|
||||
// static_light_factor 1.3 (calc_point_light 0x0059c8b0 bake — the path
|
||||
// that lights dungeon/house WALLS, #133 A7) and the shader's 1/d³ ramp.
|
||||
// DYNAMIC lights (server-object/portal lights registered from the live
|
||||
// spawn path with isDynamic:true) use config_hardware_light's rangeAdjust
|
||||
// 1.5 (0x00820cc4) and the shader's D3D 1/d attenuation — softer, broader.
|
||||
Range = info.Falloff * (isDynamic ? 1.5f : 1.3f),
|
||||
ConeAngle = info.ConeAngle,
|
||||
OwnerId = ownerId,
|
||||
IsLit = true,
|
||||
IsDynamic = isDynamic,
|
||||
};
|
||||
results.Add(light);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -260,6 +260,7 @@ public sealed class LightManager
|
|||
Range = ViewerLightFalloff * 1.5f, // dynamic rangeAdjust 1.5
|
||||
OwnerId = ViewerLightOwnerId,
|
||||
IsLit = true,
|
||||
IsDynamic = true, // #143: D3D 1/d attenuation (soft fill, not 1/d³)
|
||||
};
|
||||
_all.Add(_viewerLight);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ public sealed class LightSource
|
|||
public float ConeAngle = 0f; // radians, Spot only
|
||||
public uint OwnerId; // attached entity id; 0 = world-global
|
||||
public bool IsLit = true; // SetLightHook latch
|
||||
public bool IsDynamic; // #143: true = D3D hardware path (1/d att, range×1.5);
|
||||
// false = static dat-baked bake (1/d³, range×1.3)
|
||||
|
||||
// Cached each frame by LightManager.
|
||||
public float DistSq;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue