Commit graph

423 commits

Author SHA1 Message Date
Erik
1438d73a43 docs(physics): handoff reframe — membership is STATE not recomputation (user analysis)
User's own decomp dig (verified): the flap's deepest root is architectural, not the
find_cell_list pick ordering. Retail membership is persistent object STATE (curr_cell
mutated ONLY by change_cell at a portal crossing); acdream RE-DERIVES CellId from
FindCellSet geometry every tick → ping-pong. Plus multi-valued CELLARRAY (retail) vs
single CellId (acdream), uniform vs forked collision (0x0100), intrinsic vs bridge
building entry. Reframed the handoff + prompt: the pick-ordering port (§4.3) is
SUPERSEDED/symptomatic; the job is STAGE 1 = persistent + multi-valued + portal-
crossing membership (change_cell 281192, find_transit_cells, SetPositionInternal),
drop the 5ca2f44 pre-check; STAGE 2 = uniform collision + intrinsic entry. New §4.4
(the 4-point analysis) + §4.5 (staged fix).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 08:20:55 +02:00
Erik
298b3b92b8 docs(physics): handoff — verbatim find_cell_list port (the R1 membership flap fix)
Canonical pickup for a fresh session. R1 (per-cell DrawInside render) shipped + is
correct (cellar seals); it exposed a pre-existing cell-membership ping-pong (the
flap). Root cause: CellTransit.BuildCellSetAndPickContaining picks from an UNORDERED
HashSet, dropping retail find_cell_list's current-cell-first ordering (CELLARRAY
index-0 + interior-wins-break, pc:308742-308825). Next job: verbatim port of that
ordered pick, replacing the HashSet + the 5ca2f44 pre-check approximation. User
authorized breaking any physics to get membership faithful. Full diagnosis, verbatim
retail source, fix plan, KEEP/don't-redo, test baseline, and a copy-paste pickup
prompt in the doc.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 22:24:22 +02:00
Erik
ce7404b92b docs(render): R1 implementation plan — per-cell DrawInside (TDD)
Bite-sized plan for R1 (the per-cell DrawInside core): InteriorEntityPartition
(3-bucket, TDD), InteriorRenderer per-cell loop, the binary render decision in
OnRender (indoor = DrawInside only), and the :1756 bypass repurpose. Particles
deferred to R1b. Grounded in the live code surface (exact file:line + signatures).
Ends at the R1 user visual gate (sealed Holtburg cottage, no bleed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 19:45:50 +02:00
Erik
7aca79f8eb docs(render): Phase R0 — lock the render-redesign design spec (brainstorm outcome)
Resolves the plan §3 open questions with the user this session:
- object/entity/particle draw = LITERAL PER-CELL LOOP (retail DrawCells),
  not a global MDI batch with per-instance clip. Fidelity > perf > blast-radius.
- sequencing = HOLISTIC: build the per-cell DrawInside directly; no intermediate
  global-pass gate-fix. First visual gate = sealed cottage interior, no bleed.
- terrain in the seal = FAITHFUL: drawn only through the exit-portal clip, never
  as a floor under the interior. Inventory's 'relax Skip' suggestion REJECTED as a
  non-retail workaround; grey-floor = a sealing bug (verify cell mesh in R1).
- WB mesh pipeline KEPT (per-cell draws from the global buffers, batched within a
  cell); two-camera invariant preserved (eye projects, player cell roots visibility).

Phases (holistic): R1 unified per-cell DrawInside (the core) -> R2 outside-looking-in
(DrawPortal) -> R3 dungeons -> R4 polish+cleanup. Each ends GREEN + a user visual gate.
Retail anchors cited throughout (RenderNormalMode 0x453aa0, DrawCells 0x5a4840, etc).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 19:18:59 +02:00
Erik
21bf97ed35 docs(render): REOPEN the render half — full retail-faithful redesign dossier (handoff + huge plan + 3 research docs)
The Phase W indoor seal did NOT land. The 2026-06-02 visual gate proved the interior render is fundamentally broken (#78: transparent walls, outdoor terrain + scenery entities bleeding in, grey floors, no outside-looking-in). Stage 4 (sky-through-door clip) was real but a top layer on a base that never sealed.

DECISIVE EVIDENCE (committed in the handoff): the PVS computes correctly AND the cell shells render correctly (opaque, textured, complete — the [shell] probe shows zero NOSNAP / zero missing-texture). The failure is the SEAL + three inconsistent gates — concretely the WbDrawDispatcher.cs:1756 ParentCellId==null -> return true bypass draws outdoor scenery indoors, and the indoor path draws the outdoor world then gates it instead of running ONLY DrawInside. Retail, when inside, runs ONE PView flood: visibility IS the cull; the landscape enters only through clipped exit portals + a conditional depth-only clear.

Dossier (per the user's mandate: NO shortcuts/bandaids, port from retail, redesign the whole pipeline if needed, brainstorm first):
- Master handoff (root cause + retail target + reusable-vs-redesign + apparatus + do-not-repeat + copy-paste pickup prompt).
- Huge staged redesign plan R0(brainstorm)->R1(one visibility authority, kill the bleed)->R2(indoor=DrawInside-only)->R3(the seal, DrawCells port)->R4(per-cell object/particle clip)->R5(outside-looking-in)->R6(dungeons)->R7(polish/conformance). Each ends at a user visual gate.
- 3 research docs: full retail render pipeline reference (705 lines, decomp-verified), acdream pipeline inventory + failure map, reference cross-check (WB two-pipe is the wrong model).

#78 promoted to the redesign. The 5 remaining Core test failures are pre-existing physics/collision bugs, none render-related.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 18:28:01 +02:00
Erik
21609a7cd7 test(physics): Phase W triage — fix stale GetMaxSpeed tests; file #104 (particle cell-clip deferral)
GetMaxSpeed deliberately does NOT branch on ForwardCommand — it returns RunAnimSpeed x run-rate as the InterpolationManager.AdjustOffset catch-up speed (doc comment + ACE MotionInterp.cs:670-678, retail-verified; the slow catch-up fixed the 1-Hz remote-blip). The 3 failing tests (WalkForward/WalkBackward/Idle) asserted a REMOVED command-branching design. Consolidated into one [Theory] pinning the no-branch contract across commands.

Also files #104 (LOW): scene VFX particles not clipped to the PView visible cell set — deferred out of the Phase W seal (entity bleed already gated by Stage 5; scene particles depth-tested; sky particles scissored). Needs OwnerCellId plumbing (~6-8 files).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 16:37:49 +02:00
Erik
55e1b30553 docs(render): Phase W session-2 handoff — membership FIXED + render rewrite (Stage 3 done)
Canonical pickup for the next session. Membership root cause (static :1947 re-derive)
FIXED the retail way (find_cell_list interior-wins pick + swept determination, 59f3a13)
and offline-verified (doorway strobe -> one clean transition). T0 made the suite
deterministic (12 known failures, none Phase-W regressions). Stage 3 (render-root
unification) DONE (6a1fbbd->573c555). Remaining: Stage 4 (the seal: sky/landscape inside
the portal-clip bracket + conditional doorway Z-clear = no blue-hole), Stage 5 (entity/
particle clip), green-tests triage, then the single final visual verification. Render is
wire-and-fill-gaps (PView infra exists). Flags a stash discrepancy (1 of 2 stashes missing
from the shared refs/stash) for the user to check against other worktrees.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 15:39:41 +02:00
Erik
a06226f9a2 docs(render): Phase W render-rewrite plan (Stages 3-5) — grounded, per-step
Per-step subagent-driven plan for the render half: T0 test-hygiene baseline,
Stage 3 render-root unification (root at CellGraph.CurrCell + seen_outside, drop
the FindCameraCell grace-frame fallback), Stage 4 PView seal (sky/landscape inside
the portal-clip bracket + conditional doorway Z-clear = no blue-hole; EnvCellRenderer
GL_BLEND verify), Stage 5 entity/particle cell-clip. Key reframe from grounding the
plan in the actual code: the PView infra (PortalVisibilityBuilder BFS + OutsideView,
ClipFrame, EnvCellRenderer GL_BLEND fix, WbDrawDispatcher cell gate) ALREADY EXISTS and
the A8 stencil split is already gone — so the render half is wire-and-fill-gaps, not a
from-scratch port. Execution policy: no intermediate user gates, single final visual
verification, full suite green at verification.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 15:13:35 +02:00
Erik
ed00719cf4 docs(render): Phase W §1a — Stage-1 gate finding (deeper root: FindEnvCollisions:1947)
Stage 1 (return swept sp.CurCellId, 3e1d502) was gated and the doorway strobe
PERSISTS: [cell-transit] still flips 0170<->0031. Airtight root from code analysis:
Transition.FindEnvCollisions re-derives the cell from the STATIC origin via
engine.ResolveCellId at TransitionTypes.cs:1947 and clobbers sp.CheckCellId (:1949)
at the start of every sweep pass — a second, earlier static re-derive the four
studies missed (they targeted the late return-site). It is the sole path that can
set an indoor swept cell outdoor (the containment pick at :2075 skips outdoor cells).
:1947 is dual-purpose (jitter source AND the only indoor->outdoor exit), so Stage 2
must replace it with a directed exit-portal crossing + do_not_load prune + exitOutside
re-gate — a careful #98-area rework, not a one-line delete. Render residuals at the
gate (no interior outside-looking-in, blue-through-door, particle/NPC bleed) are all
expected Stages 3-5, not Stage-1 regressions. Stage 1 is kept (correct + necessary).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 14:26:00 +02:00
Erik
50b168bc1e docs(render): Phase W chunk-1 plan — transition-owned membership flicker fix
Bite-sized TDD plan for design Stages 0-1 + W2b revert + visual gate: add the
[cell-swept] diagnostic, return the swept sp.CurCellId from ResolveWithTransition
(retail SetPositionInternal), revert the superseded W2b hysteresis, visual-gate the
doorway/cellar strobe, then lock it with a doorway replay regression. Render chunk
(Stages 3-5) gets its own spec+plan after this gate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 14:04:45 +02:00
Erik
840c1b6442 docs(render): Phase W (rev) — 4-model research + transition-membership/PView design
Four independent decomp studies (Opus 4.8 x2, Sonnet 4.6, external Codex)
converge: retail carries the cell through the collision sweep (validate_transition
advances curr_cell only on an accepted move, reverts on a block) and commits it in
SetPositionInternal — it never re-derives membership from a static resting position.
acdream already ports the sweep machinery (sp.CurCellId/CheckCellId, ValidateTransition,
CheckOtherCells) but ResolveWithTransition discards the swept cell and re-derives
statically via ResolveCellId (PhysicsEngine.cs:909/928) — the root of the
0170<->0031 doorway/cellar ping-pong. The do_not_load_cells prune is secondary
(static/cross-cell lists), not the anti-flicker; W2b was doubly misplaced and is reverted.

Render: one PView::ConstructView portal traversal over the same cell graph, rooted at
the physics current cell; seen_outside (not a dungeon flag) gates landscape; the outside
draws through exit portals clipped to the doorway (no blue-hole, no stencil split).
Dungeons/interiors share the machinery; "underground" is emergent.

Design doc lays out the staged, evidence-first rewrite (Stage 0 diagnostic ->
Stage 1 transition-owned membership [visual gate] -> Stage 2 CELLARRAY/prune parity ->
Stages 3-5 render root + PView seal + entity clip). Adds the shared research prompt and
all four study reports as the grounding record.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 13:58:51 +02:00
Erik
3622a658fd docs(render): Phase W — W2a shipped+verified + baseline handoff
W2a (render reads physics CurrCell) visually verified: indoor world-from-below fixed (cellar/stairs seal). Baseline scopes the residuals: W2b (doorway ping-pong 0170<->0031, confirmed) + W3 (one-gate seal: roof, entity bleed, openings) + the EnvCellRenderer GL_BLEND fix (transparent walls). Membership (W2) done.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 10:55:21 +02:00
Erik
83c452b87f docs: UCG W2 (one membership) spec + plan
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 10:14:06 +02:00
Erik
07e68e0aff docs(roadmap): register Phase W — Unified Cell Graph (UCG); W1 shipped
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 10:05:34 +02:00
Erik
bd0244f203 docs(plan): UCG Stage 1 (ObjCell scaffold) implementation plan
8 TDD tasks (RED->GREEN), Core-only, zero behavior change, built alongside the legacy cell systems. Grounded in the retail CObjCell survey + acdream inventory + #98 fixtures.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 08:46:27 +02:00
Erik
e8c7164ad9 docs(render): Unified Cell Graph pivot — evidence model + Stage 1 spec
Pixel-grounded investigation concluded the indoor 'world from below' is a cell-MEMBERSHIP disagreement between render-side CellVisibility and physics-side ResolveCellId, not any single draw gate (terrain has one gated draw path; it leaks only on render null-root frames). Decision with user: full migration onto one retail CObjCell graph across physics+collision+render+streaming, staged in 5 verify-each cycles. This lands the evidence model + the Stage 1 (ObjCell scaffold) design. No code yet.

- docs/research/2026-06-02-render-cell-membership-evidence.md (the why, from pixels)

- docs/superpowers/specs/2026-06-02-unified-cell-graph-stage1-design.md (Stage 1)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 08:27:00 +02:00
Erik
1d7d8b1de4 docs(render): session-2 handoff — stencil attempt reverted, evidence-first pickup prompt
Net code change this session = 0 (stencil-occlusion T1-T4 implemented, regressed,
reverted to baseline 9bff2b0). Documents the honest failure + lessons (patchwork via
flag-based gate routing; the interior-writes-mask rule breaks outdoors; coded before
screenshotting), the still-useful evidence (cottage = IsBuildingShell GfxObjs not cell
shells; two redundant traversals; retail DrawCells outside_view gate; working window
screenshot tooling), the open questions to answer with pixels first, and a refined
evidence-first pickup prompt.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 13:51:22 +02:00
Erik
9bff2b0462 docs: render-pipeline SSOT section + #78 reset pointer (redo of edits that silently failed)
The architecture and ISSUES edits in the prior commit (0013819) failed silently because
they were anchored on the session-reminder's rendering of the files, not the real text.
Redone against actual content:
- architecture doc: new 'Render Pipeline (SSOT)' section — the 3-gate patchwork vs the
  unified-PView target + the one rule (compute visibility once, enforce it once).
- ISSUES #78: promoted to the render-architecture-reset target; points to the canonical
  handoff + the architecture section.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 21:50:07 +02:00
Erik
0013819fa1 docs(render): ARCHITECTURE RESET — indoor render is a 3-gate patchwork; handoff + unified-PView target
A week on the indoor render (Phase U.4 → U.4c → 2026-05-31) fixed the flap but
produced NO shippable progress: walls/ceiling don't seal, outdoor terrain is
visible from inside (#78), the enclosure reads grey/transparent. Root cause is
ARCHITECTURAL, not a bug.

Evidence this session (direct, via the new [shell] probe + screenshots) RULED OUT
every subsystem except the gating architecture: the interior cell shells render
fine (geometry/texture/opaque/depth all correct, zh=0 tr=0); the visibility
traversal computes correct sets + non-empty portal clips; cull mode is fine; the
camera/eye thread was a detour. The residual is that OUTDOOR geometry is not gated
to portal openings when indoors, and acdream enforces visibility THREE inconsistent
ways (TerrainClipMode / per-cell shell clip / entity ParentCellId filter with an
outdoor-stab bypass) instead of retail's ONE PView gate.

This commit is the reset handoff + documentation, not a code fix:
- docs/research/2026-05-31-render-architecture-reset-handoff.md — canonical: honest
  state, evidence ledger (ruled-out / do-not-repeat), the mapped 3-gate patchwork,
  the retail PView target (one traversal → one gate for ALL geometry), the reset
  mission, and a copy-paste pickup prompt.
- docs/architecture/acdream-architecture.md — new "Render Pipeline" SSOT section
  (current divergence + unified-PView target + the one rule: compute visibility
  once, enforce it once). (Doc has pre-existing corruption below this section —
  flagged for separate cleanup.)
- Apparatus: ACDREAM_PROBE_SHELL → [shell] (EnvCellRenderer per-cell prepared/drawn
  geometry + flags) added to RenderingDiagnostics + EnvCellRenderer. Throwaway.
- docs/superpowers/specs/2026-05-31-camera-collision-indoor-engagement-design.md —
  spec for e099b4c (camera collision; now parked as orthogonal to the seam).

Next session: STOP point-fixing; do the architecture reset to a single PView gate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 21:35:55 +02:00
Erik
3066460370 diag(render): camera-collision indoor non-engagement — RED test + diagnosis
Root cause (b): ShadowObjectRegistry.GetNearbyObjects (line 480) returns early
when primaryCellId is an indoor cell, skipping the outdoor radial sweep that
contains the landblock-baked cottage exterior-shell GfxObj. The issue-#98 fix
that prevents the player's head sphere from being capped by the cottage floor
also prevents the IsViewer camera sweep from finding the exterior building shell.
Result: camera passes through exterior walls unimpeded, driving the residual
transparent-walls symptom after the U.4c flap fix.

Evidence: live capture shows eyeInRoot=n ~90% of frames, eye-player distance
3.43m (full chase, no pull-in). RED test deterministically reproduces: synthetic
indoor cell (0xA9B40175) + exterior GfxObj registered at cellScope=0; probe
SweepEye returns pulledIn=0.0000m (full eye distance Y=5.0, wall at Y=4.0).

Fix design: exempt IsViewer from the indoor-primary early-return gate in
GetNearbyObjects — retail's find_obj_collisions (named-retail :308918) has no
indoor/outdoor cell gate; the acdream fix is correct only for IsPlayer.

Apparatus committed:
- tests/AcDream.App.Tests/Rendering/CameraCollisionIndoorTests.cs (RED test)
- docs/research/2026-05-31-camera-collision-indoor-diagnosis.md (findings + design)
- PhysicsCameraCollisionProbe.cs [flap-sweep] diagnostic retained (U.4c spike)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 18:02:37 +02:00
Erik
95b6874c12 docs(render): Phase U.4c — flap fixed + residuals handoff (checkpoint)
Canonical handoff (research note) for the U.4c flap fix + the three residuals the
visual gate revealed (#78 terrain-not-gated-inside, camera-collision need, U.5).
Records the full hypothesis journey (H1/H2 both evidence-disproven) so the next
session doesn't re-walk them. ISSUES.md: flap recorded in Recently-closed; #78
annotated (more visible post-fix). CLAUDE.md: U-phase orientation updated with the
flap-fixed status + the canonical handoff pointer + camera-collision-next.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 17:01:23 +02:00
Erik
f47895cc73 research(render): Phase U.4c — CONVERGED root cause (eye-rooted grace-stale root)
Live ACDREAM_PROBE_FLAP moving capture: flap frames are uniformly res=Grace
eyeInRoot=n terrain=Skip; good frames eyeInRoot=Y terrain=Planes. The 3rd-person
camera EYE drifts out of the player's cell -> FindCameraCell returns the stale cell
for 3 grace frames -> from that stale root the doorway portal is behind the eye
(D=+1.26 CULL) -> exit cell drops -> terrain+shells Skip. Clip math is fine
(clip=5 when eye inside). Fix: (1) root visibility at the PLAYER's cell (retail
CellManager::ChangePosition tracks curr_cell by player; acdream already does this
for lighting at GameWindow:7152); (2) keep a player-reachable cell + exit when the
threshold eye-projection degenerates. Supersedes H2 and the earlier idle-frame
'stale root refuted' note.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 14:32:19 +02:00
Erik
8941d1e6e5 research(render): Phase U.4c — refute eye-crosses-plane; correct stale H2 note
A8CellAudit portals now dumps each cell's local AABB. Real flap cells: 0171 local
y in [-7.65, 1.15], 0170 in [-8.61, -7.65]; the 0171->0170 portal plane is at
y=-7.65 (0171's MIN boundary), no overlap. So an eye genuinely inside 0171 always
has side-test D<=0 -> always traverses 0171->0170; the side test cannot cull 0170
while the eye is in 0171. The flap therefore requires the eye OUTSIDE 0171 while
root is still 0171 (cache/grace/3rd-person camera) -> a camera-cell-resolution
issue, not the side test (H2, disproven) and not the per-frame PVS set (H1, in
doubt). Mechanism still unconfirmed -> needs a live eye-pos capture. Stale H2
conclusion in the characterization note corrected with a banner.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 10:36:30 +02:00
Erik
b5f2bf2b8f research(render): Phase U.4c — DISPROVE the side-test fix (PortalSide port is a no-op)
InitCell decode (PortalFlags.PortalSide=0x2) + a swept-pose A8CellAudit comparison
(O=centroid, A=winding-corrected PortalSide, B=opposite) over the real flap cells.
A is IDENTICAL to O at every pose/every portal — the (Flags&2)==0 boolean convention
makes the dat PortalSide sense equal to our centroid sense, so swapping is a no-op
and cannot fix the flap. B culls true-interior poses (wrong polarity). Conclusion:
the flap is NOT the side-test sense — it's the 3rd-person camera eye crossing an
interior portal plane while FindCameraCell still roots in the cell; ANY plane-side
test culls there. No production code changed (no no-op shipped).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 10:32:52 +02:00
Erik
fdeede8796 docs(render): Phase U.4c — annotate Task 3 with U.4c-1 evidence (H2 selected)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 10:14:22 +02:00
Erik
13d58cae6a research(render): Phase U.4c-1 — characterize the flap on real dat evidence
A8CellAudit portals dump extended to print per-portal plane + centroid-derived
InsideSide vs the dat's authored PortalSide. Real Holtburg cottage cells show:
the flap is a DIRECT 0xA9B40171->0xA9B40170 portal side-test flip (0170 is a
direct neighbour, not multi-hop), and our centroid-derived InsideSide is
anti-correlated with the dat PortalSide that retail InitCell (432896) uses.
Evidence selects H2 (port the side test) over H1 (PVS set-grounding). Camera
cell 0171 seenOutside=Y. Full reading + fix direction + open sign question in
the note.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 10:13:16 +02:00
Erik
211350b8a6 docs(render): Phase U.4c — implementation plan (stabilize portal visibility)
Five tasks: (1) RED apparatus reproducing the doorway flap on a synthetic
C0->C1->C2(exit) chain; (2) Layer 1 LoadedCell.VisibleCells + SeenOutside
plumbing; (3) oracle-ported PVS grounding of set membership (the fix, gated by
task 1); (4) seen_outside invariants (sealed=empty, threshold=stable); (5)
live [vis] + visual gate. Task 3 is a faithful port (add_views 433382 /
InitCell 432896 / ClipPortals 433572), pseudocode-first, not fabricated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 09:53:38 +02:00
Erik
31f265d8ec docs(render): Phase U.4c — design spec (stabilize portal visibility / fix the flap)
Grounds the visible-cell SET in the stable per-cell PVS (stab_list) + seen_outside,
refreshed on cell entry, the way retail does (grab_visible_cells 311878, add_views
433382, DrawInside 433793). Our PortalVisibilityBuilder rebuilds the set per-frame
from a pose-brittle CameraOnInteriorSide walk, so a flipped side-test drops the exit
cell, empties OutsideView, and TerrainMode.Skip flaps terrain/shells off at the
doorway. Both stable inputs already live in-process (envCell.VisibleCells,
envCell.Flags & SeenOutside); U.4c is plumbing + grounding, not new dat parsing.

Apparatus-first: characterize the flap on a live ACDREAM_PROBE_VIS capture + port the
add_views/ClipPortals/AddToCell semantics to pseudocode before implementing; the
builder is not declared correct until a live [vis] shows non-empty + narrowing
OutsideView. No hysteresis band-aid (forbidden). Indoor rendering untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 09:44:14 +02:00
Erik
a3ecac5369 docs(render): Phase U.4 shipped (indoor rendering verified) + flap handoff
Phase U (U.1-U.4) shipped: the unified retail-faithful render pipeline replacing the
abandoned two-pipe split (#103). Indoor rendering VISUALLY VERIFIED — solid walls, no
terrain bleed, per-cell clip gating works. Two root-caused EnvCellRenderer
self-contained-GL-state fixes landed (uViewProjection stale-matrix; inherited
blend/depth-mask). Residual threshold "flap" (OutsideView instability from the per-frame
view-dependent portal BFS) is precisely root-caused via ACDREAM_PROBE_VIS and scoped to
U.4c (PVS / stab_list grounding, retail-faithful). Handoff captures the [vis] evidence,
the retail anchors, and the next-session pickup. U.5 (outdoor->building peering) + U.6
(dungeon scale) remain.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 09:16:35 +02:00
Erik
65781f5768 fix(render): Phase U.2b — resolve reciprocal portal by other_portal_id (retail 433557)
Code review caught a CRITICAL under-inclusion: ApplyReciprocalClip scanned for the
first OtherCellId match, so a cell with two portals to the same neighbour clipped both
near-side openings against the FIRST reciprocal polygon — hiding geometry through the
second opening (real on Holtburg cellar cells 0x148<->0x149). Plumb the dat's
OtherPortalId back-link through CellPortalInfo + BuildLoadedCell and index the reciprocal
directly (retail arg2->other_portal_id, 433557). Skip (degrade to over-include) when the
index is unresolvable — never clip against a guessed polygon. Adds a disjoint two-back-
portal regression test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 16:56:00 +02:00
Erik
3916b2b23e feat(render): Phase U.2b — reciprocal OtherPortalClip (retail 433524)
Clip the portal opening against the neighbour's matching back-portal polygon
before propagating, so a cell's clip region is the intersection of the opening
seen from both sides. Closes the M-4 stub in ISSUES #102. Can only tighten,
never under-include; degrades to prior behavior when no back-portal is found.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 16:37:14 +02:00
Erik
306cdb069c docs(render): Phase U.2a review fixups — LIFO-on-ties comment + ISSUES #102
Code-review minor follow-ups: correct the CellTodoList comments (ties are LIFO,
not FIFO — an equal-distance newcomer lands at the tail and pops first, matching
retail's break-on-first-not-greater + pop-from-tail). Update ISSUES #102 to record
that U.2a closes I-1/I-2 (under-count + duplicate accumulation) via the enqueue-once
gate, narrowing the residual to diamond-topology clip-completeness (AddToCell onward
re-propagation, tracked under U.6).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 16:30:41 +02:00
Erik
0f7b395be1 docs(render): Phase U — implementation plan (U.1-U.4 detailed, U.5/U.6 stubbed)
Ten bite-sized tasks to the first visual gate: U.1 delete two-pipe; U.2 GL-free
core (builder ordering+fixpoint, OtherPortalClip, ClipPlaneSet, ACDREAM_PROBE_VIS);
U.3 GPU gate (gl_ClipDistance in mesh_modern/terrain_modern + clip SSBO/UBO upload);
U.4 unified gated draw (EnvCellRenderer cell shells + WbDrawDispatcher All +
gated terrain; live-dynamic unclipped per retail) + per-instance slot assignment +
probe validation. U.5 outdoor-peering / U.6 dungeon-scale detailed after the gate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 15:48:17 +02:00
Erik
8601137330 docs(render): Phase U — unified retail-faithful render pipeline design spec
One PView-faithful portal-visibility pass replacing the abandoned two-pipe
(inside/outside) split (#103). Settled in brainstorm 2026-05-30:
- Full Phase U in one spec (indoor BFS + outdoor building-peering + dungeon
  fixpoint + distance-priority ordering + reciprocal OtherPortalClip).
- Per-cell gate = hardware clip planes (gl_ClipDistance) + scissor pre-check
  (retail's two-level model); structurally immune to the #103 global-mask flood.
- Terrain stays its own path, gated to OutsideView (retail-faithful; NOT the
  handoff's "terrain as cells" sketch).
- Salvage = reuse the clip math (PortalView/ScreenPolygonClip/PortalProjection,
  ~36 tests), rework the builder (PortalViewBuilder), delete the stencil pipeline
  + GameWindow two-pipe orchestration. Audited keep-list preserves the real
  EnvCellRenderer / BuildingId / camera-collision fixes.

Staged U.1-U.6 with three visual gates. Retail anchors + acdream file:line
injection points catalogued in the spec.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 15:38:09 +02:00
Erik
75b1df9cc3 docs: abandon two-pipe render approach; scope Phase U (unified retail-faithful pipeline)
Decision (2026-05-30, with user): the WB-inherited two-pipe (inside/outside) render
split is the root cause of the indoor seam bugs (flap, missing/transparent walls,
terrain bleed) and cannot be seamless. Abandon A8/A8.F (#103); build ONE unified
pipeline driven by retail's PView portal visibility — seamless by construction. The
2026-05-30 camera-collision + physics viewer-cap work is kept (retail-faithful, but a
detour from the seam fix). New Phase U scoped; #103 superseded; CLAUDE.md / roadmap /
milestones updated; full decision + scope + next-session pickup prompt in
docs/research/2026-05-30-unified-render-pipeline-decision-and-handoff.md.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 11:35:41 +02:00
Erik
05161399de docs(render): Phase A8.F — sync plan Task 2 moverFlags to shipped 0x5c
The plan's Task 2 code block still showed moverFlags: ObjectInfoState.None; the
shipped code (fcea05f) and spec §5.1 use IsViewer|PathClipped|FreeRotate|
PerfectClip (retail init_object(player, 0x5c)). Update the stale snippet so the
plan matches reality (this stale block was the likely source of a re-report).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 21:42:03 +02:00
Erik
53634b5089 docs(render): Phase A8.F — supersede the old "no camera collision" note
The 2026-05-18 retail-chase-camera spec scoped collision out citing "retail
doesn't raycast." Phase A8.F falsified that (SmartBox::update_viewer DOES sweep
viewer_sphere); mark the note superseded and point to the A8.F spec.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 19:46:53 +02:00
Erik
fcea05f808 fix(render): Phase A8.F — camera sweep uses retail moverFlags 0x5c (PathClipped hard-stop)
Code review found the probe passed ObjectInfoState.None; retail's
SmartBox::update_viewer calls init_object(player, 0x5c) =
IsViewer|PathClipped|FreeRotate|PerfectClip (pseudo-C :92864). PathClipped makes
the sweep hard-stop at first contact (TransitionTypes.cs:811) instead of
edge-sliding around corners (which would re-trigger the A8.F camera-cell
instability); IsViewer lets the eye pass through creatures, colliding only with
world geometry. Resolves the spec's slide-vs-stop open question. Also reset
CollideCamera in the Defaults_AreRetailValues baseline test (review: maintenance
trap). Spec §5.1/§11.1 synced.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 19:11:53 +02:00
Erik
77a6331ecd docs(render): Phase A8.F — camera-collision implementation plan
Bite-sized TDD plan for the swept-sphere camera collision: CollideCamera flag,
ICameraCollisionProbe + PhysicsCameraCollisionProbe (reuses ResolveWithTransition),
RetailChaseCamera slot-in, GameWindow wiring, Camera-menu toggle, visual
acceptance. Also refines the spec from planning findings: the InitPath +radius
sphere-center offset (ToSpherePath/FromSpherePath z-shift) and the deterministic
probe test scope (z-offset round-trip + cellId==0 guard; collision correctness
rides the existing sweep suite + visual).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 18:45:58 +02:00
Erik
9bdd50287b docs(render): Phase A8.F — swept-sphere camera collision design spec
Design for porting retail's stage-2 camera collision (SmartBox::update_viewer):
sweep a 0.3 m sphere from the head-pivot to the damped eye via the existing
ResolveWithTransition engine (collides both indoor cell walls and GfxObj
building shells, e.g. the cottage cellar per #98/#101), publish the stopped
position as the eye. Fixes the A8.F flap by keeping the eye out of walls so the
camera-cell + portal side-tests stay stable. Self-skip via LocalEntityId; gated
by CameraDiagnostics.CollideCamera (default ON). Corrects the prior
retail-chase-camera spec's "no camera collision" note.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 18:07:56 +02:00
Erik
9757818e95 docs(render): Phase A8.F — correct camera handoff; retail DOES collide the camera
Correction after the user (who has played retail and observed the camera pull in
at walls) flagged the prior "no camera collision" conclusion. Verified against the
decomp: retail's camera collision lives in SmartBox::update_viewer (0x00453ce0),
NOT CameraManager::UpdateCamera. The earlier research traced only the producer
(UpdateCamera computes the desired/damped eye -> viewer_sought_position) and missed
the consumer (update_viewer), which sweeps a 0.3 m viewer_sphere via
CTransition::find_valid_position from the head-pivot to that eye and uses the
stopped position (fallbacks: AdjustPosition, then snap to player). The player-fade
when super close (CameraSet::UpdateCamera -> SetTranslucencyHierarchical) is a
SEPARATE stage, already ported as RetailChaseCamera.ComputeTranslucency.

Implication: a swept-sphere camera collision is RETAIL-FAITHFUL, not a divergence —
no special sign-off needed, and acdream already owns the Transition swept-sphere
engine. Updated TL;DR, KEY FINDING, the fix section (was "design decision"),
slot-in (collide the damped eye, after RetailChaseCamera.cs:131), open questions,
pickup prompt, and reference index. Memory updated likewise.

Lesson recorded: when the decomp says "no X" but a domain expert says X exists,
trace the CONSUMER of the computed value, not just the producer.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 17:51:44 +02:00
Erik
ce909ad0a8 docs(render): Phase A8.F — camera-collision root cause + handoff (session 2)
Root cause of the A8.F flap / missing-walls reframed (with the user's help):
the 3rd-person camera EYE passes through walls, and the A8.F renderer keys its
"am I inside?" (PointInCell) and portal side-tests (CameraOnInteriorSide) off
that eye position (camPos = invView translation, GameWindow.cs:7271). Eye clips
a wall -> those decisions flip frame-to-frame -> the flap.

Key finding from camera research (Opus agent + verified against the decomp):
retail's camera does NOT collide with walls either — it fades the player to
translucent (CameraSet::UpdateCamera @ 0x00458ae0 -> SetTranslucencyHierarchical),
which acdream already ports as RetailChaseCamera.ComputeTranslucency. So a
"spring arm that pulls the eye in on a wall hit" is a deliberate divergence from
retail, not a faithful port — needs user sign-off before coding.

Handoff documents: the eye->visibility coupling + flap mechanism, acdream's
current camera (the ported turn/jump input-lag = damping + velocity ring +
mouse filter; no collision), retail's camera (symbols+addresses), the reusable
swept-sphere collision machinery (BSPQuery.FindCollisions vs CellPhysics.BSP),
3 fix options (lead: modern spring arm), open design questions, apparatus, and a
pickup prompt.

Bug A (cellar terrain flood) already fixed + committed in 9417d3c; the
recursive-clip builder works (the prior "Bug B" framing was wrong).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 16:40:41 +02:00
Erik
cf3d49cbd7 docs: Phase A8.F visual-gate failure handoff + issue #103
A8.F (retail portal-frame port) shipped Tasks 0-8 but failed its visual gate:
indoor branch renders broadly wrong at runtime (terrain over walls, transparent/
invisible walls). Default game unaffected (branch gated behind
ACDREAM_A8_INDOOR_BRANCH). Two compounding root causes documented (OutsideView
under-produces; Job-A/B else-branch floods ungated terrain) + apparatus + a
first-fix hypothesis + pickup prompt. Filed #103.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 14:43:24 +02:00
Erik
270c21f263 refactor(render): Phase A8.F — Task 4 review follow-up (honest cap comment, cycle guard test, file fixpoint fast-follow)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 12:16:11 +02:00
Erik
c665f3eef3 docs: A8.F plan — record Task 3 near-clip correction + Task 4 winding requirement 2026-05-29 11:59:04 +02:00
Erik
612400f998 docs: Phase A8.F implementation plan — retail portal-frame visibility port
10 tasks (0-9): strip diag flags; GL-free CPU layer (ViewPolygon/CellView,
ScreenPolygonClip, PortalProjection, PortalVisibilityBuilder) with TDD;
stencil NDC entry; RenderInsideOut rewrite + Job-A/B decouple + three
wire-ins; visual gate. Bite-sized steps, complete code, retail anchors.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 10:58:00 +02:00
Erik
ca62d745fb docs: A8.F spec — correct Step 0 (A8 batch already committed in 5dc4140)
The 2026-05-28 handoff's "uncommitted A8 batch" is stale: 5dc4140 landed
the batch after the handoff. Step 0 reduces to stripping the leftover
ACDREAM_A8_DIAG_* flags (still present in RuntimeOptions + GameWindow).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 10:46:36 +02:00
Erik
d9d0809549 docs: Phase A8.F design — retail portal-frame visibility port (cellar-flap fix)
Faithful port of retail PView recursive portal-clip visibility
(ConstructView/ClipPortals/GetClip) to fix the residual A8 cellar flap.
Key finding: WB has no per-portal recursion — the flat-stencil algorithm
cannot express the fix; the recursion is retail-only. Builder ports as
GL-free CPU math producing a recursively-clipped OutsideView; enforcement
maps onto the existing A8 stencil pipeline. Builds on (does not supersede)
the A8 WB full-port baseline.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 10:44:44 +02:00
Erik
5dc4140c11 feat(render): Phase A8 — indoor visibility + streaming fixes batch
Lands the working A8 indoor-rendering and streaming fixes accumulated this
session. User has verified these visually to some degree (e.g. lifestone /
translucent meshes confirmed fine under the FrontFace flip; bridge / wall /
collision regressions confirmed fixed after travel); not every path has been
exhaustively gated. The cellar-flap defect remains OPEN and will be solved
the retail-faithful way via a dedicated brainstorm (see handoff docs).

Rendering core (reviewed, high confidence):
- EnvCellRenderer SSBO stride fix: upload packed Matrix4x4[] (64B) instead of
  the 80B CPU InstanceData struct the shader never expected — fixes the
  transform/texture "explosion" for any draw with >1 instance (cells that
  dedupe to a shared cellGeomId). Real root cause.
- WB-style global FrontFace(CW) + per-batch CullMode carried through the MDI
  layout (GroupKey + BuildIndirectArrays + DrawIndirectRange split into
  same-cull runs with absolute uDrawIDOffset per run).
- EntitySet partitioning (IndoorPass / OutdoorScenery / LiveDynamic) +
  WorldEntity.BuildingShellAnchorCellId so building shells scope to their
  dat-derived building cell instead of rendering everywhere.
- RenderOutsideInAcdream (look into buildings from outside) +
  CollectVisiblePortalBuildings frustum cull of portal bounds.
- Sky-when-inside-building + per-cell audit probe + GL-state probe.

Streaming / perf (test-covered; not independently code-reviewed this session):
- Near/far priority queues so near work wins over far; PromoteToNear carries
  full landblock + mesh data; LandblockEntriesWithoutAnimatedIndex avoids
  rebuilding the animated-lookup dict in the hot draw path. Fixes the
  bridge-not-appearing / missing-walls / broken-collision-after-travel
  regressions and improves post-transition FPS.

Tooling + docs:
- tools/A8CellAudit: offline dat cell/portal/building dumper (portals +
  buildings modes) — reproduces the cellar-flap investigation with no launch.
- docs/research cellar-flap root-cause + option-2 handoff (the didInsideStencil
  double-duty finding + the WB-recursive design decision + brainstorm prompt),
  entity-taxonomy, replan, issue-78 visibility investigation.

Diagnostics retained on purpose: ACDREAM_A8_DIAG_* gates, portal_stencil.vert
provisional pos.w clamp, and the probe families are kept (env-var gated, zero
cost when off) because the pending option-2 cellar-flap brainstorm needs them.
Strip in the option-2 ship commit.

Indoor branch stays behind ACDREAM_A8_INDOOR_BRANCH=1 (default off = pre-A8
visual). Build green; App tests + Core (streaming/dispatcher/loader) tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 10:14:50 +02:00
Erik
e415bb3863 docs: Phase A8 — session 2 handoff (pool fix shipped + 4 partial fixes + residuals)
After 5 visual gates, the session shipped 5 commits closing real bugs
(pool aliasing was the catastrophic root cause), but residual symptoms
(transparent floor, texture warping, flickering, distortion) didn't
yield to surgical fixes. Per systematic-debugging skill's >=3-failures
rule, stop and capture state.

Doc covers:
- Pool aliasing root cause + fix (the big win — closes session-1's
  visual chaos).
- Sky-when-building, LiveDynamic, Landblock→None — all real bug closures.
- Apparatus state (GL state probe + per-cell audit + pool diagnostics).
- Three theories for the residual issues (FrontFace=CW global match to
  WB / per-poly Stippling audit / WB side-by-side render).
- Pickup prompt for next session with ranked options.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 20:32:22 +02:00