BT410 5.3.65: THE MECH STANDS -- wire matrix convention proven from engine source
pose_chase.png: the MadCat assembled from its 19 parts, standing in the arena at its live articulated position. Feet, legs, torso, weapon pods, canopy plate. Not a collapsed stack at the origin, which is what every prior frame actually showed. The convention is no longer inferred -- it is read from the engine. RootRenderable's ctor writes an entity pose into a DCS with *(Matrix4x4*)dpl_GetDCSMatrix(myDCS) = localToWorld, so Matrix4x4::operator=(const AffineMatrix&) IS the wire layout: row-major, rotation in rows 0-2, zeros in column 3, translation in ROW 3 (entries 12/13/14). The old 3/7/11 choice rested on two errors, both corrected in the code comments: AffineMatrix does keep translation at 3/7/11, but because it is COLUMN-major -- citing it as precedent for a row-major layout read the storage and ignored the indexer (AFFNMTRX.HPP:99). And 'the last row made the maths blow up' came from the contaminated bisect; those crashes were the RIO fault. Rotation now composes through the engine's own Matrix4x4::operator=(const EulerAngles&) from the page's pitch/yaw/roll, and the node write uses the RootRenderable idiom (dpl_GetDCSMatrix + assign). Also banked: the chase-frame fifobridge variant -- camera framed on the articulated root instead of the arena bounds -- as the offline proof harness for any future pose question. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,13 @@
|
||||
#if !defined(BTL4VID_HPP)
|
||||
# include <btl4vid.hpp>
|
||||
#endif
|
||||
//
|
||||
// Matrix4x4 -- the DCS wire layout (see RecurseSKLFile). l4video.hpp pulls
|
||||
// rotation.hpp but not matrix.hpp; L4VIDRND.CPP includes it the same way.
|
||||
//
|
||||
#if !defined(MATRIX_HPP)
|
||||
# include <matrix.hpp>
|
||||
#endif
|
||||
|
||||
//
|
||||
// The engine's damage-zone tagging callback reads this while geometry is
|
||||
@@ -222,55 +229,61 @@ dpl_DCS *
|
||||
dpl_SetDCSZone(dcs, zone);
|
||||
|
||||
//
|
||||
// STAGE 2a -- the node's LOCAL TRANSLATION.
|
||||
// The node's LOCAL TRANSFORM, in the engine's own terms -- the wire
|
||||
// convention is no longer inferred from captures, it is READ from the
|
||||
// engine: RootRenderable's ctor (L4VIDRND.CPP:853) writes an entity pose
|
||||
// into a DCS with
|
||||
//
|
||||
// Slot convention, derived rather than guessed. A DCS flush body is
|
||||
// [remote][type_check][node][pad][16 x float32]: our own identity
|
||||
// matrices were found on the wire at body offset 16, which also
|
||||
// showed analyze_scene.py's rest[4:68] read to be one word early.
|
||||
// Re-reading a real BT capture with that correction gives a clean
|
||||
// identity with the translation in the LAST ROW -- a row-vector
|
||||
// convention, the transpose of MUNGA's AffineMatrix (which is 3x4
|
||||
// row-major with translation in the 4th COLUMN, entries[3/7/11], per
|
||||
// AFFNMTRX.CPP:50).
|
||||
// *(Matrix4x4*)dpl_GetDCSMatrix(myDCS) = myEntity->localToWorld;
|
||||
//
|
||||
// Rotation is deliberately left identity for now: every base-pose
|
||||
// pitch/yaw/roll in MAD.SKL is 0 or ~1e-3, so TRANSLATION alone
|
||||
// assembles the model -- which isolates this one convention and
|
||||
// makes a wrong guess about rotation order impossible to mistake for
|
||||
// a wrong guess about translation.
|
||||
// so whatever Matrix4x4::operator=(const AffineMatrix&) produces IS the
|
||||
// DCS matrix layout. Reading it (MATRIX.CPP:130): Matrix4x4 is ROW-major
|
||||
// (MATRIX.HPP:113, entries[(Row<<2)+Column]) with rotation in rows 0-2,
|
||||
// ZEROS in column 3, and the TRANSLATION IN ROW 3 -- entries 12/13/14.
|
||||
//
|
||||
// (The previous revision put translation at entries 3/7/11, quoting
|
||||
// AffineMatrix as precedent. AffineMatrix does keep translation at
|
||||
// 3/7/11 -- but because it is COLUMN-major, entries[(column<<2)+row]
|
||||
// (AFFNMTRX.HPP:99); its (3,c) translation row lands at 3/7/11 by
|
||||
// storage, not by convention. Matrix4x4 transposes that on copy. The
|
||||
// 'last row made the maths blow up' claim attached to the old choice
|
||||
// came from the contaminated bisect -- the crashes were the RIO fault.)
|
||||
//
|
||||
// Rotation comes from the page's pitch/yaw/roll through the engine's own
|
||||
// Matrix4x4::operator=(const EulerAngles&), so the rotation ORDER is the
|
||||
// engine's by construction, not a guess. MAD.SKL base-pose angles are
|
||||
// all 0 or ~1e-3, so this is nearly identity today -- but it is the
|
||||
// correct compose for any skeleton authored with real angles.
|
||||
//
|
||||
Scalar
|
||||
tran_x = 0.0f,
|
||||
tran_y = 0.0f,
|
||||
tran_z = 0.0f;
|
||||
tran_z = 0.0f,
|
||||
rot_pitch = 0.0f,
|
||||
rot_yaw = 0.0f,
|
||||
rot_roll = 0.0f;
|
||||
skeleton->GetEntry(page_name, "tranx", &tran_x);
|
||||
skeleton->GetEntry(page_name, "trany", &tran_y);
|
||||
skeleton->GetEntry(page_name, "tranz", &tran_z);
|
||||
skeleton->GetEntry(page_name, "pitch", &rot_pitch);
|
||||
skeleton->GetEntry(page_name, "yaw", &rot_yaw);
|
||||
skeleton->GetEntry(page_name, "roll", &rot_roll);
|
||||
|
||||
Matrix4x4
|
||||
node_matrix;
|
||||
node_matrix = EulerAngles(rot_pitch, rot_yaw, rot_roll);
|
||||
node_matrix(3,0) = tran_x;
|
||||
node_matrix(3,1) = tran_y;
|
||||
node_matrix(3,2) = tran_z;
|
||||
|
||||
//
|
||||
// Write in place and flush -- the same idiom as RootRenderable's ctor
|
||||
// (dpl_GetDCSMatrix + assign), not dpl_SetDCSMatrix.
|
||||
//
|
||||
float32
|
||||
matrix[16];
|
||||
int
|
||||
i;
|
||||
for (i = 0; i < 16; ++i)
|
||||
{
|
||||
matrix[i] = 0.0f;
|
||||
}
|
||||
matrix[0] = matrix[5] = matrix[10] = matrix[15] = 1.0f;
|
||||
//
|
||||
// Translation goes in the 4th COLUMN (3/7/11), the same layout as
|
||||
// MUNGA's own AffineMatrix (3x4 row-major, entries[3/7/11] --
|
||||
// AFFNMTRX.CPP:50). Putting it in the last ROW instead made the
|
||||
// matrix PROJECTIVE under a column-vector convention and the
|
||||
// transform maths blew up: the walk died on the first node that
|
||||
// carries geometry, reproducibly, and bisecting with the reads kept
|
||||
// but the values suppressed ran clean. Same library house as
|
||||
// MUNGA, same convention.
|
||||
//
|
||||
matrix[3] = (float32)tran_x;
|
||||
matrix[7] = (float32)tran_y;
|
||||
matrix[11] = (float32)tran_z;
|
||||
dpl_SetDCSMatrix(dcs, matrix);
|
||||
*dcs_matrix = dpl_GetDCSMatrix(dcs);
|
||||
Check_Pointer(dcs_matrix);
|
||||
*(Matrix4x4 *)dcs_matrix = node_matrix;
|
||||
|
||||
if (parent_dcs != NULL)
|
||||
{
|
||||
|
||||
@@ -1274,3 +1274,48 @@ a boot-time state where no frame is ever outstanding.
|
||||
|
||||
STATUS: emulator rebuilding; verification = the same norio run drawing PAST
|
||||
frame 232 with the camera tracking, then two agreeing runs before belief.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
THE MECH STANDS: MATRIX CONVENTION PROVEN FROM ENGINE SOURCE, CONFIRMED VISUALLY
|
||||
--------------------------------------------------------------------------------
|
||||
emulator/render-bridge/pose_chase.png -- the MadCat assembled from its 19
|
||||
parts, standing in the arena at its live articulated position (chase render of
|
||||
the live fifodump, framed on anim_abs root 0x672). Feet, legs, torso, weapon
|
||||
pods, canopy plate with the purple glass panels. Not a collapsed stack.
|
||||
|
||||
THE CONVENTION, now read from the engine rather than inferred from captures:
|
||||
RootRenderable's ctor writes an entity pose into a DCS via
|
||||
|
||||
*(Matrix4x4*)dpl_GetDCSMatrix(myDCS) = myEntity->localToWorld;
|
||||
|
||||
so Matrix4x4::operator=(const AffineMatrix&) (MATRIX.CPP:130) IS the wire
|
||||
layout: Matrix4x4 is ROW-major (entries[(Row<<2)+Column], MATRIX.HPP), rotation
|
||||
in rows 0-2, ZEROS in column 3, TRANSLATION IN ROW 3 = entries 12/13/14.
|
||||
|
||||
The previous choice (translation at 3/7/11) rested on two errors, both now
|
||||
corrected in RecurseSKLFile's comments: (1) AffineMatrix DOES keep translation
|
||||
at entries 3/7/11 -- but because it is COLUMN-major (entries[(column<<2)+row],
|
||||
AFFNMTRX.HPP:99), that is its (3,c) translation ROW by storage; Matrix4x4
|
||||
transposes on copy. Citing it as precedent for a row-major layout was reading
|
||||
the storage and ignoring the indexer. (2) "translation in the last row made
|
||||
the maths blow up" came from the contaminated bisect -- those crashes were the
|
||||
RIO fault.
|
||||
|
||||
Rotation now composes through the engine's own Matrix4x4::operator=(const
|
||||
EulerAngles&) from the page's pitch/yaw/roll, so the rotation order is the
|
||||
engine's by construction. Node write uses the RootRenderable idiom
|
||||
(dpl_GetDCSMatrix + assign), not dpl_SetDCSMatrix.
|
||||
|
||||
Tooling: the chase-frame variant of fifobridge (camera framed on the
|
||||
articulated root instead of arena bounds) is the ten-line proof harness for
|
||||
any future pose question -- offline, against a copy of the live dump, zero
|
||||
interference with the running pod.
|
||||
|
||||
REMAINING VISUAL WORK, in order of blockingness:
|
||||
* Eye height: the inside view sits at hull Y+UPOFF and stares through the
|
||||
torso panels. The engine's answer is the siteeyepoint segment compose
|
||||
(SetupCull) -- our skeleton has the joint chain, so the eye DCS should ride
|
||||
jointcockpit/siteeyepoint rather than the root. COCKPIT-CAGE-NOTES.md
|
||||
territory (glance-hide / punch cutout) applies once the height is right.
|
||||
* Proportions/orientation fine-tune once a cleaner outside frame exists;
|
||||
limbs may need per-joint rotation signs checked against a shipped capture.
|
||||
|
||||
Reference in New Issue
Block a user