ship_capture: ingest the draw-logger format + quantify the static gaps
The correlator now accepts BOTH capture formats: the F10 ship-capture snapshot and the draw-logger format (mission_draws.log / xenia_re_draws.log) via parse_drawlog — group the ship shader's draws by vertex-buffer base, vcount = size_words/stride_words, keep the first c0..c2 WVP. So a capital ship seen in a normal instrumented run bakes without a dedicated F10 pass. correlate_capture auto-detects the format. Shared normalize_wvp between both parsers. Also add examples/ship_dump.rs (offline static-placement inspector) and record the measured static-assembler gaps for e106 in the doc: bdy_01/bdy_02 overlap (runtime port/starboard mirror at X=+-264), eng_02 and wep_02_01 never placed. These confirm exact placement is runtime-only — nothing more to squeeze statically; the capture-driven table is the only path. The draw logs present on this box are the player fighter, so no capital-ship table can be baked offline. 7 ship_capture tests (adds draw-log parse + correlate); 77 formats-lib green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
|
||||
use sylpheed_formats::mesh::{xbg7_resource_names, Xbg7Model};
|
||||
use sylpheed_formats::ship::{is_base_part, ship_id_of};
|
||||
use sylpheed_formats::ship_capture::{correlate, parse_capture, serialize_table};
|
||||
use sylpheed_formats::ship_capture::{correlate, parse_capture, parse_drawlog, serialize_table};
|
||||
use sylpheed_formats::xiso::open_iso;
|
||||
use std::collections::HashSet;
|
||||
use std::path::Path;
|
||||
@@ -33,8 +33,16 @@ fn main() {
|
||||
let ref_sub = positional.get(3).map(|s| s.as_str()).unwrap_or("bdy_04");
|
||||
let iso = std::env::var("SYLPHEED_ISO").expect("SYLPHEED_ISO");
|
||||
|
||||
let draws = parse_capture(&std::fs::read_to_string(log).expect("read log"));
|
||||
eprintln!("parsed {} draws with WorldView", draws.len());
|
||||
// Accept either the F10 ship-capture format or the draw-logger format
|
||||
// (mission_draws.log); auto-detect by trying the F10 parser first.
|
||||
let text = std::fs::read_to_string(log).expect("read log");
|
||||
let mut draws = parse_capture(&text);
|
||||
if draws.is_empty() {
|
||||
draws = parse_drawlog(&text);
|
||||
eprintln!("parsed {} draws (draw-logger format)", draws.len());
|
||||
} else {
|
||||
eprintln!("parsed {} draws (F10 capture format)", draws.len());
|
||||
}
|
||||
|
||||
// Decode the ship's base parts to get each part's vertex count (the match key).
|
||||
let bytes = {
|
||||
|
||||
50
crates/sylpheed-formats/examples/ship_dump.rs
Normal file
50
crates/sylpheed-formats/examples/ship_dump.rs
Normal file
@@ -0,0 +1,50 @@
|
||||
//! Dump a ship's static placement + scene-graph nodes for a stage container FILE
|
||||
//! (works offline from an extracted disc, no ISO needed).
|
||||
//! cargo run --release --example ship_dump -- <Stage_SNN.xpr path> <ship_id>
|
||||
use sylpheed_formats::mesh::{scene_world_nodes, xbg7_resource_names, Xbg7Model};
|
||||
use sylpheed_formats::ship::{assemble_ship, is_base_part, ship_id_of};
|
||||
use std::collections::HashSet;
|
||||
|
||||
fn main() {
|
||||
let a: Vec<String> = std::env::args().collect();
|
||||
let (path, id) = (&a[1], &a[2]);
|
||||
let bytes = std::fs::read(path).unwrap();
|
||||
let names = xbg7_resource_names(&bytes);
|
||||
|
||||
// Base parts + their vertex counts.
|
||||
let want: HashSet<String> = names.iter().filter(|n| is_base_part(n) && ship_id_of(n)==Some(id.as_str())).cloned().collect();
|
||||
let models = Xbg7Model::models_named(&bytes, &want, &|| false);
|
||||
println!("== base parts ({}) ==", want.len());
|
||||
for m in &models {
|
||||
let vc: usize = m.meshes.iter().map(|s| s.positions.len()).sum();
|
||||
println!(" {:20} vtx={}", m.name, vc);
|
||||
}
|
||||
|
||||
// Composites present for this id.
|
||||
println!("== composites ==");
|
||||
for n in &names {
|
||||
if n.contains(&format!("rou_{id}")) && ship_id_of(n).is_none() {
|
||||
let nodes = scene_world_nodes(&bytes, n);
|
||||
println!(" {:24} {} nodes", n, nodes.len());
|
||||
}
|
||||
}
|
||||
|
||||
// assemble_ship result: centroids + extent.
|
||||
for ext in [false, true] {
|
||||
let placed = assemble_ship(&bytes, id, ext);
|
||||
println!("== assemble_ship(external={ext}) -> {} parts ==", placed.len());
|
||||
let mut lo=[f32::MAX;3]; let mut hi=[f32::MIN;3];
|
||||
for p in &placed {
|
||||
let src = models.iter().find(|m| m.name==p.resource);
|
||||
let (mut c, mut n) = ([0.0f32;3], 0f32);
|
||||
if let Some(src)=src { for sub in &src.meshes { for v in &sub.positions {
|
||||
let w=p.apply(*v); for k in 0..3 { c[k]+=w[k]; lo[k]=lo[k].min(w[k]); hi[k]=hi[k].max(w[k]); } n+=1.0; }}}
|
||||
let n=n.max(1.0);
|
||||
println!(" {:20} T=[{:8.1}{:8.1}{:8.1}] centroid=[{:8.1}{:8.1}{:8.1}]",
|
||||
p.resource, p.t[0],p.t[1],p.t[2], c[0]/n,c[1]/n,c[2]/n);
|
||||
}
|
||||
if placed.iter().any(|p| models.iter().any(|m| m.name==p.resource)) {
|
||||
println!(" EXTENT=[{:.0} {:.0} {:.0}]", hi[0]-lo[0], hi[1]-lo[1], hi[2]-lo[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user