Correct the tfrag pointer-field claim, and show morphing regenerates VU packets
Two corrections to the previous commit, both from continuing the RAM comparison.
1. The previous commit said the RAM comparison "names two fields the disc-side
work had left as data and an unnamed word". It does not. +0x10 is
TfragHeader::data and +0x38 is TfragHeader::cube_ofs, both already named in the
struct. What RAM adds is that both are POINTERS -- relocated by adding the
block's RAM base -- rather than plain offsets. That is still worth knowing, but
it is not a naming.
2. The previous commit attributed the 8.6% of rewritten bytes to "morphed
positions with topology and UVs left verbatim". That is wrong, and patch 1 shows
why. Comparing 4 KiB, disc 0xdf60 against RAM 0x855f60:
bytes differing 354 of 4,096 = 8.6%
contiguous runs 120
longest run 8 bytes
extent +0xc1 through the end of the window
Patch 1 has 83 positions and 83 vertex-info entries, of which 13 morph. If
morphing rewrote position fields alone, the differing bytes would be a 13/83 slice
of a 498-byte position array -- a couple of dozen bytes in one contiguous region.
Instead 354 bytes differ, spread over 120 short runs across the whole window.
So the loader is not patching positions in place: it REGENERATES the VU packet
stream for the current morph state. That is what morphing is on this hardware --
VU1 GIF packets, not a CPU-side vertex array.
Consequence for verification, and the reason this is worth committing rather than
just noting: the plan of "map RAM vertices to disc indices and solve for the weight"
does not work against this memory image. The positions are inside regenerated
packets, so there is no stable per-vertex correspondence to read a weight out of.
Verifying the morph weight against the original needs the CAMERA DISTANCE at the
savestate, so the weight can be computed from the derivation and compared with the
rendered result, rather than read back out of RAM.
Population figures gathered on the way: 833 of 833 patches parse, carrying 43,613
vertex-info entries; 499 of 833 patches have at least one morphing entry, and
patch 0 -- the first record -- is base-only with none.
Also fixed a measurement error mid-run: I compared patch 1 at disc 0xdf60 against
RAM 0x855f00, off by 0x60, and got "every byte differs". The corrected base is
0x855f60.
This commit is contained in:
@@ -844,7 +844,11 @@ block's RAM base to each. Every other byte of all 833 records is verbatim. The
|
||||
remaining six fields differ by 0xF80000 and 0x1F00000 rather than 0x848000 --
|
||||
three records pointing into a different buffer, recorded rather than averaged.
|
||||
|
||||
This names two fields the disc-side work had left as `data` and an unnamed word.
|
||||
**Correction:** both fields were already named. `+0x10` is
|
||||
`TfragHeader::data` -- the byte offset of the tfrag's data block, relative to the
|
||||
table -- and `+0x38` is `TfragHeader::cube_ofs`. The RAM comparison does not name
|
||||
them; what it adds is that both are **pointers**, relocated by adding the block's
|
||||
RAM base, rather than plain offsets.
|
||||
|
||||
**Result 2 -- behind the pointers, about 8.6% of bytes are rewritten.** Following
|
||||
record 0's `+0x10` to disc `0xd040` / RAM `0x855040` and comparing 16 KiB:
|
||||
@@ -858,7 +862,34 @@ command lists stay verbatim while a fraction of the position bytes move. 8.6% is
|
||||
about half of a 16-byte vertex's 6 position bytes, i.e. consistent with roughly
|
||||
half the vertices in this patch being mid-morph at the saved camera distance.
|
||||
|
||||
**Not established:** that the morphed bytes match `resolve_vertex`'s formula. That
|
||||
needs mapping RAM vertices to disc vertex indices and solving for the weight, which
|
||||
is the next step -- but the framing is now known well enough to attempt it, and
|
||||
the two pointer fields are confirmed rather than inferred.
|
||||
### How much of a patch actually changes, and where
|
||||
|
||||
Patch 1 (13 morphing entries of 83) compared over 4 KiB, disc `0xdf60` against RAM
|
||||
`0x855f60`:
|
||||
|
||||
bytes differing 354 of 4,096 = 8.6%
|
||||
contiguous runs 120
|
||||
longest run 8 bytes
|
||||
extent +0xc1 through the end of the window
|
||||
|
||||
83 positions at 6 bytes each is 498 bytes, and only 13 of 83 vertices morph, so
|
||||
**if morphing rewrote position fields alone** the differing bytes would be a
|
||||
13/83 slice of a 498-byte array -- a couple of dozen bytes in one contiguous
|
||||
region. Instead 354 bytes differ, spread over 120 short runs across the whole
|
||||
window.
|
||||
|
||||
So the loader is not patching positions in place. It **regenerates the VU packet
|
||||
stream** for the current morph state, which on this hardware is what morph
|
||||
actually is -- VU1 GIF packets, not a CPU-side vertex array.
|
||||
|
||||
**Consequence for verification.** The plan of "map RAM vertices to disc indices and
|
||||
solve for the weight" does not work against this memory image: the positions are
|
||||
inside regenerated packets, so there is no stable per-vertex correspondence to
|
||||
read a weight out of. Verifying the morph weight against the original needs the
|
||||
**camera distance at the savestate** so the weight can be computed from the
|
||||
derivation and compared with the rendered result, rather than read back out of
|
||||
RAM.
|
||||
|
||||
Along the way, two population figures worth having: **833 of 833 patches parse**,
|
||||
carrying **43,613** vertex-info entries, and **499 of 833 patches have at least
|
||||
one morphing entry** -- patch 0, the first record, is base-only with none.
|
||||
|
||||
+17
-10
@@ -249,16 +249,23 @@ Phase 1 hands over the extents; this phase works out what each one contains.
|
||||
`+0x10` and `+0x38`, verified against PCSX2 across all **833** records: the
|
||||
loader adds the block's RAM base (`0x848000`) to each, every other byte of
|
||||
every record is verbatim, and 1,665 fields differ where 1,666 were possible.
|
||||
This names two fields previously left as `data` and an unnamed word.
|
||||
- [~] Behind those pointers, **8.6% of bytes are rewritten** (14,977 of 16,384
|
||||
identical), spread across many field positions rather than concentrated --
|
||||
consistent with morphed positions with topology and UVs left verbatim.
|
||||
- [ ] What tfrag is **not** yet verified is that the morphed bytes match
|
||||
`resolve_vertex`'s formula. The LOD switch distances, morph weight and
|
||||
collapse edge are derived from the data (tiers classified from the four V4_8
|
||||
parent arrays, weight running zero to one across each band); the framing is
|
||||
now known well enough to map RAM vertices to disc indices and solve for the
|
||||
weight, which is the next step.
|
||||
They are the existing `TfragHeader::data` and `::cube_ofs` -- what RAM adds
|
||||
is that both are **pointers**, not plain offsets. All **833** patches parse,
|
||||
carrying **43,613** vertex-info entries; **499 of 833** have at least one
|
||||
morphing entry.
|
||||
- [~] Behind those pointers, **8.6% of bytes are rewritten** (354 of 4,096 on
|
||||
patch 1), spread over 120 short runs across the whole window rather than
|
||||
concentrated in a position array. Morphing on this hardware is VU1 GIF
|
||||
packets, and the loader **regenerates the packet stream** rather than
|
||||
patching position fields in place.
|
||||
- [ ] What tfrag is **not** yet verified is that the morph matches
|
||||
`resolve_vertex`'s formula. Reading a weight back out of RAM does **not**
|
||||
work -- the positions live inside regenerated VU packets, so there is no
|
||||
stable per-vertex correspondence. The LOD switch distances, morph weight and
|
||||
collapse edge remain derived from the data (tiers classified from the four
|
||||
V4_8 parent arrays, weight running zero to one across each band); verifying
|
||||
them needs the **camera distance at the savestate** so the weight can be
|
||||
computed and compared against the rendered result.
|
||||
- [x] `tie` — **fully chained.** `resolve_tie_class` walks both indirections: the
|
||||
class table is in `core_index.bin`, and the entry's `offset_in_asset_wad`
|
||||
addresses `core_data.bin` (not `core_data.wad`). All **1,864** tie classes
|
||||
|
||||
Reference in New Issue
Block a user