acdream/docs/research/2026-07-06-180-camera-sweep-strobe-handoff.md

156 lines
9.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Pickup handoff — #180 camera-sweep eye strobe (the #176 "stripes") — port retail's stateful sought-position
**Paste the companion prompt (bottom of this file / the session log) into a fresh
session.** Read this file, then `claude-memory/project_physics_collision_digest.md`
(top banner) + `claude-memory/project_render_pipeline_digest.md` (top banner), then
**ISSUES #180** (and #176's gate-2 note).
## TL;DR — what #180 is
The user's #176 residual — "geometrical patterns, triangles, that get weird
stripes… especially when I push the camera to the wall or the openings; if I zoom
out and the camera does not touch the walls I get no pattern" — is **NOT a render
defect. The camera-collision sweep is BISTABLE while the compressed chase boom
moves along a wall**, and the per-frame view hard-cuts tear-interleave on screen:
> `[flap-sweep]` (launch-176-cameye.log, frames ~3860738861): consecutive sweeps
> whose INPUT moved **~1.4 mm** (the player's glide) flipped the OUTPUT **0.27 m
> along the boom** — `pulledIn` 0.27 ↔ 0.53, eye (78.500,38.633,3.845) ↔
> (78.669,38.815,3.938) — re-flipping every ~510 frames. All 368,357 sweeps
> returned `ok=True` (this is NOT the fallback path — the sweep itself has two
> first-contact solutions at an r±ε graze on the corridor's double-faced slabs,
> the #137 threshold family, camera edition). At ~1700 fps with no vsync, each
> monitor refresh interleaves scanline slices of BOTH views → the fine
> stripe/hatch patterns (worst near seams where the 0.27 m parallax is largest),
> flickering with movement. Parked camera = static (all prior "camera stable"
> evidence was parked-camera; the strobe needs motion + a compressed boom).
This very plausibly also produced the ORIGINAL #176 seam flashes (one of the two
alternating eyes sees the purple under-room parallax, the other doesn't).
## Eliminated for the stripes (DO-NOT-RETRY — all isolation-killed)
| Ruled OUT | How |
|---|---|
| Texture / grout-line interaction | `ACDREAM_LIGHT_DEBUG=3` (raw vLit field, texture ignored) still shows the pattern |
| The per-cell shell clip trim (gl_ClipDistance) | `ACDREAM_CLIP_DEBUG=1` (shells drawn WHOLE, slots forced 0) — user: "Not gone, still there" |
| Coplanar dat geometry | 2 cm-epsilon sweep: corridor floor (z=6) clean; the only stack is the z=12 under-hall quad×4 — SAME-mesh, bit-identical depth ⇒ stable winner, cannot moiré |
| Shell double-instance / floor-coincident entity / portal seals | `[seam-*]` probe: n=1 per cell at the lifted z; portal entities at z=12.05; zero depth fans (sealed dungeon) |
| Light-set churn under movement | `[seam-blk]`: the floor's applied set held the SAME 8 identities through a 148k-frame session incl. ~12k distinct positions |
| The light pool tracking the camera | FIXED `d8984e87` (resident collection, player anchor) — verified: flood churned 8→41 across a full turn, set constant. Do NOT re-open; re-gate VISUALLY only after #180 |
| SweepEye itself misporting update_viewer | Read side-by-side: flags 0x5c, AdjustPosition seating, fallbacks all match 0x00453ce0. The bistability is in the transition's contact solution, not the port shape |
## The retail anchor (the fix direction — read BEFORE coding)
Retail's camera target is **STATEFUL**; ours re-demands the full boom every frame:
- `SmartBox` per-frame (pseudo-C line ~91842, addr 0x00452d75):
`CameraManager::UpdateCamera(this->camera_manager, &ret, &this->viewer)` — the
argument is the **CURRENT swept viewer**, and the RETURN is assigned to
`this->viewer_sought_position` (0x00452d84). So the next frame's sweep target
derives from the collided position — the target CONVERGES onto the wall
contact and re-extends gradually; the knife-edge full-length ray is not
re-rolled per frame. That is why retail's camera glides along walls instead of
strobing.
- `CameraManager::UpdateCamera` = **0x00456660** (pseudo-C line ~95505). Only the
head was read this session (offset-movement integration, pivot query); the
sought-position derivation in the tail is UNREAD — **read it fully, write the
pseudocode doc (workflow step 3), then port**.
- `SmartBox::update_viewer` = **0x00453ce0** (line ~92761) — already faithfully
ported by `PhysicsCameraCollisionProbe.SweepEye`; leave it alone.
- **No damping band-aid on the swept result without the UpdateCamera reading**
(the no-workaround rule; the user has already reverted redesigns that replaced
retail mechanisms).
Port site: `src/AcDream.App/Rendering/RetailChaseCamera.cs` (the desired-eye /
boom derivation) — it must consume the previous swept viewer per retail.
## Also open from this arc
- **Site-A weenie light-registration LEAK**: `[seam-ent]` showed one portal cell's
I100 light ×2 → ×4 within a session — re-CreateObject re-registers the light
under a fresh `entity.Id` (GameWindow ~3679: `Id = _liveEntityIdCounter++`)
without tearing down the previous registration. The wash intensifies over
time. Fix with #180's gate or fold into A7 (noted in ISSUES #176).
- **#176 re-gate** happens only after #180 lands: camera pressed into
walls/openings while moving → no patterns; corridor seam turn → steady purple
wedge (the faceted SHAPE is cdb-proven retail — only instability is a defect).
- After BOTH pass: strip the throwaway probes in one cleanup commit —
`[seam-*]` (ACDREAM_PROBE_SEAMDRAW), `ACDREAM_LIGHT_DEBUG` modes,
`ACDREAM_CLIP_DEBUG`. Keep `[flap-sweep]` (it rides ACDREAM_PROBE_FLAP).
## Apparatus (reuse, don't rebuild)
- `ACDREAM_PROBE_FLAP=1``[flap-sweep]` per-sweep line: `desiredBack/eyeBack/
pulledIn/in=/out=` — THE #180 instrument (plus [flap]/[flap-cam]).
- `ACDREAM_PROBE_PVINPUT=1` → per-frame `[pv-input] eye=` at 6 dp.
- **The autonomous visual loop** (no user needed, works where RenderDoc can't —
RenderDoc doesn't support GL_ARB_bindless_texture and the modern gate throws):
1. Launch via `dotnet run` (background) with probes; wait ~12 s.
2. Input: `keybd_event` **with real scan codes + KEYEVENTF_EXTENDEDKEY**
(GLFW decodes scancodes; vk-only silently does nothing). Arrows: Left
0x25/0x4B, Right 0x27/0x4D, Down 0x28/0x50. `SetForegroundWindow` first,
verify with `GetForegroundWindow`. Hold-key = down, Sleep, up.
3. Capture: `GetWindowRect` + `Graphics.CopyFromScreen` → PNG in the
scratchpad → **the Read tool renders it** (take 3 captures ~150 ms apart —
frame-pair diffs expose per-frame strobes).
4. Repro recipe: back the character into a wall (hold Down ~2.5 s) — the boom
compresses; the strobe fires DURING/just after motion near the wall.
5. Close gracefully via `CloseMainWindow()` (ACE session clears in ~5 s).
- Launch logs on disk (worktree root): `launch-176-cameye.log` (THE strobe
capture), `launch-176-user-gate.log` (148k-frame user session incl. the light
leak), `launch-176-seamdraw.log`, `launch-176-fix-verify.log`.
- `ACDREAM_LIGHT_DEBUG=1/2/3` + `ACDREAM_CLIP_DEBUG=1` — shader/clip isolation
toggles (throwaway; strip at cleanup).
## State
- `main` = worktree branch `claude/vigorous-joliot-f0c3ad` = **`6aabe0b5`**
(lighting fix `d8984e87` + isolation apparatus `6aabe0b5`). Suites green
(Core 2599+2skip / App 726+2skip / UI 425 / Net 385).
- Register: AP-85 rewritten (resident+player-anchored pool; residual = single
128 pool vs retail 7-dyn/40-static). The a7 pseudocode doc carries a
correction banner (`visible_cell_table` = RESIDENT registry — the
identifier-name-≠-semantics lesson, memory
`feedback_bn_decomp_field_names` class 4).
- A client may be running — the user manages its lifecycle; ASK before
rebuilding if the App DLL is locked (MSB3027 = lock, not code).
- The test char spawns in the Facility Hub corridor (cell `0x8A020164`).
## Companion prompt (paste into the fresh session)
```
Fix #180 — the camera-collision sweep strobes the eye 0.27m per few frames when
the compressed chase boom moves along walls; at ~1700fps the tear-interleaved
views ARE the #176 "stripes/triangles" the gates keep failing on. The mechanism
is probe-pinned; every render suspect is isolation-killed. Do NOT re-investigate
the render, the light pool (fixed d8984e87, data-verified), or SweepEye's port
(faithful, exonerated).
READ FIRST, in order:
1. docs/research/2026-07-06-180-camera-sweep-strobe-handoff.md (evidence,
DO-NOT-RETRY table, retail anchors, the autonomous visual-loop recipe)
2. claude-memory/project_physics_collision_digest.md (top banner)
3. docs/ISSUES.md #180 (+ #176's gate-2 note)
THE FIX = port retail's STATEFUL camera target: SmartBox (0x00452d75) feeds the
CURRENT swept viewer into CameraManager::UpdateCamera (0x00456660) and assigns
the RETURN to viewer_sought_position (0x00452d84) — the target converges onto
the wall contact instead of re-rolling the full knife-edge boom ray per frame
like RetailChaseCamera does. Workflow: read UpdateCamera's tail (the sought
derivation — UNREAD so far, pseudo-C line ~95505), write the pseudocode doc,
port into RetailChaseCamera. NO damping band-aids on the swept result.
Verify autonomously with the handoff's visual loop (keybd_event scan-codes +
GDI window capture + [flap-sweep]): back into a wall, confirm consecutive-frame
eye deltas stay continuous and no stripe patterns in captures. Then the user
gate covers #180 AND the #176 re-gate (steady purple wedge at the seam — the
wedge SHAPE is retail, cdb-proven). After both pass: fix the site-A light-
registration leak (one portal light stacked ×4/session — re-CreateObject
re-registers under fresh entity ids) and strip the throwaway probes
([seam-*], ACDREAM_LIGHT_DEBUG, ACDREAM_CLIP_DEBUG) in a cleanup commit.
State: main @ 6aabe0b5, suites green (Core 2599+2skip). Test char spawns in the
Facility Hub corridor 0x8A020164. A client may be running — the user manages
its lifecycle; ASK before rebuilding if the App DLL is locked.
```