The runtime capture served its true purpose: as the answer key that exposed the static encoding. The XBG7 node joint table (node+0x44) holds NINE keyframe channels [TX TY TZ RY RX RZ SX SY SZ] behind EIGHT pointer slots shifted one track forward: channel k+1 = f64@(ptr[k]+8), and channel 0 -- TX, which no pointer names -- sits at ptr[0]-0x48 (tracks are 0x50-byte records, value at +8). The old 8-slot read took TY as X, RY as Y (usually 0 -- why both hulls stacked on the centreline) and never saw TX at all (the +-264 hull offsets). Euler order is Ry(ch3)*Rx(ch4)*Rz(ch5) with angles applied directly, pinned by the captured engine nacelle Rx(-15)*Rz(30) decomposition. mesh.rs gains read_trs9/node_rotation; scene_world_nodes and node_transforms both fixed (saber fin overrides unaffected). assemble_ship rewritten fully static and exact: - every composite-node instance placed (alias duplicates collapsed, real instances kept) including cross-id turret mounts (rou_e303_wep_01_root x2) - the e_rou_<id>_eng cluster rig mounts at GN_Engine_01 (two mirrored nacelles +-131 with -+30-degree cant + centre engine) -- world = frame o rig_local - brg/sld at their exact GN frames (frames were always exact) - shared-geometry twin pairs at +-TX: X-reflect the instance whose offset opposes the geometry's dominant side (viewer reverses winding on det<0) - squeezed node names resolve (wep02 -> wep_02_01) Ground-truth gate: ship::tests::static_assembly_matches_runtime_capture asserts static == the baked e106 capture for all 8 parts (T<1.0, R<0.02) plus both nacelles, both turrets, and the starboard-hull mirror. Viewer now uses pure static assembly; ship_capture + the baked table remain as the verification oracle. Renders show a coherent, bilaterally-symmetric destroyer. 81 formats-lib + all disc tests green; viewer builds. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3.6 KiB
3.6 KiB