9.8 KiB
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 ~38607–38861): consecutive sweeps whose INPUT moved ~1.4 mm (the player's glide) flipped the OUTPUT 0.27 m along the boom —pulledIn0.27 ↔ 0.53, eye (78.500,−38.633,−3.845) ↔ (78.669,−38.815,−3.938) — re-flipping every ~5–10 frames. All 368,357 sweeps returnedok=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:
SmartBoxper-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 tothis->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 byPhysicsCameraCollisionProbe.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 freshentity.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_DEBUGmodes,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):
- Launch via
dotnet run(background) with probes; wait ~12 s. - Input:
keybd_eventwith real scan codes + KEYEVENTF_EXTENDEDKEY (GLFW decodes scancodes; vk-only silently does nothing). Arrows: Left 0x25/0x4B, Right 0x27/0x4D, Down 0x28/0x50.SetForegroundWindowfirst, verify withGetForegroundWindow. Hold-key = down, Sleep, up. - 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). - 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.
- Close gracefully via
CloseMainWindow()(ACE session clears in ~5 s).
- Launch via
- 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 branchclaude/vigorous-joliot-f0c3ad=6aabe0b5(lighting fixd8984e87+ isolation apparatus6aabe0b5). 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, memoryfeedback_bn_decomp_field_namesclass 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.