Decode tfrag texture bindings, vertex colours and light records
Adds level_core.rs (the uncompressed core index header and texture table) and extends tfrag.rs with the data a draw call needs beyond geometry. An ad-gif is five A+D quadwords holding packed fields rather than GS registers; the level load rewrites them in place before drawing. On disc TEX0_1.data_lo indexes the level's texture table, TEX1_1.data_lo is the LOD bias K and data_hi the minification filter, and CLAMP_1 holds both wrap modes. Three things are easy to get wrong here: * K is a signed 12-bit value in 1/16 units, sign-extended by shifting the field up 4 and back down. Retail values are -137..-89 mip-scaled; reading it unsigned gives 1911 instead of -137. * TEX1_1.data_hi is MMIN, and 4 decodes as LinearMipmapNearest, not LinearMipmapLinear as the raw bit layout suggests. * RGBA and the light/normal records are not in the VIF lists at all. They sit at fixed offsets in the patch data block: rgba_size*4 colours at rgba_ofs, vert_count light records at light_ofs+0x10. The tfrag origin lives there too -- if no STROW row supplied one, the quadword at light_ofs is it. The texture table checks out on its own terms: 93 entries at index offset 0x2900 on level 0, and 0x2900 + 93*16 lands exactly on the next range. Verification: an ad-gif index outside the level's table would mean the geometry and the textures were misread independently, so every one is range-checked against real entries. All 27 levels: 47,950 ad-gifs over 18,102 patches, all in range, referencing 1429 of 1595 level texture entries (90%). Each patch's light array is exactly vert_count records and its RGBA array exactly rgba_size*4. Still open: LOD selection and morphing, and the texel data itself.
This commit is contained in:
@@ -357,8 +357,45 @@ element size from the immediate, 2-byte payload padding, ignoring non-unpack
|
||||
payloads, and banking strips by arrival order. Every one produced parseable,
|
||||
plausible geometry; only the header's own triangle count caught them.
|
||||
|
||||
### Texture bindings, colours and lights
|
||||
|
||||
Also settled. Adds `level_core.rs` for the index header and the texture table,
|
||||
which is the join between tfrags and texel data.
|
||||
|
||||
A tfrag's **ad-gifs** are five A+D quadwords (0x50 bytes) holding packed
|
||||
fields rather than GS registers; the level load rewrites them in place before
|
||||
drawing. On disc `TEX0_1.data_lo` indexes the level's texture table,
|
||||
`TEX1_1.data_lo` is the LOD bias K and `data_hi` the minification filter, and
|
||||
`CLAMP_1` holds the two wrap modes. The texture table is a count/offset pair at
|
||||
index offset 0x30, entries of 16 bytes.
|
||||
|
||||
The table checks out on its own terms: on level 0 it is 93 entries at 0x2900,
|
||||
and 0x2900 + 93 x 16 lands exactly on the next range at 0x38.
|
||||
|
||||
Three details worth writing down:
|
||||
|
||||
* **K is a signed 12-bit value in 1/16 units**, sign-extended by shifting the
|
||||
field up 4 and back down. Retail values are -137..-89, which is -8.56..-5.56
|
||||
mip levels. Reading the field as unsigned gives 1911 instead of -137.
|
||||
* **`data_hi` of TEX1_1 is MMIN, and 4 is `LinearMipmapNearest`**, not
|
||||
`LinearMipmapLinear` as the bit layout alone suggests.
|
||||
* **RGBA and the light/normal records are not in the VIF lists.** They sit at
|
||||
fixed offsets in the patch's data block: `rgba_size * 4` four-byte colours at
|
||||
`rgba_ofs`, and `vert_count` eight-byte records at `light_ofs + 0x10`. The
|
||||
origin also lives there — if no STROW row supplied one, the quadword at
|
||||
`light_ofs` is the origin.
|
||||
|
||||
### Verification
|
||||
|
||||
Every ad-gif must index a texture that exists, or the geometry and the texture
|
||||
data have been misread independently of each other. Across all 27 levels:
|
||||
**47,950 ad-gifs over 18,102 patches, all indices in range**, referencing
|
||||
1429 of the 1595 level texture entries (90%). Each patch's light array is
|
||||
exactly `vert_count` records and its RGBA array exactly `rgba_size * 4`.
|
||||
|
||||
### Still open
|
||||
|
||||
The ad-gif texture bindings (which texture each primitive samples), the RGBA
|
||||
per-vertex colours and light records, and the LOD switch and morph rules in
|
||||
`TfragProc`. Positions and topology are drawable but not yet textured.
|
||||
The LOD switch and morph rules in `TfragProc`, the texel data itself, and the
|
||||
GS register values the level load builds from the texture entry. A tfrag can
|
||||
now be turned into positioned, UV-mapped, textured triangles; what is missing
|
||||
is choosing which LOD to draw and decoding pixels.
|
||||
|
||||
+8
-3
@@ -20,7 +20,7 @@ early, while they are still cheap to retire.
|
||||
header, and the `data` extent is parsed as a container of byte ranges. The
|
||||
geometry decompresses: level 0 yields 16,969,296 bytes of level content
|
||||
from a 9.7 MB stream.
|
||||
- [ ] Next: tfrag texture bindings, then the geometry directory's class tables.
|
||||
- [ ] Next: tfrag LOD selection and morphing, then the first drawable frame.
|
||||
|
||||
The disc being the exact build the reference projects pin is the single
|
||||
biggest favourable fact so far: no disc conversion, no regional timing work,
|
||||
@@ -128,8 +128,13 @@ Phase 1 hands over the extents; this phase works out what each one contains.
|
||||
rather than positions. Every patch's strips expand to exactly its own
|
||||
`tri_count`: **18,102 patches, 809,879 triangles**, all indices in range.
|
||||
See `plan/level-assets.md` and `gc-formats::tfrag`.
|
||||
- [ ] tfrag shading inputs: the ad-gif texture bindings, per-vertex RGBA and
|
||||
light records, then the LOD switch and morph rules in `TfragProc`.
|
||||
- [x] **tfrag texture bindings and per-vertex data.** The ad-gifs decode to a
|
||||
texture index, wrap modes, minification filter and LOD bias; the level
|
||||
index gained a texture table (`level_core.rs`) for those indices to land
|
||||
in. Per-vertex RGBA and light/normal records recovered from their fixed
|
||||
offsets. All 27 levels: 47,950 ad-gifs, every index in range, 90% of the
|
||||
level texture tables referenced.
|
||||
- [ ] tfrag LOD selection: the switch and morph rules in `TfragProc`.
|
||||
- [ ] `gameplay` — begins `WAD` + a per-level byte, and does *not* decode with
|
||||
the WAD LZ77 decoder. All 27 levels fail identically, so the input is
|
||||
wrong rather than the decoder. Ruled out: bit order, header length, the
|
||||
|
||||
Reference in New Issue
Block a user