Disprove that morphing causes the tfrag byte changes: LOD selection does

Read the whole tfrags block from RAM -- 4,379,200 bytes at 0x848000 -- and compared
every one of the 833 patches against its disc copy, grouped by whether it CAN morph:

  patches with NO morphing entries   334   differing: max 10.59%, median 7.80%
  patches WITH morphing entries     499   differing: max 10.49%, median 8.12%
  patches 100% identical to disc   0/334 and 0/499

The two groups are indistinguishable. Morphing is NOT what changes the bytes, and
this disproofs the claim two commits ago that the 8.6% was "morphed positions with
topology and UVs left verbatim". If it were, the 334 patches with no morphing
entries would be untouched -- and none of them is.

Nor is it a pointer relocation: the differing bytes fall in short structured runs
(257-263, 272-277, 305 on patch 0) whose u32 deltas share no common value.

The explanation consistent with everything measured is that the loader REGENERATES
each patch's VU packet stream for the LOD tier it selected. That happens to every
patch, morphing or not -- and it is why morphing shows no signal.

This also settles the verification route, which the previous commit left as "needs
the camera distance at the savestate". It does not, and no camera distance would
help: this memory image shows the LOD-selected form rather than the morph state,
so there is nothing in it to solve a weight against. Verifying the derivation needs
a captured frame at a known view distance instead.

What is established now, all measured on the full population rather than inferred
from one patch:
  - data and cube_ofs are relocated pointers, delta 0x848000, on all 833 records
  - every patch's bytes are rewritten, ~8%, by LOD-driven packet regeneration
  - morphing is not observable in this memory image
  - 833 of 833 patches parse; 499 of 833 have morphing entries; 43,613 vertex-info
    entries across the level
This commit is contained in:
2026-10-13 05:14:40 +02:00
parent aff977e8c5
commit 3ee0b3d0dc
2 changed files with 30 additions and 5 deletions
+22
View File
@@ -864,6 +864,28 @@ half the vertices in this patch being mid-morph at the saved camera distance.
### How much of a patch actually changes, and where
The whole tfrags block was read from RAM -- 4,379,200 bytes at `0x848000` -- and
every one of the 833 patches compared against its disc copy, grouped by whether
it *can* morph at all.
patches with NO morphing entries 334 differing bytes: max 10.59%, median 7.80%
patches WITH morphing entries 499 differing bytes: max 10.49%, median 8.12%
patches 100% identical to disc 0/334 and 0/499
**The two groups are indistinguishable.** Morphing is **not** what changes the
bytes. This is the disproof of the claim above that the 8.6% was "morphed
positions with topology and UVs left verbatim" -- if it were, the 334 patches with
no morphing entries would be untouched, and none of them is.
Nor is it a relocation: the differing bytes fall in short structured runs
(257-263, 272-277, 305 on patch 0) whose `u32` deltas have no common value.
The remaining explanation, consistent with everything measured, is that **the
loader regenerates each patch's VU packet stream for the LOD tier it selected** --
which happens to every patch, morphing or not. That also settles the verification
route: this memory image shows the LOD-selected form, not the morph weight, so the
weight cannot be read out of it at any camera distance.
Patch 1 (13 morphing entries of 83) compared over 4 KiB, disc `0xdf60` against RAM
`0x855f60`:
+8 -5
View File
@@ -253,11 +253,14 @@ Phase 1 hands over the extents; this phase works out what each one contains.
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.
- [x] Behind those pointers, **every patch's bytes are rewritten** -- and it is
**not morphing**. Across all 833 patches: the 334 with **no** morphing
entries differ by a median **7.80%**, the 499 with morphing entries by
**8.12%**, and **0 of 833** are byte-identical to disc. Morphing does not
predict which bytes change. Nor is it a relocation: the differences fall in
short structured runs with no common `u32` addend. The loader regenerates
each patch's **VU packet stream for the LOD tier it selected**, which
happens to every patch regardless of morphing.
- [ ] 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