Renderer: vendor live-bridge scripts; GPU backend unblocked (60fps)
- 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>
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Wire Dave's dpl3-revive renderer into our LIVE running pod.
|
||||
|
||||
Tails our device's VPX_FIFODUMP (VPXM records = the game's live VelociRender
|
||||
wire), feeds each message into Dave's VirtualBoard, and renders every
|
||||
draw_scene with his vrview software rasterizer in a real window -- using the
|
||||
game's own camera (the player's RIO input drives it).
|
||||
|
||||
py live_bridge.py <live.fifodump>
|
||||
"""
|
||||
import os, sys, struct, time
|
||||
import numpy as np
|
||||
from _backend import pick_renderer
|
||||
from vrboard import VirtualBoard, Msg, A
|
||||
Renderer, backend = pick_renderer()
|
||||
|
||||
path = sys.argv[1]
|
||||
board = VirtualBoard()
|
||||
board.munga = False
|
||||
r = Renderer(w=832, h=512,
|
||||
title=f"dpl3-revive renderer (Dave) -- LIVE from our pod [{backend}]")
|
||||
r.fps = int(os.environ.get('BRIDGE_FPS', '60' if backend == 'GL' else '30'))
|
||||
# eye height above the mech origin (cockpit). Mutable: UP/DOWN arrows = +-5,
|
||||
# LEFT/RIGHT = +-1, live (the render window must be focused). 35 was tuned
|
||||
# before the ground rendered and is way too high against real terrain.
|
||||
UPOFF = [float(os.environ.get('FP_UPOFF', '12'))]
|
||||
|
||||
def fp_cam(board, cache):
|
||||
"""First-person cockpit camera from the player vehicle's 0x1f pose: eye at
|
||||
the mech (+up), looking along its nose (+Z row), y-up world. Beats Dave's
|
||||
11-DCS cam chain (which renders sky through our wire's convention)."""
|
||||
anim = board.anim_abs
|
||||
chain = cache.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:
|
||||
return None
|
||||
f = anim[h]
|
||||
R = np.array(f[:9]).reshape(3, 3)
|
||||
t = np.array(f[9:12])
|
||||
# look along the mech's travel direction. The +Z row rendered BACKWARD
|
||||
# (user: "geometry moving away as I drive"), so forward = -Z row.
|
||||
# FP_FWD_SIGN flips it back if needed.
|
||||
fwd = float(os.environ.get('FP_FWD_SIGN', '-1')) * R[2]
|
||||
n = np.linalg.norm(fwd)
|
||||
if n < 1e-6:
|
||||
return None
|
||||
fwd = fwd / n
|
||||
worldup = np.array([0.0, 1.0, 0.0])
|
||||
eye = t + worldup * UPOFF[0]
|
||||
back = -fwd
|
||||
right = np.cross(worldup, back)
|
||||
rn = np.linalg.norm(right)
|
||||
if rn < 1e-6:
|
||||
return None
|
||||
right /= rn
|
||||
up = np.cross(back, right)
|
||||
# FP_RIGHT_SIGN=-1 mirrors the image X (verified: exact fliplr). Tried as a
|
||||
# yaw fix 2026-07-06 but the inverted-yaw report was about the NATIVE C++
|
||||
# render, not this bridge -- the mirror just flipped the arena layout and
|
||||
# made the bridge yaw wrong too. Keep +1 (the validated look).
|
||||
right *= float(os.environ.get('FP_RIGHT_SIGN', '1'))
|
||||
M = np.eye(4)
|
||||
M[0, :3], M[1, :3], M[2, :3], M[3, :3] = right, up, back, eye
|
||||
return M
|
||||
|
||||
def render(board):
|
||||
try:
|
||||
pg = r.pygame
|
||||
for ev in pg.event.get(): # drain before r.draw; tune eye height
|
||||
if ev.type == pg.QUIT:
|
||||
raise KeyboardInterrupt
|
||||
if ev.type == pg.KEYDOWN:
|
||||
step = {pg.K_UP: 5, pg.K_DOWN: -5,
|
||||
pg.K_RIGHT: 1, pg.K_LEFT: -1}.get(ev.key)
|
||||
if step:
|
||||
UPOFF[0] = max(0.0, UPOFF[0] + step)
|
||||
print(f"eye height = {UPOFF[0]:.0f}", flush=True)
|
||||
r.cache.maybe_rebuild(board)
|
||||
M = fp_cam(board, r.cache)
|
||||
if M is not None:
|
||||
r.cam_matrix = lambda _b, _M=M: _M
|
||||
r.draw(board)
|
||||
except KeyboardInterrupt:
|
||||
raise
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
print(f"waiting for {path} ...")
|
||||
while not os.path.exists(path):
|
||||
time.sleep(0.2)
|
||||
f = open(path, 'rb')
|
||||
print("tailing live wire -> Dave's renderer; drive the pod")
|
||||
|
||||
pending = b''
|
||||
frames = 0
|
||||
last_report = time.time()
|
||||
while True:
|
||||
chunk = f.read(1 << 20)
|
||||
if chunk:
|
||||
pending += chunk
|
||||
off = 0
|
||||
n = len(pending)
|
||||
while n - off >= 8:
|
||||
if pending[off:off + 4] != b'VPXM':
|
||||
off += 1
|
||||
continue
|
||||
ln = struct.unpack_from('<I', pending, off + 4)[0]
|
||||
if n - off < 8 + ln:
|
||||
break # incomplete record: wait for more
|
||||
body = pending[off + 8:off + 8 + ln]
|
||||
off += 8 + ln
|
||||
if len(body) >= 4:
|
||||
action = struct.unpack_from('<I', body, 0)[0]
|
||||
if action < 0x100:
|
||||
try:
|
||||
board.handle(Msg(False, 0xff, action, body[4:]))
|
||||
except Exception:
|
||||
pass
|
||||
if action == 9: # draw_scene -> present a frame
|
||||
render(board)
|
||||
frames += 1
|
||||
pending = pending[off:]
|
||||
if not chunk:
|
||||
try: r.pump()
|
||||
except KeyboardInterrupt: break
|
||||
except Exception: pass
|
||||
time.sleep(0.02)
|
||||
if time.time() - last_report > 4:
|
||||
last_report = time.time()
|
||||
print(f"frames={frames} nodes={len(board.nodes)} "
|
||||
f"uploads={len(board.uploads)} tex={len(board.tex)} "
|
||||
f"munga={board.munga} anim_abs={len(board.anim_abs)}")
|
||||
Reference in New Issue
Block a user