M5 integrate: recognizable arena interior -- verified divide makes texturing land

render_final.py applies the oracle-verified perspective divide (texel8 =
texu/texz*2048, the *8 from the '3 integer bits') with per-quad texel tiling.
The battle frame now reads as a real first-person arena interior: a tiled
floor receding correctly to the vanishing point (panels shrinking toward the
horizon), a paneled ceiling, and a horizon band of structures. The verified
divide fixed the tiling frequency that washed out earlier attempts.

Monochrome because surfaces map to one texture family -- the exact
texid->handle binding needs the board texture-RAM model (texels go to board
tex memory via TXDN/io, not watchable DRAM; confirmed by the storage trace).
Geometry, perspective, texel decode, and the divide are all solved from the
live wire; per-surface texture selection is the last remaining piece.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-19 22:15:44 -05:00
co-authored by Claude Opus 4.8
parent b74b0c89e8
commit a24210adf0
@@ -0,0 +1,77 @@
"""M5 integrate: battle frame with the VERIFIED perspective divide (correct
texel scale/tiling) + per-quad texid -> texture (best-effort binding, since the
exact texid->handle needs the board texture-RAM model). Real colours from real
textures, perspective-correct."""
import pickle, struct, os, sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, r'C:\VWE\TeslaRel410\dpl3-revive\patha')
import numpy as np
from PIL import Image
from texstore import build_texstore
HERE=os.path.dirname(os.path.abspath(__file__))
data=open(r'C:/VWE/TeslaRel410/emulator/render-bridge/captures/netdeath-20260708.fifodump','rb').read()
recs=[]; off=0
while True:
i=data.find(b'VPXM',off)
if i<0: break
ln=struct.unpack_from('<I',data,i+4)[0]; body=data[i+8:i+8+ln]; off=i+8+ln
if len(body)>=4:
a=struct.unpack_from('<I',body,0)[0]
if a<0x100: recs.append((a,body[4:]))
store=build_texstore(recs)
texlist=list(store.items()) # [(handle,(u,v,mode,arr)), ...]
print("%d textures"%len(texlist))
d=pickle.load(open('battle_prog.pkl','rb')); prog=d['prog']
def f32(w): return struct.unpack('<f',struct.pack('<I',w&0xffffffff))[0]
def rd(a): return prog.get(a,0)
setups=[a for a in sorted(prog) if rd(a)==0x100]
EDGE={0x42,0x0d,0x2c}
W,H=832,512
yy,xx=np.mgrid[0:H,0:W].astype(np.float64)
zbuf=np.full((H,W),-1e30)
img=np.zeros((H,W,3),np.uint8)
drawn=0
for a in setups:
p=a+4; edges=[]
while ((rd(p)>>8)&0xff) in EDGE and len(edges)<6:
edges.append((f32(rd(p+4)),f32(rd(p+8)),f32(rd(p+12)))); p+=16
if len(edges)<3: continue
ep=np.ones((H,W),bool); en=np.ones((H,W),bool)
for A,B,C in edges:
v=A*xx+B*yy+C; ep&=v>=0; en&=v<0
inside=ep|en
if not inside.any(): continue
zp=tzp=up=vp=None; tid=None; q=p
for _ in range(70):
w=rd(q); op=(w>>8)&0xff; ad=w&0xff
if op==0x21 and zp is None: zp=(f32(rd(q+4)),f32(rd(q+8)),f32(rd(q+12)))
if op==0x43 and ad==32 and tzp is None: tzp=(f32(rd(q+4)),f32(rd(q+8)),f32(rd(q+12)))
if op==0x43 and ad==58 and up is None: up=(f32(rd(q+4)),f32(rd(q+8)),f32(rd(q+12)))
if op==0x43 and ad==78 and vp is None: vp=(f32(rd(q+4)),f32(rd(q+8)),f32(rd(q+12)))
if op==0xf7 and ad in (141,142) and tid is None: tid=(rd(q+4)>>2)&0x3f
q+=4
if zp is None: continue
z=zp[0]*xx+zp[1]*yy+zp[2]
inside&=z>zbuf
if not inside.any(): continue
zbuf[inside]=z[inside]
if up and vp and tzp:
# best-effort texid -> texture (exact binding = board tex-RAM model, pending)
h,(tu,tv,mode,arr)=texlist[(tid or 0)%len(texlist)]
tzv=tzp[0]*xx+tzp[1]*yy+tzp[2]
tzv=np.where(np.abs(tzv)<1.0, np.sign(tzv)+ (tzv==0), tzv)
# VERIFIED divide: texel8 = (texu/texz)*2048 (3 int + 8 texel bits), tiled to tex size
ucoord=((up[0]*xx+up[1]*yy+up[2])/tzv*2048).astype(np.int64)
vcoord=((vp[0]*xx+vp[1]*yy+vp[2])/tzv*2048).astype(np.int64)
ui=np.mod((ucoord*tu)>>8, tu) # 8-bit texel -> texture-size index, tiled
vi=np.mod((vcoord*tv)>>8, tv)
samp=arr[vi,ui]
img[inside]=samp[inside]
else:
img[inside]=(60,60,70)
drawn+=1
print("drawn %d quads"%drawn)
Image.fromarray(img,'RGB').save(os.path.join(HERE,'battle_final.png'))
print("wrote battle_final.png")