test(phys): A6.P4 door inside-out — collision-geometry gap diagnosis

Added diagnostic apparatus that pinpoints the inside-out walkthrough
as a collision-geometry GAP, not a collision-detection bug.

New tests in DoorBugTrajectoryReplayTests:
- InsideOut_Tick3254_WithCottageWalls_ShouldBlock: hypothesis test that
  registered cottage GfxObj 0x01000A2B and replayed the captured tick.
  Cottage blocked sphere but with cn=(0,0,1) floor-cap normal, not a
  wall normal — first signal that cottage geometry near the sphere
  isn't a wall.
- Diagnostic_CottagePolys_NearWalkthroughPosition: dumps cottage polys
  near sphere XY=(133.655, 17.59) at any Z. Result: ZERO cottage
  polygons in that area. The cottage GfxObj has no geometry where the
  sphere walks through.

DoorSetupGfxObjInspectionTests.HoltburgCottage_CellPortals_DatInspection
extended to dump cell 0xA9B40150's 4 physics polygons in world frame:
- floor (Z=94), ceiling (Z=96.5), west wall (X=131.6), east wall (X=133.5)
- All walls only span Y=[16.5, 17.1] — the small doorway alcove volume
- North of Y=17.1, no wall

Captured sphere at (133.655, 17.59) is 0.155 m east of cell east wall
AND 0.49 m north of the wall's Y range. No collision geometry exists
at that XY past Y=17.1. The collision representation has a gap that
the visual cottage covers with a wall.

Production capture confirms the diagnosis: cottage GfxObj fires
[bsp-test] 425 times during inside-out walking — visibility IS
correct post-AddAllOutsideCells fix. Door slab fires 245 times. But
the BSP queries find no polygon at (133.655, 17.6+, 94-95.20). The
slab's east face blocks WEST motion (cn=(+1,0,0) as captured), sphere
free to move +Y past it because no wall is there to block.

Three candidates for next-session investigation:
1. Different cottage GfxObj (Holtburg cottages may be multi-piece)
2. Landblock-baked stab static at the cottage exterior wall location
3. Cottage GfxObj's visual polygons wider than physics polygons (dat fact)

Cheapest next step: add LandblockStatics_DatInspection test that
loads LandBlockInfo 0xA9B4FFFE + iterates StaticObjects + prints
every entity at world XY in [131,135] x [16,19]. Reveals what other
entities live at the cottage doorway.

Full handoff: docs/research/2026-05-25-door-bug-inside-out-geometry-gap.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-25 08:27:52 +02:00
parent 85a164f4a8
commit da798b2071
3 changed files with 333 additions and 0 deletions

View file

