fix(lighting): A7 Fix D round 2 — outdoor objects get NO torches (retail useSunlight gate) (#140)
The Holtburg meeting-hall facade washed out warm/bright vs retail. The round-1 checkpoint blamed torch REACH (acdream Falloff 6×1.3=7.8m vs a supposed retail Falloff 4). That theory is WRONG, and this commit fixes the real cause. Empirical (HoltburgTorchFalloffProbeTests, headless dat dump via the production LightInfoLoader): the orange entrance torch (setup 0x020005D8) is raw dat Falloff 6 and acdream reads it FAITHFULLY — there is no Falloff-4 torch anywhere in Holtburg. Both clients read the same dat float, so reach was never inflated. Decomp (read verbatim + corroborated by an independent adversarial workflow): retail's per-object torch binder minimize_object_lighting (0x0054d480) is gated in RenderDeviceD3D::DrawMeshInternal (0x0059f398) by `if (Render::useSunlight == 0)`. The outdoor landscape stage runs useSunlightSet(1) (PView::DrawCells 0x005a485a, before LScape::draw), so the building EXTERIOR shell — drawn via DrawBlock→DrawSortCell→DrawBuilding→CPhysicsPart::Draw→DrawMeshInternal — is lit by SUN + ambient ONLY; torches are SKIPPED. The static bake (SetStaticLightingVertexColors 0x0059cfe0) is EnvCell-only. So retail NEVER torch-lights outdoor objects. This exactly explains the isolation test (object point lights OFF → building matches retail). Fix: WbDrawDispatcher.ComputeEntityLightSet gates per-object torch selection on the object being INDOOR (ParentCellId is an EnvCell, (id&0xFFFF)>=0x0100) via the pure predicate IndoorObjectReceivesTorches. Outdoor objects (building shells with null ParentCellId, outdoor scenery, outdoor creatures) keep the all-(-1) light set ⇒ sun + ambient only = retail. The indoor "no sun" half is already handled by the global sun-kill when the player is inside a cell (UpdateSunFromSky). No dungeon regression: EnvCell statics get ParentCellId set (keep torches). Divergence register: AP-37 (residual: acdream keys sun/torch on the object's own cell + a per-frame player-inside sun-kill, vs retail's per-draw-stage useSunlight; only matters for through-doorway look-ins). The round-1 CHECKPOINT got a RESOLVED banner correcting the reach theory. Tests: WbDrawDispatcherTorchGateTests (7), HoltburgTorchFalloffProbeTests (dat dump). App 280/1skip, Core 1486/2skip green. Held at the visual gate — not merged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
1e6fbff9bc
commit
b7d655bce7
5 changed files with 254 additions and 0 deletions
|
|
@ -2026,6 +2026,26 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
|
|||
/// a static building's torches stay constant as the viewer moves. Fills
|
||||
/// <see cref="_currentEntityLightSet"/>; unused slots are -1. On the no-lights
|
||||
/// path (no snapshot handed in) every slot is -1 ⇒ shader adds no point light.
|
||||
///
|
||||
/// <para>
|
||||
/// A7 Fix D round 2 (2026-06-19): retail lights OUTDOOR objects with the SUN +
|
||||
/// ambient ONLY — never the static wall torches. The per-object torch step
|
||||
/// (<c>minimize_object_lighting</c>, 0x0054d480) runs ONLY in the indoor stage:
|
||||
/// <c>RenderDeviceD3D::DrawMeshInternal</c> (0x0059f398) calls it under
|
||||
/// <c>if (Render::useSunlight == 0)</c>, and the outdoor landscape stage runs
|
||||
/// <c>Render::useSunlightSet(1)</c> (<c>PView::DrawCells</c> 0x005a485a, right
|
||||
/// before <c>LScape::draw</c> which draws buildings/scenery). So a building
|
||||
/// EXTERIOR shell (<see cref="WorldEntity.IsBuildingShell"/>,
|
||||
/// <see cref="WorldEntity.ParentCellId"/> = null) and all outdoor scenery /
|
||||
/// creatures get the sun, not torches. We mirror that: only objects parented to
|
||||
/// an EnvCell (indoor) select torches; outdoor objects keep the all-(-1) set so
|
||||
/// the sun path alone lights them. This is what made the Holtburg meeting-hall
|
||||
/// facade wash out warm — the dat's intensity-100 wall torches (range
|
||||
/// Falloff×1.3) were flooding the exterior shell that retail never torch-lights.
|
||||
/// The indoor "no sun" half is already handled by the global sun kill when the
|
||||
/// player is inside a cell (<c>UpdateSunFromSky</c>). See the divergence register
|
||||
/// (AP-37) and docs/research/2026-06-19-lighting-a7-fixD-round2-*.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
private void ComputeEntityLightSet(WorldEntity entity)
|
||||
{
|
||||
|
|
@ -2033,12 +2053,29 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
|
|||
var snap = _pointSnapshot;
|
||||
if (snap is null || snap.Count == 0) return;
|
||||
|
||||
// Retail useSunlight gate: outdoor objects receive no per-object torches.
|
||||
if (!IndoorObjectReceivesTorches(entity.ParentCellId)) return;
|
||||
|
||||
if (entity.AabbDirty) entity.RefreshAabb();
|
||||
Vector3 center = (entity.AabbMin + entity.AabbMax) * 0.5f;
|
||||
float radius = (entity.AabbMax - entity.AabbMin).Length() * 0.5f;
|
||||
LightManager.SelectForObject(snap, center, radius, _currentEntityLightSet);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retail's <c>useSunlight</c> gate for per-object torch lighting, as a pure
|
||||
/// predicate. An object receives the static wall torches (the indoor
|
||||
/// <c>minimize_object_lighting</c> pass) ONLY when it is parented to an EnvCell
|
||||
/// — an interior cell, by the AC convention <c>(cellId & 0xFFFF) >= 0x0100</c>.
|
||||
/// Outdoor objects (building shells with null <paramref name="parentCellId"/>,
|
||||
/// outdoor scenery in a land sub-cell <c>0x0001..0x00FF</c>, outdoor creatures)
|
||||
/// are sun-lit only and return false. Mirrors
|
||||
/// <c>RenderDeviceD3D::DrawMeshInternal</c> (0x0059f398): torches enabled iff
|
||||
/// <c>Render::useSunlight == 0</c>, which is true only in the indoor draw stage.
|
||||
/// </summary>
|
||||
internal static bool IndoorObjectReceivesTorches(uint? parentCellId)
|
||||
=> parentCellId.HasValue && (parentCellId.Value & 0xFFFFu) >= 0x0100u;
|
||||
|
||||
/// <summary>
|
||||
/// Fix B: append the current entity's 8-slot light set to a group's
|
||||
/// <see cref="InstanceGroup.LightSets"/>, parallel to its Matrices (one
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue