test(phys): A6.P4 door inside-out — locate cottage wall, identify corner-slide hypothesis
Followed up the geometry-gap diagnosis with a wider polygon search. Result: the cottage's north exterior wall east of doorway DOES exist in cottage GfxObj 0x01000A2B (polys 0x0032, 0x0033) at world X=[133.5, 136.3], Y=17.10, Z=[94, 97], normal +Y. Symmetric polys cover the west side and above the doorway lintel. The wall SHOULD block sphere at X=133.655 (sphere west edge at 133.175 overlaps wall X range; sphere south edge at 17.11 aligns with wall at Y=17.10). New hypothesis: the bug is sphere-vs-corner collision at the meeting point of cell 0x0150's east wall (X=133.5, Y=[16.5, 17.1]) and the cottage's north exterior wall (X=[133.5, 136.3], Y=17.10). Cell transit data shows sphere going from X=132.859 entering alcove to X=134.022 leaving alcove — sphere reached X=134.022 INSIDE cottage geometry somehow. The sliding along the slab east face (cn=(+1,0,0) in captured tick 3254) gradually pushes sphere east. Eventually it shifts past X=133.5 — the corner where alcove east wall meets cottage north wall. The corner-handling in our BSP collision may incorrectly let the sphere slide past, or the alcove cell's east wall and cottage GfxObj's north wall don't compose correctly at the corner. Diagnostic apparatus extensions: - HoltburgLandblockStatics_DatInspection: dumps LandBlockInfo for landblock 0xA9B4. Shows 114 stabs + 12 buildings. The cottage IS Building[6] with modelId=0x01000A2B (the GfxObj we already loaded). - Diagnostic_CottagePolys_NearWalkthroughPosition: widened search reveals the cottage's full north exterior wall geometry. - HoltburgCottage_CellPortals_DatInspection: extended with cell PhysicsPolygon world-frame dump (already in prior commit). Full updated handoff: docs/research/2026-05-25-door-bug-inside-out-geometry-gap.md Next-session move: add a "sphere walks +Y from inside alcove at X near 133" test. If harness slides past the corner like production, investigate BSPQuery's sphere-vs-edge case. If harness blocks at corner, the bug is elsewhere (cell 0x0150 BSP not queried, or cottage GfxObj BSP traversal misses the wall poly). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
da798b2071
commit
fe29db5691
3 changed files with 190 additions and 2 deletions
|
|
@ -474,10 +474,39 @@ public class DoorBugTrajectoryReplayTests
|
|||
Console.WriteLine($" Sphere Y=[{sphereCenterY-0.48f:F3}, {sphereCenterY+0.48f:F3}]");
|
||||
Console.WriteLine($" Sphere Z=[94.000, 95.200]");
|
||||
|
||||
// Also dump ALL polys with vertices near sphere XY (loose: 3m window)
|
||||
// so we can see what wall geometry the cottage HAS in the area.
|
||||
Console.WriteLine("");
|
||||
Console.WriteLine("=== Cottage polys with bbox extending into (X in [130,138], Y in [13,21]) ===");
|
||||
int nearXYCount = 0;
|
||||
foreach (var (polyId, poly) in physics.Resolved)
|
||||
{
|
||||
float wxMin = float.MaxValue, wxMax = float.MinValue;
|
||||
float wyMin = float.MaxValue, wyMax = float.MinValue;
|
||||
float wzMin = float.MaxValue, wzMax = float.MinValue;
|
||||
foreach (var v in poly.Vertices)
|
||||
{
|
||||
var rotated = Vector3.Transform(v, cottageRot);
|
||||
var world = cottagePos + rotated;
|
||||
if (world.X < wxMin) wxMin = world.X; if (world.X > wxMax) wxMax = world.X;
|
||||
if (world.Y < wyMin) wyMin = world.Y; if (world.Y > wyMax) wyMax = world.Y;
|
||||
if (world.Z < wzMin) wzMin = world.Z; if (world.Z > wzMax) wzMax = world.Z;
|
||||
}
|
||||
// Wide search window.
|
||||
if (wxMax < 130 || wxMin > 138) continue;
|
||||
if (wyMax < 13 || wyMin > 21) continue;
|
||||
nearXYCount++;
|
||||
var nWorld = Vector3.Transform(poly.Plane.Normal, cottageRot);
|
||||
Console.WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture,
|
||||
" poly 0x{0:X4} n=({1:F2},{2:F2},{3:F2}) X=[{4:F2},{5:F2}] Y=[{6:F2},{7:F2}] Z=[{8:F2},{9:F2}]",
|
||||
polyId, nWorld.X, nWorld.Y, nWorld.Z, wxMin, wxMax, wyMin, wyMax, wzMin, wzMax));
|
||||
}
|
||||
Console.WriteLine($" Total: {nearXYCount}");
|
||||
|
||||
int matched = 0;
|
||||
int matchedXY = 0;
|
||||
Console.WriteLine("");
|
||||
Console.WriteLine("=== All cottage polys with XY overlap (any Z) ===");
|
||||
Console.WriteLine("=== Tight: All cottage polys with XY overlap of sphere AABB (any Z) ===");
|
||||
foreach (var (polyId, poly) in physics.Resolved)
|
||||
{
|
||||
// Transform vertices to world space.
|
||||
|
|
|
|||
|
|
@ -262,6 +262,80 @@ public class DoorSetupGfxObjInspectionTests
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A6.P4 door inside-out (2026-05-25 late) — inspects LandBlockInfo
|
||||
/// 0xA9B4FFFE to identify all static entities at the Holtburg
|
||||
/// cottage doorway area. The captured walkthrough has sphere at
|
||||
/// world (133.655, 17.59) — no cottage GfxObj polys exist there.
|
||||
/// Maybe a different entity (stab object, second cottage GfxObj,
|
||||
/// building wall sub-piece) lives at that XY.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void HoltburgLandblockStatics_DatInspection()
|
||||
{
|
||||
var datDir = Env.GetEnvironmentVariable("ACDREAM_DAT_DIR")
|
||||
?? Path.Combine(Env.GetFolderPath(Env.SpecialFolder.UserProfile),
|
||||
"Documents", "Asheron's Call");
|
||||
if (!Directory.Exists(datDir))
|
||||
{
|
||||
_out.WriteLine($"SKIP: dat directory not found at {datDir}");
|
||||
return;
|
||||
}
|
||||
|
||||
using var dats = new DatCollection(datDir, DatAccessType.Read);
|
||||
|
||||
// Landblock 0xA9B4 — the captured Holtburg cottage area.
|
||||
// LandBlockInfo id = (landblockId & 0xFFFF0000) | 0xFFFE
|
||||
var lbInfo = dats.Get<DatReaderWriter.DBObjs.LandBlockInfo>(0xA9B4FFFEu);
|
||||
if (lbInfo is null)
|
||||
{
|
||||
_out.WriteLine("LandBlockInfo 0xA9B4FFFE: NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
_out.WriteLine($"=== LandBlockInfo 0xA9B4FFFE ===");
|
||||
_out.WriteLine($" NumCells = {lbInfo.NumCells}");
|
||||
_out.WriteLine($" Objects = {lbInfo.Objects.Count} (landblock stabs)");
|
||||
_out.WriteLine($" Buildings = {lbInfo.Buildings.Count}");
|
||||
|
||||
// The captured walkthrough sphere position.
|
||||
const float SphereX = 133.655f;
|
||||
const float SphereY = 17.59f;
|
||||
const float Window = 4f; // search window
|
||||
|
||||
_out.WriteLine("");
|
||||
_out.WriteLine($"=== Stabs (Objects) within {Window} m of sphere XY ({SphereX:F2}, {SphereY:F2}) ===");
|
||||
int stabHits = 0;
|
||||
for (int i = 0; i < lbInfo.Objects.Count; i++)
|
||||
{
|
||||
var stab = lbInfo.Objects[i];
|
||||
float dx = stab.Frame.Origin.X - SphereX;
|
||||
float dy = stab.Frame.Origin.Y - SphereY;
|
||||
float dist = MathF.Sqrt(dx * dx + dy * dy);
|
||||
if (dist > Window) continue;
|
||||
stabHits++;
|
||||
_out.WriteLine($" [{i}] id=0x{stab.Id:X8} pos=({stab.Frame.Origin.X:F3},{stab.Frame.Origin.Y:F3},{stab.Frame.Origin.Z:F3}) dist={dist:F3}");
|
||||
}
|
||||
_out.WriteLine($" Total stab hits: {stabHits}");
|
||||
|
||||
_out.WriteLine("");
|
||||
_out.WriteLine($"=== ALL Buildings (sorted by distance to sphere) ===");
|
||||
var buildings = lbInfo.Buildings
|
||||
.Select((b, i) => new {
|
||||
Idx = i, B = b,
|
||||
Dist = MathF.Sqrt(
|
||||
(b.Frame.Origin.X - SphereX) * (b.Frame.Origin.X - SphereX) +
|
||||
(b.Frame.Origin.Y - SphereY) * (b.Frame.Origin.Y - SphereY))
|
||||
})
|
||||
.OrderBy(x => x.Dist)
|
||||
.Take(6)
|
||||
.ToList();
|
||||
foreach (var x in buildings)
|
||||
{
|
||||
_out.WriteLine($" [{x.Idx}] modelId=0x{x.B.ModelId:X8} pos=({x.B.Frame.Origin.X:F3},{x.B.Frame.Origin.Y:F3},{x.B.Frame.Origin.Z:F3}) dist={x.Dist:F3} portals={x.B.Portals.Count} numLeaves={x.B.NumLeaves}");
|
||||
}
|
||||
}
|
||||
|
||||
private void InspectCell(DatCollection dats, uint cellId)
|
||||
{
|
||||
string typeLabel = (cellId & 0xFFFFu) >= 0x0100u ? "indoor" : "outdoor";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue