Locate the tie texcoord field at i16[4]/i16[5], and rule out the (6,7) artifact

Scoring each adjacent i16 slot pair of the 16-byte tie vertex for 12-bit
fixedness over 36,501 level-0 vertices locates the fields:

  (i16[0], i16[1])    1.1%   not a texcoord
  (i16[2], i16[3])    4.8%   not a texcoord
  (i16[4], i16[5])   73.6%   <- the texcoords
  (i16[6], i16[7])   76.6%   <- the near-constant 4096/0 pair

(6,7) scores HIGHER and is not a texcoord. It is the near-constant 4096, 0 pair:
4096 is 1.0 in 12-bit fixed and 0 is zero, so both are trivially multiples of
4096. That is the constant artifact again -- a fixed pair would score 100%
anywhere. The field is (4,5), with (0,1) and (2,3) ruled out at 1.1% and 4.8%.

The first four slots span the full i16 range with 10,803 to 13,421 distinct values
each, so they are neither texcoords nor small fields.

The vertex base is confirmed to be vert_ofs alone. Adding the shader or control
region makes it worse --

  vert_ofs only                               73.6%
  bfc + vert_ofs                              69.8%
  bfc + control_count*control_size + vert_ofs  66.7%

-- so the 73.6% residual is not a mislocated base. Worth recording because
vert_ofs (3..7) numerically overlaps the shader region (5*shader_count, 5..20
quadwords), which made a combined base look plausible; measurement says the
simple one is right.

The test asserts (4,5) above 60% and (0,1)/(2,3) below 5%/15%, and names the
(6,7) artifact in its failure message so the higher-scoring pair cannot be
mistaken for the answer later.
This commit is contained in:
2026-10-13 04:30:45 +02:00
parent 35b087c882
commit 180fc27b04
3 changed files with 110 additions and 9 deletions
+76
View File
@@ -3433,6 +3433,82 @@ fn tie_vertex_element_is_16_bytes_with_fixed_texcoords() {
}
}
// Which i16 slot pair is the texcoord? Scored per pair on level 0.
let core0 = std::fs::read(data.join("level0/core_data.bin")).expect("core_data");
let idx0 = std::fs::read(data.join("level0/core_index.bin")).expect("core_index");
let t0 = LevelCoreHeader::parse(&idx0).expect("hdr").tie_classes;
let (mut pair_hits, mut pair_tot) = ([0usize; 4], 0usize);
for c in 0..t0.count as usize {
let o = t0.offset as usize + c * 0x20;
let e = ClassEntry::parse(&idx0[o..o + 0x20]).expect("entry");
let base = e.offset_in_asset_wad as usize;
if base == 0 || base + 0x48 > core0.len() {
continue;
}
let Ok(hdr) = TieClassHeader::parse(&core0[base..base + 0x48]) else {
continue;
};
for lod in [1usize, 2] {
let off = hdr.packets[lod] as usize;
if off == 0 {
continue;
}
let s = base + off;
let Some(blk) = parse_tie_block(&core0, s) else {
continue;
};
let rr: Vec<&[u8]> = blk.header.records(&core0[s..]).collect();
for (ri, rec) in rr.iter().enumerate() {
let Ok(tr) = TieRecord::parse(rec) else {
continue;
};
let start = s + 16 + ri * 16 + tr.pointer as usize + tr.vert_ofs as usize * 16;
for v in 0..tr.vert_size as usize {
let q = start + v * 16;
if q + 16 > core0.len() {
break;
}
pair_tot += 1;
for p in 0..4usize {
let a =
i16::from_le_bytes(core0[q + p * 4..q + p * 4 + 2].try_into().unwrap());
let b = i16::from_le_bytes(
core0[q + p * 4 + 2..q + p * 4 + 4].try_into().unwrap(),
);
if a % 4096 == 0 && b % 4096 == 0 {
pair_hits[p] += 1
}
}
}
}
}
}
// (4,5) is the texcoord field. (6,7) scores *higher* only because it is the
// near-constant 4096/0 pair, which is trivially a pair of multiples of 4096
// -- the same artifact a constant would produce anywhere.
assert!(pair_tot > 30_000, "only {pair_tot} level-0 vertices scored");
let r = |p: usize| pair_hits[p] as f64 / pair_tot as f64;
assert!(r(2) > 0.60, "texcoord pair (4,5) fell to {:.3}", r(2));
assert!(
r(0) < 0.05,
"pair (0,1) looks like a texcoord at {:.3}",
r(0)
);
assert!(
r(1) < 0.15,
"pair (2,3) looks like a texcoord at {:.3}",
r(1)
);
eprintln!(
"tie vertex slot pairs, level 0 (n={pair_tot}): (0,1) {:.1}% (2,3) {:.1}% \
(4,5) {:.1}% <- texcoords (6,7) {:.1}% <- the constant 4096/0 pair",
r(0) * 100.0,
r(1) * 100.0,
r(2) * 100.0,
r(3) * 100.0
);
assert!(verts > 90_000, "only {verts} tie vertices reached");
let r16 = fixed16 as f64 / verts as f64;
let r8 = fixed8 as f64 / verts as f64;