acdream/docs/research/2026-07-08-186-connector-grey-flap-handoff.md
Erik 9d35a9786f docs(#186): handoff — connector grey flap narrowed to the doorway-flap flood/pick family
Retail SEAMLESS at the same spot (user-confirmed) => real acdream bug: the eye seats
in a sparse 5-poly connector cell 0xF6820118 looking back at the player's room 0116,
and acdream drops 0116 (back-portal side-culled) so the doorway aperture shows the fog
clear color = grey; retail keeps 0116 drawn. RULED OUT: null-root/AD-20/AD-21 (root
valid, eyeInRoot=Y); the color-clear gating (retail's gated DrawCells Clear is
depth/stencil, post-LScape::draw). Next step = retail cdb trace (viewer_cell +
cell_draw_list at the grey pose) to pin viewer-cell PICK vs portal FLOOD, then a careful
frozen-render fix. Full handoff + apparatus + DO-NOT-RETRY + code/decomp sites in the doc.
Keeps the offline cell-geometry inspection test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 14:04:23 +02:00

11 KiB
Raw Blame History

Handoff: #186 — indoor GREY flap at a top-floor connecting room (new house type)

Date: 2026-07-08 · Status: ROOT CAUSE NARROWED (report-only) — fix NOT started. It lives in the FROZEN portal-flood / viewer-cell render code (the doorway-FLAP family that cost weeks), so it needs a retail cdb trace to pin the last fork BEFORE any code, then a careful change. Milestone: M1.5.

Read these first, in order:

  1. This handoff.
  2. claude-memory/project_render_pipeline_digest.md — the render/doorway-FLAP SSOT + DO-NOT-RETRY table ("root at VIEWER cell"; the deleted EyeInsidePortalOpening rescue; PortalSideEpsilon root-lag).
  3. docs/architecture/retail-divergence-register.md rows AD-17 (≤8 clip-plane / union-AABB scissor), AD-20 (camera-sweep fallback seeds eye cell from PLAYER cell), AD-21 (null-clipRoot outdoor fallback).
  4. docs/ISSUES.md #186.

0. Symptom (user, live)

Top floor of a house type not tried before; a connecting opening/room between two parts of the house. Passing through → a brief GREY flap (the framebuffer clear color). Stopping at the precise spot → the WHOLE screen is grey and stays; turning the camera clears it, turning back → grey again (camera-DIRECTION dependent, player stationary). A jump is NOT involved (that's #185). Retail is SEAMLESS at the same spot (user-confirmed) → this is a real acdream bug, not inherent.

1. The cells + the grey pose (ACDREAM_PROBE_FLAP capture)

Landblock 0xF682. Three cells, all envId 0x0365, cell-struct origin (132,84,62):

cell render polys physics polys portals role
0xF6820116 15 12 0117,0118,0113 player's room (closed)
0xF6820117 10 6 0118,0116,0115,0119 next room (closed)
0xF6820118 5 3 0117,0116 thin connector (root when grey)

Grey-pose facts (from [flap]/[flap-cam]/[flap-sweep], 88,249 frames; the grey frames are vis=1 root=0xF6820118, ~6,725 frames):

  • [flap-cam]: root=viewerCell=0xF6820118 playerCell=0xF6820116 eyeInRoot=Y playerInRoot=n terrain=Skip outVisible=False eye=(134.81,83.59,69.07) player=(137.30,83.32,66.82).
  • [flap] grey line: root=0118 | p0->0x0117 D=0.14 TRV proj=4 clip=0 | p1->0x0116 D=0.18 CULL proj=5 clip=4 || outPolys=0 vis=1. (Dominant D to 0117 ≈ 0.14; ALWAYS positive 0.000.50 → eye is genuinely INSIDE 0118, never across the plane.)
  • [flap-sweep]: ok=True resolved=Y pulledIn=-0.00 (the chase boom reaches FULL distance, NO collision pull-in, seating the eye in the connector).

Read of the geometry: the eye sits in the sparse connector 0118, in the doorway threshold, looking BACK toward the player's room 0116. p1->0116 is ON-screen (proj=5 clip=4) but side-CULLED (eye ~0.180.31 on 0116's side of the p1 plane — a back-portal). p0->0117 is genuinely OFF-screen (clip=0 with proj≥3 = off-screen per the builder's own probe doc, PortalVisibilityBuilder.cs:818-821). So no neighbour floods, only the 5-poly connector draws, and its shell doesn't cover the forward view → the framebuffer clear color (fog, GameWindow.cs:8968) shows through = GREY. Turning re-aims off the back-portal → a room floods → picture.

2. What is CONFIRMED / RULED OUT (workflow wf_362bbeda-933, 4 tracers + synthesis, + my own re-checks)

  • NOT null viewer-root / AD-21 — the render root is a valid indoor cell on 88,248/88,249 frames (one lone [outdoor-node] root=OUT). (This refutes THE FIRST hypothesis in this investigation.)
  • NOT viewer-cell root-lag / AD-20eyeInRoot=Y at every grey frame: the eye is geometrically INSIDE 0118's BSP. The root is the fresh eye-derived swept curr_cell (PhysicsCameraCollisionProbe.cs:92), NOT a stale player-seeded fallback. (Refutes the workflow synthesis's own leading "31 cm gap" theory — the eye is NOT across the boundary; I checked the eyeInRoot flag directly.)
  • NOT the flood-admission being unfaithful — the empty-clip cull (PortalVisibilityBuilder.cs:320-324) and the back-portal side-CULL (:292-298, CameraOnInteriorSide :857-863) are faithful ports: retail PView::ClipPortals gates neighbour processing on if (ecx_8 != 0) (pc 005a5660) and AddViewToPortals on view_count != 0 (pc 005a5357). #177 already proved acdream's flood is retail-identical.
  • NOT the color clear (the tempting "3-line fix" — DEAD END, do not revisit). Retail's DrawCells (0x005a4840, decomp line 432709) has its only Clear INSIDE if (outside_view.view_count > 0) (line 432715), right AFTER LScape::draw (the landscape draw, 432719). The Clear is Clear(4, &RGBAColor_Black, 1.0f) (0x820fc0 = RGBAColor_Black) — but clearing COLOR after drawing the landscape would erase it, so that 4 flag is depth/stencil, not color. So retail's color clear is NOT gated here, and matching "don't color-clear a sealed interior" would be WRONG. (This is why I did the cdb prep — it killed a wrong fix before shipping.)
  • It IS a real acdream bug (retail seamless) in the doorway-FLAP flood/pick family: acdream drops the room the camera looks back at (0116) from the connector root; retail keeps it drawn.

3. THE ONE OPEN FORK → the retail cdb trace (do this FIRST in the new session)

Retail is seamless, so retail either (A) roots at the player's room 0116, not the connector (a viewer-cell PICK divergence — acdream over-switches to 0118), or (B) roots at 0118 but its flood still admits 0116 (a FLOOD divergence — retail keeps the back-portal / uses an on-screen sliver acdream drops). Static decomp cannot decide; observe retail.

Toolchain (CLAUDE.md "Retail debugger toolchain"; binary CONFIRMED pairs with refs/acclient.pdb via py tools/pdb-extract/check_exe_pdb.py "C:/Turbine/Asheron's Call/acclient.exe" → MATCH).

  • User: launch retail, connect to local ACE, walk to the SAME connecting room, and HOLD the grey pose (so most captured frames are it).
  • bp acclient!SmartBox::update_viewer (0x00453ce0) → dump viewer_cell (a CObjCell*, acclient.h:35194) and its cell id. AND/OR bp acclient!PView::DrawCells (0x005a4840) → dump cell_draw_num + cell_draw_list.data[] ids (acclient.h:45939-45940) — the drawn cell set.
  • Decide: retail viewer_cell == 0116PICK fix (acdream's viewer-cell pick/hysteresis over-retains the connector — CellTransit.cs:862-916 BuildCellSetAndPickContaining, or the camera boom coasting the eye into the connector at pulledIn=-0.00 where retail's PathClipped viewer_sphere sweep hard-stops in 0116). retail viewer_cell == 0118 but cell_draw_list0116FLOOD fix (retail admits the back-portal from the connector; acdream's side-CULL / the deleted EyeInsidePortalOpening rescue is the site).
  • cdb watchouts (CLAUDE.md): qd ONLY at a top-level threshold, never inside a conditional bp action (strands cdb → kills retail); dt acclient!Class @ecx field for reads (@@c++ is broken here); .sympath <local> (no srv*, the ; splits); spaced .if () lets qd detach.

4. Apparatus (re-create in the new session — temp files won't persist)

  • Flap capture: launch acdream with ACDREAM_PROBE_FLAP=1 (canonical live cmd, CLAUDE.md). Emits per frame: [flap] (root + per-portal proj/clip/side, PortalVisibilityBuilder.cs:790), [flap-cam] (root/viewerCell/playerCell/eyeInRoot/playerInRoot/terrain/outVisible, GameWindow.cs:11142-11152), [flap-sweep] (ok/resolved/pulledIn/viewerCell, PhysicsCameraCollisionProbe.cs:74-87), [outdoor-node] (root=IN/OUT). ⚠️ heavy (per-frame); output goes to the raw background-task .output, not the Tee'd log. Have the user HOLD the grey pose + turn to clear + turn back a couple times.
  • Offline cell geometry dump: tests/AcDream.Core.Tests/Physics/Issue186ConnectorCellGeometryInspectionTests.cs (Dump_ConnectorCells_ShellAndCollision) loads 0116/0117/0118 from the dat and prints render/physics poly counts + normal-axis buckets. No live launch needed. (That's how the §1 table was produced.)
  • Decomp workflow: wf_362bbeda-933 (this session) — the 4-tracer + synthesis analysis; results in its journal under the session's subagents/workflows/ dir.

5. DO-NOT-RETRY (each already refuted or a known trap)

  • Do NOT re-open the null-root / AD-20 / AD-21 angle — root is valid, eyeInRoot=Y.
  • Do NOT implement the color-clear gating — retail's gated Clear is depth/stencil (post-LScape::draw); matching it would add non-retail stale-content.
  • Do NOT re-add the deleted EyeInsidePortalOpening rescue (PortalVisibilityBuilder.cs:314-319) blindly — it caused the 0171↔0173 flood-cycle churn (the ORIGINAL doorway flap).
  • Do NOT widen PortalSideEpsilon (:38-56) — a documented root-lag band, NOT retail (F_EPSILON=0.0002); BR-4 refuted tightening/widening without eye-exact tracking.
  • Do NOT "fix" the flood by propagating an empty/edge-on portal — the empty-clip cull is retail-faithful.
  • The sparse connector geometry (0118 = 5 render / 3 physics polys) is FAITHFUL to the dat (retail draws the same). It is context, not the bug — the bug is that 0116 isn't drawn, not that 0118 is thin.

6. Key code sites

  • Frame-top clear (unconditional color+depth+stencil to fog): GameWindow.cs:8968-8983. NOT the fix.
  • Viewer-cell resolution: GameWindow.cs:9137-9148 (viewerCellId = _retailChaseCamera.ViewerCellId).
  • Camera boom sweep: PhysicsCameraCollisionProbe.cs (SweepEye, PathClipped, pulledIn, r.CellId=curr_cell).
  • Viewer-cell PICK: CellTransit.cs:862-916 (BuildCellSetAndPickContaining, center-based point_in_cell, current-cell-first hysteresis, port of find_cell_list pseudo_c:308788-308825).
  • Portal FLOOD: PortalVisibilityBuilder.cs (Build; side-test CameraOnInteriorSide :857-863; empty-clip cull :320-324; PortalSideEpsilon :38-56; the off-screen-vs-sliver note :818-821).

7. Retail decomp anchors

  • PView::DrawCells 0x005a4840 (decomp line 432709): Clear gated on outside_view.view_count>0 (432715), Clear at 432732 (depth/stencil, post-LScape::draw); cells drawn unconditionally after (432815+).
  • SmartBox::update_viewer 0x00453ce0: viewer_cell = sphere_path.curr_cell (the render root = swept cell).
  • PView::ClipPortals empty-clip gate if(ecx_8!=0) pc 005a5660; AddViewToPortals view_count!=0 pc 005a5357.
  • PView::InitCell side test decomp:432962. RGBAColor_Black @ 0x820fc0.

8. Acceptance

  • No grey flap passing through OR stopping in the connecting room at any camera angle; matches retail's seamless.
  • Regression: the outside→inside doorway + other multi-room houses + the #177/#181 render-flap suites unchanged.

9. Context: this session also FIXED #185 (unrelated, DONE + gated)

The outdoor-stairs "invisible wall" (07c5b832) — a shadow part-id uint32 overflow dropping stair collision. Unrelated to #186 (that was collision/registration; #186 is render/visibility). See docs/superpowers/specs/2026-07-08-185-outdoor-stairs-fix-design.md.