diag(phys): A6.P3 slice 4 — add [poly-dump] probe for #98 investigation

Adds a polygon-geometry dump probe that fires alongside [push-back]
whenever AdjustSphereToPlane lands a push-back. Gated by
ACDREAM_PROBE_POLY_DUMP=1.

Output format:
  [poly-dump] cell=0xA9B40147 polyId=0x0042 numPts=4 sides=Single
              n=(0.000,-0.719,0.695) d=-0.1007
              verts=[(x1,y1,z1),(x2,y2,z2),(x3,y3,z3),(x4,y4,z4)]

Purpose: investigate #98 (cellar-up stuck at top step). The push-back
trace shows the player hitting a sloped surface n=(0,-0.719,0.695) at
the cellar stair top. Two possibilities:
  1. The polygon really IS sloped 44° in the dat (genuine geometry).
  2. Our dat-read produces wrong vertices → wrong normal → wrong plane.

The dump lets us:
- Identify which dat polygon was hit (cell + poly ID)
- Compare our extracted vertices against WorldBuilder's straight-from-
  dat read for the same poly
- Or spawn the cell in ACViewer to visually verify the geometry

Changes:
- Added `ushort Id` property to ResolvedPolygon (defaults to 0 for test
  fixtures that don't care; production code in PhysicsDataCache.cs +
  BSPQuery.cs sets it from the dictionary key).
- Added ProbePolyDumpEnabled + LogPolyDump in PhysicsDiagnostics.
- Wired the dump into AdjustSphereToPlane's apply-branch (after the
  existing push-back log; same gating pattern).

Test suite: 1148 pass + 8 pre-existing fail (baseline maintained).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-22 12:17:47 +02:00
parent ceeb06be7d
commit 0b449968a7
3 changed files with 64 additions and 0 deletions

View file

@ -394,6 +394,14 @@ public static class BSPQuery
applied: true);
}
if (PhysicsDiagnostics.ProbePolyDumpEnabled)
{
// A6.P3 slice 4 (2026-05-22): dump the polygon geometry to
// cross-reference against WorldBuilder's straight-from-dat
// read. CellId taken from path.CheckCellId.
PhysicsDiagnostics.LogPolyDump(path.CheckCellId, poly);
}
return true;
}
@ -2212,6 +2220,7 @@ public static class BSPQuery
Plane = new Plane(normal, planeD),
NumPoints = n,
SidesType = poly.SidesType,
Id = id,
};
}
return resolved;

View file

@ -289,6 +289,7 @@ public sealed class PhysicsDataCache
Plane = new Plane(normal, d),
NumPoints = numVerts,
SidesType = poly.SidesType,
Id = id,
};
}
return resolved;
@ -383,6 +384,15 @@ public sealed class ResolvedPolygon
public required Plane Plane { get; init; }
public required int NumPoints { get; init; }
public required CullMode SidesType { get; init; }
/// <summary>
/// Polygon index within its parent (cell or GfxObj). Used by the
/// `ACDREAM_PROBE_POLY_DUMP` probe (A6.P3 slice 4 investigation,
/// 2026-05-22) to identify which dat polygon a push-back hit so we
/// can compare our extracted vertices/plane against WorldBuilder's
/// straight-from-dat read. Defaults to 0 for test fixtures that
/// don't care about polygon identity.
/// </summary>
public ushort Id { get; init; }
}
/// <summary>Cached physics data for a single GfxObj part.</summary>

View file

@ -304,6 +304,51 @@ public static class PhysicsDiagnostics
public static bool ProbePushBackEnabled { get; set; } =
Environment.GetEnvironmentVariable("ACDREAM_PROBE_PUSH_BACK") == "1";
/// <summary>
/// A6.P3 slice 4 investigation (2026-05-22) — dumps the polygon's
/// vertices + plane + sidesType + cell id whenever a push-back fires.
/// Lets us compare our extracted polygon (from our cache) against
/// WorldBuilder's straight-from-dat read for the same poly index, to
/// falsify the "is our dat-read producing wrong polygon geometry?"
/// hypothesis for issue #98 (cellar-up stuck at top step).
///
/// <para>
/// Initial state from <c>ACDREAM_PROBE_POLY_DUMP=1</c>.
/// Heavy output (one dump per AdjustSphereToPlane call); use briefly
/// to capture a specific scenario, then turn off.
/// </para>
/// </summary>
public static bool ProbePolyDumpEnabled { get; set; } =
Environment.GetEnvironmentVariable("ACDREAM_PROBE_POLY_DUMP") == "1";
/// <summary>
/// Emit one <c>[poly-dump]</c> line with the full polygon geometry:
/// cell id, polygon index, num vertices, sides flag, plane normal+D,
/// and all vertex coordinates. Used in conjunction with the push-back
/// probe to identify which dat polygon a push-back hit so we can
/// cross-reference against WorldBuilder's straight-from-dat read.
///
/// <para>Caller MUST guard with <c>if (!ProbePolyDumpEnabled) return;</c>.</para>
/// </summary>
public static void LogPolyDump(uint cellId, ResolvedPolygon poly)
{
var ci = System.Globalization.CultureInfo.InvariantCulture;
var sb = new System.Text.StringBuilder(256);
sb.AppendFormat(ci,
"[poly-dump] cell=0x{0:X8} polyId=0x{1:X4} numPts={2} sides={3} " +
"n=({4:F6},{5:F6},{6:F6}) d={7:F6} verts=[",
cellId, poly.Id, poly.NumPoints, poly.SidesType,
poly.Plane.Normal.X, poly.Plane.Normal.Y, poly.Plane.Normal.Z, poly.Plane.D);
for (int i = 0; i < poly.Vertices.Length; i++)
{
if (i > 0) sb.Append(',');
sb.AppendFormat(ci, "({0:F6},{1:F6},{2:F6})",
poly.Vertices[i].X, poly.Vertices[i].Y, poly.Vertices[i].Z);
}
sb.Append(']');
Console.WriteLine(sb.ToString());
}
/// <summary>
/// A6.P1 emission helper for the <c>AdjustSphereToPlane</c> site.
/// One line per call: input sphere center, plane geometry, push-back