fix(#79/#93): mesh-empty hydration gate was dropping light-only fixtures
Root-caused via dat-truth inspection, not inference: the A7.L1 visible- cell scoping fix (previous commit) had zero visual effect because the Town Network fountain room (cell 0x00070144) registers ZERO lights of its own — confirmed directly against the dat, not assumed. The room's one dat-authored fixture, Setup 0x02000365 (a ceiling light 5m above the fountain, warm color, intensity 100), has a single visual part that is a #136-class runtime-hidden marker. Flattened mesh ref count: 0. GameWindow 's per-stab hydration loop treated meshRefs.Count==0 as "doesn't exist" and dropped the whole entity before the Setup's Lights were ever read — so a mesh-less "light attach point" fixture, a normal AC dat authoring pattern, could never register. Retail's light registration (add_light, CEnvCell::UnPack) is architecturally independent of a fixture's own mesh visibility. Fix: track the stab's Setup.Lights.Count alongside meshRefs during hydration; keep the entity (with empty MeshRefs — nothing to draw, still something to light) whenever either is nonzero. Extracted the decision into EntityHydrationRules.ShouldKeepEntity (pure, unit-tested) since GameWindow's hydration loop isn't independently testable. Confirmed no downstream consumer assumes MeshRefs.Count >= 1 (WbDrawDispatcher already guards on it before any indexing). Core 2666+2skip / App 741+2skip / UI 425 / Net 385 green. Apparatus: Issue93TownNetworkFountainRoomLightInspectionTests (dat-truth dump, reusable for other rooms in this class). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
d275ed554e
commit
9ebb206086
4 changed files with 221 additions and 2 deletions
|
|
@ -7263,6 +7263,14 @@ public sealed class GameWindow : IDisposable
|
|||
|
||||
var meshRefs = new List<AcDream.Core.World.MeshRef>();
|
||||
var interiorBounds = new AcDream.Core.Meshing.LocalBoundsAccumulator();
|
||||
// #79/#93 (2026-07-09): a Setup-sourced stab whose sole visual part is a
|
||||
// runtime-hidden marker (#136) flattens to zero mesh refs even though its
|
||||
// Setup carries real Lights — a "light attach point" fixture (e.g. the Town
|
||||
// Network fountain room's ceiling light, Setup 0x02000365). Track the dat
|
||||
// Setup's Lights.Count here so the meshRefs==0 gate below doesn't also drop
|
||||
// the entity that would otherwise carry those lights to the registration
|
||||
// pass (GameWindow.cs ~7900).
|
||||
int stabLightCount = 0;
|
||||
if ((stab.Id & 0xFF000000u) == 0x01000000u)
|
||||
{
|
||||
var gfx = _dats.Get<DatReaderWriter.DBObjs.GfxObj>(stab.Id);
|
||||
|
|
@ -7285,6 +7293,7 @@ public sealed class GameWindow : IDisposable
|
|||
if (setup is not null)
|
||||
{
|
||||
_physicsDataCache.CacheSetup(stab.Id, setup);
|
||||
stabLightCount = setup.Lights.Count;
|
||||
var flat = AcDream.Core.Meshing.SetupMesh.Flatten(setup);
|
||||
if (dumpStab)
|
||||
{
|
||||
|
|
@ -7321,12 +7330,14 @@ public sealed class GameWindow : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
if (meshRefs.Count == 0)
|
||||
if (!AcDream.Core.Meshing.EntityHydrationRules.ShouldKeepEntity(meshRefs.Count, stabLightCount))
|
||||
{
|
||||
if (dumpStab)
|
||||
Console.WriteLine($"[dump-entity] HYDRATE src=0x{stab.Id:X8} cell=0x{envCellId:X8} meshRefs=0 -> entity dropped");
|
||||
Console.WriteLine($"[dump-entity] HYDRATE src=0x{stab.Id:X8} cell=0x{envCellId:X8} meshRefs=0 lights=0 -> entity dropped");
|
||||
continue;
|
||||
}
|
||||
if (meshRefs.Count == 0 && dumpStab)
|
||||
Console.WriteLine($"[dump-entity] HYDRATE src=0x{stab.Id:X8} cell=0x{envCellId:X8} meshRefs=0 lights={stabLightCount} -> KEPT as mesh-less light carrier");
|
||||
|
||||
// Stabs inside EnvCells are already in landblock-local coordinates
|
||||
// (same space as LandBlockInfo.Objects stabs). Adding cellOrigin would
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue