RecurseSKLFile now builds a joint renderable for any node whose page name
resolves to a live skeleton Joint: HingeX/Y/Z -> HingeRenderable (watching
Joint::GetHinge), Ball -> BallJointRenderable (watching GetEulerAngles),
otherwise the static path. Each holds the rest offset in one DCS and the live
rotation in a child DCS, and its Execute diffs the watched value and calls
DPL_FLUSH_DCS -- the engine's own mechanism (L4VIDRND.CPP:1026+). The value
comes from the mech's JointSubsystem via ResolveJoint, so sim and renderer
read one source. Gated on BT_JOINTS while it proves out.
[skl] video\max.skl -> 26 nodes, 1 objects, 1 eye, 22 articulated
The bridge reported anim_abs=1 joints=0 twist=+0.00 before; it now reports
joints=1 twist=-0.86, matching the game's [torso] twist=-0.856. With the mech
stationary, frames that differed by 0.0% now differ by 62-80%.
A crash it exposed: Mech::ResolveJoint passed segment->GetJointIndex()
straight to GetJoint unchecked, and a segment with no joint reports -1 --
GetNthImplementation then indexes [base + -1*4] and dies (guest 00426A1D).
Torso never hit it because it only asks for its own authored joint name; the
walk asks for every page. Now bounds-checked against GetJointCount.
Open: the canopy does not stay rigid in the view, though it and the eye hang
off the same articulated node. Cancelling the bridge's cage compensation
(CAGE_TWIST_SIGN=0) did not close it. Leading hypothesis: SetupCull builds
worldToEyeMatrix from GetSegmentToWorld(siteeyepoint) -- the SIMULATION's
segment transform -- independent of the render tree, so the canopy follows our
render chain and the eye follows the sim's, and they diverge whenever one
carries the twist and the other does not.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>