docs(render): FLAP settled by live-retail measurement — full retail port DECIDED (Option A) + exhaustive handoff
Attached cdb to the live 2013 retail client at the Holtburg doorway + read the decomp. The indoor flap is a STRUCTURAL divergence, settled by measurement (not inference): - Retail has ONE render path: DrawInside(viewer_cell) every frame. NO inside/outside branch (RenderNormalMode's outside branch is dead code; is_player_outside only gates sky/lighting). "Entering a building" is not a render event — only the camera sweep resolving a different viewer_cell. Same path before/after threshold -> no seam. - Retail's eye JITTERS ~36um at rest yet membership is stable -> robustness is STRUCTURAL: many small per-building floods (~7/frame, ~2 cells each, via terrain BSP -> DrawPortal -> ConstructView(CBldPortal)), not one giant knife-edge flood. - Our 3 divergences: (D1) invented inside/outside branch (GameWindow.cs:7498, clipRoot = viewerRoot ?? _outdoorNode :7396); (D2) synthetic _outdoorNode; (D3) one unified flood. DECISION (user-approved): Option A — rip out branch + outdoor node, root always at the real viewer_cell, one DrawInside, per-building rendering. Phased, conformance-tested, visual-gated. REFUTED by measurement (do not retry): bounded-propagation/churn (maxPop=1, 0/63k reciprocals empty); byte-stable eye (retail's jitters ~36um — rest-snapcd974b2failed + regressed, reverted9b1857a). Lands the canonical exhaustive handoff for a FRESH session (docs/research/2026-06-08-full-retail-render-port-OPTION-A-handoff.md), the CLAUDE.md READ-THIS-FIRST banner, and reusable cdb apparatus. No project code changed; working tree at the known-good baseline. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9b1857ac52
commit
fe87e9794a
6 changed files with 629 additions and 0 deletions
22
CLAUDE.md
22
CLAUDE.md
|
|
@ -763,6 +763,28 @@ H1 (PVS grounding) or H2 (`PortalSide` side-test) — both evidence-disproven.
|
|||
**Currently working toward: M1.5 — Indoor world feels right** (resumed
|
||||
from 2026-05-20 baseline after Phase O ship).
|
||||
|
||||
**2026-06-08 (evening) — FLAP ROOT CAUSE SETTLED BY LIVE-RETAIL MEASUREMENT; full retail render
|
||||
port DECIDED (Option A). READ THIS FIRST — it supersedes EVERY flap banner below, including the
|
||||
bounded-propagation/churn direction (REFUTED by measurement: `maxPop=1`, 0 churn).** We attached cdb
|
||||
to the **live 2013 retail client** at the Holtburg doorway + read the decomp. Findings (measured, not
|
||||
inferred): **retail has ONE render path — `DrawInside(viewer_cell)` every frame, NO inside/outside
|
||||
branch** (`RenderNormalMode`'s outside branch is dead code; `is_player_outside` only gates
|
||||
sky/lighting). "Entering a building" is NOT a render event — only the camera sweep resolving a
|
||||
different `viewer_cell` (outdoor `CLandCell` → indoor `CEnvCell`); same path before/after the
|
||||
threshold → no seam → no flap. **Retail's eye JITTERS ~36 µm at rest** (so a byte-stable eye is the
|
||||
WRONG target — my render-position rest-snap fix `cd974b2` failed + regressed, reverted `9b1857a`);
|
||||
retail's membership is stable anyway because it does **many small per-building floods** (~7/frame,
|
||||
~2 cells each, via the terrain BSP → `DrawPortal` → `ConstructView(CBldPortal)`), not one giant
|
||||
unified flood. **Our 3 divergences:** (D1) we invented an inside/outside branch
|
||||
(`GameWindow.cs:7498`, `clipRoot = viewerRoot ?? _outdoorNode` :7396); (D2) a synthetic `_outdoorNode`;
|
||||
(D3) one unified flood. **Decision (user-approved): Option A — rip out the branch + outdoor node, root
|
||||
always at the real `viewer_cell`, one `DrawInside`, per-building rendering.** DO NOT retry: byte-stable
|
||||
eye, bounded-propagation/churn, physics rest-jitter, viewer-cell dead-zone, two-pipe split (all
|
||||
evidence-disproven). **CANONICAL PICKUP (exhaustive — read top-to-bottom before any code):**
|
||||
[`docs/research/2026-06-08-full-retail-render-port-OPTION-A-handoff.md`](docs/research/2026-06-08-full-retail-render-port-OPTION-A-handoff.md).
|
||||
Close its §8 open traces (viewer_sought_position write site; ClipPortals/AddViewToPortals; how
|
||||
`DrawInside` handles an outdoor `CLandCell` root) BEFORE writing the implementation plan.
|
||||
|
||||
**2026-06-05 (PM) — Indoor FLICKER + bluish VOID ROOT CAUSE CONFIRMED (decomp + live cdb); 3-part retail-faithful fix PLANNED (READ THIS FIRST).**
|
||||
The "core inside render / cellar floor drops" framing below is **SUPERSEDED** by this session's diagnosis.
|
||||
R1's per-cell `DrawInside` is already built and the cottage/cellar **seals** (user visual-verified). The
|
||||
|
|
|
|||
|
|
@ -0,0 +1,545 @@
|
|||
# HANDOFF — Full Retail Render Port (Option A): one `DrawInside(viewer_cell)` path, no inside/outside branch
|
||||
|
||||
**Date:** 2026-06-08 (evening)
|
||||
**Branch:** `claude/thirsty-goldberg-51bb9b` (HEAD `9b1857a`)
|
||||
**Status:** Design DECIDED (Option A). No implementation started. This is the canonical pickup
|
||||
document for a FRESH session. Read it top-to-bottom before touching code.
|
||||
**Author's note to the next session:** this is the payoff of a ~4-week saga + one long
|
||||
measurement session. The information below was *expensive* to obtain (live cdb on the real
|
||||
2013 retail client). Do not re-derive it; do not re-guess. Build from it.
|
||||
|
||||
---
|
||||
|
||||
## 0. TL;DR (read this, then read the rest)
|
||||
|
||||
The indoor "flap"/flicker is **not a bug to be fixed with a point change.** It is the symptom
|
||||
of a **structural divergence** from how retail renders. We confirmed this by attaching cdb to
|
||||
the **live retail client** and reading the decompilation. The findings are unambiguous:
|
||||
|
||||
- **Retail has exactly ONE render path: `DrawInside(viewer_cell)`, every frame.** There is **no
|
||||
inside/outside render branch.** The "outside" branch in `RenderNormalMode` is dead code
|
||||
(compiler-constant). `is_player_outside` only gates sky/weather/lighting, never the render path.
|
||||
- **"Entering a building" is NOT a rendering event in retail.** It is *only* the camera sweep
|
||||
resolving a different `viewer_cell` (an outdoor `CLandCell` → an indoor `CEnvCell`). The render
|
||||
code never asks "am I inside?". Same path before and after the threshold → **no seam → no flap.**
|
||||
- **Retail's eye JITTERS ~36 µm at rest** (measured, live). Retail's membership is stable anyway.
|
||||
So retail's stability is **structural** (coarse per-building visibility robust to jitter), NOT
|
||||
from a stable eye. **Chasing a byte-stable eye is the wrong target** — retail itself doesn't
|
||||
have one.
|
||||
- **We diverged in three ways:** (1) we invented an inside/outside branch + a synthetic
|
||||
`_outdoorNode`; (2) we do ONE giant unified flood where retail does many small per-building
|
||||
floods; (3) our camera boom jitters ~36× more than retail's.
|
||||
|
||||
**The decision (user-approved 2026-06-08): Option A — full retail structural port.** Rip out the
|
||||
branch and the outdoor node. Always root at the real `viewer_cell`. One `DrawInside`. Render
|
||||
terrain + per-building interiors from *within* that path the way retail does. Phase it; conformance-
|
||||
test each phase against the measured retail values in this doc; visual-gate.
|
||||
|
||||
**Next session's first move:** run `superpowers:brainstorming` is NOT needed (design is decided);
|
||||
go to `superpowers:writing-plans` to turn §6 (the design) into a phased implementation plan, then
|
||||
`superpowers:executing-plans`/`subagent-driven-development`. But FIRST close the open traces in §8.
|
||||
|
||||
---
|
||||
|
||||
## 1. The decision and its scope
|
||||
|
||||
**Option A — Full retail structural port.** In scope:
|
||||
|
||||
1. **Remove the inside/outside render branch.** Today `GameWindow.cs:7498` does
|
||||
`if (clipRoot is not null) { DrawInside } else { DrawPortal }`, where
|
||||
`clipRoot = viewerRoot ?? _outdoorNode` (`GameWindow.cs:7396`). Retail has no such branch.
|
||||
2. **Root always at the real `viewer_cell`** (the cell the camera-collision sweep resolves — an
|
||||
outdoor `CLandCell` or an indoor `CEnvCell`), never a synthetic outdoor node.
|
||||
3. **One `DrawInside(viewer_cell)` per frame.** Terrain + sky draw from *within* it when the flood
|
||||
"sees outside"; per-building interiors draw per-portal via the terrain BSP.
|
||||
4. **Per-building view construction** (retail does ~7 small per-building floods/frame), replacing
|
||||
our single unified flood.
|
||||
|
||||
Explicitly NOT the goal: "make the eye byte-stable" (retail's isn't); "add hysteresis/dead-zone to
|
||||
the clip" (band-aid, forbidden); "bound the portal re-enqueue churn" (there is no churn — measured).
|
||||
|
||||
The user's words: *"Yes lets do full retail! A!"* and earlier *"NO code of the project is frozen so
|
||||
all options on the table."* Nothing is frozen. This is a render-orchestration rewrite, done
|
||||
retail-faithfully, in phases.
|
||||
|
||||
---
|
||||
|
||||
## 2. Why this took ~4 weeks (the pattern is the diagnosis)
|
||||
|
||||
Over ~4 weeks the "root cause" was declared, with apparatus, **at least seven times**: two-pipe
|
||||
split → root-at-player-cell → viewer-cell metastability → camera-boom drift → physics rest-jitter →
|
||||
portal-flood re-enqueue churn → render-position jitter. **Every one was a real, measured
|
||||
perturbation. Every fix failed or moved the symptom.** That pattern is the signature of a
|
||||
**system-level problem attacked one stage at a time** (systematic-debugging skill, Phase 4.5:
|
||||
"3+ fixes failed → question the architecture").
|
||||
|
||||
**The fundamental issue.** The flicker is a **binary** decision ("is this cell visible: yes/no")
|
||||
made at a **grazing knife-edge** (the doorway portal, near-zero-area sliver), fed by a **long,
|
||||
coupled chain that amplifies**:
|
||||
|
||||
```
|
||||
physics body → render-position interpolation → camera boom → camera-collision sweep
|
||||
→ viewer cell → render branch → portal flood → clip → VISIBLE / NOT VISIBLE
|
||||
```
|
||||
|
||||
Measured amplification: physics body byte-stable → render position jitters µm → eye jitters
|
||||
~1.3 mm → at the end the continuous wobble is forced into a yes/no at a knife-edge → cell pops in
|
||||
and out. **It is a pencil balanced on its tip:** it doesn't matter which draft of air tips it,
|
||||
there's always another. You cannot stabilize a pencil-on-tip by hunting individual air currents.
|
||||
Every "I found the jitter source!" fix closed one draft while the pencil stayed on its point.
|
||||
|
||||
**Why retail has the same knife-edge but doesn't flicker:** retail uses the *exact same* grazing
|
||||
clip (we ported it). Retail doesn't flicker because **its structure is robust to the jitter** —
|
||||
many small per-building visibility decisions, not one giant knife-edge flood. Retail did NOT remove
|
||||
the jitter (its eye jitters ~36 µm too); it made the *decision* robust to it. **That is the thing
|
||||
we never did, because we kept patching the noise instead of the structure.**
|
||||
|
||||
---
|
||||
|
||||
## 3. THE ORACLE — how retail actually renders (measured + decompiled GROUND TRUTH)
|
||||
|
||||
This is the irreplaceable part. It was obtained from **the live retail client** (cdb) + the named
|
||||
decomp. Cite it; do not re-derive it.
|
||||
|
||||
### 3.1 Retail render architecture: ONE path
|
||||
|
||||
`SmartBox::RenderNormalMode` (`0x453aa0`, decomp line 92635) **always** calls
|
||||
`DrawInside(viewer_cell)`. The "outside" branch (`LScape::draw`) is **dead code** — the branch
|
||||
predicate is the Binary-Ninja artifact `edi_2 = -((edi - edi))` = `xor edi,edi; neg edi` = **always
|
||||
0**, so the inside branch is taken every frame. `is_player_outside` (`0x451e80`, line 90996) returns
|
||||
nonzero for an outdoor land cell (low 16 bits of `objcell_id` in `[1, 0xFF]`) but is **not called
|
||||
from the render dispatch** — only from `GameSky::Draw`, UI, and lighting. **There is no
|
||||
inside/outside render branch in retail.**
|
||||
|
||||
### 3.2 Call graph (from the decomp-flow research agent, verified against addresses)
|
||||
|
||||
```
|
||||
SmartBox::RenderNormalMode (0x453aa0, line 92635)
|
||||
└─ ALWAYS: RenderDevice::vtable->DrawInside(viewer_cell)
|
||||
→ RenderDeviceD3D::DrawInside (0x59f0d0, line 427843)
|
||||
→ PView::DrawInside(indoor_pview, viewer_cell) (0x5a5860, line 433793)
|
||||
→ CEnvCell::curr_view_push(viewer_cell)
|
||||
→ PView::add_views(this, cell->num_stabs, cell->stab_list)
|
||||
→ Render::copy_view(cell->portal_view[-1], null, 4)
|
||||
→ PView::ConstructView(this, viewer_cell, 0xffff) [CEnvCell overload, 0x5a57b0]
|
||||
→ PView::DrawCells(this, result) (0x5a4840, line 432709)
|
||||
├─ if outside_view.view_count > 0: LScape::draw(lscape) [terrain + sky]
|
||||
└─ for each cell in cell_draw_list: draw portals, env geometry, objects
|
||||
|
||||
PView::DrawCells → LScape::draw (0x506330, line 267912)
|
||||
→ GameSky::Draw
|
||||
→ for each land block: RenderDeviceD3D::DrawBlock (0x5a17c0, line 430027)
|
||||
→ DrawLandCell (terrain) ; DrawSortCell → DrawBuilding (0x59f2a0, line 427938)
|
||||
outdoor_pview->outdoor_portal_list = building->portals <<< KEY
|
||||
→ terrain BSP walk reaches BSPPORTAL leaves (magic "PORT" 0x504f5254):
|
||||
BSPPORTAL::portal_draw_portals_only (0x53d870, line 326881)
|
||||
→ for i in num_portals: RenderDevice::vtable->DrawPortal(in_portals[i], frame, 1)
|
||||
→ RenderDeviceD3D::DrawPortal (0x59f0e0, line 427852)
|
||||
→ PView::DrawPortal(outdoor_pview, portalPoly, ...) (0x5a5ab0, line 433895)
|
||||
CBldPortal* bp = outdoor_portal_list[portalPoly->portal_index]
|
||||
PView::add_views(this, bp->num_stabs, bp->stab_list)
|
||||
PView::ConstructView(this, bp, portal, ...) [CBldPortal overload, 0x5a59a0]
|
||||
viewpoint side-test vs portal plane
|
||||
PView::GetClip(...) ; CEnvCell::GetVisible(bp->other_cell_id)
|
||||
Render::copy_view(...)
|
||||
PView::ConstructView(this, other_cell, bp->other_portal_id) [recurse]
|
||||
if result: PView::DrawCells(this, ...) [draw that building's interior]
|
||||
|
||||
SmartBox::update_viewer (0x453ce0, line 92761)
|
||||
→ compute pivot from part_array + camera_manager->pivot_offset
|
||||
→ choose start cell: indoor (objcell low16 >= 0x100) → AdjustPosition(pivot); outdoor → player->cell
|
||||
→ CTransition: init_object(player, 0x5c) ; init_sphere(1, viewer_sphere, 1.0) ; init_path(cell)
|
||||
→ find_valid_position:
|
||||
success → set_viewer(sphere_path.curr_pos, 0) ; viewer_cell = sphere_path.curr_cell
|
||||
else AdjustPosition(sought_eye) → set_viewer ; viewer_cell = that cell
|
||||
else set_viewer(player->m_position, 1) ; viewer_cell = null
|
||||
NO snap / NO quantize / NO dead-zone. (The eye jitters anyway — see §3.4.)
|
||||
```
|
||||
|
||||
### 3.3 Verbatim decomp excerpts (the load-bearing ones)
|
||||
|
||||
**(a) `RenderNormalMode` branch — the "always DrawInside" proof (lines 92635–92702):**
|
||||
|
||||
```c
|
||||
this = RenderDevice::render_device->m_bOpenScene;
|
||||
if (this != 0) {
|
||||
int32_t edi_2 = -((edi - edi)); // == 0 ALWAYS (xor edi,edi; neg edi)
|
||||
int32_t ebx_1 = (edi_2 != 0 || this_1->viewer_cell->seen_outside != 0) ? 1 : 0;
|
||||
// ... FOV ...
|
||||
if (edi_2 == 0) { // ALWAYS taken — the INSIDE path
|
||||
if (ebx_1 != 0) { // viewer cell can see outside →
|
||||
uint32_t eax_1 = Position::get_outside_cell_id(&this_1->viewer);
|
||||
LScape::update_viewpoint(this_1->lscape, eax_1); // aim terrain viewpoint outside
|
||||
}
|
||||
Render::update_viewpoint(&this_1->viewer);
|
||||
RenderDevice::render_device_2->vtable->DrawInside(rd2, this_1->viewer_cell);
|
||||
} else { // DEAD CODE — edi_2 is constant 0
|
||||
LScape::update_viewpoint(...); Render::update_viewpoint(...);
|
||||
Render::set_default_view(); Render::useSunlightSet(1);
|
||||
LScape::draw(this_1->lscape);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**(b) `PView::DrawInside` (lines 433793–433823) — how the indoor flood is set up:**
|
||||
|
||||
```c
|
||||
void PView::DrawInside(PView* this, CEnvCell* arg2) {
|
||||
CEnvCell::curr_view_push(arg2);
|
||||
PView::add_views(this, arg2->num_stabs, arg2->stab_list);
|
||||
Render::copy_view(arg2->portal_view.data[arg2->num_view - 1], null, 4);
|
||||
edx_2 = PView::ConstructView(this, arg2, 0xffff); // flood from viewer_cell
|
||||
PView::DrawCells(this, edx_2);
|
||||
PView::remove_views(this, arg2->num_stabs, arg2->stab_list);
|
||||
}
|
||||
```
|
||||
|
||||
**(c) `PView::ConstructView(CEnvCell*, 0xffff)` (lines 433750–433789) — the flood loop:**
|
||||
|
||||
```c
|
||||
void PView::ConstructView(PView* this, CEnvCell* arg2, uint16_t arg3) {
|
||||
this->outside_view.view_count = 0;
|
||||
PView::master_timestamp += 1;
|
||||
this->cell_todo_num = 0;
|
||||
this->cell_draw_num = 0;
|
||||
PView::InitCell(this, arg2, arg3);
|
||||
PView::InsCellTodoList(this, arg2, 0.0);
|
||||
while (this->cell_todo_num > 0) {
|
||||
CEnvCell* cell = cell_todo_list.data[this->cell_todo_num - 1]->cell;
|
||||
if (cell == 0) return;
|
||||
this->cell_todo_num -= 1;
|
||||
cell_draw_list.data[this->cell_draw_num++] = cell; // <- membership append
|
||||
cell->portal_view.data[cell->num_view - 1]->cell_view_done = 1;
|
||||
if (PView::ClipPortals(this, cell, 0) != 0) // clip → enqueue neighbours
|
||||
PView::AddViewToPortals(this, cell);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**(d) Per-building portal loop — `BSPPORTAL::portal_draw_portals_only` (lines 326940–326953):**
|
||||
|
||||
```c
|
||||
// Reached at each BSPPORTAL leaf during the terrain BSP walk (front-to-back vs viewer):
|
||||
int32_t i = 0;
|
||||
if (this_1->num_portals > 0) do {
|
||||
int32_t edx_4 = this_1->in_portals[i]; // CPortalPoly*
|
||||
RenderDevice::render_device->vtable->DrawPortal(/*portal*/edx_4, /*frame*/arg2, /*mode*/1);
|
||||
i += 1;
|
||||
} while (i < this_1->num_portals);
|
||||
```
|
||||
…and `PView::DrawPortal` (lines 433895–433933) looks up `outdoor_portal_list[portalPoly->portal_index]`,
|
||||
`add_views`, then `ConstructView(CBldPortal*)` → if non-empty, `DrawCells` that building's interior.
|
||||
**This is the ~7 `cv-bld` calls/frame we measured. Per-building, small, robust.**
|
||||
|
||||
**(e) `update_viewer` eye-set (lines 92761–92892) — NO stabilization:** see the call graph §3.2.
|
||||
The eye is the result of a per-frame `CTransition::find_valid_position` sweep from the pivot to the
|
||||
sought eye. **No snap / quantize / dead-zone.** (The research agent *inferred* "stable because inputs
|
||||
stable"; the LIVE trace contradicts that — the eye jitters ~36 µm — see §3.4. The agent did NOT trace
|
||||
where `viewer_sought_position` is written; that is open trace #1 in §8.)
|
||||
|
||||
### 3.4 LIVE MEASUREMENTS (cdb on retail at the Holtburg cottage doorway, 2026-06-08)
|
||||
|
||||
Retail binary: `C:\Turbine\Asheron's Call\acclient.exe`, **MATCHES** our PDB
|
||||
(`refs/acclient.pdb`, GUID `9e847e2f-777c-4bd9-886c-22256bb87f32`). PID this session: 32360.
|
||||
|
||||
- **Membership at rest is STABLE.** Camera held still: `PView.cell_draw_num` settled to a long
|
||||
unbroken run of **2** (brief `4` only at startup). `SmartBox.viewer_cell` pointer = **1 distinct
|
||||
value** across the whole capture (byte-stable cell). Retail does NOT flap at rest.
|
||||
- **Retail does PER-BUILDING floods.** `ConstructView(CBldPortal*)` (`0x5a59a0`) fired ~7×/frame,
|
||||
each `cell_draw_num ≈ 2`. The `CEnvCell` overload (`0x5a57b0`) fired far less. Retail does NOT do
|
||||
one unified flood.
|
||||
- **Retail's EYE JITTERS ~36 µm at rest** — the decisive measurement. Reading
|
||||
`SmartBox.viewer.frame.m_fOrigin` (raw float bits) with the camera held still:
|
||||
```
|
||||
pub=(431a51ab, 41d1d3c4, 42c0a914) x≈154.32 y≈26.23 z≈96.33 (raw IEEE-754 hex)
|
||||
pub=(431a51ab, 41d1d3c1, 42c0a914)
|
||||
pub=(431a51ac, 41d1d3bf, 42c0a914) ← X flips 1 ULP
|
||||
pub=(431a51ab, 41d1d3cc, 42c0a915) ← Z flips 1 ULP
|
||||
pub=(431a51ac, 41d1d3b9, 42c0a913) ← Y spans ~19 ULPs
|
||||
```
|
||||
Decoded jitter: **X ~15 µm, Y ~36 µm, Z ~8 µm.** `pub == sought` (eye uncollided at the open door,
|
||||
so the jitter is the camera boom itself, not the collision sweep). **Retail's eye is NOT byte-stable.**
|
||||
- **Compare to acdream** (measured earlier this session via `[pv-input]` at the same doorway): our eye
|
||||
jitters **~1.3 mm in Y** (≈36× retail), our `RenderPosition` shows 15 distinct values at rest, our
|
||||
membership oscillates (flood `8↔3`, `6↔3`, etc.). Our physics body (`rawPlayer`) IS byte-stable —
|
||||
the jitter enters in the camera chain, NOT physics.
|
||||
|
||||
### 3.5 Struct offsets + symbols (from `flap-render-lookup.cdb` / `flap-pos-lookup.cdb`)
|
||||
|
||||
```
|
||||
acclient!SmartBox::update_viewer @ 0x453ce0
|
||||
acclient!SmartBox::RenderNormalMode @ 0x453aa0
|
||||
acclient!SmartBox::is_player_outside @ 0x451e80
|
||||
acclient!PView::ConstructView(CEnvCell*, ushort) @ 0x5a57b0
|
||||
acclient!PView::ConstructView(CBldPortal*, CPolygon*,int,int) @ 0x5a59a0
|
||||
acclient!PView::DrawInside(CEnvCell*) @ 0x5a5860
|
||||
acclient!RenderDeviceD3D::DrawInside @ 0x59f0d0
|
||||
|
||||
struct PView:
|
||||
+0x000 outside_view : portal_view_type
|
||||
+0x048 draw_landscape : Int4B
|
||||
+0x04c outdoor_portal_list : CBldPortal** (set per-building by DrawBuilding)
|
||||
+0x050 cell_draw_list : DArray<CEnvCell*>
|
||||
+0x060 cell_draw_num : Uint4B (THE membership count)
|
||||
+0x064 cell_todo_list : DArray<CellListType*>
|
||||
+0x074 cell_todo_num : Uint4B
|
||||
+0x078 lscape : LScape*
|
||||
|
||||
struct SmartBox:
|
||||
+0x008 viewer : Position (the published eye)
|
||||
+0x050 viewer_cell : CObjCell* (the cell the eye occupies)
|
||||
+0x058 viewer_sought_position : Position (pre-sweep desired eye)
|
||||
+0x0f8 player : CPhysicsObj*
|
||||
|
||||
struct Position: +0x004 objcell_id:Uint4B +0x008 frame:Frame
|
||||
struct Frame: +0x000 qw,qx,qy,qz:Float +0x010 m_fl2gv[9]:Float +0x034 m_fOrigin:Vector3
|
||||
⇒ SmartBox.viewer.objcell_id = +0x0c ; viewer origin x/y/z = +0x44 / +0x48 / +0x4c
|
||||
⇒ SmartBox.viewer_sought_position.origin = +0x94 / +0x98 / +0x9c
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Our divergences (precise, with file:line)
|
||||
|
||||
| # | Divergence | Where (acdream) | Retail truth |
|
||||
|---|---|---|---|
|
||||
| D1 | **Inside/outside render branch** | `GameWindow.cs:7498` `if (clipRoot is not null){DrawInside}else{DrawPortal}`; root at `GameWindow.cs:7396` `clipRoot = viewerRoot ?? _outdoorNode` | No branch. Always `DrawInside(viewer_cell)`. |
|
||||
| D2 | **Synthetic `_outdoorNode`** (outdoor-as-cell) as root when eye outside | `GameWindow.cs:7396`, `OutdoorCellNode.cs`, `PortalVisibilityBuilder.Build` `if (cameraCell.IsOutdoorNode)` (`PortalVisibilityBuilder.cs:88`) | Root is the real outdoor `CLandCell` the eye occupies. |
|
||||
| D3 | **One unified flood** (`PortalVisibilityBuilder.Build` from one root) | `RetailPViewRenderer.DrawInside` → `PortalVisibilityBuilder.Build` (`RetailPViewRenderer.cs:43`); look-in via `DrawPortal` → `BuildFromExterior` (`RetailPViewRenderer.cs:92`) | Many small per-building floods via terrain BSP → `DrawPortal` → `ConstructView(CBldPortal)`. |
|
||||
| D4 | **`MaxReprocessPerCell = 16` cap** (re-enqueue band-aid) | `PortalVisibilityBuilder.cs:51` | No cap; bounded structurally. (And: there is no re-enqueue *churn* — measured `maxPop=1`.) |
|
||||
| D5 | **`EyeInsidePortalOpening` guard** (degenerate-portal hack) | `PortalVisibilityBuilder.cs` (`EyeInsidePortalOpening`, ~235–244, 793–833) | Retail's 3D clip needs no such special case. |
|
||||
| D6 | **Reciprocal clip on `ProjectToNdc` not `ProjectToClip`** | `PortalVisibilityBuilder.ApplyReciprocalClip` (~697–747) | acdream split to dodge drift. |
|
||||
| D7 | **Render-position interpolation layer** (ours, not retail) | `PlayerMovementController.ComputeRenderPosition` (`PlayerMovementController.cs:810`) `Lerp(prev, curr, accumFrac)` | Retail renders at the authoritative position; the only nearby retail cite is the 30 Hz *physics* tick gate (`CPhysicsObj::update_object` :283950), NOT a render-interp. |
|
||||
| D8 | **Camera boom ~36× looser than retail** | `RetailChaseCamera` (`RetailChaseCamera.cs`) damping + `ApplyConvergenceSnap` (SnapEpsilon 0.0004 m); collision sweep `PhysicsCameraCollisionProbe.SweepEye` | Retail boom jitters ~36 µm; no snap in `update_viewer`. SECONDARY — fix the structure first. |
|
||||
|
||||
D1–D3 are the **primary** structural divergences Option A removes. D4–D8 are accumulated band-aids /
|
||||
secondary; most fall away once D1–D3 are done, but each must be removed deliberately (each was added
|
||||
to paper over a real problem — see §7 DO-NOT and the in-code comments).
|
||||
|
||||
---
|
||||
|
||||
## 5. The render pipeline as it exists today (so you know what you're rewriting)
|
||||
|
||||
- Entry: `GameWindow.cs` render loop, ~7180–7800. `RetailChaseCamera.Update` produces `Position`
|
||||
(eye) + `ViewerCellId`. `viewerRoot` resolved ~7209–7211; `clipRoot = viewerRoot ?? _outdoorNode`
|
||||
(7396). Branch at 7498: `DrawInside` (indoor/unified) vs `DrawPortal` (exterior look-in).
|
||||
- `RetailPViewRenderer` (`src/AcDream.App/Rendering/RetailPViewRenderer.cs`):
|
||||
`DrawInside(ctx)` → `PortalVisibilityBuilder.Build(rootCell, eye, lookup, viewProj)` (line 43);
|
||||
`DrawPortal(ctx)` → `PortalVisibilityBuilder.BuildFromExterior(candidateCells, …)` (line 92).
|
||||
Post-flood: `ClipFrameAssembler.Assemble`, then `DrawLandscapeThroughOutsideView`,
|
||||
`DrawExitPortalMasks`, `DrawEnvCellShells`, `DrawCellObjectLists`.
|
||||
- `PortalVisibilityBuilder` (`src/AcDream.App/Rendering/PortalVisibilityBuilder.cs`): the flood. Ports
|
||||
`ConstructView`/`ClipPortals`/`AddViewToPortals` BUT as ONE flood with the `MaxReprocessPerCell`
|
||||
cap, the `EyeInsidePortalOpening` guard, and the NDC reciprocal. `OutsideView` is the
|
||||
terrain-through-door region. `IsOutdoorNode` special-cases the synthetic outdoor root.
|
||||
- `PortalProjection` (`PortalProjection.cs`): `ProjectToClip` + `ClipToRegion` — **faithful** port of
|
||||
retail `PView::GetClip` (`0x5a4320`/`:432344`) + `ACRender::polyClipFinish` (`:702749`, the w=0
|
||||
clip). KEEP THIS — it is correct; the problem is never the clip math, it's what feeds it.
|
||||
- `CellVisibility` (`CellVisibility.cs`): cell membership / `stab_list` / `seen_outside` / InsideSide
|
||||
side-test — faithful to `CellManager::ChangePosition` (`0x4559B0`) + `grab_visible_cells` (`:311878`).
|
||||
- Camera: `RetailChaseCamera.cs` (boom, `ApplyConvergenceSnap` from `d2212cf`),
|
||||
`PhysicsCameraCollisionProbe.cs` (`SmartBox::update_viewer` sweep port), `CameraController.cs`
|
||||
(picks RetailChaseCamera vs legacy `ChaseCamera`).
|
||||
|
||||
---
|
||||
|
||||
## 6. The design — Option A (phased; each phase conformance-tested + visual-gated)
|
||||
|
||||
> The fresh session should run `superpowers:writing-plans` to expand this into a task plan. The
|
||||
> phases below are the architecture; the plan adds the bite-sized steps.
|
||||
|
||||
**Guiding invariant (retail):** every frame, root the render at the *real* cell the camera eye is
|
||||
in (`viewer_cell`), and run ONE `DrawInside`. Outdoor terrain + per-building interiors are products
|
||||
of that single path, not of a separate branch.
|
||||
|
||||
**Phase R-A1 — Collapse to one root, one path (remove D1 + D2).**
|
||||
- Make `clipRoot` = the real cell the camera-collision sweep resolved (`RetailChaseCamera.ViewerCellId`
|
||||
→ the actual `LoadedCell`, outdoor `CLandCell` or indoor `CEnvCell`). Delete the `?? _outdoorNode`
|
||||
fallback and the `IsOutdoorNode` special-case in `PortalVisibilityBuilder`.
|
||||
- Delete the `else { DrawPortal(...) }` branch (`GameWindow.cs:7613–7690`). One call site:
|
||||
`DrawInside(viewer_cell)` every frame.
|
||||
- Requires: an outdoor `CLandCell` must be a valid `DrawInside` root whose flood immediately "sees
|
||||
outside" (`OutsideView` full) so terrain draws. This is the retail behavior (`viewer_cell` is a
|
||||
land cell when outside). **Open design point:** acdream's `LoadedCell` model may not currently
|
||||
represent the outdoor land cell as a floodable cell — see open trace #3 (§8). Resolve before coding.
|
||||
- Conformance: at the doorway, the frame *before* and *after* crossing the threshold run the same
|
||||
code path; `[pv-input]` `outRoot` stops toggling (there is no outRoot concept anymore).
|
||||
|
||||
**Phase R-A2 — Per-building floods (remove D3).**
|
||||
- Replace the single unified `Build` (when looking at buildings from outside) with retail's
|
||||
per-building constructions: during the terrain/landblock draw, for each visible building portal,
|
||||
run a small `ConstructView` rooted at that building portal (the `CBldPortal` overload), flooding
|
||||
only that building's cells. Port `BSPPORTAL::portal_draw_portals_only` (`0x53d870`) →
|
||||
`PView::DrawPortal` (`0x5a5ab0`) → `ConstructView(CBldPortal)` (`0x5a59a0`).
|
||||
- This is the **robustness mechanism** (small coarse per-building visibility absorbs eye jitter).
|
||||
- Conformance: capture `cell_draw_num` per building ≈ 2 (matches retail §3.4); membership stable as
|
||||
the (jittering) eye moves within a cell.
|
||||
|
||||
**Phase R-A3 — Remove the band-aids (D4, D5, D6) made dead by R-A1/R-A2.**
|
||||
- With per-building bounded floods, `MaxReprocessPerCell` (D4), `EyeInsidePortalOpening` (D5), and the
|
||||
NDC reciprocal (D6) should be removable. Remove each deliberately, re-running the conformance + the
|
||||
existing `PortalVisibilityBuilderTests`. Do NOT remove `ProjectToClip`/`ClipToRegion` (faithful).
|
||||
|
||||
**Phase R-A4 (optional, secondary) — Tighten the camera boom toward retail (D8); reconsider the
|
||||
render-position interpolation (D7).**
|
||||
- Only if, after R-A1–R-A3, residual flicker remains AND it correlates with eye jitter > retail's
|
||||
~36 µm. Match retail's boom damping/snap. Do NOT chase byte-stable (retail isn't). Treat the
|
||||
render-position interpolation as suspect but DO NOT rip it out blindly (it prevents 30 Hz judder;
|
||||
removing it regressed the branch last time — see §7).
|
||||
|
||||
**Testing strategy (critical — this is how we stop shipping unverified changes):**
|
||||
- **Conformance tests against the measured retail values in §3.4** (cell_draw_num per building ≈ 2,
|
||||
membership stable under eye jitter, one path across the threshold). These run WITHOUT the live
|
||||
client.
|
||||
- Keep all existing `PortalVisibilityBuilderTests` green where still applicable.
|
||||
- Keep the 14 `PlayerMovementControllerTests` green.
|
||||
- **Visual gate is the acceptance test** (the user at the doorway). But the conformance tests are the
|
||||
PRE-gate — never ship to the visual gate on a red/absent conformance test again.
|
||||
- Re-attach cdb to retail to capture any NEW retail value the implementation needs (the workflow in
|
||||
§7 is proven and fast).
|
||||
|
||||
---
|
||||
|
||||
## 7. DO NOT RETRY (every one of these is evidence-disproven — re-trying wastes days)
|
||||
|
||||
- **"Make the eye byte-stable at rest."** Retail's eye jitters ~36 µm (§3.4, MEASURED). Byte-stable
|
||||
is the wrong target. My render-position rest-snap fix this session did this, **failed (no change)
|
||||
AND regressed the inside/outside flap**, and was reverted (`cd974b2` → revert `9b1857a`). The
|
||||
jitter source is also NOT `RenderPosition` (stabilizing it changed nothing) — it is downstream in
|
||||
the camera boom / sweep. Don't re-snap RenderPosition.
|
||||
- **"Bound the portal re-enqueue churn" / bounded-propagation / enqueue-once.** There is **no churn**:
|
||||
measured `maxPop = 1` across 13k oscillating frames; 0 of 63k reciprocals ever clipped empty
|
||||
(`ACDREAM_PROBE_PORTAL_CHURN`, this session). The whole bounded-propagation plan
|
||||
(`docs/superpowers/plans/2026-06-08-portal-flood-bounded-propagation.md` + the
|
||||
`2026-06-08-portal-flood-enqueue-once-port-design.md` spec) is REFUTED by measurement. The
|
||||
apparatus commits (`687040b`, `e6fe4c6`, `a866c51`) are fine to keep as probes; the *fix premise*
|
||||
is dead.
|
||||
- **Physics rest µm-jitter** (`d6aa526` era). Refuted: 216k standstill records, 0 re-snaps; body
|
||||
byte-stable. 4 GREEN rest-stability tests prove it.
|
||||
- **Camera-drift / viewer-cell metastability dead-zone** (2026-06-05 3-part plan). The dead-zone is
|
||||
±0.2 mm in `point_inside_cell_bsp`; the eye crosses by metres, not sub-mm — irrelevant to this
|
||||
symptom. The boom snap (Part 1, `d2212cf`) is already shipped and KEPT.
|
||||
- **Two-pipe inside/outside split** — that was the ORIGINAL approach, abandoned 2026-05-30. Do not
|
||||
resurrect it. Retail has neither two pipes NOR a branch — it has ONE path (§3.1).
|
||||
- **Render-side debounce/grace/hysteresis on the branch or the clip** — forbidden band-aid
|
||||
(`feedback_no_workarounds`).
|
||||
- **Trusting a decomp INFERENCE about runtime behavior without a live trace.** This session burned a
|
||||
fix on the inference "RenderPosition jitter → eye jitter." The cdb-on-retail workflow (§ below) is
|
||||
the antidote: MEASURE, don't infer.
|
||||
|
||||
---
|
||||
|
||||
## 8. OPEN TRACES — finish these BEFORE writing the implementation plan
|
||||
|
||||
The oracle is ~90% complete. Three things must be traced/decided first (each is a focused cdb capture
|
||||
and/or decomp read; the workflow below makes each ~10 min):
|
||||
|
||||
1. **Where `SmartBox::viewer_sought_position` is written** (the camera boom that produces the
|
||||
~36 µm-jittering sought eye). The decomp agent did NOT find the write site (it's in the
|
||||
`camera_manager` / spring-arm chain). Trace it (cdb bp on writes to `SmartBox+0x58`, or read
|
||||
`CameraManager` methods) to know exactly how tight retail's boom is and what to match in D8.
|
||||
2. **`PView::ClipPortals` (`0x5a4...`) and `PView::AddViewToPortals` (`0x5a52d0` :433446)** —
|
||||
the per-cell flood propagation. Not yet read in detail. Needed for a faithful per-building
|
||||
`ConstructView` port (Phase R-A2). Read both.
|
||||
3. **How retail's `DrawInside`/`ConstructView` handle a `CLandCell` (outdoor) `viewer_cell`** — i.e.
|
||||
the pure-outdoor and outside-looking-in root. Confirm the outdoor land cell floods such that
|
||||
`outside_view` is full and per-building portals render via the terrain BSP. AND decide how
|
||||
acdream's `LoadedCell`/cell model represents the outdoor land cell as a floodable root (Phase R-A1
|
||||
open design point). This is the single biggest unknown for the rewrite.
|
||||
|
||||
Also nice-to-have: `viewer_sphere` radius used in `update_viewer` (the agent didn't look it up; our
|
||||
port uses 0.3 m — `PhysicsCameraCollisionProbe.ViewerSphereRadius`).
|
||||
|
||||
---
|
||||
|
||||
## 9. Apparatus (this session — REUSE IT, don't rebuild it)
|
||||
|
||||
**Retail-debugger toolchain (PROVEN this session):**
|
||||
- Binary: `C:\Turbine\Asheron's Call\acclient.exe` — verified pairs with `refs/acclient.pdb`
|
||||
(`py tools/pdb-extract/check_exe_pdb.py "C:/Turbine/Asheron's Call/acclient.exe"` → MATCH).
|
||||
- cdb: `C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\cdb.exe`.
|
||||
- Attach + capture pattern (background, tee to a log):
|
||||
```powershell
|
||||
& "C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\cdb.exe" -p <PID> -cf <script>.cdb *>&1 |
|
||||
Tee-Object -FilePath <log>
|
||||
```
|
||||
- **Watchouts learned this session:** (1) inside a bp action use **`.detach`**, NOT `qd` (cdb ignores
|
||||
`qd` in bp actions — per `retail-viewer-cell.cdb`); top-level `qd` is fine. (2) The captures exit
|
||||
with **code 5** but still produce the full log — exit 5 is expected here, not a failure. (3) The
|
||||
bps fire every render frame regardless of camera motion, so a counter-bounded capture auto-completes
|
||||
fast; cue the user to be doing the thing (sweeping) the whole window, or just capture the still pose.
|
||||
(4) Read floats as raw hex (`%08x` of `poi(addr)`) to see byte-stability directly. (5) `dt
|
||||
acclient!Type @ecx field` reads a named field with no manual offset.
|
||||
|
||||
**cdb scripts created (in `tools/cdb/`):**
|
||||
- `flap-render-lookup.cdb` — symbol + PView/SmartBox type dump (the source of §3.5).
|
||||
- `flap-pos-lookup.cdb` — Position/Frame type dump (eye-origin offsets).
|
||||
- `flap-render-capture.cdb` — per-frame `cell_draw_num` + `viewer_cell` ptr (both ConstructView
|
||||
overloads). The membership-stability capture. (`.logopen` path is hardcoded — edit per run.)
|
||||
- `flap-eye-stability.cdb` — per-frame eye origin (raw hex) pub + sought. The ~36 µm finding.
|
||||
|
||||
**Capture logs (worktree root, UNTRACKED — large; gitignore or delete after):**
|
||||
- `flap-render-retail.log` (still pose), `flap-render-retail-sweep.log`, `flap-eye-stability.log`,
|
||||
`flap-render-lookup.log`, `flap-pos-lookup.log`, and the `*-cdb-console.log` files.
|
||||
|
||||
**acdream-side probes (still in code, useful for the diff):**
|
||||
- `ACDREAM_PROBE_PVINPUT=1` → `[pv-input]` (eye/player/rawPlayer/yaw + flood count, 6dp) — the acdream
|
||||
eye/membership signal. `launch-flap-verify.ps1` runs it light.
|
||||
- `ACDREAM_PROBE_PORTAL_CHURN=1` → `[portal-churn]` (per-Build re-enqueue + reciprocal pre/post) — the
|
||||
apparatus that proved churn=0. Heavy (1.5 KB/line) — lags the client; use sparingly.
|
||||
- `ACDREAM_PROBE_FLAP=1` → `[flap]`/`[flap-cam]`/`[flap-sweep]`/`[pv-trace]` — per-frame side-test +
|
||||
projection. Heavy.
|
||||
- All gated in `AcDream.Core.Rendering.RenderingDiagnostics` (and `PhysicsDiagnostics`).
|
||||
- **These are throwaway apparatus.** Strip them once the port ships and is visual-gated.
|
||||
|
||||
**Launch scripts:** `launch-flap-verify.ps1` (light pv-input), `launch-flap-churn.ps1`,
|
||||
`launch-flap-capture.ps1`. acdream live-launch env vars: see CLAUDE.md "Running the client".
|
||||
|
||||
---
|
||||
|
||||
## 10. Git state & test baseline (start point for the fresh session)
|
||||
|
||||
- Branch `claude/thirsty-goldberg-51bb9b`, HEAD **`9b1857a`** = revert of the failed rest-snap fix.
|
||||
Working tree is at the **known-good baseline** (the cutover-flip state: the *huge* inside↔outside
|
||||
flap is GONE; only the grazing-doorway flicker residual remains).
|
||||
- This session's commits (newest first): `9b1857a` (revert) ← `cd974b2` (the bad rest-snap fix,
|
||||
reverted) ← `b3a9884` (launch-flap-churn.ps1) ← `a866c51` (churn anchor test) ← `e6fe4c6`
|
||||
(churn probe) ← `687040b` (churn flag) ← `a3dadbf` (bounded-propagation plan — now REFUTED) ←
|
||||
`ab6ed90`/`6c3a96b`/`d6aa526`/… (the refuted-diagnosis trail).
|
||||
- **The churn probe (`687040b`/`e6fe4c6`/`a866c51`) is fine to keep** (inert when off; it's the
|
||||
apparatus that disproved the churn hypothesis). The bounded-propagation *plan/spec* are refuted —
|
||||
mark them superseded by this handoff.
|
||||
- Test baseline (post-revert, verified): `PlayerMovementControllerTests` 14/14 green;
|
||||
`PortalVisibilityBuilderTests` (App) green; build green. (Full-suite has documented static-leak
|
||||
flakiness — run targeted.)
|
||||
- The two NEW tests I added for the rest-snap were removed by the revert (correct — the fix was wrong).
|
||||
|
||||
---
|
||||
|
||||
## 11. How the fresh session should start (concrete)
|
||||
|
||||
1. **Read this whole doc**, then §3 (the oracle) again. Read the two memory entries
|
||||
`project_indoor_flap_rootcause` (will be updated to point here) and
|
||||
`reference_render_pipeline_state`.
|
||||
2. **Confirm the baseline:** `git log --oneline -3` (HEAD `9b1857a`); `dotnet build` green;
|
||||
the targeted tests green.
|
||||
3. **Close the §8 open traces** (viewer_sought_position write site; ClipPortals/AddViewToPortals;
|
||||
the outdoor `CLandCell` root). Use the §9 cdb workflow on live retail if the user can run it; else
|
||||
decomp-read. **Do not start the implementation plan until these three are answered** — they
|
||||
determine the Phase R-A1 design (especially how to represent the outdoor land cell as a floodable
|
||||
root).
|
||||
4. **`superpowers:writing-plans`** → expand §6 into a phased task plan (R-A1 → R-A4) with conformance
|
||||
tests against §3.4's measured values.
|
||||
5. Implement phase-by-phase; conformance test PRE-gate; visual gate is acceptance; cdb-measure any new
|
||||
retail value you need. **Never ship to the visual gate on an unverified change again.**
|
||||
6. When it lands and is visual-verified: strip the apparatus (§9 probes), update the roadmap +
|
||||
milestones (M1.5), update memory, mark the flap CLOSED.
|
||||
|
||||
---
|
||||
|
||||
## 12. The one-sentence version (for when you're tired)
|
||||
|
||||
Retail draws inside and outside with **one path rooted at whatever cell the eye is in**, and it
|
||||
survives a jittering eye because its **per-building visibility is coarse and robust** — so stop
|
||||
patching the noise, delete our inside/outside branch and unified flood, and **build the one retail
|
||||
path.**
|
||||
17
launch-flap-verify.ps1
Normal file
17
launch-flap-verify.ps1
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
$env:ACDREAM_DAT_DIR = "$env:USERPROFILE\Documents\Asheron's Call"
|
||||
$env:ACDREAM_LIVE = "1"
|
||||
$env:ACDREAM_TEST_HOST = "127.0.0.1"
|
||||
$env:ACDREAM_TEST_PORT = "9000"
|
||||
$env:ACDREAM_TEST_USER = "testaccount"
|
||||
$env:ACDREAM_TEST_PASS = "testpassword"
|
||||
|
||||
# Verify the render-position rest-snap fix (cd974b2). LIGHT probe only (no portal-churn
|
||||
# spam, which lagged the client) so the visual gate is smooth. [pv-input] logs eye +
|
||||
# player(RenderPosition) + rawPlayer per frame; at the doorway, STANDING STILL, the fix
|
||||
# must make RenderPosition + eye byte-stable (was 15 / 17 distinct before; expect ~1 now).
|
||||
Remove-Item Env:\ACDREAM_PROBE_FLAP -ErrorAction SilentlyContinue
|
||||
Remove-Item Env:\ACDREAM_PROBE_PORTAL_CHURN -ErrorAction SilentlyContinue
|
||||
$env:ACDREAM_PROBE_PVINPUT = "1"
|
||||
|
||||
dotnet run --project src\AcDream.App\AcDream.App.csproj --no-build -c Debug 2>&1 |
|
||||
Tee-Object -FilePath "flap-verify.log"
|
||||
15
tools/cdb/flap-eye-stability.cdb
Normal file
15
tools/cdb/flap-eye-stability.cdb
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
* Retail eye-position stability at rest (2026-06-08)
|
||||
* THE crux is retail viewer (eye) origin byte-stable at rest or does it jitter
|
||||
* SmartBox+0x08 viewer Position objcell_id +0x04 frame +0x08 m_fOrigin +0x34
|
||||
* so viewer.objcell_id = ecx+0x0c origin x/y/z = ecx+0x44 / +0x48 / +0x4c (raw float bits in hex)
|
||||
* Also viewer_sought_position at SmartBox+0x58 (pre-collision desired eye) origin at ecx+0x94
|
||||
* Hold the camera still during this capture
|
||||
|
||||
.logopen C:\Users\erikn\source\repos\acdream\.claude\worktrees\thirsty-goldberg-51bb9b\flap-eye-stability.log
|
||||
.sympath C:\Users\erikn\source\repos\acdream\refs
|
||||
.symopt+ 0x40
|
||||
.reload /f acclient.exe
|
||||
|
||||
r $t0 = 0
|
||||
bp acclient!SmartBox::update_viewer "r $t0 = @$t0 + 1; .printf \"[eye] h=%d cell=%x pub=(%08x,%08x,%08x) sought=(%08x,%08x,%08x)\\n\", @$t0, poi(@ecx+0x0c), poi(@ecx+0x44), poi(@ecx+0x48), poi(@ecx+0x4c), poi(@ecx+0x94), poi(@ecx+0x98), poi(@ecx+0x9c); .if (@$t0 >= 120) { .detach } .else { gc }"
|
||||
g
|
||||
11
tools/cdb/flap-pos-lookup.cdb
Normal file
11
tools/cdb/flap-pos-lookup.cdb
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
.sympath C:\Users\erikn\source\repos\acdream\refs
|
||||
.symopt+ 0x40
|
||||
.reload /f acclient.exe
|
||||
.echo ===TYPE-Position===
|
||||
dt acclient!Position
|
||||
.echo ===TYPE-Frame===
|
||||
dt acclient!Frame
|
||||
.echo ===TYPE-CObjCell-id===
|
||||
dt acclient!CObjCell id
|
||||
.echo ===DONE===
|
||||
qd
|
||||
19
tools/cdb/flap-render-capture.cdb
Normal file
19
tools/cdb/flap-render-capture.cdb
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
* Retail render-state stability at the Holtburg doorway (2026-06-08)
|
||||
* Central question does retail cell_draw_num (visible-cell membership) flicker
|
||||
* as the camera sweeps or stay stable. Also viewer_cell ptr and eye cell.
|
||||
* Offsets from flap-render-lookup PView +0x60 cell_draw_num +0x74 cell_todo_num
|
||||
* +0x48 draw_landscape SmartBox +0x50 viewer_cell +0x08 viewer.objcell_id
|
||||
* Auto-detach via .detach after threshold (cdb ignores qd inside bp actions)
|
||||
* No semicolons in comment lines they split into commands
|
||||
|
||||
.logopen C:\Users\erikn\source\repos\acdream\.claude\worktrees\thirsty-goldberg-51bb9b\flap-render-retail-sweep.log
|
||||
.sympath C:\Users\erikn\source\repos\acdream\refs
|
||||
.symopt+ 0x40
|
||||
.reload /f acclient.exe
|
||||
|
||||
r $t0 = 0
|
||||
bp acclient!SmartBox::update_viewer "r $t0 = @$t0 + 1; .printf \"[uv] h=%d eyeCell=%x viewerCellPtr=%x\\n\", @$t0, poi(@ecx+0x08), poi(@ecx+0x50); .if (@$t0 >= 2500) { .detach } .else { gc }"
|
||||
bp 0x005a57b0 "r $t0 = @$t0 + 1; .printf \"[cv-cell] h=%d drawNum=%d todoNum=%d land=%d root=%x\\n\", @$t0, poi(@ecx+0x60), poi(@ecx+0x74), poi(@ecx+0x48), poi(@esp+4); .if (@$t0 >= 2500) { .detach } .else { gc }"
|
||||
bp 0x005a59a0 "r $t0 = @$t0 + 1; .printf \"[cv-bld] h=%d drawNum=%d todoNum=%d land=%d\\n\", @$t0, poi(@ecx+0x60), poi(@ecx+0x74), poi(@ecx+0x48); .if (@$t0 >= 2500) { .detach } .else { gc }"
|
||||
.printf "flap render trace armed sweep the camera at the doorway now\n"
|
||||
g
|
||||
Loading…
Add table
Add a link
Reference in a new issue