ship: STATIC assembly is now EXACT — node-TRS off-by-one cracked via the capture
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>
This commit is contained in:
@@ -3952,15 +3952,11 @@ fn build_ship_model(
|
||||
};
|
||||
let should_cancel = || cancel.load(Ordering::Relaxed);
|
||||
|
||||
// Placement: prefer a runtime-captured ground-truth table (exact, baked from
|
||||
// an F10 ship capture) when this ship has one; otherwise fall back to the
|
||||
// static scene-graph assembler (hull exact, external parts approximate — the
|
||||
// `external` toggle). A captured table already includes every captured part,
|
||||
// so the toggle doesn't apply to it.
|
||||
let placed = match sylpheed_formats::ship_capture::embedded_placement(id) {
|
||||
Some(cap) => sylpheed_formats::ship_capture::to_scene_parts(&cap),
|
||||
None => sylpheed_formats::ship::assemble_ship(&bytes, id, external),
|
||||
};
|
||||
// Placement: fully static scene-graph assembly — exact, validated
|
||||
// part-for-part (translations + rotations + instances + mirror) against the
|
||||
// e106 runtime capture (`ship::tests::static_assembly_matches_runtime_capture`).
|
||||
// The captured table in ship_capture stays as the verification oracle only.
|
||||
let placed = sylpheed_formats::ship::assemble_ship(&bytes, id, external);
|
||||
if placed.is_empty() {
|
||||
return PreparedXpr::Info(format!("No parts found for {label}."));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user