Decode tfrag normals from the overlay's sine table
Normals are per-vertex, and the tfrag's light records store them as an azimuth and elevation *index* into a 256-entry sine table rather than as angles. This decodes that table and builds normals for the renderer. The reference reads the table from an ELF at BOOT_NORMAL_TABLE_VADDR (0x165500) and notes every level overlay carries a copy. The overlay has no ELF header -- it is a raw loadable image starting with code -- so there is no program header to map a virtual address through, and the offset is not fixed either: level 0 has the table at 0xb650c, level 5 at 0xcca30, level 26 at 0xb6284. So it is located by content. Every table starts with exactly (1.0, 0.0), angle zero, and the scan then requires all 256 entries to match cos(i*pi/128), sin(i*pi/128). Validating the whole curve is what makes the search safe: a lone (1.0, 0.0) pair of floats occurs by accident in a 2.7 MB executable, and a test plants one as a decoy. The values are read from the disc rather than recomputed, since those are the numbers the game indexes. A normal is (cos(az)cos(el), sin(az)cos(el), sin(el)) *negated* -- the stored normals point into the surface -- and then given the same Z-up to Y-up swap as positions, or the lighting comes out sideways. The azimuth and elevation fields are stored signed but index 0..=255, so entry 200 must be read as 200, not as -56; there is a test for that. Also frames and parses the level light bank from the gameplay extent, and the pipeline now writes overlay.bin. The per-vertex lighting evaluation is not written yet, so the renderer stays unlit by default. GC_ENGINE_LIT=1 substitutes a host directional light for inspection -- that is a stand-in, not the game's lighting, and it is deliberately not the default. The hash table is regenerated for the 27 new overlay.bin files: now 5,213 files and 5,762,497,559 bytes.
This commit is contained in:
@@ -158,6 +158,19 @@ pub fn unpack(iso_path: &Path, out_dir: &Path) -> Result<Vec<Extracted>, String>
|
||||
);
|
||||
parts += 1;
|
||||
}
|
||||
// The level's executable. It carries the normal
|
||||
// table the tfrag lighting microcode indexes, so
|
||||
// it is not optional background.
|
||||
let overlay = data_header.part(LevelDataPart::Overlay);
|
||||
if let Ok(bytes) = overlay.slice(data_extent, data_extent.len()) {
|
||||
write_file(
|
||||
out_dir,
|
||||
&format!("{stem}/overlay.bin"),
|
||||
bytes,
|
||||
&mut written,
|
||||
);
|
||||
parts += 1;
|
||||
}
|
||||
// Graphics Synthesizer RAM holds the CLUTs the tfrag
|
||||
// texture table's palette indices point into, so it
|
||||
// is needed to decode any level texture.
|
||||
|
||||
Reference in New Issue
Block a user