fix(mesh): pre-pivot sub-meshes must cover their pool, not just index in range
The last two blocks that under-covered their vertex pool were f102_break.dat and f104_break.dat in ptc_pack.xpr, each reading a neighbouring block's index buffer against the wrong declaration (414 verts indexed to 404; 160 indexed to 79). Their marker lists do not map onto the stored blocks -- only 2 of 9 and 4 of 10 sub-meshes decoded at all. Requiring coverage (max_idx + 4 >= vtx_count) for pre-pivot sub-meshes drops exactly the mismatched pieces. Every decoded sub-mesh disc-wide now covers its pool: 8580 at slack 0, 49 within tolerance, none beyond, none negative. Coverage 6069/6294, inconsistency 56, truth table 46/46 -- all unchanged. Suite green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1397,9 +1397,20 @@ fn anchor_grouped_meshes(
|
||||
// quality question — it is unusable. Measured 2026-08-12: 18
|
||||
// sub-meshes disc-wide carried indices up to 364 vertices past
|
||||
// the end (`coverage_audit`), which any renderer would fault on.
|
||||
let in_range =
|
||||
(0..ic).all(|k| (be16(bytes, ib + k * 2) as usize) < vc);
|
||||
if in_range {
|
||||
// Same two structural requirements the searched path enforces:
|
||||
// every index inside the buffer, and the indices reaching the
|
||||
// end of it. Real geometry covers its pool exactly — 8 586 of
|
||||
// 8 636 decoded sub-meshes reference their last vertex, none
|
||||
// more than 3 short (`coverage_audit`) — so a sub-mesh whose
|
||||
// indices stop well short is reading the wrong block, not a
|
||||
// sparse one.
|
||||
let mut max_idx = 0usize;
|
||||
let in_range = (0..ic).all(|k| {
|
||||
let i = be16(bytes, ib + k * 2) as usize;
|
||||
max_idx = max_idx.max(i);
|
||||
i < vc
|
||||
});
|
||||
if in_range && max_idx + 4 >= vc {
|
||||
meshes.push(read_pool_mesh(bytes, ib, vb, ic, vc, decl));
|
||||
}
|
||||
vb += vc * stride;
|
||||
|
||||
Reference in New Issue
Block a user