diff --git a/emulator/firmware-decomp/emu860c/render_textured.py b/emulator/firmware-decomp/emu860c/render_textured.py new file mode 100644 index 00000000..5e570a9e --- /dev/null +++ b/emulator/firmware-decomp/emu860c/render_textured.py @@ -0,0 +1,90 @@ +"""Proof of texture sampling on real battle geometry: each textured quad's +texu/texv planes sample a real game texture (nearest, tiled) with z-buffer. +Binding note: per-quad texid->handle binding is not yet resolved, so this maps +all textured surfaces to one representative texture to demonstrate the +perspective-correct SAMPLING on the real geometry (honest proof, not final).""" +import pickle, struct, os, sys +sys.path.insert(0, r'C:VWETeslaRel410dpl3-revivepatha') +import sys, os +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 +from vrboard import A +ANAME={int(a):a.name for a in A} +HERE=os.path.dirname(os.path.abspath(__file__)) + +# reload the wire for the texstore +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('=4: + a=struct.unpack_from('>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 Aa,Bb,Cc in edges: + v=Aa*xx+Bb*yy+Cc; ep&=v>=0; en&=v<0 + inside=ep|en + if not inside.any(): continue + # z + texu/texv planes + zp=up=vp=tzp=None; q=p + for _ in range(60): + 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 zp and up and vp and tzp: break + 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: + tz=tzp[0]*xx+tzp[1]*yy+tzp[2] + tz=np.where(np.abs(tz)<1e-6, 1e-6, tz) + u=(up[0]*xx+up[1]*yy+up[2])/tz # perspective-correct texel coords + v=(vp[0]*xx+vp[1]*yy+vp[2])/tz + ui=np.mod(u.astype(np.int64), pu) + vi=np.mod(v.astype(np.int64), pv) + samp=parr[vi, ui] + img[inside]=samp[inside] + else: + img[inside]=(70,70,80) + drawn+=1 +print("drawn %d quads"%drawn) +Image.fromarray(img,'RGB').save(os.path.join(HERE,'battle_textured.png')) +print("wrote battle_textured.png")