//! Do port/starboard twins decode to mirror images of each other? //! //! A runtime capture proved the container stores both halves of the `e106` hull //! as separate, X-reflected buffers. That gives a **capture-free invariant**: //! a `…_01`/`…_02` pair of equal vertex count should decode to geometry that is //! an exact X-mirror — never to identical geometry (that is the collapse the //! distinct-assignment fix targets), and never to something unrelated (that is a //! mis-anchor no count-based metric can see). //! //! Usage: twin_mirror_audit use sylpheed_formats::mesh::Xbg7Model; use std::collections::BTreeMap; fn key(p: [f32; 3]) -> (i64, i64, i64) { ( (p[0] * 1e3).round() as i64, (p[1] * 1e3).round() as i64, (p[2] * 1e3).round() as i64, ) } /// Elementwise equality with a tolerance. Truncating keys is too strict for a /// mirrored pair: the halves are authored, not bit-negated, so they differ in /// the last digits and an exact key test reports them as unrelated. fn near(p: [f32; 3], q: [f32; 3]) -> bool { (0..3).all(|c| (p[c] - q[c]).abs() <= 1e-3 * (1.0 + q[c].abs())) } fn main() { let dir = std::env::args().nth(1).expect("resource3d dir"); let mut files: Vec<_> = std::fs::read_dir(&dir) .unwrap() .flatten() .map(|e| e.path()) .filter(|p| p.extension().and_then(|s| s.to_str()) == Some("xpr")) .collect(); files.sort(); let (mut same, mut mirrored, mut unrelated, mut related) = (0usize, 0usize, 0usize, 0usize); let mut examples: Vec = Vec::new(); for f in &files { let Ok(bytes) = std::fs::read(f) else { continue }; let models = Xbg7Model::anchor_models_cancellable(&bytes, 0.0, &|| false); let by_name: BTreeMap<&str, &Xbg7Model> = models.iter().map(|m| (m.name.as_str(), m)).collect(); for m in &models { let Some(stem) = m.name.strip_suffix("_01") else { continue }; let Some(t) = by_name.get(format!("{stem}_02").as_str()) else { continue }; let a: Vec<[f32; 3]> = m.meshes.iter().flat_map(|s| s.positions.clone()).collect(); let b: Vec<[f32; 3]> = t.meshes.iter().flat_map(|s| s.positions.clone()).collect(); if a.len() != b.len() || a.is_empty() { continue; } let ident = a.iter().zip(&b).all(|(p, q)| near(*p, *q)); let mirr = a.iter().zip(&b).all(|(p, q)| near([-p[0], p[1], p[2]], *q)); // A pair that is neither may still be RELATED: mirrored on another // axis, or the same point cloud in a different vertex order. Only a // pair that is none of these is evidence of a mis-anchor. let mirr_y = a.iter().zip(&b).all(|(p, q)| near([p[0], -p[1], p[2]], *q)); let mirr_z = a.iter().zip(&b).all(|(p, q)| near([p[0], p[1], -p[2]], *q)); let set = |v: &Vec<[f32; 3]>| { let mut s: Vec<_> = v.iter().map(|p| key(*p)).collect(); s.sort_unstable(); s }; let same_cloud = set(&a) == set(&b); // …and the same point cloud after mirroring, for a pair whose // halves are authored in different vertex order. let mirrored_cloud = { let am: Vec<[f32; 3]> = a.iter().map(|p| [-p[0], p[1], p[2]]).collect(); set(&am) == set(&b) }; if !ident && !mirr && (mirr_y || mirr_z || same_cloud || mirrored_cloud) { related += 1; continue; } // Identical GEOMETRY is only a collapse if it also comes from the // same buffer: a container may store a part twice unmirrored, and // then two twins on two copies decode identically and correctly. let shared_buffer = m.meshes[0].vbuf_offset.is_some() && m.meshes[0].vbuf_offset == t.meshes[0].vbuf_offset; if ident && !shared_buffer { related += 1; continue; } if ident { same += 1; if examples.len() < 6 { examples.push(format!( "identical: {} / {}_02 in {}", m.name, stem, f.file_name().unwrap().to_string_lossy() )); } } else if mirr { mirrored += 1; } else { unrelated += 1; if examples.len() < 6 { examples.push(format!( "unrelated: {} / {}_02 in {}", m.name, stem, f.file_name().unwrap().to_string_lossy() )); } } } } println!("twin pairs of equal vertex count: {}", same + mirrored + unrelated + related); println!(" exact X-mirror (expected) : {mirrored}"); println!(" IDENTICAL, one buffer (collapse) : {same}"); println!(" related other way (Y/Z mirror, reordered): {related}"); println!(" unrelated (mis-anchor?) : {unrelated}"); for e in examples { println!(" e.g. {e}"); } }