BT410 5.3.81: the canopy question answered -- our wire is right, the bridge cannot decode it

Decoding the articulated run's fifodump settles it, and the answer is not in
our code.  26133 vr_flush_dcs_artic records this run (was ~200), every one
n=1 with a 12-byte body: [handle][sine][cosine].  Sample: handle 0x684,
sine -0.04536 cosine 0.99897 = -2.60 deg, then -7.60 deg.  That is the torso
twist sweeping, authentically encoded -- our HingeRenderable emits exactly
what the 1995 engine emits.

vrboard's 0x1f parser accepts record widths 12, 2 and 5 floats but only
applies the 12-float form: "joint sin/cos records: axis semantics unknown --
the flushed matrix stands (mech limbs won't articulate yet)".  So the canopy's
DCS never rotates in the bridge's scene while its camera picks the twist up by
another route, and the two diverge -- the artefact.  anim_abs reading 0 in the
same run is innocent: the root only re-flushes when localToWorld changes, and
this mech is deliberately stationary.

The axis genuinely is not on the wire: dpl_SetDCSXAxis/YAxis/ZAxis all take
(dcs, sin, cos) and all produce the same record (DPL_VPX.H:23-25), so the
board carries it per node.  A stream-only decoder cannot recover it.

Two ways forward, both legitimate: tell the bridge the axis out of band (the
.SKL's Type=hingex/y/z names it per node -- a handle->axis map at scene-build
time, and this fixes the leg gait too), or use the engine's other hinge path
(HingeRenderable::Execute is written both ways behind #if SINGLE_AXIS_HINGE;
the #else branch writes a full matrix, producing the 12-float records the
bridge already applies).  Both are the shipped engine's own code.

The reconstruction's articulation work is done and correct as of 5.3.80; what
remains is a decoder gap in the viewing tool.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-29 20:53:26 -05:00
co-authored by Claude Fable 5
parent 07eb74d1f7
commit ed218483ec
3 changed files with 3294 additions and 1 deletions
Binary file not shown.
File diff suppressed because it is too large Load Diff
@@ -1919,3 +1919,53 @@ the eye follows the sim's segment chain, and they diverge exactly like this
whenever one carries the twist and the other does not. Test: log both
per frame and compare; if that is it, the fix is making the segment chain and
the render chain agree about which node owns the twist.
--------------------------------------------------------------------------------
THE CANOPY QUESTION, ANSWERED: OUR WIRE IS RIGHT, THE BRIDGE CANNOT DECODE IT
--------------------------------------------------------------------------------
Decoding the fifodump from the articulated run settles it, and the answer is
not in our code at all.
0x1f records this run: 26133 (was ~200 before articulation)
every one: n=1, payload 20 bytes = [action][count][handle][2 floats]
sample: handle 0x684, sine=-0.04536 cosine=0.99897 -> -2.60 deg
handle 0x684, sine=-0.13226 cosine=0.99122 -> -7.60 deg
That is the torso twist, sweeping, authentically encoded. Our HingeRenderable
path emits exactly what the 1995 engine emits: dpl_SetDCSYAxis(dcs, sin, cos)
followed by a batched articulation flush.
The bridge cannot use it. vrboard's 0x1f parser accepts record widths of 12,
2 and 5 floats but only APPLIES the 12-float form, with the comment "joint
sin/cos records: axis semantics unknown -- the flushed matrix stands (mech
limbs won't articulate yet)". So the canopy's DCS never rotates in the
bridge's scene, while the bridge's own camera machinery picks the twist up
by another route -- and the two diverge, which is exactly the artefact.
(anim_abs reading 0 in the same run is consistent and innocent: the root only
re-flushes when localToWorld changes, and this mech is deliberately
stationary.)
WHY THE AXIS IS MISSING, and it is not an oversight in the decoder:
dpl_SetDCSXAxis / YAxis / ZAxis all take (dcs, sin, cos) and all produce the
SAME 12-byte record (DPL_VPX.H:23-25). The axis is not on the wire; the
board must carry it per node from the DCS's own state. A decoder that only
ever sees the stream genuinely cannot recover it.
TWO WAYS FORWARD, both legitimate:
1. Tell the bridge the axis out of band. It already parses the .SKL family
for other purposes, and Type=hingex/hingey/hingez names the axis per
node -- a handle->axis map built at scene-build time closes it.
2. Use the engine's OTHER hinge path. HingeRenderable::Execute is written
both ways behind `#if SINGLE_AXIS_HINGE` (L4VIDRND.CPP:1044): the #else
branch converts the Hinge to a Quaternion, writes the FULL matrix and
flushes -- producing the 12-float records the bridge already applies.
Both are the shipped engine's own code; the flag is a compile-time
choice, not a reconstruction decision.
Route 1 is the faithful one (it keeps our wire byte-identical to the 1995
board's) and route 2 is the quick one. Route 1 also fixes the LEG GAIT for
free, since every limb hinge has the same problem.
So the reconstruction's articulation work is DONE and correct as of 5.3.80;
what remains is a decoder gap in the viewing tool, which is a different
component with a different owner.