Entry 55
ccMorpher::Modify against the game: the weight's lanes and the EE rescale
This entry answers the question left open by Stream 2's ribbons morph: ccMorpher::Modify ported, the tendrils around the figure.
Filescrates/piney-data/src/model.rscrates/piney-data/examples/morph_probe.rstools/test_morph_rs.py
54 ported ccMorpher::Modify from the disassembly alone. This entry
runs the game's own function against the port.
The check
tools/test_morph_rs.py runs Modify (0x0013af10) natively in eemu, with
test_anim's VU0 macro and MMI support. Each run sets memory up as
ccModel::Draw leaves it for the call:
- the morpher and its targets (
ccModel, index, chunk andccMmateach); ccDrawModelParamwith rwflag bit 0 set, so the blend is written in place instead of into aGetWorkbuffer (the arithmetic is the same);ccSys's work pointer (+604) at free memory.
It compares every position with what the morph_probe example gives, over
302 morphers:
- one to four targets;
- weights in and outside [0, 1], negative ones included;
- positions over the whole s16 range;
- targets at the base's scale and at others;
- two cases for the 16-bit wrap and saturation.
What the first runs found
Simple cases matched from the start, which showed the harness was right. Two things did not:
-
Negative weights. The weight goes into halfword lanes as
(r << 16 | r) << 16 | r, whererholdsvftoi12's resultxin its low word and, above it, the float'slwsign extensiony. That leaves:x.loin the x lane;x.lo | x.hiin the y lane;x.lo | x.hi | y.loin the z lane.
For weights in [0, 8) all three are the weight. A negative weight leaves the y and z lanes at -1.
Model::morphnow spreads the weight the same way. -
The rescale. A target at another scale is multiplied by
div.s(target, base)withmul.s, then truncated byvftoi0. In IEEE arithmetic the factor rounds to nearest, and a value could land one off. The rescale now usespiney_data::field::ee, the bit-exact EE float arithmetictest_field_rs.pyalready checks against eemu.
After both changes, all 302 morphers give equal positions.
GetWorknever ran. The first-call path, where the blend goes into a work buffer fromccDrawPacketCtrl::GetWorkand replaces the mmat'svertexData, is not run here. Its arithmetic is the same code. - Weights in the data. Whether any stream uses a negative weight or one of 8 or more, where the lanes differ, is not surveyed.