From 31ea849277eb7bc5b7f1e880e82cd91b635a3ba9 Mon Sep 17 00:00:00 2001 From: Erik Date: Thu, 11 Jun 2026 05:54:12 +0200 Subject: [PATCH] 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 --- .../Issue113DoorVanishDiagnosticTests.cs | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/tests/AcDream.Core.Tests/Conformance/Issue113DoorVanishDiagnosticTests.cs b/tests/AcDream.Core.Tests/Conformance/Issue113DoorVanishDiagnosticTests.cs index f698b0b0..78420fa2 100644 --- a/tests/AcDream.Core.Tests/Conformance/Issue113DoorVanishDiagnosticTests.cs +++ b/tests/AcDream.Core.Tests/Conformance/Issue113DoorVanishDiagnosticTests.cs @@ -181,6 +181,56 @@ public sealed class Issue113DoorVanishDiagnosticTests return n.LengthSquared() < 1e-10f ? Vector3.Zero : Vector3.Normalize(n); } + /// + /// 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. + /// + [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(mid); + if (gfx?.DrawingBSP?.Root is null) continue; + + var walked = new HashSet(); + 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(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)}]"); + } + } + } + /// /// Control group: the two #113 models (hall + cottage) whose orphans ARE /// the phantom geometry, for type-model comparison against the door.