docs: handoff + kickoff for Render Residual A (camera collision verbatim port)
Session wrap: cellar-lip wedge fixed + visual-verified (cc4590f/9fdf6a5/41db027). Next task per the plan = Render Residual A: keep the chase camera eye inside the player's cell by porting retail SmartBox::update_viewer verbatim (fixes interior walls going grey/transparent from inside). - New canonical handoff with copy-paste fresh-session kickoff prompt, the retail update_viewer decode, the V1 current-state map, the gap to pin (faithful start-cell + AdjustPosition fallbacks + the no-wall-hit cause), and the evidence-first plan ([flap-sweep] capture → deterministic SweepEye test → port). - Key finding recorded: find_valid_position (pc:273890) just calls find_transitional_position — the sweep function is faithful, NOT the divergence. - CLAUDE.md banner updated to point at the new state + handoff. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
41db027f34
commit
2c7948a9f1
2 changed files with 199 additions and 1 deletions
20
CLAUDE.md
20
CLAUDE.md
|
|
@ -763,7 +763,25 @@ H1 (PVS grounding) or H2 (`PortalSide` side-test) — both evidence-disproven.
|
||||||
**Currently working toward: M1.5 — Indoor world feels right** (resumed
|
**Currently working toward: M1.5 — Indoor world feels right** (resumed
|
||||||
from 2026-05-20 baseline after Phase O ship).
|
from 2026-05-20 baseline after Phase O ship).
|
||||||
|
|
||||||
**2026-06-03 — P1 membership DONE + P2 active (read this first).** The verbatim spatial-pipeline
|
**2026-06-05 — P2 cellar-lip wedge FIXED; next = Render Residual A (camera collision) (READ THIS FIRST).**
|
||||||
|
The "stuck on the last cellar step" wedge is FIXED + visual-verified (user: "Yes all works!" — cellar
|
||||||
|
ascent smooth, inn door still BLOCKS, generic step-up climbs). Root cause: `Transition.CheckOtherCells`
|
||||||
|
collided the other cells against a STALE pre-step-up `footCenter`; retail's `check_other_cells` reads the
|
||||||
|
LIVE `sphere_path.global_sphere` (pc:272735). Fix = re-read `footCenter = sp.GlobalSphere[0].Origin` in
|
||||||
|
`RunCheckOtherCellsAndAdvance` (commits `cc4590f`/`9fdf6a5`/`41db027`; 0/29→20/29 captured wedge frames
|
||||||
|
climb; zero regression; Core 1317p/4f/1s). This DISPROVED the prior "find_walkable never called" framing
|
||||||
|
(it was a probe-reading artifact — `find_walkable` IS called; the `[fc-dispatch]` cell logged the carried
|
||||||
|
cell, not the iterated one). The remaining 9/29 are a separate `(0,-1,0)` sliding-normal +Y-kill that did
|
||||||
|
NOT manifest in live play (buggy-trajectory artifact; documented as `DocumentsResidualWedge_*`, deferred —
|
||||||
|
slide territory). **NEXT per the plan = Render Residual A: camera collision** — verbatim port of retail
|
||||||
|
`SmartBox::update_viewer` (pc:92761) to keep the 3rd-person chase eye INSIDE the player's cell (fixes
|
||||||
|
interior walls going grey/transparent while inside). User-confirmed approach (verbatim port, no hybrids)
|
||||||
|
+ order (A→C→B; C = outside-looking-in `DrawPortal`, B = particles). **CANONICAL PICKUP:**
|
||||||
|
[`docs/research/2026-06-05-camera-collision-residual-a-handoff.md`](docs/research/2026-06-05-camera-collision-residual-a-handoff.md)
|
||||||
|
(+ cellar-lip writeup in the top banner of
|
||||||
|
[`docs/research/2026-06-04-p2-cellar-lip-flatfloor-cp-handoff.md`](docs/research/2026-06-04-p2-cellar-lip-flatfloor-cp-handoff.md)).
|
||||||
|
|
||||||
|
**2026-06-03 — P1 membership DONE + P2 active (history; cellar-lip now FIXED per the 2026-06-05 banner above).** The verbatim spatial-pipeline
|
||||||
port (master plan
|
port (master plan
|
||||||
[`docs/superpowers/specs/2026-06-03-verbatim-spatial-pipeline-port-master-plan.md`](docs/superpowers/specs/2026-06-03-verbatim-spatial-pipeline-port-master-plan.md))
|
[`docs/superpowers/specs/2026-06-03-verbatim-spatial-pipeline-port-master-plan.md`](docs/superpowers/specs/2026-06-03-verbatim-spatial-pipeline-port-master-plan.md))
|
||||||
is the active effort. **P1 (membership) = DONE** — proven to ALREADY match retail; the believed
|
is the active effort. **P1 (membership) = DONE** — proven to ALREADY match retail; the believed
|
||||||
|
|
|
||||||
180
docs/research/2026-06-05-camera-collision-residual-a-handoff.md
Normal file
180
docs/research/2026-06-05-camera-collision-residual-a-handoff.md
Normal file
|
|
@ -0,0 +1,180 @@
|
||||||
|
# Handoff — Render Residual A: camera collision (verbatim port of `SmartBox::update_viewer`) — 2026-06-05
|
||||||
|
|
||||||
|
## ▶ FRESH-SESSION KICKOFF PROMPT (copy-paste)
|
||||||
|
|
||||||
|
```
|
||||||
|
Continue acdream M1.5 render work: Render Residual A — CAMERA COLLISION (keep the 3rd-person camera
|
||||||
|
eye inside the player's cell so interior walls stop going grey/transparent from inside). This is a
|
||||||
|
VERBATIM port of retail SmartBox::update_viewer — no hybrids, no bandaids (master-plan mandate).
|
||||||
|
Branch claude/thirsty-goldberg-51bb9b (do NOT branch/worktree; do NOT push without asking; NEVER
|
||||||
|
git stash/gc). PowerShell on Windows; launch logs are UTF-16 (Select-String / rg --encoding utf-16le,
|
||||||
|
NOT GNU grep). Use superpowers:systematic-debugging; the user pre-approved the verbatim-port APPROACH
|
||||||
|
and the A→C→B order, so when you reach the design step use superpowers:brainstorming only to present
|
||||||
|
the concrete port design for sign-off before editing.
|
||||||
|
|
||||||
|
READ FIRST (in order):
|
||||||
|
1. docs/research/2026-06-05-camera-collision-residual-a-handoff.md (THIS file — canonical).
|
||||||
|
2. docs/research/2026-06-03-membership-and-bluehole-shipped-handoff.md (§3 residuals A/B/C; the
|
||||||
|
blue-hole DON'T-redo: never re-add a CurrCell write inside ResolveWithTransition/ResolveCellId).
|
||||||
|
3. docs/superpowers/specs/2026-06-03-verbatim-spatial-pipeline-port-master-plan.md (§C Camera: C1/C3).
|
||||||
|
4. memory/reference_render_pipeline_state.md + project_camera_visibility_coupling.md.
|
||||||
|
|
||||||
|
STATE: M1.5 "indoor world feels right." The cellar-lip step-up wedge is FIXED + visual-verified
|
||||||
|
(committed cc4590f/9fdf6a5/41db027 — check_other_cells now reads the LIVE sphere position). Per the
|
||||||
|
plan the next task is Render Residual A: camera collision. User-confirmed problem mapping: Residual A
|
||||||
|
= interior walls/seams go grey/transparent WHILE INSIDE (the chase eye drifts OUT of the player's
|
||||||
|
cell → near walls back-face/clip away); Residual C = outside-looking-in glass-box (separate, bigger
|
||||||
|
DrawPortal phase, do AFTER A); Residual B = particles (smallest, last).
|
||||||
|
|
||||||
|
GOAL: port retail SmartBox::update_viewer (0x453ce0, pc:92761) faithfully so [flap-cam] eyeInRoot=y
|
||||||
|
while inside and interior walls stay opaque. Retail behavior: pivot at player head → (indoor) pick the
|
||||||
|
PIVOT's cell via CPhysicsObj::AdjustPosition → SWEEP the 0.3 viewer_sphere pivot→sought-eye via a
|
||||||
|
CTransition, stop at first wall → viewer=curr_pos, viewer_cell=curr_cell → fallback AdjustPosition at
|
||||||
|
sought-eye → fallback snap-to-player.
|
||||||
|
|
||||||
|
KEY FINDINGS (do NOT re-derive):
|
||||||
|
- find_valid_position (pc:273890) is literally `return find_transitional_position(this)` (pc:273898).
|
||||||
|
So acdream's SweepEye→ResolveWithTransition→FindTransitionalPosition IS the faithful sweep. The
|
||||||
|
sweep FUNCTION is NOT the divergence — do not re-port it.
|
||||||
|
- The sweep + viewer_cell are ALREADY wired (V1): RetailChaseCamera.Update (damped eye → pivot →
|
||||||
|
CollisionProbe.SweepEye) + PhysicsCameraCollisionProbe.SweepEye (viewer sphere r=0.3, moverFlags
|
||||||
|
IsViewer|PathClipped|FreeRotate|PerfectClip, gated on CameraDiagnostics.CollideCamera).
|
||||||
|
- THE BUG (per handoff §3 + the [flap-sweep] probe comment): the sweep RUNS but finds NO wall
|
||||||
|
(pulledIn≈0, resolved=Y, bsp=ok) → the eye flies to full chase distance (eyeInRoot=n ~90%) in cells
|
||||||
|
like 0xA9B40174/0175. Root cause of the no-wall-hit is NOT yet pinned.
|
||||||
|
- GAPS per master-plan C1: (a) faithful START-CELL — retail uses AdjustPosition to find the PIVOT's
|
||||||
|
cell; acdream passes the player cellId straight in. (b) the two AdjustPosition FALLBACKS are missing.
|
||||||
|
(c) C3 find_visible_child_cell (pc:311397) is not ported (viewer cell uses the sweep curr_cell —
|
||||||
|
fine for now). Whether (a)/(b) actually cause the no-wall-hit is UNVERIFIED — pin it with evidence.
|
||||||
|
|
||||||
|
THE JOB (evidence-first; the saga lesson = do NOT guess):
|
||||||
|
1. Live capture: launch with ACDREAM_PROBE_FLAP=1 (+ CameraDiagnostics.CollideCamera on), stand inside
|
||||||
|
the Holtburg cottage, rotate the chase camera into a back wall. Capture [flap-sweep] (cell/resolved/
|
||||||
|
bsp/desiredBack/eyeBack/pulledIn/collNormValid) + [flap-cam] (root/eyeInRoot). Use the probe-comment
|
||||||
|
fork in PhysicsCameraCollisionProbe.cs to read WHY: pulledIn≈0 + bsp=ok ⇒ the sweep reaches no wall
|
||||||
|
geometry in the candidate set (clip/candidate-cell issue or wrong start cell); resolved=n/bsp=nobsp
|
||||||
|
⇒ collision can't run there (cell/BSP not loaded).
|
||||||
|
2. Diagnose the no-wall-hit from the capture (likely: the sweep's candidate-cell set doesn't include
|
||||||
|
the wall's cell, OR the start cell is wrong because AdjustPosition isn't seating the pivot). Confirm
|
||||||
|
against retail update_viewer before changing anything.
|
||||||
|
3. Port verbatim: the faithful start-cell (AdjustPosition for the pivot's cell, indoor branch) + the
|
||||||
|
two AdjustPosition fallbacks, plus whatever the capture proves is the no-wall-hit cause. Consider a
|
||||||
|
DETERMINISTIC SweepEye test (cell fixture + seed pivot/eye, assert the sweep stops at the wall) —
|
||||||
|
the CellarLipWedgeTests pattern made the stairs fix iterable in <200ms; do the same here.
|
||||||
|
4. VALIDATE: eyeInRoot=y inside; build + Core(1317p/4f/1s)/App green. VISUAL GATE: stand inside the
|
||||||
|
cottage + rotate — interior walls stay solid (no grey/transparent, no NPCs/particles through walls);
|
||||||
|
inside-looking-out still correct (don't regress the fixed flap); generic outdoor chase unaffected.
|
||||||
|
|
||||||
|
DO NOT: guess / speculative-edit (the saga's failure mode); re-add a CurrCell write inside
|
||||||
|
ResolveWithTransition/ResolveCellId (the blue-hole clobber — CurrCell is player-only via
|
||||||
|
UpdatePlayerCurrCell); conflate A (camera-eye containment, this task) with C (DrawPortal outside-
|
||||||
|
looking-in, next task); re-port find_valid_position/the sweep (it's faithful).
|
||||||
|
|
||||||
|
TEST BASELINE: Core 1317 pass / 4 fail (documented: Apparatus_Grounded_50cmOffCenter, 2×
|
||||||
|
DoorBugTrajectoryReplay LiveCompare_*, BSPStepUpTests.D4) / 1 skip. App green. Branch HEAD 41db027.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Session summary (2026-06-05)
|
||||||
|
|
||||||
|
**Shipped + visual-verified: the P2 cellar-lip step-up wedge.** Root cause = `Transition.CheckOtherCells`
|
||||||
|
collided the other cells against a STALE `footCenter` snapshotted before the primary collide; after a
|
||||||
|
step-up climbed the foot onto the cottage floor, the stale (pre-climb, penetrating) position spuriously
|
||||||
|
near-missed that floor → a doomed second step-up → revert → 0% advance. Fix: re-read
|
||||||
|
`footCenter = sp.GlobalSphere[0].Origin` in `RunCheckOtherCellsAndAdvance` (retail `check_other_cells`
|
||||||
|
reads the live `sphere_path.global_sphere`, pc:272735). 0/29 → 20/29 captured wedge frames climb; zero
|
||||||
|
regression. User visual-gate: **"Yes all works!"** (cellar smooth, door blocks, step-up climbs).
|
||||||
|
Commits `cc4590f` (fix + tests) / `9fdf6a5` (strip probes) / `41db027` (visual-gate note). Full writeup
|
||||||
|
+ the disproven prior framings: [`2026-06-04-p2-cellar-lip-flatfloor-cp-handoff.md`](2026-06-04-p2-cellar-lip-flatfloor-cp-handoff.md)
|
||||||
|
(top banner) + memory `project_p2_door_stepup_findings`.
|
||||||
|
|
||||||
|
**Then: picked + scoped the next task (this handoff).** Per the plan the next step after the collision
|
||||||
|
fix is Render Residual A — camera collision. Aligned with the user on the problem statement (the two
|
||||||
|
symptoms → residuals A/C) and the approach (verbatim port of `SmartBox::update_viewer`, A→C→B order).
|
||||||
|
Did the read-only investigation below; NO camera code changed (next session implements after the
|
||||||
|
evidence-first diagnosis).
|
||||||
|
|
||||||
|
## 2. The problem (user-confirmed)
|
||||||
|
|
||||||
|
| Symptom (user words) | Residual | Cause | Fix |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Inside a building, walls/seams flicker grey/transparent; can see through walls | **A** (this task) | 3rd-person chase eye drifts OUTSIDE the player's cell → near walls seen from their back-faces → culled | camera collision: sweep the eye, stop at the wall, keep it in the cell |
|
||||||
|
| Outside looking in through a doorway, building is a see-through glass box; ground over the floor | **C** (next) | outdoor→interior portal render (retail `DrawPortal`) not built | build that render phase |
|
||||||
|
| Particles bleed through floor | **B** (last) | scene particles not cell-clipped (#104) | cell-link the emitters |
|
||||||
|
|
||||||
|
Order **A → C → B**: A is smaller + builds the shared "which cell is the viewpoint in" machinery that C
|
||||||
|
also needs (shrinks C). Mechanism for "transparent wall" = **back-face culling** (a wall is a one-sided
|
||||||
|
sheet facing into the room; from outside the room you see its culled back) + the renderer drawing from
|
||||||
|
the **viewer's cell** then flooding portals (so the viewer's cell must be right).
|
||||||
|
|
||||||
|
## 3. Retail target — `SmartBox::update_viewer` (0x453ce0, pc:92761)
|
||||||
|
|
||||||
|
Decoded this session (read the decomp directly for the verbatim port):
|
||||||
|
1. If `player->cell == 0` → `reenter_visibility`; still 0 → `set_viewer(player_pos, 1)`, `viewer_cell=null`, return.
|
||||||
|
2. Compute the desired eye (`viewer_sought_position`) from the pivot (head + `pivot_offset`).
|
||||||
|
3. **Start cell:** if player indoor (`objcell_id >= 0x100`), `CPhysicsObj::AdjustPosition(&var_90, &viewer_sphere, &cell_1, 0, 1)` to find the PIVOT's cell; success → `cell = cell_1`, else `cell = player->cell`. Outdoor → `cell = player->cell`.
|
||||||
|
4. **Sweep:** `makeTransition` → `init_object(player, 0x5c)` → `init_sphere(1, &viewer_sphere, 1.0)` (ONE sphere) → `init_path(cell_1, pivot, sought_eye)` → `find_valid_position`.
|
||||||
|
- success → `set_viewer(curr_pos, 0)`, `viewer_cell = sphere_path.curr_cell`, return.
|
||||||
|
- **fallback 1:** `AdjustPosition(sought_eye, &viewer_sphere, &var_170, 0, 1)` → `set_viewer(var_120, 0)`, `viewer_cell = var_170`, return.
|
||||||
|
- **fallback 2:** `set_viewer(player_pos, 1)`, `viewer_cell = null`.
|
||||||
|
- `0x5c` = `IsViewer | PathClipped | FreeRotate | PerfectClip` (PathClipped = hard-stop at first contact).
|
||||||
|
- **`find_valid_position` (pc:273890) = `return find_transitional_position(this)` (pc:273898)** — the
|
||||||
|
sweep is the ordinary transition; acdream's `ResolveWithTransition` is faithful to it. **The sweep
|
||||||
|
function is NOT the divergence.**
|
||||||
|
|
||||||
|
## 4. acdream current state (V1, partial)
|
||||||
|
|
||||||
|
- `RetailChaseCamera.Update` ([src/AcDream.App/Rendering/RetailChaseCamera.cs:102](../../src/AcDream.App/Rendering/RetailChaseCamera.cs)):
|
||||||
|
damps `_dampedEye`; `pivotWorld = playerPos + (0,0,1.5)`; if `CameraDiagnostics.CollideCamera &&
|
||||||
|
CollisionProbe != null` → `swept = CollisionProbe.SweepEye(pivotWorld, _dampedEye, cellId, selfEntityId)`;
|
||||||
|
`publishedEye = swept.Eye`, `ViewerCellId = swept.ViewerCellId`. (Collides into a LOCAL, leaves
|
||||||
|
`_dampedEye` clean to avoid wall-press oscillation — keep that.)
|
||||||
|
- `PhysicsCameraCollisionProbe.SweepEye` ([src/AcDream.App/Rendering/PhysicsCameraCollisionProbe.cs:24](../../src/AcDream.App/Rendering/PhysicsCameraCollisionProbe.cs)):
|
||||||
|
shifts pivot/eye down by the radius (InitPath sphere-center convention), `ResolveWithTransition`
|
||||||
|
(viewer sphere r=0.3, height 0, isOnGround=false, body=null, moverFlags
|
||||||
|
`IsViewer|PathClipped|FreeRotate|PerfectClip`, `movingEntityId=selfEntityId`), returns swept eye +
|
||||||
|
`r.CellId`. **Passes the player `cellId` straight in — does NOT do retail's AdjustPosition pivot-cell;
|
||||||
|
has NO AdjustPosition fallbacks.**
|
||||||
|
- The `[flap-sweep]` probe (in SweepEye, gated `RenderingDiagnostics.ProbeFlapEnabled` = `ACDREAM_PROBE_FLAP`)
|
||||||
|
+ the builder's `[flap-cam]`/`[flap]`/`[shell]`/`[vis]` probes are the diagnosis apparatus — already
|
||||||
|
in the tree.
|
||||||
|
|
||||||
|
## 5. The gap to pin (next session, evidence-first)
|
||||||
|
|
||||||
|
The symptom is "sweep runs, finds no wall" (`pulledIn≈0`, `eyeInRoot=n ~90%`). Candidates, in order of
|
||||||
|
suspicion:
|
||||||
|
1. **Start cell** — acdream passes the player cell; retail seats the start cell at the PIVOT via
|
||||||
|
`AdjustPosition`. If the pivot/eye path's walls live in a cell that isn't the start cell and isn't
|
||||||
|
reached by the sweep's `check_other_cells` candidate set, the sweep misses them. (Most likely +
|
||||||
|
matches master-plan C1's "faithful start-cell" gap.)
|
||||||
|
2. **Candidate-cell tracking across the multi-step sweep** — the eye is ~2.6 m behind the player and the
|
||||||
|
sweep subdivides; if the carried cell doesn't advance into the wall's cell, the wall poly is never
|
||||||
|
in the per-cell BSP queried. (Related to the Stage-1 membership work; the player path now tracks the
|
||||||
|
carried cell correctly — verify the camera sweep does too.)
|
||||||
|
3. **AdjustPosition missing** — fallbacks aside, retail's start-cell AdjustPosition may be what seats
|
||||||
|
the sweep so it engages geometry; acdream has no AdjustPosition port at all (check
|
||||||
|
`CPhysicsObj::AdjustPosition`).
|
||||||
|
|
||||||
|
Pin with the live `[flap-sweep]` capture FIRST, then port. A deterministic `SweepEye` test (cottage
|
||||||
|
cell fixture, seed pivot inside + eye behind the back wall, assert the swept eye stops at the wall and
|
||||||
|
`ViewerCellId` stays the room) would make this iterable like the cellar-lip fix.
|
||||||
|
|
||||||
|
## 6. Apparatus + anchors
|
||||||
|
|
||||||
|
- **Probes:** `ACDREAM_PROBE_FLAP=1` → `[flap-sweep]` (PhysicsCameraCollisionProbe) + `[flap-cam]`/
|
||||||
|
`[flap]`/`[shell]`/`[vis]` (CellVisibility / the render builder). `CameraDiagnostics.CollideCamera`
|
||||||
|
toggles the spring-arm.
|
||||||
|
- **Decomp anchors:** `SmartBox::update_viewer` 0x453ce0 pc:92761 · `find_valid_position` pc:273890 →
|
||||||
|
`find_transitional_position` pc:273613 · `CPhysicsObj::AdjustPosition` (grep the decomp) ·
|
||||||
|
`CEnvCell::find_visible_child_cell` 0x52dc50 pc:311397 (C3, viewer child cell — not yet ported,
|
||||||
|
optional for A).
|
||||||
|
- **DON'T-redo:** the blue-hole fix (`UpdatePlayerCurrCell` player-only render-root write) — never
|
||||||
|
re-add a `CurrCell` write in `ResolveWithTransition`/`ResolveCellId`. Don't conflate A with C.
|
||||||
|
|
||||||
|
## 7. Brainstorming state (for the fresh session)
|
||||||
|
|
||||||
|
Approach + order are USER-APPROVED (verbatim port of `update_viewer`; A→C→B). The brainstorming design
|
||||||
|
step was NOT completed — resume by doing the evidence-first diagnosis (§5), then present the concrete
|
||||||
|
port design (start-cell + fallbacks + the no-wall-hit fix) for sign-off before editing (HARD-GATE:
|
||||||
|
no code until the design is approved).
|
||||||
Loading…
Add table
Add a link
Reference in a new issue