The second F10 capture (ship at _m LOD distance) closes e106 entirely: all 8 parts placed including the bridge both earlier captures missed, cross-validating the doc's 7 known translations to 0.1 units and adding brg_01 at the exact centreline (0, 153.3, -164.0). Matching hardened by what the real capture taught us: - LOD-aware: each part tries every variant vcount (base/_m/_l/_d) — a distant ship draws its LOD copies, same local frame. - Position-validated, SET-based, against the UNION of a part's variants: buffer order != decode order, and one draw's vcount equalled the _m count while its buffer held the FULL 1633-vert geometry. A coincidental vcount (foreign 51-vert mesh vs the bridge) is rejected by geometry. - Runtime mirror handled: twins share one file geometry; the engine uploads the starboard copy X-reflected. Mirror-validated draws bake diag(-1,1,1) and the viewer reverses triangle winding for det<0 so front faces stay outward. - Near-axis rotation entries snapped to exact 0/+-1 for a clean table; eng_01 keeps its genuine 30-degree nacelle rotation. data/ship_placements.txt now ships e106 (viewer renders via the captured table); embedded_e106_is_complete locks the baked data. part_pos + capture_match helper examples added. 10 ship_capture tests; 80 formats-lib tests green; viewer builds. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
44 lines
2.2 KiB
Rust
44 lines
2.2 KiB
Rust
//! Identify which stage + ship a capture log came from: match the capture's
|
|
//! draw vertex counts against every resource (incl. LODs) of every Stage_SNN.xpr.
|
|
//! cargo run --release --example capture_match -- <capture.log> <resource3d dir>
|
|
use sylpheed_formats::mesh::{xbg7_resource_names, Xbg7Model};
|
|
use sylpheed_formats::ship_capture::{parse_capture, parse_drawlog};
|
|
use std::collections::{BTreeMap, HashSet};
|
|
|
|
fn main() {
|
|
let a: Vec<String> = std::env::args().collect();
|
|
let text = std::fs::read_to_string(&a[1]).unwrap();
|
|
let mut draws = parse_capture(&text);
|
|
if draws.is_empty() { draws = parse_drawlog(&text); }
|
|
let caps: HashSet<u32> = draws.iter().map(|d| d.vcount).filter(|v| *v >= 100).collect();
|
|
eprintln!("{} draws, {} distinct vcounts>=100", draws.len(), caps.len());
|
|
|
|
let mut entries: Vec<_> = std::fs::read_dir(&a[2]).unwrap().filter_map(|e| e.ok())
|
|
.filter(|e| { let n = e.file_name().to_string_lossy().to_string(); n.starts_with("Stage_") && n.ends_with(".xpr") })
|
|
.map(|e| e.path()).collect();
|
|
entries.sort();
|
|
for path in entries {
|
|
let bytes = std::fs::read(&path).unwrap();
|
|
let names = xbg7_resource_names(&bytes);
|
|
let want: HashSet<String> = names.iter().cloned().collect();
|
|
let models = Xbg7Model::models_named(&bytes, &want, &|| false);
|
|
// resource -> vcount; group hits by ship-id prefix
|
|
let mut hits: BTreeMap<String, Vec<(String, u32)>> = BTreeMap::new();
|
|
for m in &models {
|
|
let vc: usize = m.meshes.iter().map(|s| s.positions.len()).sum();
|
|
if vc >= 100 && caps.contains(&(vc as u32)) {
|
|
let id = m.name.get(..4).unwrap_or("?").to_string();
|
|
hits.entry(id).or_default().push((m.name.clone(), vc as u32));
|
|
}
|
|
}
|
|
let total: usize = hits.values().map(|v| v.len()).sum();
|
|
if total >= 3 {
|
|
println!("== {} : {} matching resources ==", path.file_name().unwrap().to_string_lossy(), total);
|
|
for (id, v) in &hits {
|
|
let s: Vec<String> = v.iter().map(|(n, c)| format!("{n}({c})")).collect();
|
|
println!(" {id}: {}", s.join(" "));
|
|
}
|
|
}
|
|
}
|
|
}
|