Files
TeslaRel410/emulator/firmware-decomp/M5-TEXTURING.md
T
CydandClaude Opus 4.8 b74b0c89e8 M5-A: the faithful IGC perspective divide, verified against the divlogo oracle
igc_divide.py implements EOF.C's texdivide bit-serial restoring division on
the pixel bit-memory, and extracts the texel per the '3 int | 8 texel | 5
subtexel' result layout ((field>>9)&0xff). Verified against the divlogo's
KNOWN mapping (u:0.001->0.999 across screen x[532,660]): the extracted texel
sweeps monotonically 0->251, and a fast float equivalent (texu/texz*2048)
tracks the bit-serial result to +/-1. This fixes the missing scale factor
(the '3 integer bits' = x8) that made earlier renders wash out. M5 plan in
M5-TEXTURING.md. Next: M5-B scene-graph material binding, then integrate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 22:13:10 -05:00

48 lines
2.5 KiB
Markdown

# M5 — faithful texturing (the last mile to recognizable frames)
Fresh, deliberate effort. Geometry, perspective structure, and the texel format
are all solved from the live wire (M3). What remains is making the textures land
on the surfaces *correctly*, which is two real sub-projects — not scale-guessing.
## Sub-project A: the faithful bit-serial perspective divide
The IGC computes per-pixel texel coords by an actual restoring division, not a
float. From EOF.C `perspective_divides` / `texdivide`:
```
IGC_CLEAR(texz + texzbits, 1) // lose 1 bit of texz
texdivide(texu, texz+1, 17) // texu <- texu / (texz>>1), 17 sig bits
texdivide(texv, texz+1, 17)
// texdivide: for i in 0..sigbits-1:
// SETENABS
// MEMgeMEM(num, denom, divbits) // en = mem[num:divbits] >= mem[denom:divbits]
// MEMminuseqMEM(num, denom, divbits, divbits) // if en: num -= denom
// ENABintoMEM(res) // quotient bit
// res--, denom++, divbits--
```
Result layout (comment): "16 bits == 3 (0..7.99 integer) | 8 (0..255 texel) | 5
sub-texel". So the texel index is an 8-bit slice of the divided field.
**Oracle**: the `#if divlogo` block (EOF.C ~612-660) builds a KNOWN mapping — the
Division logo, texture id 0, size 64, into screen rect x[532,660] y[382,440],
with u:[0.001,0.999], v:[0.999,0.001]. uscale = (1<<(texubits-3))*0.999 ~= 2^17.
A correct divide makes the extracted texel sweep 0->63 linearly across the rect.
This is the unit test (igc_divide.py) — implement the exact op loop, feed the
logo planes, assert the texel sweep. No ground-truth screenshot needed.
## Sub-project B: DPL scene-graph material binding
Per-quad texture = the texmap bound to that object, set by the `flush` before
each draw (observed: `flush [0x72, 0xd, ...]`, 0x72 = a PLAYER texmap handle).
Resolve by tracking create/flush/list_add into a scene graph:
object -> material/texmap -> texture handle, then map each draw's primitives to
their object's texture. The texid 6-bit field in the packet indexes a bound-slot
table the flush maintains.
## Sequence
1. A: faithful divide + divlogo unit test (self-contained, verifiable). ← start
2. B: scene-graph material tracker (from the wire).
3. Integrate: full battle frame, correct per-quad textures, perspective-correct
texels. Re-run against a battle frame; the arena should read as real imagery.
Everything upstream (firmware on C core, geometry, texel decode) is committed;
this note scopes the honest remainder.