Inverting the capture↔part question (invert_capture over one container, vcount_index over all 166) identifies every large draw in the 2026-07-31 capture: the player's own DeltaSaber (10891 verts), its weapon packs, the backdrop and particles. Of f101/e105/e106 only 1-3 of 15-37 resources have a drawn vcount, each a 44-225-vertex far-LOD/effect piece whose count collides with dozens of unrelated resources. So the zero-correlation was not an LOD-list gap, not over-strict position validation and not a different draw path: the ships were too far away to be drawn. approach_capture.py flies at a locked capital ship and presses F10 per range band, stamping each capture with its distance. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
154 lines
8.8 KiB
Markdown
154 lines
8.8 KiB
Markdown
# Capital-ship placement — does the `e106` result generalise? (WIP, 2026-07-31)
|
||
|
||
**Status:** 🚧 **WIP, time-boxed session.** Two results so far: a static audit across all
|
||
22 stage containers (done, below) and a first in-mission F10 capture run in Stage 02
|
||
(done — three capture logs, but **no capital-ship part correlated**; see "Open").
|
||
|
||
Context: [`BACKLOG.md`](BACKLOG.md) — "Capital ships assemble wrong in the viewer",
|
||
reported 2026-07-30. The oracle and the correlator already exist
|
||
([`ship-placement-runtime-capture.md`](ship-placement-runtime-capture.md)); the open
|
||
question is whether the rules derived from the one validated ship (`e106`, Stage_S01)
|
||
hold for other classes.
|
||
|
||
## 1. Static audit across all stages (offline, reproducible)
|
||
|
||
```
|
||
cargo run --release --example ship_audit -- ../sylph_extract/hidden/resource3d
|
||
```
|
||
|
||
87 lines of output, of which:
|
||
|
||
- **Only two OUTLIER lines, and they are the same ship twice**:
|
||
`Stage_S03`/`Stage_S27`, `f002_bdy_05` centroid `[-1398 6251 918]`, `dist=6540`
|
||
vs a cluster spread of `1071`. Every other assembled ship in every other stage
|
||
has all parts inside its own cluster.
|
||
→ **The user-visible breakage is NOT a gross static-placement outlier for most
|
||
classes.** Whatever is wrong in the viewer is either subtler than "part flung far
|
||
away" (wrong rotation, wrong mirror, missing part) or lives in the viewer, not in
|
||
`assemble_ship`. `f002_bdy_05` is a genuine, separate, reproducible static bug.
|
||
- **MULTIKEY**: joint tracks with more than one keyframe, which `read_trs9`'s
|
||
single-key read does not model. Recurring rigs: `e_rou_f104` (3), `e_rou_f105` (2),
|
||
`e_rou_f106` (2), `e_rou_e102` (6), `e_rou_e108_Missile_open` (2), `e_rou_e501` (1),
|
||
and the `e901` boss with 2–15 tracks per pose. Several of these (`Missile_open`, the
|
||
`e901_attack*` poses) are obviously *animation* and harmless for a static pose; the
|
||
plain hull rigs `f104`/`f105`/`f106`/`e102` are **not** obviously animation and are
|
||
the best hypothesis for a class-specific assembly error. ❔ **HYPOTHESIS — not
|
||
verified.** `e106`, the one validated ship, has **no** multikey tracks, which is
|
||
exactly how a rule that only works for single-key rigs could have passed unnoticed.
|
||
|
||
Raw audit output is reproducible with the command above (not checked in; it is
|
||
deterministic from the disc).
|
||
|
||
## 2. First in-mission capture run (Stage 02)
|
||
|
||
New tool: [`tools/re-capture/ship_capture_session.sh`](../../tools/re-capture/ship_capture_session.sh)
|
||
— one blocking session (per the session-lifetime rule): boot → Stage 02 in flight →
|
||
N× {screenshot, F10, small yaw}. Each F10 writes its own
|
||
`xenia_ship_capture_NN.log` next to the binary.
|
||
|
||
Run 2026-07-31, 5 presses requested:
|
||
|
||
- **Boot to in-flight took 24 s** (`skip_intro.sh` skipped the movie at 1 s and 6 s,
|
||
title at 11 s, HUD shield bar at 24 s) — much faster than the ~100 s in the notes.
|
||
- **3 of 5 F10 presses produced a log** (`_01`…`_03`, 2964 / 3111 / 3668 draws).
|
||
Logs (8–10 MB each) and the screenshots are at `/sylph-home/re/shipcap/`; not
|
||
committed for size. One screenshot is checked in as
|
||
[`captures/shipcap-stage02-launch.png`](captures/shipcap-stage02-launch.png).
|
||
- The screenshot confirms the capture frames are real in-mission combat frames
|
||
(HUD live, `REMAINING OB 004`, ACROPOLIS + a Destroyer labelled on screen, a
|
||
capital-ship hull filling the bottom of the frame).
|
||
|
||
### Result: no correlation yet ❌
|
||
|
||
```
|
||
correlate_capture xenia_ship_capture_03.log Stage_S02 <id> bdy_01
|
||
```
|
||
for `f101` (ACROPOLIS), `f105`, `f106`, `e105` reports *"no draw matches any LOD
|
||
(culled/off-screen?)"* for essentially every part — only two speculative LOD tries
|
||
(`f101_bdy_03` vcount 90 `[l]`, `e105_wep_01` vcount 60 `[l]`) and **zero accepted
|
||
matches**.
|
||
|
||
That is a **negative result, and it is not yet explained**. Facts collected:
|
||
|
||
- The capture is not empty or degenerate: 3668 draws in `_03`, top shaders
|
||
`0xDA51B0745ABF85D2` (1258), `0xE0BAFB4F520FE441` (1091), `0xEEA84C59D7F95371` (770).
|
||
None is the `e106` ship-shader hash from the 2026-07-26 capture; the F10 path does
|
||
not filter by hash, so this alone is not the cause.
|
||
- Large vertex counts *are* present (3024, 2772, 1736, 1612, 1240 …), so capital-ship-
|
||
sized geometry is being drawn.
|
||
|
||
Candidate explanations, **untested**:
|
||
1. the Stage-02 capital ships on screen are drawn from LOD/damage variants
|
||
(`_d00`, `_m`, `_l`) whose vcounts the correlator's variant list does not cover;
|
||
2. the position-validation step rejects otherwise-correct vcount hits (the capture
|
||
dumps ≤64 positions — a set-membership test against the wrong variant fails);
|
||
3. the ships in view at launch are drawn by a *different* draw path than `e106` in
|
||
Stage_S01 (e.g. instanced/batched), so no single draw equals one part.
|
||
|
||
**First step next session:** take the largest few vcounts in the capture and ask which
|
||
decoded part in `Stage_S02.xpr` has that count (invert the match), instead of asking
|
||
per-part whether a draw exists. That distinguishes (1)/(2) from (3) immediately.
|
||
|
||
## 3. The inverted match — the ships were never drawn (2026-08-10) ✅ explained
|
||
|
||
The inversion was run and it settles the negative result. Two new tools:
|
||
|
||
```
|
||
cargo run --release --example invert_capture -- <capture.log> Stage_S02 [top_n] [--ship f101]
|
||
cargo run --release --example vcount_index -- ../sylph_extract/hidden/resource3d <capture.log>
|
||
```
|
||
`invert_capture` asks, of the capture's own vertex counts, which resource in one stage
|
||
container has that count; `vcount_index` asks the same across **all 166 containers**
|
||
(5480 resources), so a draw whose geometry lives in `Common.xpr`, a `rou_*` weapon pack
|
||
or a `DeltaSaber_*` player-craft pack is identified instead of coming back "unknown".
|
||
|
||
On `xenia_ship_capture_03.log` (3668 draws, Stage 02):
|
||
|
||
| capture vcount | draws | what it is |
|
||
|---|---|---|
|
||
| 10891 | 28 | **`DeltaSaber_T:f001`** — the player's own craft |
|
||
| 6000 | 14 | `Stage_S02:n006_02` — backdrop |
|
||
| 1096 / 1008 / 841 / 215 / 127 | 14–112 | `rou_f001_wep_*` — the player's weapons |
|
||
| 417 / 279 / 201 / 167 | 104–448 | `Base:j00*`, `ptc_pack:*` — HUD/particles |
|
||
| 8 / 4 / 3 / 1 | 317–590 | particle quads |
|
||
|
||
- **Not one capital-ship hull part appears.** Per ship: `f101` **1 of 15** resources had a
|
||
drawn vcount (`f101_bdy_03_l`, 90 verts), `e105` 3 of 37, `e106` 3 of 34 — and each of
|
||
those hits is a 44–225-vertex `_l`/`_b` piece whose count also collides with dozens of
|
||
unrelated resources, i.e. probably not even the ship.
|
||
- The 3668 draws span **~14 frames** per F10 press and use only **10 distinct vertex
|
||
shaders**, and the player's own craft is captured at **full detail with its `c0..c2`
|
||
WVP rows** — so the capture path itself is healthy and unfiltered.
|
||
- The screenshot ([`captures/shipcap-stage02-launch.png`](captures/shipcap-stage02-launch.png))
|
||
agrees once read carefully: the hull "filling the bottom of the frame" is the **player's
|
||
own craft** in the chase view. The nearest contact on the HUD is a wingman's engine trail.
|
||
|
||
**So hypothesis (3) is dead, and (1)/(2) never applied.** The correlator's message
|
||
"no draw matches any LOD (culled/off-screen?)" was literally true: the ships were far
|
||
enough away that the renderer drew nothing of them. `correlate` additionally cannot
|
||
anchor without the reference part, and `f101_bdy_01` was never drawn at any LOD.
|
||
|
||
**The variable that was never controlled is RANGE.** New tooling closes that gap:
|
||
[`tools/re-capture/approach_capture.py`](../../tools/re-capture/approach_capture.py)
|
||
locks onto a capital ship (definition size-radius ≥ 150 = not a fighter), flies at it
|
||
with navigator.py's drift compensation and CPA avoidance, firing disabled, and presses
|
||
F10 as each range band is crossed (8000 / 6000 / 4500 / 3000 / 2000 / 1400 / 900),
|
||
stamping every capture with its distance in `approach-bands.jsonl`. Driver:
|
||
[`ship_capture_close.sh`](../../tools/re-capture/ship_capture_close.sh). Besides giving
|
||
the correlator a full-detail frame, the stamped bands measure the game's own **LOD
|
||
ladder** per part, which the reborn renderer needs anyway.
|
||
|
||
## Honest summary
|
||
|
||
- ✅ Static assembly is **not** grossly broken across stages — 1 outlier ship
|
||
(`f002_bdy_05`), reproducible.
|
||
- 🟡 A concrete, testable hypothesis for class-specific breakage exists (multikey joint
|
||
tracks on `f104`/`f105`/`f106`/`e102`; `e106` has none).
|
||
- ❌ The runtime oracle has **not** reproduced on a second ship class yet — but the
|
||
reason is now known and is not a format or correlator bug: **the captures were taken
|
||
at ranges where no capital-ship geometry is drawn at all** (§3, verified by inverting
|
||
the match against all 5480 decoded resources). Do not assume the `e106` rules
|
||
generalise; equally, do not read the zero-match as evidence against them.
|
||
- ▶ Next: run `ship_capture_close.sh` so the capture happens with a hull actually on
|
||
screen, then re-run `correlate_capture` on the closest band.
|