docs(#180): camera-sweep strobe handoff - evidence, retail anchors, autonomous visual-loop recipe, companion prompt

This commit is contained in:
Erik 2026-07-06 16:29:57 +02:00
parent 6aabe0b5ac
commit 7272235a22

View file

@ -0,0 +1,156 @@
# 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.
```