re: exact anchor offsets (GameMesh.vbuf_offset) -- correcting yesterday's defect table
The first read of the capture-truth table located our resources by searching the container for their leading vertices, which reads much worse than reality: the same leading run occurs at several offsets in one container. GameMesh now carries the offset the anchor scan actually chose, so the comparison is exact -- 4 of the ship's drawn buffers are anchored correctly, 2 are the twin collapse, and 2 are real mis-anchors of a size we do decode (brg 51 verts, eng_02_l 44). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -117,15 +117,27 @@ fn main() {
|
||||
// scan chose for each resource — a direct read-out of what we got wrong.
|
||||
if let Some(a) = args.iter().find_map(|a| a.strip_prefix("--truth=")) {
|
||||
let base = u32::from_str_radix(a.trim_start_matches("0x"), 16).expect("base");
|
||||
// Where the anchor scan actually put each sub-mesh — exact, from the
|
||||
// decoder, not inferred by searching for its leading vertices (the same
|
||||
// leading run occurs at several offsets in a container, so a search
|
||||
// cannot tell where a resource was anchored).
|
||||
let mut ours: BTreeMap<usize, Vec<(String, usize)>> = BTreeMap::new();
|
||||
for m in &models {
|
||||
let pos: Vec<[f32; 3]> =
|
||||
m.meshes.iter().flat_map(|s| s.positions.iter().copied()).take(8).collect();
|
||||
let n: usize = m.meshes.iter().map(|s| s.positions.len()).sum();
|
||||
for (o, _) in locate_run(&bytes, &pos) {
|
||||
ours.entry(o).or_default().push((m.name.clone(), n));
|
||||
for sub in &m.meshes {
|
||||
if let Some(o) = sub.vbuf_offset {
|
||||
ours.entry(o).or_default().push((m.name.clone(), sub.positions.len()));
|
||||
}
|
||||
}
|
||||
}
|
||||
if args.iter().any(|a| a == "--anchors") {
|
||||
println!("{:<12} where our decode put each resource", "file offset");
|
||||
for (o, v) in &ours {
|
||||
for (n, c) in v {
|
||||
println!("0x{o:<10x} {n} ({c} verts)");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
let mut drawn: BTreeMap<usize, u32> = BTreeMap::new();
|
||||
for (_, draws) in &logs {
|
||||
for d in draws {
|
||||
@@ -135,7 +147,14 @@ fn main() {
|
||||
}
|
||||
}
|
||||
}
|
||||
println!("{:<12} {:>7} claimed by our decode", "file offset", "vcount");
|
||||
// Which resources have the drawn vertex count, wherever we put them?
|
||||
// Right size + wrong place is a different bug from never finding it.
|
||||
let mut by_count: BTreeMap<usize, Vec<String>> = BTreeMap::new();
|
||||
for m in &models {
|
||||
let n: usize = m.meshes.iter().map(|s| s.positions.len()).sum();
|
||||
by_count.entry(n).or_default().push(m.name.clone());
|
||||
}
|
||||
println!("{:<12} {:>7} {:<44} our resources with that vcount", "file offset", "vcount", "claimed by our decode");
|
||||
for (off, vcount) in &drawn {
|
||||
let who = ours
|
||||
.get(off)
|
||||
@@ -143,7 +162,18 @@ fn main() {
|
||||
v.iter().map(|(n, c)| format!("{n}({c})")).collect::<Vec<_>>().join(", ")
|
||||
})
|
||||
.unwrap_or_else(|| "— NOBODY".into());
|
||||
println!("0x{off:<10x} {vcount:>7} {who}");
|
||||
let same = by_count
|
||||
.get(&(*vcount as usize))
|
||||
.map(|v| v.join(", "))
|
||||
.unwrap_or_else(|| "— none".into());
|
||||
// Nearest resource we anchored at or before this offset — the
|
||||
// likely owner of a buffer nobody claims.
|
||||
let near = ours
|
||||
.range(..=*off)
|
||||
.next_back()
|
||||
.map(|(o, v)| format!("{} @ -0x{:x}", v[0].0, off - o))
|
||||
.unwrap_or_default();
|
||||
println!("0x{off:<10x} {vcount:>7} {who:<44} {same:<34} {near}");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -115,6 +115,12 @@ pub struct GameMesh {
|
||||
pub indices: Vec<u32>,
|
||||
/// Sub-mesh / node name from the descriptor, when available.
|
||||
pub name: Option<String>,
|
||||
/// Byte offset of this sub-mesh's vertex buffer inside the container, when
|
||||
/// the decode path knows it. The content-anchored stage path does — and a
|
||||
/// runtime capture names the same offset (a draw's `vbase` is this plus the
|
||||
/// container's load address), so this is what makes an anchor checkable
|
||||
/// against ground truth. See `examples/shared_vbase_check.rs`.
|
||||
pub vbuf_offset: Option<usize>,
|
||||
}
|
||||
|
||||
/// A model = the set of sub-meshes recovered from one XPR2 container's first
|
||||
@@ -376,6 +382,7 @@ impl Xbg7Model {
|
||||
uvs,
|
||||
indices,
|
||||
name: None,
|
||||
vbuf_offset: Some(vb),
|
||||
});
|
||||
off = align16(ve) - base;
|
||||
}
|
||||
@@ -976,6 +983,7 @@ fn read_pool_mesh(
|
||||
uvs,
|
||||
indices,
|
||||
name: None,
|
||||
vbuf_offset: Some(vb),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3677,6 +3677,7 @@ fn exhaust_cone_mesh() -> sylpheed_formats::mesh::GameMesh {
|
||||
uvs: Vec::new(),
|
||||
indices,
|
||||
name: Some("exhaust".to_string()),
|
||||
vbuf_offset: None, // procedural, not read from a container
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user