ship: bake pipeline — correlator as a tested module + viewer prefers captured table

Promote the F10 ship-capture correlator from an example into a reusable, unit-
tested library module (ship_capture): parse_capture (log -> per-draw rigid
WorldView from the c0..c2 WVP constants), correlate (match parts to draws by
vertex count, express each in the reference part's frame), and a checked-in
placement-table format (serialize_table/parse_table, embedded via
embedded_placement from data/ship_placements.txt).

build_ship_model now prefers a ship's captured placement over the static
assemble_ship when a table entry exists (empty table -> unchanged fallback).
correlate_capture example refactored onto the module + gains --emit to print a
table block. Doc updated: bake plumbing done; remaining is running real F10
captures to populate the table (needs the emulator).

5 new ship_capture tests (parse/normalize/correlate/table round-trip); 76
formats-lib tests + viewer build green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-07-25 20:48:00 +02:00
parent a68405216f
commit c6ca5ce9e7
6 changed files with 476 additions and 192 deletions

View File

@@ -3952,9 +3952,15 @@ fn build_ship_model(
};
let should_cancel = || cancel.load(Ordering::Relaxed);
// Scene-graph placement: which resources to draw and where (bridge fore,
// engines aft, hardpoints on their frames).
let placed = sylpheed_formats::ship::assemble_ship(&bytes, id, external);
// 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),
};
if placed.is_empty() {
return PreparedXpr::Info(format!("No parts found for {label}."));
}