test(conformance): skipNoTexture confirmation - ALL Holtburg portal-fill quads are Base1Solid (untextured)

Phase A confirmation fact (DumpPortalFillSurfaceTypes): every portal-fill
polygon on the audited building models (hall 0x010014C3, cottages
0x01000827/0x0100082E/0x01000C17) carries an untextured surface
(Base1Solid, mostly +Translucent) with Stippling=NoPos and no negative
surface. Retail's skipNoTexture rule (D3DPolyRender inner draw 0x0059d4a0,
default on @0x00820e30) therefore skips ALL of them on the building/cell
pass - door fills, window fills, AND the phantom stair-ramp. Retail never
draws any baked fill; visible doors are door ENTITIES. acdream draws the
solid batches as colored geometry, which is both the phantom staircase AND
why dropping them read as 'doors disappeared'.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-11 05:54:12 +02:00
parent e223325410
commit 31ea849277

View file

@ -181,6 +181,56 @@ public sealed class Issue113DoorVanishDiagnosticTests
return n.LengthSquared() < 1e-10f ? Vector3.Zero : Vector3.Normalize(n);
}
/// <summary>
/// Phase A confirmation: retail's building/cell mesh pass skips surface
/// batches whose CSurface type has neither Base1Image (0x2) nor
/// Base1ClipMap (0x4) — the skipNoTexture rule (D3DPolyRender inner draw
/// 0x0059d4a0; default skipNoTexture=1). Prediction: the hall's phantom
/// stair-ramp portal-fill polys {0,1} reference SOLID (untextured)
/// surfaces while cottage door/window fills reference TEXTURED surfaces —
/// which is exactly why retail shows doors but not the ramp.
/// </summary>
[Fact]
public void DumpPortalFillSurfaceTypes()
{
var datDir = ConformanceDats.ResolveDatDir();
if (datDir is null) { _out.WriteLine("dats unavailable — skipped"); return; }
using var dats = new DatCollection(datDir, DatAccessType.Read);
foreach (uint mid in new uint[] { 0x010014C3u, 0x01000827u, 0x0100082Eu, 0x01000C17u })
{
var gfx = dats.Get<DatReaderWriter.DBObjs.GfxObj>(mid);
if (gfx?.DrawingBSP?.Root is null) continue;
var walked = new HashSet<ushort>();
void Walk(DatReaderWriter.Types.DrawingBSPNode? n)
{
if (n is null) return;
if (n.Polygons is not null) foreach (var pid in n.Polygons) walked.Add((ushort)pid);
Walk(n.PosNode); Walk(n.NegNode);
}
Walk(gfx.DrawingBSP.Root);
var portalFills = gfx.Polygons.Keys.Where(k => !walked.Contains(k)).OrderBy(k => k).ToList();
_out.WriteLine($"=== model 0x{mid:X8}: {portalFills.Count} portal-fill polys; Surfaces list has {gfx.Surfaces.Count} entries ===");
foreach (var pid in portalFills)
{
var poly = gfx.Polygons[pid];
string Describe(short surfIdx)
{
if (surfIdx < 0 || surfIdx >= gfx.Surfaces.Count) return $"idx{surfIdx}=OOB";
uint sid = gfx.Surfaces[surfIdx];
var surf = dats.Get<DatReaderWriter.DBObjs.Surface>(sid);
if (surf is null) return $"0x{sid:X8}=MISSING";
bool textured = surf.Type.HasFlag(DatReaderWriter.Enums.SurfaceType.Base1Image)
|| surf.Type.HasFlag(DatReaderWriter.Enums.SurfaceType.Base1ClipMap);
return $"0x{sid:X8} type={surf.Type} -> {(textured ? "TEXTURED (drawn)" : "SOLID (skipNoTexture SKIPS on building/cell pass)")}";
}
_out.WriteLine($" poly {pid,3}: sides={poly.SidesType} stip={poly.Stippling} pos[{Describe(poly.PosSurface)}] neg[{Describe(poly.NegSurface)}]");
}
}
}
/// <summary>
/// Control group: the two #113 models (hall + cottage) whose orphans ARE
/// the phantom geometry, for type-model comparison against the door.