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>
48 lines
2.5 KiB
Markdown
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.
|