re: XPR2 channel order confirmed against the running game -- the second "dynamic unknown" closed

INDEX carried XPR2 as "colours unverified (dynamic item)". The failure mode that
matters is a swapped red/blue channel: invisible on greys, invisible without
ground truth, and it poisons every asset silently.

Method: pick an asset that is strongly asymmetric in hue and has known in-game
appearance. DeltaSaber_T.xpr (1024x1024 Dxt1, 11 mips) decodes to white/grey
panels with orange-rust accents; the game renders the same hull in the ARSENAL
scene.

  decoded texture : 1073 warm px, 0 cool, median saturated hue 23.3 deg
  in-game render  :  916 warm px, 124 cool, median saturated hue  9.3 deg

Both orange/red; a red<->blue swap would put the texture at ~200 deg. The cool
pixels are the blue UI behind the model and the 14 deg offset is the hangar's
red key light -- neither can move a hue by 180.

Stated as a bounded result, not "colours are correct": a hue-family comparison
cannot see a gamma/sRGB error, premultiplied alpha, or a per-channel scale, so
exact fidelity stays open and needs a flat unlit known-value surface. Cubemap
face ordering untouched. BG_Earth.xpr (A8R8G8B8 cubemap face) decoding to a
coherent nebula is corroboration for the de-tile step, not proof.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-11 21:21:55 +00:00
parent 38f58ea559
commit 671891f9bf
3 changed files with 52 additions and 1 deletions

View File

@@ -0,0 +1,51 @@
# XPR2 colours: channel order ✅ confirmed against the running game
[`INDEX`](INDEX.md) carried XPR2 as 🟡 with **"colours unverified (dynamic
item)"**. The failure mode that matters for a texture decoder is a **swapped red
and blue channel** — it is invisible on greys and on anything you have no ground
truth for, and it silently poisons every asset. This settles that one, and is
explicit about what it does *not* settle.
## Method
Pick an asset whose in-game appearance is unambiguous and **strongly
asymmetric in hue** — the Delta Saber's livery is white/grey panels with
orange-rust accents, so a red↔blue swap would render it cyan. Then compare the
decoded texture against the game rendering the same asset.
- **Decoded:** `hidden/resource3d/DeltaSaber_T.xpr` — 1024×1024, **Dxt1**, 11 mip
levels — exported with `sylpheed-cli texture export`.
- **In-game:** the Delta Saber standing in the ARSENAL scene, captured at
`x 700..830, y 180..480` of the framebuffer so the crop is hull, not UI.
Both images were resampled to 96×96 and every pixel classified; for the
saturated pixels (`maxmin ≥ 50`) the median hue was taken.
| | warm px (`R > B+40`) | cool px (`B > R+40`) | median hue of saturated px |
|---|---|---|---|
| decoded texture | **1073** | **0** | **23.3°** (orange) |
| in-game render | 916 | 124 | **9.3°** (orange-red) |
**Both land in the orange/red family; a red↔blue swap would put the decoded
texture at ≈200° (azure).** The 124 cool pixels in the game crop are the blue UI
panel behind the model, and the 14° offset is the hangar's red key lighting
tinting the lit model — both expected, neither able to move a hue by 180°.
Evidence: [`captures/xpr2-colour-check-deltasaber.png`](captures/xpr2-colour-check-deltasaber.png)
— decoded atlas beside the in-game hull.
## What this establishes, and what it does not
**Channel order is right** for the XPR2 path, on a DXT1 texture with mips.
Separately, `BG_Earth.xpr` decodes as a 1024×1024 **A8R8G8B8** cubemap face and
produces a coherent nebula rather than noise, so the de-tile step is sound on
that path too — though a nebula has no ground truth, so it is corroboration, not
proof.
🟡 **Exact colour fidelity is still open.** A hue-family comparison cannot see a
gamma/sRGB curve error, a premultiplied-alpha mistake, or a small per-channel
scale — all of which preserve hue. Settling those needs a flat, unlit,
known-value surface rather than a lit hero model.
❔ The `~15 %` of T8aD variants noted elsewhere and the XPR2 **cubemap face
ordering** are untouched by this.