ACCEPTANCE 2: fxtest content renders -- perspective ground + textured boxes
The per-draw effect programs (0x816f000, second DMA emitter) parse and execute through the named ISA: 9 primitives, z-buffered perspective scene with u/v texture gradients. The earlier 'fixed per-tile program' conclusion was a sniffer artifact. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -322,3 +322,16 @@ Remaining to full purity: name the texz seed op + extract the real ramp table.
|
||||
the emulator (the dn* unpack programs then define the texel wire format).
|
||||
- Registry census at cmd 13000: 1186 live objects (405 type-9 + 405 type-10
|
||||
pairs = effect+texmap, 159 type-11, 48 type-8, ...).
|
||||
|
||||
## ACCEPTANCE 2: fxtest CONTENT RENDERS (2026-07-19 ~10:30)
|
||||
The draws build FRESH float-coefficient programs at 0x816f000 (emitter
|
||||
0xf0400ebc — a second DMA writer; the earlier "4 fixed payloads" was a sniffer
|
||||
artifact) + transformed vertex buffers (0x2c7xxxxx+, 80B records). Packet
|
||||
shape per primitive: {19w setup: SETENABS + op-0x2c edges + MEMltTREE z +
|
||||
z-write} + {80w x4-MIP ladder: TREEintoMEM_L3(texz/texu/texv) x5 rungs, with
|
||||
FOOTER.SS's Cturn_z_to_tex_by_4 constant verbatim}. render_fx.py executes
|
||||
them: 9 primitives -> a PERSPECTIVE GROUND PLANE + TEXTURED 3D BOXES (the
|
||||
classic VWE building scene) with correct z-occlusion; u/v gradients shown as
|
||||
false colour (fx_render/fx_depth.png in the scratchpad). Remaining polish:
|
||||
op-0x2c edge exact semantics (treated as winding-agnostic edge) + real texel
|
||||
sampling via the TXDN stream.
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
"""Render fxtest's per-draw effect primitives from fx_program.pkl: per pair
|
||||
{19w setup: edges+z, 80w ladder: texz/u/v planes}, rasterize with z-buffer and
|
||||
visualize (u,v) as colour + z as depth. Structure render (no texel sampling)."""
|
||||
import pickle, os, struct
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
S = r'C:\Users\cyd\AppData\Local\Temp\claude\c--VWE-TeslaRel410\4e848c76-6e89-4034-8047-d8d491cb32d8\scratchpad'
|
||||
d = pickle.load(open(os.path.join(S, 'fx_program.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)
|
||||
|
||||
# find setup blocks: SETENABS followed by 0x..2c00-family edge headers
|
||||
setups = []
|
||||
addrs = sorted(prog)
|
||||
for a in addrs:
|
||||
if rd(a) == 0x100 and ((rd(a + 4) >> 8) & 0xff) == 0x2c:
|
||||
setups.append(a)
|
||||
print("%d effect primitives" % len(setups))
|
||||
|
||||
W, H = 832, 512
|
||||
yy, xx = np.mgrid[0:H, 0:W].astype(np.float64)
|
||||
zbuf = np.full((H, W), -1e30)
|
||||
rgb = np.zeros((H, W, 3))
|
||||
drawn = 0
|
||||
for a in setups:
|
||||
# edges: 4-word {hdr(0x2c), A, B, C} groups until a non-0x2c header
|
||||
en_p = np.ones((H, W), bool); en_n = np.ones((H, W), bool)
|
||||
p = a + 4
|
||||
nedges = 0
|
||||
while ((rd(p) >> 8) & 0xff) == 0x2c and nedges < 6:
|
||||
A, B, C = f32(rd(p+4)), f32(rd(p+8)), f32(rd(p+12))
|
||||
v = A * xx + B * yy + C
|
||||
en_p &= v >= 0; en_n &= v < 0
|
||||
p += 16; nedges += 1
|
||||
en = en_p | en_n
|
||||
if not en.any():
|
||||
continue
|
||||
# scan forward for the z instr (op 0x21) then the ladder (first texu/texv L3)
|
||||
zp = None; up = None; vp = None
|
||||
q = p
|
||||
for _ in range(40):
|
||||
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 == 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 up and vp and zp:
|
||||
break
|
||||
q += 4
|
||||
if zp is None:
|
||||
continue
|
||||
z = zp[0] * xx + zp[1] * yy + zp[2]
|
||||
en &= z > zbuf
|
||||
if not en.any():
|
||||
continue
|
||||
zbuf[en] = z[en]
|
||||
if up and vp:
|
||||
u = up[0] * xx + up[1] * yy + up[2]
|
||||
v = vp[0] * xx + vp[1] * yy + vp[2]
|
||||
# u/v as colour: hue-ish mapping, normalized per prim
|
||||
un = np.clip((u - u[en].min()) / max(1e-9, np.ptp(u[en])), 0, 1)
|
||||
vn = np.clip((v - v[en].min()) / max(1e-9, np.ptp(v[en])), 0, 1)
|
||||
rgb[..., 0][en] = 60 + 180 * un[en]
|
||||
rgb[..., 1][en] = 40 + 160 * vn[en]
|
||||
rgb[..., 2][en] = 220 - 140 * un[en]
|
||||
else:
|
||||
rgb[..., 0][en] = 200; rgb[..., 1][en] = 200; rgb[..., 2][en] = 200
|
||||
drawn += 1
|
||||
print("drawn %d" % drawn)
|
||||
img = np.clip(rgb, 0, 255).astype(np.uint8)
|
||||
Image.fromarray(img, 'RGB').save(os.path.join(S, 'fx_render.png'))
|
||||
zv = zbuf.copy(); m = zv > -1e29
|
||||
zi = np.zeros((H, W), np.uint8)
|
||||
if m.any():
|
||||
zn = (zv - zv[m].min()) / max(1e-9, (zv[m].max() - zv[m].min()))
|
||||
zi[m] = (40 + 215 * zn[m]).astype(np.uint8)
|
||||
Image.fromarray(zi, 'L').save(os.path.join(S, 'fx_depth.png'))
|
||||
print("wrote fx_render.png + fx_depth.png")
|
||||
Reference in New Issue
Block a user