- emulator/render-bridge/: the pod->Dave's-renderer live bridge (rescued from session scratchpad): live_bridge.py (first-person cam from the 0x1f pose, arrow-key eye-height trim), fp/chase offline renders, fifobridge, diagnostics, gauge_arena[_sound].conf, and launch_pod.ps1 (one-command pod+bridge restart: pentapus VPX_EXPLODE + AWE32 sound + GL bridge). - _backend.py: renderer selection -- vrview_gl.GLRenderer (moderngl) by default, VRVIEW_SOFT=1 for the software reference; backend-agnostic frame save via last_frame. - GPU backend runs on side-by-side CPython 3.13 (moderngl/glcontext cp313 wheels; no MSVC needed): 63fps headless / 60.7 windowed vs 21fps software on the same scene, and the GL path also draws the vertex-alpha cloud dome. - vrview.py: VRVIEW_GAMMA env selects DAC gamma (1.25 live-renderer legacy vs 1.7 per the original GAMMA.C) for A/B against the real pod. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
54 lines
1.9 KiB
Python
54 lines
1.9 KiB
Python
"""First-person cockpit camera from the player vehicle's 0x1f pose.
|
|
py fp_render.py <fifodump> <out.png> [upoff] [worldup_y] [fwd_sign]"""
|
|
import os, sys, struct
|
|
os.environ.setdefault('SDL_VIDEODRIVER', 'dummy')
|
|
import numpy as np
|
|
from _backend import pick_renderer, save_frame
|
|
from vrboard import VirtualBoard, Msg, A
|
|
Renderer, backend = pick_renderer()
|
|
|
|
data = open(sys.argv[1], 'rb').read()
|
|
out = sys.argv[2]
|
|
upoff = float(sys.argv[3]) if len(sys.argv) > 3 else 8.0
|
|
wuy = float(sys.argv[4]) if len(sys.argv) > 4 else 1.0 # worldup y sign
|
|
fsign = float(sys.argv[5]) if len(sys.argv) > 5 else 1.0 # nose sign
|
|
board = VirtualBoard()
|
|
off = 0
|
|
while off + 8 <= len(data):
|
|
if data[off:off + 4] != b'VPXM':
|
|
off += 1; continue
|
|
ln = struct.unpack_from('<I', data, off + 4)[0]
|
|
body = data[off + 8:off + 8 + ln]; off += 8 + ln
|
|
if len(body) >= 4:
|
|
a = struct.unpack_from('<I', body, 0)[0]
|
|
if a < 0x100:
|
|
try: board.handle(Msg(False, 0xff, a, body[4:]))
|
|
except Exception: pass
|
|
|
|
r = Renderer(w=832, h=512)
|
|
c = r.cache; c.maybe_rebuild(board)
|
|
anim = board.anim_abs
|
|
chain = c.cam_chain
|
|
h = None
|
|
if chain and chain[-1] in anim:
|
|
h = chain[-1]
|
|
elif anim:
|
|
h = max(anim, key=lambda k: float(np.abs(np.array(anim[k][9:12])).sum()))
|
|
if h is None:
|
|
print("no animated vehicle"); sys.exit(1)
|
|
f = anim[h]
|
|
R = np.array(f[:9]).reshape(3, 3); t = np.array(f[9:12])
|
|
fwd = fsign * R[2] # vehicle nose (+Z), sign-tunable
|
|
fwd = fwd / max(np.linalg.norm(fwd), 1e-6)
|
|
worldup = np.array([0.0, wuy, 0.0])
|
|
eye = t + worldup * upoff # up out of the cockpit
|
|
back = -fwd
|
|
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
|
|
r.cam_matrix = lambda _b, _M=M: _M
|
|
r.draw(board)
|
|
save_frame(r, out)
|
|
print(f"wrote {out} [{backend}] veh={hex(h)} eye={eye.round(1)} fwd={fwd.round(2)}")
|