Cluster A's investigation pinned #86 (picker) as structural and closed it (Phase B). #84 and #85 both pinned on missing indoor cell tracking; Phase D promoted CellId via AABB containment which un-stuck the spawn-in-building case (closes #84 partially) but proved too tight for threshold/doorway cells to keep CellId indoor during normal walking. The proper fix is retail's portal-based cell traversal; filed as a new ISSUES.md issue (see body) for the follow-up phase. Phase E diagnostic infrastructure ([cell-cache] + extended [indoor-bsp]) stays in place as scaffolding for that work. ISSUES.md: #86 → Recently closed. #84 status updated to PARTIAL with resolution paragraph. #85 status update note added. New issue #87 filed for portal-based indoor cell tracking. Roadmap: Cluster A added to Recently shipped with partial-ship note. Forward entry added for the portal-traversal follow-up under Phase G. CLAUDE.md: current-phase paragraph updated to reflect Cluster A partial ship. Next phase deferred to Claude's choice in a future session. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
256 lines
12 KiB
Markdown
256 lines
12 KiB
Markdown
# Indoor walking Phase 1 — BSP cluster (Cluster A) — handoff (2026-05-19)
|
|
|
|
**Date:** 2026-05-19.
|
|
**Branch:** `claude/competent-robinson-dec1f4` (commits land here; merge to main handled by controller).
|
|
**Predecessor:** Indoor lighting + rendering Phase 2 (fix) — floors now render in Holtburg Inn. Nine pre-existing indoor bugs surfaced the moment floors were visible; this cluster addresses the collision/interaction subset (#84, #85, #86) and adds diagnostic infrastructure for the follow-up portal-traversal phase.
|
|
**Plan:** [`docs/superpowers/plans/2026-05-19-indoor-walking-phase1-bsp-cluster.md`](../superpowers/plans/2026-05-19-indoor-walking-phase1-bsp-cluster.md).
|
|
|
|
---
|
|
|
|
## TL;DR
|
|
|
|
Cluster A shipped **partially**. Three of the five planned phases (A, B, D)
|
|
produced real behavior changes; two (C — obstacle audit — and E — cell-cache
|
|
diagnostics) are diagnostic/research phases. The cluster's investigation
|
|
confirmed that the wall-collision failures (#84, #85) all root in one cause:
|
|
the player's `CellId` is never promoted to an indoor cell during normal
|
|
walking, so the indoor-BSP collision branch in `TransitionTypes.FindEnvCollisions`
|
|
never fires. Phase D implemented an AABB-containment shortcut that resolves
|
|
the specific "spawn inside a building and be stuck above the floor" case but
|
|
proved too tight to keep `CellId` promoted through threshold/doorway cells
|
|
during normal outdoor→indoor entry.
|
|
|
|
**#86** (click selection penetrates walls) is **fully closed** — a clean,
|
|
self-contained fix in `WorldPicker`.
|
|
|
|
**#84** is **partially closed** — the spawn-in-building symptom is gone; the
|
|
remaining wall-collision symptom during normal walking is tracked under the
|
|
new **#87**.
|
|
|
|
**#85** remains **open**; its root cause is confirmed identical to #84's
|
|
remaining symptom and is also tracked under #87.
|
|
|
|
**#87** (indoor portal-based cell tracking) is **filed** and ready for the
|
|
follow-up phase.
|
|
|
|
---
|
|
|
|
## Commits
|
|
|
|
| # | SHA | Subject | Phase |
|
|
|---|---|---|---|
|
|
| 1 | `18a2e28` | `docs(plan): implementation plan written` | Plan doc |
|
|
| 2 | `27d7de1` | `feat(physics): Cluster A — indoor BSP collision probe` | Phase A |
|
|
| 3 | `3764867` | `fix(picker): Cluster A #86 — cell-BSP ray occlusion in WorldPicker` | Phase B |
|
|
| 4 | `4e308d5` | `test(picker): Cluster A #86 — screen-rect cell-occlusion tests` | Phase B follow-up |
|
|
| 5 | `c19d6fb` | `fix(physics): Cluster A #84 + #85 — indoor cell tracking` | Phase D |
|
|
| 6 | `fda6af7` | `feat(physics): Cluster A — cell-cache diagnostic` | Phase E (1st) |
|
|
| 7 | `1f11ba9` | `feat(diag): Cluster A — extend [cell-cache] with AABB + bsphere + recursive poly count` | Phase E (2nd) |
|
|
|
|
**Build:** clean on all commits.
|
|
**Tests:** `dotnet test` shows the same 8 pre-existing failures in
|
|
`AcDream.Core.Tests` (MotionInterpreter / BSPStepUp / etc., unchanged across
|
|
the entire cluster). All targeted test projects green. Phase B follow-up
|
|
adds screen-rect occlusion tests; Phase D adds `RegisterCellStructForTest`
|
|
helper used by caller-side tests.
|
|
|
|
---
|
|
|
|
## What shipped
|
|
|
|
### Phase A — `[indoor-bsp]` probe
|
|
|
|
New `PhysicsDiagnostics.ProbeIndoorBspEnabled` toggle (env var
|
|
`ACDREAM_PROBE_INDOOR_BSP` + DebugPanel checkbox under
|
|
`ACDREAM_DEVTOOLS=1`). When enabled, logs one `[indoor-bsp]` line each time
|
|
`TransitionTypes.FindEnvCollisions` takes the indoor-cell branch —
|
|
i.e., when `CellId` is an EnvCell id and the BSP contains physics polys. The
|
|
probe serves as a presence detector: if `[indoor-bsp]` never fires during
|
|
indoor walking, the BSP is not being consulted at all.
|
|
|
|
### Phase B — WorldPicker cell-BSP ray occlusion (closes #86)
|
|
|
|
New `CellBspRayOccluder` class (in `src/AcDream.App/Rendering/`) computes
|
|
`NearestWallT`: the smallest ray parameter at which the pick ray intersects
|
|
any cached EnvCell BSP polygon. Both `WorldPicker.Pick` overloads now accept
|
|
an optional `cellOccluder` callback and filter out any hit candidate whose
|
|
ray T exceeds `NearestWallT`. The occluder is wired from `GameWindow` using
|
|
the `PhysicsDataCache` cell structs that Phase D also extends.
|
|
|
|
Before Phase B: clicking through a wall from the outside selected NPCs/items
|
|
inside the building — `WorldPicker.BuildRay + Pick` (Phase B.4b) tested only
|
|
entity AABBs and scenery BSPs, not EnvCell BSP geometry.
|
|
|
|
After Phase B: entities behind the nearest wall from the camera's perspective
|
|
are filtered out of the candidate set. Screen-rect unit tests verify the
|
|
filter across hit/miss/occlusion scenarios.
|
|
|
|
### Phase D — AABB containment for indoor CellId (partial #84 fix)
|
|
|
|
`PhysicsEngine.ResolveOutdoorCellId` is extended with an indoor
|
|
cell-containment scan. After resolving the outdoor cell, the method checks
|
|
whether the player's world position falls inside any cached `CellPhysics`
|
|
AABB; if so, `CellId` is promoted to that EnvCell. This enables the
|
|
`FindEnvCollisions` indoor-BSP branch.
|
|
|
|
New `PhysicsDataCache.TryFindContainingCell(worldPos)` does the AABB scan.
|
|
New `CellPhysics.WorldAabb` caches the cell-local AABB in world space on
|
|
first call (transforms the BSP bounding sphere's local AABB by the cell
|
|
origin). New `RegisterCellStructForTest` helper allows unit test callers to
|
|
populate the cache directly.
|
|
|
|
Also fixes the L.2e bare-low-byte preservation bug: `ResolveOutdoorCellId`
|
|
was silently truncating the player CellId to the low 16 bits; the fix
|
|
preserves the full 32-bit value.
|
|
|
|
**What this solved:** player spawning inside a building (e.g., logging in
|
|
from a position inside Holtburg cottage) no longer sees `walkable=False` for
|
|
hundreds of resolves with world Z=94.000. Phase D promotes CellId to the
|
|
indoor cell, the floor's BSP polys are found, the player can move.
|
|
|
|
**What this did NOT solve:** the `[indoor-bsp]` probe fires only 6 times
|
|
during an entire indoor walking session (all mid-jump, when the body happens
|
|
to be at a height that falls inside a room AABB). During normal walking on
|
|
the floor, the player's world Z is at the AABB floor level or lower —
|
|
outside the AABB for threshold/doorway cells that have only a 0.2 m Z range.
|
|
See Phase E evidence below.
|
|
|
|
### Phase E — Cell-cache diagnostic infrastructure
|
|
|
|
Two commits add `[cell-cache]` log output (env var
|
|
`ACDREAM_PROBE_CELL_CACHE`, also DebugPanel). For each EnvCell in the
|
|
physics cache, the probe logs:
|
|
|
|
```
|
|
[cell-cache] id=0xA9B40143 physicsPolyCount=14 bspTotalLeafPolys=14
|
|
bspUnmatchedIds=0 aabbMin=(-11.60,-1.60,0.00) aabbMax=(-6.20,7.60,2.80)
|
|
bspOrigin=(0.00,0.00,0.00) bspRadius=9.97
|
|
```
|
|
|
|
The extended second commit adds `bspTotalLeafPolys`, `bspUnmatchedIds`,
|
|
`bspOrigin`, and `bspRadius` fields to give a complete picture of cell
|
|
geometry from the physics cache perspective. This infrastructure stays in
|
|
place as scaffolding for the portal-traversal phase.
|
|
|
|
---
|
|
|
|
## Issue status after Cluster A
|
|
|
|
| Issue | Status | Notes |
|
|
|---|---|---|
|
|
| #84 Blocked by air indoors | OPEN (partial) | Spawn-in-building variant resolved by Phase D. Threshold/doorway wall-blocking remains open under #87. |
|
|
| #85 Pass through walls outside→in | OPEN | Root cause confirmed as same as #84 remaining symptom. See #87. |
|
|
| #86 Click selection penetrates walls | **CLOSED** | Phase B. `WorldPicker.Pick` + `CellBspRayOccluder`. |
|
|
| #87 Indoor portal-based cell tracking | OPEN (new) | Filed 2026-05-19. Retail-faithful fix via `CObjMaint::HandleObjectEnterCell`. |
|
|
|
|
---
|
|
|
|
## Probe evidence — log file findings
|
|
|
|
### `launch-cluster-a-capture.log`
|
|
|
|
Initial probe run with `ACDREAM_PROBE_INDOOR_BSP=1`. Result: **zero
|
|
`[indoor-bsp]` lines** during outdoor walking and during approach to the
|
|
Holtburg cottage doorway. This was the first confirmation that the indoor-BSP
|
|
branch was entirely gated out. The player's CellId remained an outdoor cell
|
|
for all movement.
|
|
|
|
### `launch-cluster-a-verify.log`
|
|
|
|
Post-Phase-D run. Observed `[indoor-bsp]` lines **only during jump frames**
|
|
(6 total). When the player jumped inside the cottage, the body briefly rose
|
|
to a height inside the room AABB, CellId promoted to `0xA9B40143`, and the
|
|
indoor-BSP branch fired. On landing, the body returned to floor level, fell
|
|
outside the AABB, and CellId reverted to the outdoor cell. Confirmed that
|
|
AABB containment works for the room cell when the player is mid-air, but
|
|
fails at floor level.
|
|
|
|
### `launch-cluster-a-cache-diag2.log`
|
|
|
|
First `[cell-cache]` probe run (Phase E first commit). Showed all cached
|
|
cells with their physics poly counts and local AABBs. Confirmed 14 physics
|
|
polys in cell `0xA9B40143` (the room), indicating BSP geometry is present
|
|
and complete. Identified cell `0xA9B40146` as a 4-poly threshold cell.
|
|
|
|
### `launch-cluster-a-cache-diag3.log`
|
|
|
|
Extended `[cell-cache]` probe run (Phase E second commit). Full data:
|
|
|
|
```
|
|
[cell-cache] id=0xA9B40143 physicsPolyCount=14 bspTotalLeafPolys=14
|
|
bspUnmatchedIds=0 aabbMin=(-11.60,-1.60,0.00) aabbMax=(-6.20,7.60,2.80)
|
|
bspOrigin=(0.00,0.00,0.00) bspRadius=9.97
|
|
```
|
|
Room cell: 2.80 m AABB height — works for mid-air player.
|
|
|
|
```
|
|
[cell-cache] id=0xA9B40146 physicsPolyCount=4
|
|
aabbMin=(-11.60,2.80,-0.20) aabbMax=(-10.00,7.60,0.00)
|
|
bspRadius=2.3
|
|
```
|
|
Threshold/doorway cell: 0.20 m AABB Z range (from -0.20 to 0.00). A standing
|
|
player at local Z=0.46 m is outside this AABB. **This is why AABB containment
|
|
fails for normal walking through doorways.**
|
|
|
|
Key conclusion: the geometry is correct and complete (14/14 polys match between
|
|
physics cache and BSP leaf count). The problem is purely in the cell-ownership
|
|
tracking mechanism, not the collision data itself.
|
|
|
|
---
|
|
|
|
## Diagnostic infrastructure remaining in place
|
|
|
|
Both probes stay committed and wired. They serve as scaffolding for the
|
|
portal-traversal follow-up phase:
|
|
|
|
- **`ACDREAM_PROBE_INDOOR_BSP=1`** / DebugPanel "Indoor BSP probe": logs one
|
|
`[indoor-bsp]` line each time `FindEnvCollisions` takes the indoor-cell
|
|
branch. After portal traversal is implemented, this probe should fire
|
|
consistently whenever the player is indoors.
|
|
|
|
- **`ACDREAM_PROBE_CELL_CACHE=1`** / DebugPanel "Cell cache probe": dumps all
|
|
cached EnvCell physics data (poly counts, BSP bounding sphere, AABB,
|
|
unmatched ID count). Useful for verifying that cell structs load correctly
|
|
and that portal connectivity data is present.
|
|
|
|
Both are gated behind `PhysicsDiagnostics` static class (existing pattern
|
|
from L.2a).
|
|
|
|
---
|
|
|
|
## Follow-up items for the portal-traversal phase
|
|
|
|
**1. Implement portal-based indoor cell tracking (issue #87).**
|
|
Replace `PhysicsDataCache.TryFindContainingCell` AABB containment with retail's
|
|
`CObjMaint::HandleObjectEnterCell` portal traversal. When the player crosses
|
|
a cell portal boundary, `CellId` propagates through `CEnvCell` portal
|
|
connectivity data. PDB symbols in `docs/research/named-retail/acclient_2013_pseudo_c.txt`
|
|
and struct definitions in `docs/research/named-retail/acclient.h` lines
|
|
31715-31726 (`CCellStructure` shape). The retail reference implementation
|
|
is the right oracle — do not guess at the traversal algorithm.
|
|
|
|
**2. Audit-trail note: add retail PDB symbol citations to `TryFindContainingCell`.**
|
|
The current implementation in `src/AcDream.Core/Physics/PhysicsDataCache.cs`
|
|
~line 261 is documented as a shortcut. The follow-up phase should add
|
|
the PDB symbol citation (e.g., `// retail: CObjMaint::HandleObjectEnterCell
|
|
// docs/research/named-retail/acclient_2013_pseudo_c.txt:XXXXX`)
|
|
per the Phase D code-review I1 note, so future readers know this is intentionally
|
|
replacing an interim implementation.
|
|
|
|
**3. Consider renaming `ResolveOutdoorCellId` → `ResolveCellId`.**
|
|
The method now handles both outdoor and indoor cell resolution. The rename
|
|
is low-risk (one call site in `PhysicsEngine.cs`) and would reduce the
|
|
cognitive overhead for the next phase's author. Noted as a Phase D code-review
|
|
M2 suggestion — do it in the same commit as the portal-traversal implementation
|
|
to keep the rename and the semantic change together.
|
|
|
|
---
|
|
|
|
## State at handoff
|
|
|
|
- **Branch:** `claude/competent-robinson-dec1f4`, 7 commits of implementation/test/diagnostic work.
|
|
- **Build state:** `dotnet build -c Debug` clean.
|
|
- **Tests:** 8 pre-existing failures unchanged (MotionInterpreter / BSPStepUp baseline). All new tests green.
|
|
- **Issues:** #86 CLOSED; #84 PARTIAL; #85 OPEN; #87 OPEN (new).
|
|
- **Diagnostic probes:** `[indoor-bsp]` + `[cell-cache]` active and wired.
|
|
- **Next:** portal-based indoor cell tracking (#87) or M2 critical path — Claude's choice per work-order autonomy.
|