Commit graph

2 commits

Author SHA1 Message Date
Erik
3d2d10b331 test(phys): A6.P3 #98 — harness extension: synthetic stair GfxObj + ShadowEntry
Extends the trajectory replay harness with a programmatic mini-stair
piece, reconstructed from the live capture's polydump data
(docs/research/2026-05-21-a6-captures/scen4_cottage_cellar_polydump).

NEW finding: the cellar ramp polygon is NOT in cellStruct.PhysicsPolygons.
It lives in a separate GfxObjPhysics (the cellar's stair-piece static
building) registered via ShadowObjectRegistry, queried via
FindObjCollisions → engine.DataCache.GetGfxObj. CellDumpSerializer is
CORRECT — it captures the cell's physics polygons accurately. The
ramp polygon comes from a different data source entirely.

The polydump probe at BSPQuery.AdjustSphereToPlane:402 reports
"cell=0xA9B40147 polyId=0x0008 sides=Landblock" because the SPHERE
is in that cell at hit time — but the polygon's actual source is the
building's GfxObj. Inside the cellar fixture, polyId=0x0008 happens
to be a wall (Normal=(1,0,0)); inside the building's GfxObj, polyId
=0x0008 is the ramp (Normal=(0,-0.719,0.695) local). Same ID, different
collection.

The new RegisterStairRampGfxObj() in the harness constructs the
building's ramp polygon in WORLD coordinates (translated from
local building frame + 180° yaw), wraps it in a minimal one-leaf
PhysicsBSPTree, registers via cache.RegisterGfxObjForTest, and
attaches a ShadowEntry with cellScope=CellarId so the shadow is only
queried when the sphere is in the cellar cell (matches retail's
per-cell shadow scoping for interior statics — Issue #91 family).

Verified: world plane n=(0,0.719,0.695), d=-69.5035 (matches live
cdb capture exactly to 4 sig figs). Ramp foot at world Y=8.745,
Z=90.955; ramp top at world Y=5.845, Z=93.955. 3.0 m vertical rise.

NEW blocker discovered: the sphere goes airborne at tick 1 (same
issue documented in the prior commit's Finding #2). Sphere FLOATS
at Z=91.43 over the cellar floor, never contacts the synthetic
ramp. The synthetic stair registration mechanics are validated (the
GfxObj is in the cache, the ShadowEntry is in the registry, the
BSP tree is well-formed) — but trajectory replay still blocked on
the seeded-grounded-state bug. Next session needs to diagnose
WHY the engine reports "hit=yes n=(0,0,1) walkable=False" on tick 1
for a sphere correctly seeded as grounded on the cellar floor.

Test baseline maintained: 1167 + 4 (harness) = 1171 + 8 pre-existing
failures.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 17:49:34 +02:00
Erik
4c9290c691 test(phys): A6.P3 #98 — trajectory replay harness (mechanics OK; fixtures incomplete)
Adds tests/AcDream.Core.Tests/Physics/CellarUpTrajectoryReplayTests.cs:
a deterministic harness that drives PhysicsEngine.ResolveWithTransition
through N ticks against pre-loaded cell fixtures, capturing per-tick
trajectory points. Pure indoor (no landblock registration needed),
runs 200 ticks in under 100 ms.

The harness MECHANICS work — engine constructs cleanly, DataCache
accepts test fixtures via RegisterCellStructForTest, PhysicsBody
carries ContactPlane state across ticks. 4/4 tests pass, baseline
maintained (1167 + 4 = 1171 + 8 pre-existing failures).

Two real findings surfaced during commissioning, both documented as
passing tests so they don't regress silently:

Finding 1 (Harness_FixtureLimitation_NoRampPolygon): the three
issue-#98 cell fixtures contain ONLY axis-aligned polygons. The
cellar fixture (0xA9B40147) has 37 polys: 8 floor (N=(0,0,1)), 7
ceiling (N=(0,0,-1)), 22 walls. The live capture's CELLAR RAMP
polygon (N ≈ (0, ±0.719, 0.695)) is NOT in any fixture. With no
ramp polygon, the harness can't reproduce the cellar-up climb —
the sphere would walk horizontally across the cellar floor without
ever encountering a slope. Re-capture needed; investigate whether
CellDumpSerializer is skipping polygons or whether the ramp lives
in a cell we didn't dump.

Finding 2 (Harness_Finding_SphereGoesAirborneAtTick1): at the
seeded grounded initial position (sphere center 0.48 m above cellar
floor, ContactPlane = (0,0,1,-90.95), OnWalkable bit set), the
engine reports `hit=yes n=(0,0,1) walkable=False` on tick 1 and
the body's IsOnGround flips to false. Subsequent ticks proceed as
airborne (Y advances, Z stays put — no gravity in the input offset).
Unclear whether this is an engine bug (floor contact classified
as non-walkable collision) or a fixture issue (cellar floor
polygon's containment test mis-firing at the seeded XY). Either
way, the harness now exposes it deterministically.

Net value of this commit: the harness CODE is ready. Once the
fixture issue is solved, fix attempts on #98 (or any trajectory-
dependent bug) iterate in <100 ms instead of 5-minute live-launch
cycles. The "why is this so hard" point #4 from the session-pause
handoff is addressed for everything except the missing-ramp gap.

Test baseline: 1171 (1167 + 4 new) + 8 pre-existing failures.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 17:33:17 +02:00