re: CORRECTION -- the static assembler is not at fault, the pre-fix decoder was
Last iteration reported e303_wep_01 as inflated 30-110x by assemble_ship. Wrong: the composite nodes carry scale 1.0 and orthonormal matrices, and under the current decoder the turret places as 49x23x42 at +/-179. The 1600x2100x4800 box only appears with XBG7_COVER_SLACK=4 -- the exact-coverage fix had already repaired the turret too. The error came from comparing a deliberately pre-fix render against post-fix measurements. The containment screen's blindness therefore has a mundane cause: one mis-decode inflated the envelope and hid another. Kept as a forward-looking invariant with that caveat. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
35
crates/sylpheed-formats/examples/filter_consistency.rs
Normal file
35
crates/sylpheed-formats/examples/filter_consistency.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
//! Does a FILTERED decode agree with the full one?
|
||||
//!
|
||||
//! Distinct anchor assignment resolves collisions against the set of resources
|
||||
//! being decoded — so `models_named` (a filtered subset, used by the ship
|
||||
//! assembler and the viewer) can reach a different answer from a whole-container
|
||||
//! decode. This measures that directly.
|
||||
//! Usage: filter_consistency <container.xpr> [resource...]
|
||||
use sylpheed_formats::mesh::Xbg7Model;
|
||||
use std::collections::HashSet;
|
||||
fn main() {
|
||||
let a: Vec<String> = std::env::args().collect();
|
||||
let bytes = std::fs::read(&a[1]).expect("container");
|
||||
let full = Xbg7Model::anchor_models_cancellable(&bytes, 0.0, &|| false);
|
||||
let names: Vec<String> = if a.len() > 2 {
|
||||
a[2..].to_vec()
|
||||
} else {
|
||||
full.iter().map(|m| m.name.clone()).collect()
|
||||
};
|
||||
let mut differ = 0usize;
|
||||
for n in &names {
|
||||
let want: HashSet<String> = std::iter::once(n.clone()).collect();
|
||||
let one = Xbg7Model::models_named(&bytes, &want, &|| false);
|
||||
let (Some(f), Some(s)) = (full.iter().find(|m| &m.name == n), one.first()) else {
|
||||
continue;
|
||||
};
|
||||
let off = |m: &Xbg7Model| m.meshes.first().and_then(|s| s.vbuf_offset).unwrap_or(0);
|
||||
if off(f) != off(s) {
|
||||
differ += 1;
|
||||
if differ <= 10 {
|
||||
println!("{n}: full decode at 0x{:x}, filtered at 0x{:x}", off(f), off(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
println!("{differ} of {} resources decode differently when filtered", names.len());
|
||||
}
|
||||
20
crates/sylpheed-formats/examples/node_scale.rs
Normal file
20
crates/sylpheed-formats/examples/node_scale.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
//! Dump a composite's scene nodes with their scale — the transform the static
|
||||
//! assembler copies verbatim into a placement.
|
||||
//! Usage: node_scale <container.xpr> <composite name>
|
||||
use sylpheed_formats::mesh::scene_world_nodes;
|
||||
fn main() {
|
||||
let a: Vec<String> = std::env::args().collect();
|
||||
let bytes = std::fs::read(&a[1]).expect("container");
|
||||
for n in scene_world_nodes(&bytes, &a[2]) {
|
||||
// Row norms of `m`: a rotation has all three at 1.0; anything else is a
|
||||
// scale baked into the matrix, which `s` does not show.
|
||||
let norm = |r: [f32; 3]| (r[0] * r[0] + r[1] * r[1] + r[2] * r[2]).sqrt();
|
||||
println!(
|
||||
"{:<26} s[{:7.3}{:7.3}{:7.3}] |m rows|[{:7.3}{:7.3}{:7.3}] t[{:9.1}{:9.1}{:9.1}]",
|
||||
n.resource,
|
||||
n.s[0], n.s[1], n.s[2],
|
||||
norm(n.m[0]), norm(n.m[1]), norm(n.m[2]),
|
||||
n.t[0], n.t[1], n.t[2]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1097,33 +1097,30 @@ 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
|
||||
reminder that a bulky hull is not a bug.
|
||||
|
||||
### The containment screen fails too — because the static placement is worse
|
||||
### The containment screen, and a correction: the assembler is NOT at fault
|
||||
|
||||
`examples/envelope_screen.rs` is the metric the eye actually used: assemble a
|
||||
ship, and for each part measure how far its world box protrudes past the box of
|
||||
all the *other* parts, **per axis** (a bow legitimately extends the long axis, so
|
||||
protrusion only means something against that axis' own envelope).
|
||||
protrusion only counts against that axis' own envelope).
|
||||
|
||||
It does not flag `e106_bdy_03` either, before or after the coverage fix. Dumping
|
||||
the static assembly shows why, and the reason is a bigger finding than the screen:
|
||||
It does not flag `e106_bdy_03` under the **pre-fix** decoder, and the reason is
|
||||
now established. Dumping the pre-fix static assembly shows the shared turret
|
||||
`e303_wep_01` occupying **1600×2100×4800** around a ~400×400×2000 hull — an
|
||||
envelope nothing can protrude past.
|
||||
|
||||
```
|
||||
e106_bdy_03 X[ -300, 300] Y[ 0, 1600] Z[ 188, 1186]
|
||||
e303_wep_01 X[-1179, 421] Y[ -996, 1104] Z[-2368, 2432] ← shared turret
|
||||
e106_bdy_04 X[ -215, 215] Y[ -165, 234] Z[ -991, 389]
|
||||
```
|
||||
⚠️ **This was first written up here as a static-assembler defect. That was
|
||||
wrong.** The composite's nodes are clean — `rou_e303_wep_01_root` carries scale
|
||||
`1.0` and an orthonormal matrix at `t[±179, 54, 32]` — and under the **current**
|
||||
decoder the same assembly places the turret as a tidy `49×23×42` box at ±179.
|
||||
The inflation only appears with `XBG7_COVER_SLACK=4`, i.e. the pre-fix decode:
|
||||
**the exact-coverage fix repaired the turret as well as `e106_bdy_03`.** The
|
||||
error came from comparing a deliberately pre-fix render against post-fix
|
||||
measurements of the same resource.
|
||||
|
||||
The **turret swallows the ship**: `e303_wep_01`'s world box is 1600×2100×4800
|
||||
around a hull of ~400×400×2000, so nothing can protrude past the envelope. Its
|
||||
*decode* is not at fault — the resource comes out 49×23×42 in `Stage_S01` through
|
||||
`Stage_S06` alike, consistent everywhere — so this is a **static-assembler**
|
||||
defect: the placement blows a 49-unit turret up by 30–110× per axis (and
|
||||
non-uniformly, so it is not a simple scale factor).
|
||||
|
||||
Two conclusions. The screen is only meaningful once placement is trustworthy, and
|
||||
**the static assembler has a worse defect than the decoder did** — one that the
|
||||
capture-baked path for `e106` hides, because the baked table places the ship
|
||||
correctly and is what the viewer prefers.
|
||||
So the screen's blindness had a mundane cause — one mis-decode hid another by
|
||||
inflating the envelope — and it is worth keeping as a forward-looking invariant,
|
||||
with that caveat: it can only see a protruding part when nothing else is inflated.
|
||||
|
||||
### The anchor work has plateaued at 98.7 % — state and what is left
|
||||
|
||||
|
||||
Reference in New Issue
Block a user