@ -0,0 +1,157 @@
# Door bug — inside-out walkthrough: missing cottage exterior wall (geometry gap)
2026-05-25, continuation of door-collision investigation
## TL;DR
The inside-out walkthrough that persisted after the
`AddAllOutsideCells` fix is **NOT a collision-detection bug**. It's a
**collision-geometry GAP**: the cottage's north exterior wall east
(and presumably west) of the doorway opening doesn't exist in any
registered entity our engine knows about. The sphere walks past the
door slab on its east side, clears the doorway alcove cell's small
east wall (Y range [16.5, 17.1]), and then has nothing in front of it
in the collision representation — even though the VISUAL cottage has
a wall there.
## Apparatus diagnostics
Three new tests landed (in `DoorBugTrajectoryReplayTests`):
1. `Directional_OutsideIn_SouthApproach_BlocksAtSlabSouthFace` — sphere
south moving north blocks. PASSES.
2. `Directional_InsideOut_NorthApproach_BlocksAtSlabNorthFace` — sphere
north moving south blocks. PASSES.
3. `Geometric_DoorSlabAtSphereHeight_OverlapsInZ` — pins slab world Z
range = [94.139, 96.630]; sphere top at Z=95.20 IS within slab.
The slab is at sphere height — BSP collision is geometrically active.
4. `InsideOut_Tick3254_WithCottageWalls_ShouldBlock` — hypothesis test
adds cottage GfxObj 0x01000A2B. Result: cottage DID block but with
cn=(0,0,1) — a floor-cap response, NOT a wall response.
5. `Diagnostic_CottagePolys_NearWalkthroughPosition` — dumps cottage
polygons near sphere XY=(133.655, 17.59), any Z. **Result: ZERO
cottage polygons in that area.** The cottage GfxObj has no
geometry where the sphere walks through.
`DoorSetupGfxObjInspectionTests.HoltburgCottage_CellPortals_DatInspection`
extended to dump cell 0xA9B40150's 4 physics polys in world frame:
```
[0] sides=Landblock X=[131.600, 133.500] Y=[16.500, 17.100] Z=[94.000, 94.000] FLOOR
[1] sides=Landblock X=[131.600, 131.600] Y=[16.500, 17.100] Z=[94.000, 96.500] WEST WALL
[2] sides=Landblock X=[131.600, 133.500] Y=[16.500, 17.100] Z=[96.500, 96.500] CEILING
[3] sides=Landblock X=[133.500, 133.500] Y=[16.500, 17.100] Z=[94.000, 96.500] EAST WALL
```
Cell 0xA9B40150 is the **doorway alcove** — a small ~1.9m × 0.6m × 2.5m
volume between the cottage interior and the outdoor area. Its east wall
only extends Y=[16.5, 17.1]. **North of Y=17.1, no wall** in this cell.
The captured failing sphere at (133.655, 17.59) is 0.155m east of the
east wall AND 0.49m NORTH of the wall's Y range. The wall doesn't
reach the sphere.
## The collision-geometry gap
Visual representation (in-client):
- Cottage has a north exterior wall east and west of the doorway opening
- The wall extends Y > 17.1 (north of the alcove)
- User sees their character partially clipping into this wall
Collision representation (what we register):
- Cottage GfxObj 0x01000A2B: **0 polygons** in the area (133.655, 17.59, 94-95.20)
- Cell 0xA9B40150 (alcove): walls only at Y=[16.5, 17.1]
- Door slab: only spans X=[131.635, 133.560] — too narrow to cover the cottage opening
- Outdoor cell 0xA9B40029: outdoor cell, no walls
**Net: no entity has wall polygons at (133.655, Y > 17.1).** Sphere can
walk there freely.
## Verification in production capture
`door-fix-inout2.launch.log` shows:
- Cottage GfxObj `[bsp-test]` fires 425 times during inside-out walking
(so visibility is correct post-fix)
- Door slab `[bsp-test]` fires 245 times
- Captured tick 3254: sphere at (133.655, 17.590), target (133.549,
17.599). Result: position X=133.655 unchanged (blocked westward),
position Y=17.599 (moved north freely). cn=(+1, 0, 0) = slab east
face normal.
- The slab east face blocks WEST motion correctly. The sphere is FREE
to move north because no geometry covers (133.655, Y > 17.1).
## What's next
**Identify which entity SHOULD own the cottage's north exterior wall
east of the doorway.** Three candidates:
1. **A different cottage GfxObj.** Holtburg cottages might be
multi-piece (separate GfxObjs for wall sections, doorway frame, roof).
The cottage we have (0x01000A2B) might be one of multiple. Check
the landblock's static-entity list for other GfxObjs at the cottage
position via `[entity-source]` log + Setup file.
2. **A landblock-baked "stab"** (separate static entity registered at
spawn time). LandblockLoader produces these. Check `LandBlockInfo`
dat record for landblock 0xA9B4 — what other entities are at world
(~133, ~18)?
3. **The cottage GfxObj's drawing geometry is wider than its physics.**
If 0x01000A2B has `Polygons` (visual) at the wall location but no
`PhysicsPolygons` (collision), the visual is wider than the
collision. This is a dat-data fact — not fixable without retail
re-engineering of the dat.
For candidates 1-2, the fix is "register the missing entity." For 3,
the bug is dat-side (or retail accepts the same walkthrough we do).
**Cheapest next-step test:** add a method to
`DoorSetupGfxObjInspectionTests` that loads `LandBlockInfo` 0xA9B4FFFE
(landblock-baked statics) and prints every static at world XY in
[131, 135] × [16, 19]. The output will name what other GfxObjs/Setups
are registered at the cottage doorway — if any include the missing
wall, we know what to register additionally.
## Apparatus committed
- `tests/AcDream.Core.Tests/Physics/DoorBugTrajectoryReplayTests.cs`:
faithful door registration, directional collision tests, geometric
pin test, cottage GfxObj hypothesis test, cottage polygon dump.
- `tests/AcDream.Core.Tests/Physics/DoorSetupGfxObjInspectionTests.cs`:
HoltburgCottage_CellPortals_DatInspection extended with cell-poly
world-frame dump.
All tests under `DoorBugTrajectoryReplayTests` and the extended
`DoorSetupGfxObjInspectionTests.HoltburgCottage_CellPortals_DatInspection`
PASS (skip on CI when dat dir absent).
## Pickup prompt for next session
```
A6.P4 door inside-out walkthrough: identified as collision-geometry
gap, NOT collision-detection bug. The cottage's north exterior wall
east+west of the doorway opening isn't represented in any registered
entity. Sphere walks freely at (133.655, 17.59) — no wall to block.
Read docs/research/2026-05-25-door-bug-inside-out-geometry-gap.md
+ Diagnostic_CottagePolys_NearWalkthroughPosition test output
+ HoltburgCottage_CellPortals_DatInspection dump for cell 0x0150
State both altitudes:
Currently working toward: M1.5 — Indoor world feels right
Current phase: A6.P4 door bug — find missing cottage wall entity.
The fix isn't in BSP, cells, or AddAllOutsideCells
— those are correct. The collision geometry has a
gap. Need to identify which entity SHOULD own the
wall and register it.
First move: add a LandblockStatics_DatInspection test to
DoorSetupGfxObjInspectionTests that loads LandBlockInfo 0xA9B4FFFE
+ iterates StaticObjects. Print every entity at world XY in
[131, 135] x [16, 19] — name + setup id + position. Will reveal
what other entities (if any) live at the cottage doorway.
If a wall-bearing entity exists but we're not registering it: fix
the registration path. If nothing exists: the dat doesn't have the
wall, and this might be retail-faithful behavior we have to accept
(or compensate for by widening the door slab via gameplay layer).
```