re: the consistency figure is mostly composite bounding boxes -- real disagreement is 1

consensus_check names the minority container instead of just flagging
disagreement: 89 minority decodes across 477 resources with a majority, and 88
are scene composites. A composite's descriptor carries a 24-vertex bounding box
(e_rou_e106 -> 22x22x22, e_rou_f106 -> 745x718x718); those boxes look
interchangeable so the assignment shuffles per container. 1141 of 6209 decoded
resources have scene nodes. Counting only real geometry the disc has ONE
disagreement: e101_wep_01_l in Stage_S25.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-12 18:02:06 +00:00
parent 0625976ed7
commit cdaeac8e1b
3 changed files with 151 additions and 0 deletions

View File

@@ -0,0 +1,34 @@
//! How much of the "decoded geometry" belongs to SCENE COMPOSITES rather than to
//! real meshes? A composite (`rou_*`, `e_rou_*`) carries node transforms; the
//! anchor scan nevertheless finds a block for it, and that pseudo-geometry lands
//! in coverage and consistency counts.
//! Usage: composite_geometry <resource3d_dir>
use sylpheed_formats::mesh::{scene_world_nodes, Xbg7Model};
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 total, mut composite, mut composite_named) = (0usize, 0usize, 0usize);
for f in &files {
let Ok(bytes) = std::fs::read(f) else { continue };
for m in Xbg7Model::anchor_models_cancellable(&bytes, 0.0, &|| false) {
total += 1;
let has_nodes = !scene_world_nodes(&bytes, &m.name).is_empty();
let named = m.name.starts_with("rou_") || m.name.starts_with("e_rou_");
if has_nodes {
composite += 1;
}
if named {
composite_named += 1;
}
}
}
println!(
"{total} decoded; {composite} have scene nodes (a composite), {composite_named} are named rou_/e_rou_"
);
}

View File

@@ -0,0 +1,87 @@
//! Minority report: which container decodes a shared resource differently from
//! all the others?
//!
//! Cross-container consistency has been measured as "do the spans agree", which
//! only says a resource is inconsistent — not which copy is wrong. With three or
//! more copies the majority is the reference, and the minority names the
//! container AND the resource to look at. That is what caught the `n054`/`n056`
//! shift chain after the exact-coverage fix.
//!
//! Usage: consensus_check <resource3d_dir> [--list]
use sylpheed_formats::mesh::Xbg7Model;
use std::collections::{BTreeMap, HashMap};
fn main() {
let dir = std::env::args().nth(1).expect("resource3d dir");
let list = std::env::args().any(|a| a == "--list");
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();
// name -> [(container, verts, tris, span)]
let mut seen: BTreeMap<String, Vec<(String, usize, usize, [i64; 3])>> = BTreeMap::new();
for f in &files {
let Ok(bytes) = std::fs::read(f) else { continue };
let where_ = f.file_name().unwrap().to_string_lossy().to_string();
for m in Xbg7Model::anchor_models_cancellable(&bytes, 0.0, &|| false) {
let (mut lo, mut hi) = ([f32::MAX; 3], [f32::MIN; 3]);
for s in &m.meshes {
for q in &s.positions {
for k in 0..3 {
lo[k] = lo[k].min(q[k]);
hi[k] = hi[k].max(q[k]);
}
}
}
if lo[0] == f32::MAX {
continue;
}
let v: usize = m.meshes.iter().map(|s| s.positions.len()).sum();
let t: usize = m.meshes.iter().map(|s| s.indices.len() / 3).sum();
let span = [
(hi[0] - lo[0]).round() as i64,
(hi[1] - lo[1]).round() as i64,
(hi[2] - lo[2]).round() as i64,
];
seen.entry(m.name.clone()).or_default().push((where_.clone(), v, t, span));
}
}
let (mut resources, mut minority) = (0usize, 0usize);
let mut rows: Vec<String> = Vec::new();
for (name, list_) in &seen {
// Only compare decodes that agree on how much geometry they found, and
// only where a majority can exist.
if list_.len() < 3 || !list_.iter().all(|e| e.1 == list_[0].1 && e.2 == list_[0].2) {
continue;
}
let mut votes: HashMap<[i64; 3], usize> = HashMap::new();
for e in list_ {
*votes.entry(e.3).or_default() += 1;
}
let (best, n) = votes.iter().max_by_key(|(_, n)| **n).unwrap();
if *n * 2 <= list_.len() {
continue; // no majority — cannot call anyone the odd one out
}
resources += 1;
for e in list_.iter().filter(|e| e.3 != *best) {
minority += 1;
rows.push(format!(
"{:<22} {name:<26} {:?} vs the other {n} containers' {:?}",
e.0, e.3, best
));
}
}
if list {
for r in &rows {
println!("{r}");
}
}
println!(
"{minority} minority decodes across {resources} resources that have a majority"
);
}

View File

@@ -1097,6 +1097,36 @@ that carry it) and `t901_e01_D` is 58×59×3013 (a mast). And `f101_bdy_01` flag
at 6× while being **capture-verified exact** in the truth table — a useful at 6× while being **capture-verified exact** in the truth table — a useful
reminder that a bulky hull is not a bug. reminder that a bulky hull is not a bug.
### The consistency figure is mostly bounding boxes — real disagreement is ONE resource
`examples/consensus_check.rs` sharpens the cross-container test: with three or
more copies of a resource, the majority span is the reference and the **minority
names the container that is wrong**, not just "these disagree".
Disc-wide: **89 minority decodes across 477 resources that have a majority**
and **88 of the 89 are scene composites** (`rou_*` / `e_rou_*`), not drawable
geometry. Inspecting one shows why: `e_rou_e106` decodes a **24-vertex,
12-triangle box** (span 22×22×22), `e_rou_f106` another (745×718×718). A
composite's descriptor carries a **bounding box**, the anchor scan finds it, and
because those boxes are interchangeable-looking the assignment shuffles between
containers. `mesh::scene_world_nodes` identifies them structurally: **1 141 of
the 6 209 decoded resources have scene nodes**, i.e. are composites (478 are
named `rou_`/`e_rou_`).
So the headline number this file has been quoting — cross-container inconsistency
— is **dominated by composite bounding boxes**. Counting only real geometry, the
whole disc now has **one** disagreement:
```
Stage_S25.xpr e101_wep_01_l [779, 5769, 5769] vs the other 3 containers' [206, 545, 545]
```
That is the sharper metric to work against, and the concrete next target. (The
composite boxes are harmless — nothing draws them — but they should be excluded
from any consistency figure quoted in future, and the ignored
`shared_resources_decode_identically_in_every_container` test measures the mixed
population, so its number is not comparable to this one.)
### What the exact-coverage fix actually reached ### What the exact-coverage fix actually reached
The fix was justified on one resource (`e106_bdy_03`) and one render. Comparing The fix was justified on one resource (`e106_bdy_03`) and one render. Comparing