docs(handoff): door collision per-part BSP session handoff (2026-05-24)
Long session that shipped A6.P4 infrastructure (Tasks 1-6 of the per-part BSP plan) but discovered the specific shapes we register from door setup 0x020019FF don't catch the player. Per-part BSP at 0x010044B5 produced ZERO collision attributions in 188K+ resolve lines despite player walking at doors. Cylinder still blocks center-only. Task 7 (refactor RegisterLiveEntityCollision) was implemented and visually tested, but reverted because the new per-part BSP shape didn't actually fix the door bug. The infrastructure stays — it's correctly modeling retail's CPhysicsObj-with-parts model — but the shapes we feed it need to be re-investigated apparatus-first. Three hypotheses ranked: (A) part BSPs are visual-only, no collision polys; (B) building BSP has a wide doorway gap our tiny cylinder doesn't fill; (C) retail uses Setup.Radius/Height directly. Next- session move: dump GfxObj 0x010044B5's PhysicsBSP first, then cdb retail at a doorway. Recommends: stop speculation, build apparatus, decide fix from evidence. #99 stays OPEN. Slice 1's "Closes #99" claim was premature; the real close requires the per-part BSP work + correct shape identification. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
1498697bc5
commit
c89df8e4c0
1 changed files with 265 additions and 0 deletions
265
docs/research/2026-05-24-door-collision-session-handoff.md
Normal file
265
docs/research/2026-05-24-door-collision-session-handoff.md
Normal file
|
|
@ -0,0 +1,265 @@
|
|||
# Door collision per-part BSP session — handoff
|
||||
|
||||
**Date:** 2026-05-24 (long session, multiple phases)
|
||||
**Branch:** `claude/strange-albattani-3fc83c`
|
||||
**Worktree:** `C:\Users\erikn\source\repos\acdream\.claude\worktrees\strange-albattani-3fc83c`
|
||||
|
||||
This handoff documents an A6.P4-driven session that:
|
||||
1. Shipped A6.P4 slice 1 (real cleanup, didn't close #99)
|
||||
2. Investigated why doors don't block (apparatus-first)
|
||||
3. Brainstormed + speced a per-part BSP collision design
|
||||
4. Shipped most of the implementation (Tasks 1-6 of 10)
|
||||
5. Discovered Task 7's per-part BSP doesn't actually fix the door bug
|
||||
6. Reverted Task 7 and paused for further investigation
|
||||
|
||||
---
|
||||
|
||||
## TL;DR
|
||||
|
||||
**Shipped (real commits):**
|
||||
- `b49ed90` — A6.P4 slice 1: drop the `< 0x0100u` filter in
|
||||
`ShadowObjectRegistry.GetNearbyObjects`'s portalReachableCells loop,
|
||||
rename `indoorCellIds` → `portalReachableCells`. Real cleanup; the
|
||||
`FindCellSet`-already-includes-outdoor-cells discovery means doors at
|
||||
building thresholds should be reachable from indoor primary spheres
|
||||
via the exit-portal logic. But the user-visible #99 close was wrongly
|
||||
claimed in the commit message — see below.
|
||||
- `d71ceab` — design spec: per-part BSP for server-spawned entities.
|
||||
- `8d4f14c` — 10-task implementation plan.
|
||||
- `ab4278c` — Task 1: ShadowShape record.
|
||||
- `7f5c287` — Task 2: ShadowShapeBuilder.FromSetup + 7 unit tests.
|
||||
- `1454eab` — Task 3: ShadowEntry adds LocalPosition + LocalRotation.
|
||||
- `fca0a13` — Task 4: ShadowObjectRegistry.RegisterMultiPart + 6 tests +
|
||||
Deregister clears `_entityShapes` (Task 6 folded in).
|
||||
- `d5ffb03` — Task 5: UpdatePosition recomposes multi-part transforms
|
||||
via `_entityShapes`.
|
||||
- `3e5dc8c` — Task 6 regression test: stray UpdatePosition after
|
||||
Deregister is no-op.
|
||||
- `1498697` — `[cyl-test]` diagnostic probe (broadly useful).
|
||||
|
||||
**Reverted (Task 7 staged, then `git restore`):** the
|
||||
`RegisterLiveEntityCollision` refactor at `GameWindow.cs:3076`. Reverted
|
||||
because visual verification showed the per-part BSP shape didn't actually
|
||||
block the door — only the small Cylinder did, and even that only at
|
||||
dead-center approach.
|
||||
|
||||
**Still pending:** Tasks 7-10 in the plan + the real fix for door
|
||||
collision.
|
||||
|
||||
---
|
||||
|
||||
## What we learned (apparatus-first findings)
|
||||
|
||||
### Door Setup 0x020019FF shape inventory (live dump captured 2026-05-24)
|
||||
|
||||
```
|
||||
[door-setup-dump] setupId=0x020019FF setupRadius=0.141 setupHeight=0.200
|
||||
cylSpheres=0 spheres=1 parts=3 placementFrames=1
|
||||
stepUp=0.090 stepDown=0.090
|
||||
[door-setup-dump] sphere[0] r=0.100 origin=(0.000,0.000,0.018)
|
||||
[door-setup-dump] part[0] gfxObj=0x010044B5
|
||||
[door-setup-dump] part[1] gfxObj=0x010044B6
|
||||
[door-setup-dump] part[2] gfxObj=0x010044B6
|
||||
```
|
||||
|
||||
### Per-shape registration (post-Task-7-experiment)
|
||||
|
||||
With `ShadowShapeBuilder.FromSetup` running over Setup 0x020019FF in the
|
||||
live launch, doors registered 2 shadows each:
|
||||
|
||||
1. `type=Cylinder radius=0.100 height=0.200 localPos=(0,0,0.018)` — from
|
||||
the Sphere converted to short Cylinder.
|
||||
2. `type=BSP gfxObj=0x010044B5 radius=2.000 localPos=(-0.006,0.125,1.275)` —
|
||||
from part 0 (the frame). The other two parts (`0x010044B6` x2) have
|
||||
`BSP=null` → skipped.
|
||||
|
||||
### Collision behavior (visual verified by user, 2026-05-24)
|
||||
|
||||
| Scenario | Result |
|
||||
|---|---|
|
||||
| Cellar climb (#98 regression check) | ✅ Works |
|
||||
| Door from outside, dead center | ⚠️ Partial — only the small Cylinder blocks; player stops at the center |
|
||||
| Door from outside, ~50 cm off-center | ❌ Pass through |
|
||||
| Door from outside (Use → swing) | ✅ Swing animation works, door opens |
|
||||
| Indoor furniture (#91 regression check) | ✅ Works |
|
||||
| Outdoor exterior wall (regression check) | ✅ Works |
|
||||
| Door from inside walking out | ❌ Pass through |
|
||||
|
||||
### Diagnostic evidence
|
||||
|
||||
In 188K+ resolve lines from the launch:
|
||||
- `Door 0xF4249 : 85 cyl-tests, 13 resolve hits attributed`
|
||||
- `Door 0xF424F : 227 cyl-tests, 16 resolve hits attributed`
|
||||
- **Zero `[resolve-bldg]` attributions for any door**
|
||||
|
||||
Conclusion: the per-part BSP at `0x010044B5` produces NO collision hits.
|
||||
Either:
|
||||
1. The PhysicsBSP at that GfxObj has no collision-bearing polygons
|
||||
(only visual polys), OR
|
||||
2. Our world-to-part-local sphere transform is wrong, OR
|
||||
3. The broadphase rejects it (unlikely with radius=2.0 default).
|
||||
|
||||
---
|
||||
|
||||
## Why this differs from M1 visual verification on 2026-05-13
|
||||
|
||||
The user remembers doors blocking on the M1 demo verification. That
|
||||
demo was "open the inn door" — clicking + watching the swing animation.
|
||||
The walking-through-an-open-door part was not deliberately tested. The
|
||||
closed-door blocking was probably observed accidentally when the user
|
||||
walked directly at a center-of-doorway cylinder; the 14 cm cylinder is
|
||||
just wide enough to catch a sphere at exactly the centerline. Today's
|
||||
careful off-center test exposed the gap.
|
||||
|
||||
So nothing regressed since 2026-05-13. The bug has been latent. Our
|
||||
investigation just exposed it.
|
||||
|
||||
---
|
||||
|
||||
## Investigation gap to close before the next implementation attempt
|
||||
|
||||
The per-part BSP design IS retail-faithful in shape (matches
|
||||
`CPhysicsObj::FindObjCollisions` → `CPartArray::FindObjCollisions` →
|
||||
`CPhysicsPart::find_obj_collisions` → `CGfxObj::find_obj_collisions`).
|
||||
But it didn't surface a working blocker for the cottage doors. Three
|
||||
hypotheses, ranked by likelihood:
|
||||
|
||||
### Hypothesis A (most likely): Part 0x010044B5 has no collision-bearing PhysicsBSP polygons
|
||||
|
||||
The Setup defines visual parts. Some parts (especially decorative
|
||||
hardware) may have a PhysicsBSP that's just the visual mesh's bounding
|
||||
volume, with no walls or threshold polygons. The door's collision might
|
||||
genuinely be just the small Cylinder by retail design, and retail
|
||||
gets full doorway blocking from the **building's BSP** having a narrow
|
||||
gap exactly the size of the door's Cylinder (~28 cm × 28 cm).
|
||||
|
||||
**How to verify:** Dump `0x010044B5`'s PhysicsBSP polygons via
|
||||
`ACDREAM_DUMP_GFXOBJS=0x010044B5`. Inspect the polygons. If they're
|
||||
just an axis-aligned bounding box matching the visual mesh, no useful
|
||||
collision data exists at the part level.
|
||||
|
||||
### Hypothesis B: Building BSP has a wide doorway gap that retail's tiny cylinder doesn't fill
|
||||
|
||||
A retail building (e.g., cottage interior 0x020XXXXX) has its walls as
|
||||
BSP polygons. The doorway is a gap. If the gap is ~2 m wide (visual
|
||||
opening), the 28 cm cylinder doesn't span it — even retail wouldn't
|
||||
block.
|
||||
|
||||
**How to verify:** Open RenderDoc on retail (or our client) and inspect
|
||||
the cottage interior GfxObj BSP at the doorway. Measure the gap. If
|
||||
it's narrow (~30 cm), the small cylinder fills it. If wide (~2 m), the
|
||||
cylinder is decorative and the actual blocker must come from elsewhere.
|
||||
|
||||
### Hypothesis C: Retail uses a different collision mechanism entirely
|
||||
|
||||
Doors might use Setup.Radius / Setup.Height (the bounding cylinder
|
||||
dimensions, 0.141 × 0.200 — slightly larger than our Sphere-derived
|
||||
0.100 × 0.200) AS THE PRIMARY BLOCKER, not the Sphere. Or retail
|
||||
overrides shape selection for `ItemType==Door` specifically.
|
||||
|
||||
**How to verify:** Attach cdb to a live retail client at a cottage
|
||||
doorway, set a breakpoint on `CPhysicsObj::FindObjCollisions` for the
|
||||
door's PhysicsObj, observe which shape branch fires.
|
||||
|
||||
---
|
||||
|
||||
## Recommended next-session approach
|
||||
|
||||
Per the project's "apparatus-first for physics divergences" rule
|
||||
(`feedback_apparatus_for_physics_bugs.md`):
|
||||
|
||||
1. **Stop coding.** Don't try another fix without evidence.
|
||||
2. **Dump 0x010044B5's PhysicsBSP** via `ACDREAM_DUMP_GFXOBJS=0x010044B5`.
|
||||
If it has zero floor-touching polygons → Hypothesis A confirmed.
|
||||
3. **Attach cdb to retail** at a cottage doorway. Trace which shapes
|
||||
block the player. See `project_retail_debugger.md` for the toolchain.
|
||||
4. **Cross-reference ACE source** for Door collision (if any) — search
|
||||
`references/ACE/Source/ACE.Server/Physics/` for door handling.
|
||||
5. **Re-brainstorm** with the new evidence. The Task 1-6 infrastructure
|
||||
stays (it's correctly modeling retail's CPhysicsObj-per-entity
|
||||
with parts iterated for collision). Only the SHAPES we register
|
||||
need to change.
|
||||
|
||||
The infrastructure investment was not wasted. The architecture is right.
|
||||
We just registered the wrong shapes from the door setup.
|
||||
|
||||
---
|
||||
|
||||
## What's in the tree right now
|
||||
|
||||
```
|
||||
$ git log --oneline -15
|
||||
1498697 diag(phys): [cyl-test] probe — log every Cylinder shadow collision test
|
||||
3e5dc8c test(phys): Task 6 regression — Deregister clears _entityShapes cache
|
||||
d5ffb03 feat(phys): UpdatePosition handles multi-part entities
|
||||
fca0a13 feat(phys): ShadowObjectRegistry.RegisterMultiPart
|
||||
1454eab feat(phys): ShadowEntry adds LocalPosition + LocalRotation
|
||||
7f5c287 feat(phys): ShadowShapeBuilder.FromSetup
|
||||
ab4278c feat(phys): add ShadowShape record (no callers yet)
|
||||
8d4f14c docs(phys): implementation plan — per-part BSP for server-spawned entities
|
||||
d71ceab docs(phys): design spec — per-part BSP collision for server-spawned entities
|
||||
b49ed90 feat(phys): A6.P4 slice 1 — portal-reachable cellSet includes outdoor cells
|
||||
b3ce505 fix(phys): A6.P3 #98 — gate outdoor shadow radial sweep on indoor primary cell
|
||||
b55ae83 docs: A6.P3 #98 resolution + A6.P4 design + #99/#100 filed
|
||||
3e3cd77 docs(handoff): A6.P4 pickup handoff — full session-resume artifact
|
||||
```
|
||||
|
||||
All 49+ tests pass:
|
||||
- 24 ShadowObjectRegistryTests
|
||||
- 7 ShadowShapeBuilderTests
|
||||
- 8 ShadowObjectRegistryMultiPartTests
|
||||
- 11 CellarUpTrajectoryReplayTests
|
||||
|
||||
Pre-existing 6-8 baseline static-state-leakage failures in the broader
|
||||
Physics suite are unchanged from prior sessions.
|
||||
|
||||
**No-commit state:** working tree is clean. `git status --short`
|
||||
shows only untracked investigation logs (`a6-issue98-*.log`,
|
||||
`launch-task7-*.log`, etc. — these accumulate from launches and don't
|
||||
get committed).
|
||||
|
||||
---
|
||||
|
||||
## #99 status: still open
|
||||
|
||||
The A6.P4 slice 1 commit message claimed "Closes #99" but the visual
|
||||
verification today proves that's premature. Slice 1 did a real cleanup
|
||||
(removed a misleading filter) but didn't fully address the user-visible
|
||||
door-block bug. Update `docs/ISSUES.md` accordingly (issue #99 remains
|
||||
OPEN; the per-part BSP architecture is NEW infrastructure built today
|
||||
that will support the eventual fix once we identify the right shapes).
|
||||
|
||||
---
|
||||
|
||||
## Pickup prompt for next session
|
||||
|
||||
```
|
||||
Door collision still doesn't fully block in M1.5 Holtburg. Per-part BSP
|
||||
infrastructure shipped 2026-05-24 (Tasks 1-6 of A6.P4 plan), but the
|
||||
specific shapes we register from door setup 0x020019FF don't catch the
|
||||
player. Need apparatus-first investigation:
|
||||
|
||||
Read docs/research/2026-05-24-door-collision-session-handoff.md
|
||||
(this doc — recent session handoff)
|
||||
|
||||
State both altitudes:
|
||||
Currently working toward: M1.5 — Indoor world feels right
|
||||
Current phase: A6.P4 — investigation phase to find the right door
|
||||
collision shapes; per-part BSP infrastructure
|
||||
already shipped; need to verify Hypothesis A/B/C
|
||||
before any more implementation
|
||||
|
||||
First moves (in order):
|
||||
1. Dump GfxObj 0x010044B5's PhysicsBSP via ACDREAM_DUMP_GFXOBJS.
|
||||
Does it have collision-bearing polygons or just visual?
|
||||
2. If yes → debug the per-part transform (likely Hypothesis B/C
|
||||
wrong); if no → confirm Hypothesis A and pivot strategy.
|
||||
3. Either way, attach cdb to retail at a cottage doorway to see
|
||||
what retail actually blocks with.
|
||||
|
||||
DO NOT speculate-and-fix again. The session 2026-05-24 already
|
||||
burned a Task 7 attempt on a hypothesis that turned out wrong. The
|
||||
6 committed implementation tasks (Tasks 1-6) are correct and stay.
|
||||
Only Tasks 7-10 of the plan need to change once we know the right
|
||||
shapes.
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue