test(phys): A6.P4 door — directional + geometric pin tests reframe inside-out bug
Built three new tests to investigate the inside-out asymmetric collision that persists after the AddAllOutsideCells coord fix: 1. Directional_OutsideIn_SouthApproach_BlocksAtSlabSouthFace — sphere south of door moving NORTH; expects block with cn.Y less than -0.5 2. Directional_InsideOut_NorthApproach_BlocksAtSlabNorthFace — sphere north of door moving SOUTH; expects block with cn.Y greater than +0.5 3. Geometric_DoorSlabZRange_AbovePlayerSphereTop — pins the slab Z range vs sphere top math BOTH directional tests PASS — collision is symmetric at unit-test level. The asymmetric production bug therefore comes from something the unit tests do not capture (multi-tick state, cell-tracking flicker, walkable polygon edge interactions). The geometric pin test reveals the real story: Setup 0x020019FF places the part-0 BSP slab 1.275 m ABOVE the entity origin via PlacementFrames[Default][0].Origin. With the cottage door entity at world Z=94.1, the slab world Z range is [95.375, 97.865]. Player sphere top reaches Z=95.20. The slab BOTTOM is 0.175 m ABOVE the sphere top — the slab NEVER collides with the player. The slab is a LINTEL (door frame above the doorway), not a leaf. The door's only effective collider at sphere height is the 0.10 m radius foot cylinder. The directional tests pass because the cylinder blocks, not the BSP. User-reported inside-out off-center walkthrough is the sphere walking AROUND the foot cylinder (sphere reach 0.48 + cyl 0.10 = 0.58 m; any sphere center over 0.58 m from cylinder center passes freely). The visual "body partially intersects door" is the character model occupying the visual door volume while the collision sphere passes beside the cylinder. Reframed handoff in docs/research/2026-05-25-door-bug-partial-fix-shipped.md points to three candidate next-step investigations: - Retail-faithfulness audit on setup.Radius / setup.Height interpretation - Re-inspect door parts 1+2 (GfxObj 0x010044B6) for missed physics shapes - Test the cottage cell BSP (cell 0x0150 walls) + door together — the COMBINED collision may be what retail relies on Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
28cd97be62
commit
c27fded61e
2 changed files with 413 additions and 20 deletions
|
|
@ -106,29 +106,90 @@ The `[bsp-test]` probe fires 245 times for the door entity during the
|
|||
post-fix inside-out attempts — door IS being queried. The
|
||||
collision-detection mechanics produce the wrong response.
|
||||
|
||||
## What's next (separate bug)
|
||||
## What's next (separate bug — REFRAMED 2026-05-25 evening)
|
||||
|
||||
**Investigate BSPQuery.FindCollisions's response for two-sided polygons
|
||||
when the sphere is already overlapping the slab.** Retail's
|
||||
`CBSPTree::find_collisions` family handles this specifically — the
|
||||
sphere's path through the slab faces gets traced and the FIRST face
|
||||
crossed in motion direction is the collision. With two-sided polygons,
|
||||
both faces are collidable; the front-vs-back determination is by
|
||||
sphere-velocity vs face-normal dot product.
|
||||
**Initial hypothesis was wrong.** Two new directional tests built
|
||||
post-fix (`Directional_OutsideIn_*`, `Directional_InsideOut_*`) BOTH
|
||||
PASS — the BSP collision response is symmetric at unit-test level.
|
||||
The asymmetric production bug must come from something the unit tests
|
||||
weren't capturing.
|
||||
|
||||
Likely files:
|
||||
- `src/AcDream.Core/Physics/BSPQuery.cs` — the BSP traversal +
|
||||
sphere-poly intersection logic.
|
||||
- Retail decomp anchors:
|
||||
`acclient_2013_pseudo_c.txt:BSPTREE::find_collisions` +
|
||||
`SPHEREPATH::sphere_intersects_poly` family.
|
||||
A geometric pin test (`Geometric_DoorSlabZRange_AbovePlayerSphereTop`)
|
||||
reveals the real story:
|
||||
|
||||
Apparatus to write next: a focused test that registers the door at its
|
||||
actual production world transform (entity origin + partFrame offset
|
||||
from the dat, with correct rotation) and replays a sphere passing
|
||||
through it from EACH side at various speeds. Compare collision normal
|
||||
+ position-resolution per side. The asymmetric response will be
|
||||
reproducible at unit-test speed.
|
||||
```
|
||||
Setup 0x020019FF (cottage door):
|
||||
CylSphere[0]: r=0.10, h=0.20, origin=(0, 0, 0.018)
|
||||
→ world Z [94.118, 94.318] when entity at Z=94.1
|
||||
Part 0 (GfxObj 0x010044B5, the BSP "slab"):
|
||||
placement frame [Default][0].Origin = (-0.006, 0.125, 1.275)
|
||||
→ BSP world Z [95.375, 97.865] when entity at Z=94.1
|
||||
|
||||
Player at floor Z=94:
|
||||
sphere height = 1.20, sphere top = 95.20
|
||||
|
||||
BSP slab BOTTOM (95.375) is ABOVE sphere TOP (95.20) by 0.175 m.
|
||||
The slab NEVER collides with the player's body sphere.
|
||||
```
|
||||
|
||||
The slab is a LINTEL (the door frame above the doorway), not a leaf.
|
||||
The door's only collision against a player at floor level is the
|
||||
0.10 m radius foot cylinder. Sphere radius 0.48 + cyl 0.10 = 0.58 m
|
||||
collision reach. Any sphere center > 0.58 m from cylinder center
|
||||
(132.6, 17.1) passes freely.
|
||||
|
||||
The user-reported "inside-out walkthrough at ~50 cm off-center" is
|
||||
the sphere walking AROUND the cylinder, at X = 132.6 ± 0.6+ m where
|
||||
collision misses entirely. "Body partially intersects door" is the
|
||||
character model occupying the visual door's volume while the collision
|
||||
sphere passes beside the foot cylinder.
|
||||
|
||||
The "outside-in works" case is also the foot cylinder doing the
|
||||
blocking — when the user approaches more centered or hits the cylinder
|
||||
head-on. The cell-visibility fix made the cylinder visible from indoor
|
||||
cells (it wasn't before), which is why outside-in went from "walks
|
||||
through" to "blocks" — but the cylinder is the actual collider.
|
||||
|
||||
**This is a door-geometry interpretation question, NOT a BSP query bug.**
|
||||
Three candidate next-step investigations:
|
||||
|
||||
1. **Retail-faithfulness audit on door collision.** Read retail's
|
||||
`CPhysicsObj::set_setup_for_door` (or similar) to determine what
|
||||
shapes retail loads for a closed cottage door. If retail uses the
|
||||
SAME setup data + finds the same shapes we do, the door geometry
|
||||
in the dat IS the spec. The "blocking" the user sees in retail
|
||||
might similarly be the foot cylinder + perhaps a different default
|
||||
sphere from setup.Radius/setup.Height that we're not registering.
|
||||
|
||||
2. **Inspect door parts 1+2 (GfxObj 0x010044B6).** Per prior session's
|
||||
handoff they are visual-only (HasPhysics=false). Verify by direct
|
||||
dat read — maybe the PhysicsBSP is null but the cylinder/sphere
|
||||
list on the GfxObj itself has collision data we're missing.
|
||||
`DoorSetupGfxObjInspectionTests` already prints this; re-read.
|
||||
|
||||
3. **The cottage cell BSP encloses the doorway.** Cell 0x0150
|
||||
(the doorway alcove) is bounded by cottage walls. The DOOR opens
|
||||
through a gap in those walls. When the door is closed, the gap
|
||||
is filled by the door geometry. Maybe retail's collision relies
|
||||
on the cottage walls (cell BSP) for the "doorway side walls" and
|
||||
the door's slab covers ONLY the opening's leaf area. The asymmetric
|
||||
block we see could be the cottage cell walls catching outside-in
|
||||
approach (cell BSP collision via FindEnvCollisions, before the
|
||||
door's shadow ever fires) but NOT catching inside-out at the same
|
||||
alcove geometry.
|
||||
|
||||
The 50cm off-center approach probably exits the cottage walls' BSP
|
||||
through the doorway gap, walks past the foot cylinder (which is small),
|
||||
and never has anything to collide against in the world's collision
|
||||
representation. Retail might block this via a `setup.Radius=0.1414`
|
||||
cylinder we're missing (Setup.Radius isn't currently registered by
|
||||
`ShadowShapeBuilder.FromSetup` for entities WITH a CylSphere).
|
||||
|
||||
Apparatus to write next: a test that registers the door using
|
||||
ShadowShapeBuilder.FromSetup AND verifies setup.Radius is reflected
|
||||
in the shape list. If it isn't, that's a candidate fix — the door's
|
||||
0.14 m radius + 0.20 m height SETUP sphere is wider than the 0.10 m
|
||||
CylSphere and would catch the off-center approach.
|
||||
|
||||
## Commits
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue