Parse tfrag terrain blocks and decode all vertex positions
Adds gc-formats::tfrag and a gated real-disc test that decodes every terrain patch on all 27 levels. Two rules decide whether this works at all, and both were wrong on the first attempt: The vector format is encoded in the VIF command byte, not the immediate. Bits 2-3 are vn (component count minus one) and bits 0-1 are vl, giving element_size = ((32 >> vl) * (vn + 1)) / 8. Terrain positions are vn=2, vl=1 (command 0x69) at 6 bytes. Reading the format from the immediate's low nibble -- the more commonly quoted layout -- yields 12 or 16 bytes and misaligns everything after it. Unpack payloads pad to 4 bytes, and several non-unpack commands carry inline data: STROW/STCOL 16, STMASK 4, MPG num*8, DIRECT/DIRECTHL imm*16. Ignoring those desynchronises the walk, and a desynchronised walk still finds code words that look like positions. Positions are also not all in one list. vert_count is the total across three tiers, the LOD-2 list precedes the common one in offset order, and the LOD-0-only list starts at shared_ofs + lod_1_size*16 rather than at an offset of its own. Scanning only the common list matches about a quarter of patches, each exactly -- which is what made it convincing. The invariant is that every patch yields exactly its own vert_count. It caught both errors, so tfrag_positions returns None on a mismatch rather than partial geometry. All 27 levels: 18,102 patches, 821,439 vertices, zero mismatches. Still open: strips, triangle indices and the RGBA vertex-info arrays. Positions alone are not drawable.
This commit is contained in:
+11
-4
@@ -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: the geometry directory's class tables, then `tfrag` terrain.
|
||||
- [ ] Next: tfrag triangle topology, then the geometry directory's class tables.
|
||||
|
||||
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,
|
||||
@@ -114,9 +114,16 @@ Phase 1 hands over the extents; this phase works out what each one contains.
|
||||
- [x] Region layout of `core_data` established: tfrag, occlusion, sky, collision
|
||||
and `textures_base` are offsets, strictly increasing on all 27 levels.
|
||||
Terrain is `[0, occlusion)` and carries inline texels.
|
||||
- [ ] **tfrag vertex format: not recovered.** The 64-byte record framing and the
|
||||
scale-8 fixed-point reading were both tested and disproven; see
|
||||
`plan/level-assets.md` for what was ruled out.
|
||||
- [x] **tfrag vertex format: recovered.** The tfrags block header gives a table
|
||||
offset (`0x40`) and a patch count; each patch is a 0x40-byte header whose
|
||||
`data` offset is relative to the table, not to the block. Positions are
|
||||
V3_16 payloads inside a VIF1 command list, split across three lists per
|
||||
patch (common, LOD 0+1, LOD 0). The whole vector format is encoded in the
|
||||
command byte and every element size follows from it. Verified on all 27
|
||||
levels: **18,102 patches, 821,439 vertices**, each patch yielding exactly
|
||||
its own `vert_count`. See `plan/level-assets.md` and `gc-formats::tfrag`.
|
||||
- [ ] tfrag topology: strips, triangle indices and the RGBA vertex-info arrays,
|
||||
which is what turns positions into drawable geometry.
|
||||
- [ ] `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