#!/usr/bin/env python3 """Phase 0: replay a captured BTL4OPT VelociRender stream through the friend's virtual board and render the decoded world from a synthetic aerial camera (the game's own camera path isn't decoded yet -> singular matrix). Proves their renderer reconstructs OUR game's geometry+textures from the live wire. py phase0_render.py [distmul] [ymul] """ import os, sys PATHA = r'C:\VWE\TeslaRel410\dpl3-revive\patha' sys.path.insert(0, PATHA) os.environ.setdefault('SDL_VIDEODRIVER', 'dummy') import numpy as np from vrboard import VirtualBoard, Assembler from decode_anim import find_framed_start from vrview import Renderer cap = sys.argv[1] upto = int(sys.argv[2]) if len(sys.argv) > 2 else 400 out = sys.argv[3] distmul = float(sys.argv[4]) if len(sys.argv) > 4 else 1.6 ymul = float(sys.argv[5]) if len(sys.argv) > 5 else 1.2 # +up (y-down world -> negate below) data = open(cap, 'rb').read() board = VirtualBoard() asm = Assembler(); asm.feed(data[find_framed_start(data):]) n = 0 for m in asm: try: board.handle(m) except Exception as e: n += 1 if board.frames >= upto: break print(f'replayed frame={board.frames} nodes={len(board.nodes)} ' f'geom={len(board.geom)} tex={len(board.tex)} handler_errs={n}') r = Renderer(w=832, h=512) r.cache.maybe_rebuild(board) c = r.cache print(f'instances={len(c.instances)}') # world-space bounds over every renderable instance triangle mins = np.array([np.inf]*3); maxs = np.array([-np.inf]*3) nverts = 0 for inst in c.instances: M = r.chain_matrix(board, inst['chain']) for gh in inst['geoms']: mesh = c._mesh(board, gh) if not mesh or 'pos' not in mesh or len(mesh['pos']) == 0: continue pw = mesh['pos'] @ M[:3, :3] + M[3, :3] mins = np.minimum(mins, pw.min(0)); maxs = np.maximum(maxs, pw.max(0)) nverts += len(pw) if not np.isfinite(mins).all(): print('NO renderable geometry'); sys.exit(1) center = (mins + maxs) / 2.0 size = maxs - mins radius = float(np.linalg.norm(size) / 2.0) or 100.0 print(f'world bounds min={mins.round(1)} max={maxs.round(1)}') print(f'center={center.round(1)} size={size.round(1)} radius={radius:.1f} verts={nverts}') # synthetic aerial look-at. DPL world is y-DOWN, so "up above" = -y. eye = center + np.array([radius*0.6, -radius*ymul, radius*distmul]) fwd = center - eye; fwd /= max(np.linalg.norm(fwd), 1e-6) back = -fwd worldup = np.array([0.0, -1.0, 0.0]) # y-down right = np.cross(worldup, back); right /= max(np.linalg.norm(right), 1e-6) up = np.cross(back, right) M = np.eye(4) M[0, :3], M[1, :3], M[2, :3], M[3, :3] = right, up, back, eye # widen the far clip so the aerial distance doesn't cull the whole scene r.cam_matrix = lambda _b, _M=M: _M if c.view is not None and c.view in board.nodes: pass # projection still read from the view body inside draw() r.draw(board) r.pygame.image.save(r.screen, out) print('wrote', out)