test #137: the seam shake reproduced offline — third mechanism characterized
The corridor gate FAILED with a changed symptom: shaking at cell seams (+ purple floor flashing there) instead of the dead stop. Deep-probe session (step-walk/push-back/indoor-bsp + full resolve capture) traced the complete chain; docs/ISSUES.md #137 carries it. Short form: - Corridor floors are double-faced portal slabs over under-rooms; the resting foot sphere lives within half a millimeter of three hit/straddle thresholds there. - Crossing a boundary, the foot penetrates the neighbor ramp slab by ~0.4mm, steps up onto it successfully (+0.6mm lift, stepped=True) — and the lifted check position is then LOST: the following pass runs at the unlifted height (the P2 stale-snapshot class; retail step_up 0x0050b6cc restores only on FAILURE). - The unlifted re-test grazes the under-room's ceiling (the slab underside) within the near-miss window, dispatches a neg-poly step-up with a DOWNWARD normal, whose nested step-down finds no walkable at exact tangency -> StepUpSlide -> slide_sphere opposing branch -> reversed-movement collision -> Collided -> revert. Every frame = shake. Apparatus committed: - Issue137CorridorSeamReplayTests: 3 deterministic offline repros (snapshot-exact west-boundary from the capture, east deep-straddle, the clean-run pin), currently Skip='#137 seam shake' pending the fix. Key: THREE portal-ring hydration (the under-room 0x8A020166 is ring-3; with fewer rings the flood can't add it and the bug vanishes) + live Setup step heights (0.6/1.5) + probe-buffer capture for line-diffing offline vs live traces. - Issue137CorridorSeamInspectionTests: portal-poly world spans (exposed the visual-vs-physics polygon-id conflation and the floor-portal topology), physics-BSP leaf membership walk, hit-normal candidate sweep (|align| both windings), downward-poly sweep. NEXT: read TransitionalInsert's attempt loop against retail 0x0050b6f0, find the restore that clobbers the successful step-up position, fix, un-skip. The purple seam flashing is expected to be the render exposing the same per-frame oscillation - re-check after the physics fix. Suites: Core 2553 / App 713 / UI 425 / Net 385, 0 failures (5 skips = 2 pre-existing + the 3 parked repros). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
e8651b3819
commit
dbddad7a5e
4 changed files with 25131 additions and 4 deletions
|
|
@ -28,6 +28,7 @@ public class Issue137CorridorSeamInspectionTests
|
|||
[Theory]
|
||||
[InlineData(0x8A02016Eu)]
|
||||
[InlineData(0x8A02017Au)]
|
||||
[InlineData(0x8A02011Eu)] // the under-floor room the corridor's floor-portals lead to
|
||||
public void CorridorCell_PhysicsPolysAndPortals_DatInspection(uint envCellId)
|
||||
{
|
||||
var datDir = Env.GetEnvironmentVariable("ACDREAM_DAT_DIR")
|
||||
|
|
@ -254,7 +255,10 @@ public class Issue137CorridorSeamInspectionTests
|
|||
System.Numerics.Vector3.Cross(w1 - w0, w2 - w0));
|
||||
|
||||
float align = System.Numerics.Vector3.Dot(n, hitNormal);
|
||||
if (align < 0.95f) continue; // within ~18° of the recorded normal
|
||||
// |align|: the vertex-fan winding convention can flip the
|
||||
// computed normal vs the physics plane's true facing — accept
|
||||
// both signs (2026-07-06 sweep flaw fix).
|
||||
if (MathF.Abs(align) < 0.95f) continue; // within ~18° of the recorded normal
|
||||
|
||||
// Plane distance from the hit point.
|
||||
float d = -System.Numerics.Vector3.Dot(n, w0);
|
||||
|
|
@ -358,4 +362,67 @@ public class Issue137CorridorSeamInspectionTests
|
|||
}
|
||||
_out.WriteLine("(downward sweep complete)");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 2026-07-06 gate-session follow-up: seam crossings SUCCEED at
|
||||
/// y≈−40.8..−41.2 and BLOCK at y≈−39.5..−39.8 (cell-transit log,
|
||||
/// launch-137-corridor-gate.log). A y-dependent boundary with no physics
|
||||
/// polygon culprit points at the PORTAL POLYGONS — if the doorway
|
||||
/// openings don't span the full corridor width, the transit/membership
|
||||
/// machinery only hands the sphere to the neighbor inside the portal
|
||||
/// poly's span. Dump every portal polygon's world-space vertex extent.
|
||||
/// </summary>
|
||||
[Theory]
|
||||
[InlineData(0x8A02016Eu)]
|
||||
[InlineData(0x8A02017Au)]
|
||||
public void CorridorCell_PortalPolygonWorldSpans(uint envCellId)
|
||||
{
|
||||
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);
|
||||
|
||||
var envCell = dats.Get<EnvCell>(envCellId);
|
||||
Assert.NotNull(envCell);
|
||||
var environment = dats.Get<DatReaderWriter.DBObjs.Environment>(0x0D000000u | envCell!.EnvironmentId);
|
||||
Assert.NotNull(environment);
|
||||
Assert.True(environment!.Cells.TryGetValue(envCell.CellStructure, out var cs));
|
||||
|
||||
var rot = new System.Numerics.Quaternion(
|
||||
envCell.Position.Orientation.X, envCell.Position.Orientation.Y,
|
||||
envCell.Position.Orientation.Z, envCell.Position.Orientation.W);
|
||||
var world = System.Numerics.Matrix4x4.CreateFromQuaternion(rot)
|
||||
* System.Numerics.Matrix4x4.CreateTranslation(
|
||||
envCell.Position.Origin.X, envCell.Position.Origin.Y, envCell.Position.Origin.Z);
|
||||
|
||||
_out.WriteLine($"=== 0x{envCellId:X8} portal polygons (world spans) ===");
|
||||
foreach (var p in envCell.CellPortals)
|
||||
{
|
||||
if (!cs!.Polygons.TryGetValue((ushort)p.PolygonId, out var poly))
|
||||
{
|
||||
_out.WriteLine($" portal poly {p.PolygonId} -> 0x{p.OtherCellId:X4} {p.Flags}: NOT in visual set");
|
||||
continue;
|
||||
}
|
||||
|
||||
var min = new System.Numerics.Vector3(float.MaxValue);
|
||||
var max = new System.Numerics.Vector3(float.MinValue);
|
||||
foreach (var vid in poly.VertexIds)
|
||||
{
|
||||
if (!cs.VertexArray.Vertices.TryGetValue((ushort)vid, out var v)) continue;
|
||||
var w = System.Numerics.Vector3.Transform(v.Origin, world);
|
||||
min = System.Numerics.Vector3.Min(min, w);
|
||||
max = System.Numerics.Vector3.Max(max, w);
|
||||
}
|
||||
_out.WriteLine(
|
||||
$" portal poly {p.PolygonId} -> 0x{p.OtherCellId:X4} [{p.Flags}] " +
|
||||
$"x=[{min.X:F2},{max.X:F2}] y=[{min.Y:F2},{max.Y:F2}] z=[{min.Z:F2},{max.Z:F2}] " +
|
||||
$"verts={poly.VertexIds.Count}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue