Merge branch 'claude/competent-robinson-dec1f4' — Indoor walking Phase 1 + Phase 2

Cluster A (Indoor walking Phase 1 — BSP cluster):
- WorldPicker cell-BSP occlusion → #86 closed
- CellId promotion via AABB containment (partial Phase D fix)
- Diagnostic infrastructure: [indoor-bsp], [cell-cache] probes

Indoor walking Phase 2 (Portal-based cell tracking):
- CellBSP + Portals wired into CellPhysics
- CellTransit static class: FindTransitCellsSphere + AddAllOutsideCells + FindCellList
- ResolveCellId rename + sphereRadius plumbing
- BuildingPhysics + CheckBuildingTransit (outdoor→indoor entry)
- Foot-sphere center fix (made portal tracking actually work in production)
- Indoor walkable-plane synthesis (closes the falling-stuck bug)

Closes ISSUES.md #84, #85, #86, #87.
Files new issues #88 (indoor object vibration) + #89 (port SphereIntersectsCellBsp).

Spec: docs/superpowers/specs/2026-05-19-indoor-portal-cell-tracking-design.md
Handoff: docs/research/2026-05-19-indoor-walking-phase2-shipped-handoff.md
This commit is contained in:
Erik 2026-05-19 19:34:13 +02:00
commit 1af49b710e
34 changed files with 6618 additions and 79 deletions

View file

@ -308,9 +308,10 @@ to the second floor without getting stuck.
---
## #84 — Blocked by air indoors
## #84 [DONE 2026-05-19] Blocked by air indoors
**Status:** OPEN
**Status:** DONE
**Closed:** 2026-05-19
**Severity:** HIGH (blocks indoor navigation)
**Filed:** 2026-05-19
**Component:** physics, collision
@ -337,57 +338,126 @@ visible cell mesh. Possibilities:
**Acceptance:** Walking through interior cell space hits collisions
only where visible walls/furniture exist.
**Resolution (2026-05-19 partial · `c19d6fb`):** Phase D of Cluster A
extended `ResolveOutdoorCellId` in `PhysicsEngine.cs` with an indoor
cell-containment scan: when the player's world position falls inside any
cached EnvCell's AABB, `CellId` is promoted to that indoor cell, which
enables the `FindEnvCollisions` indoor-BSP branch. This resolved the
"spawn in building and be stuck above the floor" variant of #84
player's CellId now promotes to the interior cell on spawn-in, the floor
is walkable, and the player can move freely. The "invisible air obstacle"
symptom for rooms the player walks INTO from outside was tracked under #87
and required portal-based cell tracking.
**Resolution (2026-05-19 full · `1969c55, aad6976, 069534a, 702b30a, 3ffe1e4, eb0f772`):**
Indoor walking Phase 2 replaced AABB containment with portal-graph cell traversal
(`CellTransit.FindCellList` + `CheckBuildingTransit`). CellId now promotes to indoor
cells via portals and remains promoted during normal walking through doorways. Indoor
cell-BSP collision fires consistently. Indoor walkable plane synthesized from floor
poly (`TryFindIndoorWalkablePlane`) so the resolver tracks walkability correctly when
the player is standing on an indoor floor. User visually verified at Holtburg cottage:
walls block from inside, multi-room navigation works, walking outdoors through a door
works. Issue fully closed.
---
## #85 — Pass through walls from outside→in
## #85 [DONE 2026-05-19 · 1969c55, aad6976, 069534a, 702b30a, 3ffe1e4, eb0f772] Pass through walls from outside→in
**Status:** OPEN
**Severity:** HIGH (gameplay-breaking)
**Status:** DONE
**Closed:** 2026-05-19
**Commits:** `1969c55, aad6976, 069534a, 702b30a, 3ffe1e4, eb0f772`
**Filed:** 2026-05-19
**Component:** physics, collision
**Description:** Approaching a building from the outside, the player
**Resolution (2026-05-19 · Indoor walking Phase 2):** The root cause (CellId never promoted
to the indoor cell during outdoor→indoor walking) was resolved by portal-graph cell
traversal in `CellTransit.CheckBuildingTransit`. Once `CellId` promotes to the indoor
cell, the indoor-BSP collision branch in `FindEnvCollisions` fires for approaches from
both inside and outside. User visually verified walls block from outside (player must
use the door portal to enter). See #87 and handoff:
[`docs/research/2026-05-19-indoor-walking-phase2-shipped-handoff.md`](2026-05-19-indoor-walking-phase2-shipped-handoff.md).
**Original description:** Approaching a building from the outside, the player
can walk THROUGH walls into the interior — one-directional wall
collision. From the inside trying to exit, the wall does block.
**Root cause / status:** Cell BSP polygons likely have one-sided
normals (front-facing only). Approach from the inside hits the front;
approach from the outside hits the back which BSP traversal treats as
"behind the plane" → no collision. Retail handles this via two-sided
collision polys or per-poly back-face handling.
**Files:**
- `src/AcDream.Core/Physics/BSPQuery.cs`
- `src/AcDream.Core/Physics/TransitionTypes.cs` (`FindObjCollisions` cell
branch).
**Acceptance:** Walking into an inn wall from outside collides; player
must enter via the door portal.
The root cause was pinned (Cluster A 2026-05-19) as the same failure as
#84's remaining symptom — `CellId` wasn't promoted to the indoor cell
during normal outdoor→indoor walking because AABB containment was too
tight for threshold/doorway cells. Without CellId in the indoor cell,
the indoor-BSP collision branch in `FindEnvCollisions` never fired
regardless of approach direction.
---
## #86 — Click selection penetrates walls
## #87 — [DONE 2026-05-19 · 1969c55, aad6976, 069534a, 702b30a, 3ffe1e4, eb0f772] Indoor cell tracking uses AABB containment instead of portal traversal
**Status:** DONE
**Closed:** 2026-05-19
**Commits:** `1969c55, aad6976, 069534a, 702b30a, 3ffe1e4, eb0f772`
**Filed:** 2026-05-19
**Component:** physics
**Resolution (2026-05-19 · Indoor walking Phase 2):** Portal-graph cell traversal
(`CellTransit.FindCellList` + `CheckBuildingTransit`) replaced the AABB containment
shortcut. Player CellId now correctly promotes to indoor cells via portals;
indoor cell-BSP collision branch fires consistently; walls block from inside.
Outdoor→indoor entry via `BuildingPhysics` + `BldPortalInfo` (`CheckBuildingTransit`)
wires the building-shell portal graph. Indoor walkable plane synthesized from the
cell's floor poly so the resolver tracks walkability during indoor movement (`TryFindIndoorWalkablePlane`).
See handoff: [`docs/research/2026-05-19-indoor-walking-phase2-shipped-handoff.md`](2026-05-19-indoor-walking-phase2-shipped-handoff.md).
**Original description:** `PhysicsDataCache.TryFindContainingCell` promotes the
player's `CellId` to an indoor EnvCell when their world position falls
inside any cached cell's local AABB. This is too tight to keep `CellId`
promoted to an indoor cell during normal walking. Threshold/doorway cells
(the polys that sit at a room boundary) have AABB Z ranges of only ~0.2 m;
a standing player at local Z=0.46 m is OUTSIDE the AABB and containment
fails. Because `CellId` drifts back to the outdoor cell, the indoor-BSP
collision branch in `TransitionTypes.FindEnvCollisions` is gated out for
most movement, so walls don't block from inside the house and the floor
physics is unreliable. The retail fix is portal-based cell traversal —
when the player crosses a cell portal boundary, the cell ownership
propagates through portal connectivity data in `CEnvCell`.
---
## #88 — Indoor static objects vibrate (bookshelves, open furnaces)
**Status:** OPEN
**Severity:** MEDIUM
**Severity:** MEDIUM (visual jitter; doesn't block gameplay)
**Filed:** 2026-05-19
**Component:** input, interaction
**Component:** rendering, animation
**Description:** Clicking through a wall from the outside selects NPCs
and objects inside the building. The `WorldPicker` raycast doesn't
intersect cell BSP geometry.
**Description:** Static objects inside cells (bookshelves, open furnaces, possibly other interior props) show per-frame transform jitter / vibration. Pre-existing (user noticed before Phase 2 shipped). Likely candidates:
**Root cause / status:** `WorldPicker.BuildRay + Pick` (introduced in
Phase B.4) tests against entity AABBs and scenery BSPs but probably
not cell BSP. Outdoor NPCs are pickable because their entity AABB is
the test target; indoor NPCs are pickable from outside because the
wall isn't in the ray's intersection set.
1. `EntityScriptActivator.OnCreate/OnRemove` firing repeatedly as the player's CellId promotes/demotes near cell boundaries (less likely after Phase 2's portal-based tracking — but worth investigating).
2. Per-part transforms for cell-static `WorldEntity` instances getting recomputed each frame with floating-point drift.
3. Particle-emitter offsets accumulating instead of resetting.
**Files to investigate:**
- `src/AcDream.App/Rendering/Vfx/EntityScriptActivator.cs` — OnCreate/OnRemove call patterns
- `src/AcDream.App/Rendering/GpuWorldState.cs` — entity transform updates per frame
- `src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs` — per-batch transform composition
**Acceptance:** Indoor static objects render stable (no per-frame jitter).
---
## #89 — Port BSPQuery.SphereIntersectsCellBsp for retail-faithful CheckBuildingTransit
**Status:** OPEN
**Severity:** LOW (Phase 2 ships with a documented approximation)
**Filed:** 2026-05-19
**Component:** physics
**Description:** Retail's `CEnvCell::check_building_transit` uses `CCellStruct::sphere_intersects_cell` — a radius-aware sphere-vs-BSP test that returns Inside/Crossing/Outside. Phase 2's `CellTransit.CheckBuildingTransit` uses `BSPQuery.PointInsideCellBsp` (radius-less, tests only the sphere CENTER). Practical effect: outdoor→indoor entry fires ~sphereRadius (~0.48m) deeper into the doorway than retail. The sphereRadius parameter is plumbed through but currently unused.
**Files:**
- `src/AcDream.App/Rendering/WorldPicker.cs` (or equivalent — check
Phase B.4b reference).
- `src/AcDream.Core/Physics/CellTransit.cs::CheckBuildingTransit` (line ~162)
- `src/AcDream.Core/Physics/BSPQuery.cs::PointInsideCellBsp` (line ~940) — existing point test to model the new sphere variant after
**Acceptance:** Clicking on a wall doesn't select NPCs behind it.
**Acceptance:** `CellTransit.CheckBuildingTransit` calls a new `BSPQuery.SphereIntersectsCellBsp(node, sphereCenter, sphereRadius)` that returns `Inside`/`Crossing`/`Outside`. Entry timing matches retail visually at the Holtburg cottage door.
---
@ -2918,6 +2988,23 @@ Unverified. The likely culprits, ranked by suspected probability:
# Recently closed
## #86 — [DONE 2026-05-19 · 3764867 + 4e308d5] Click selection penetrates walls
**Closed:** 2026-05-19
**Commits:** `3764867` — fix(picker): Cluster A #86 — cell-BSP ray occlusion in WorldPicker; `4e308d5` — test(picker): Cluster A #86 — screen-rect cell-occlusion tests
**Component:** input, interaction
**Resolution:** `WorldPicker.Pick` now accepts a `cellOccluder` callback
(`CellBspRayOccluder`). Before returning a hit, both `Pick` overloads
consult the occluder's `NearestWallT` value; any candidate entity whose
ray parameter exceeds the nearest-wall intersection is filtered out.
The occluder is wired from `GameWindow` using the loaded `PhysicsDataCache`
cell structs. Entities behind walls from the camera's perspective are no
longer selectable. Screen-rect occlusion tests verify the filter across
several hit/miss scenarios.
---
## #77 — [DONE 2026-05-18 · 3be7000] Auto-walk doesn't engage at walking range; pickup at walking range overshoots and snaps back
**Closed:** 2026-05-18