Weapons fire; hat-glance + MFD fixes; windowless bridge

vpx-device/vpxlog.cpp -- WEAPONS FIRE. The game's fire gate is
targetReticle.targetEntity (mech+0x388), filled every frame by a Division-board
reticle pick (dpl_RapidSectPixel) our HLE never answered, so every shot
misfired. The device now casts the camera centre ray against the live scene
(Moller-Trumbore) and returns the full hit -- instance + DCS + geogroup +
geometry -- piggybacked on the draw_scene reply; terrain is a valid target so
you can fire and miss. Sending geogroup=0 was hanging the game (GetAppSpecific
on a null); returning the real geogroup fixed it. Pick is default-on with a
single VPX_NO_PICK escape hatch. Also swaps the upper-left/right MFD explode
windows (screen location only -- decode untouched, real cause TBD on a pod).

dpl3-revive/patha/vrview.py -- HAT-GLANCE fix at the source. One cockpit DCS
(0xa2c) flushes a rank-2 rotation (shifted body -> wrong read window) that
collapsed the camera chain to rank 2 and smeared the head glance onto the wrong
axis (left/right hat read as pitch). chain_matrix(fix_degenerate) treats a
degenerate chain DCS as identity: preserves the look exactly and keeps stick-Y
torso pitch working (an earlier bridge-level yaw/pitch swap broke stick-Y and
was reverted). User-verified live: hat all 4 dirs + stick-Y both correct.

render-bridge/launch_pod.ps1 -- launch the bridge with pyw (windowless) so no
console window parks over the cockpit displays (Start-Process ignores
-WindowStyle once stdout/stderr are redirected).

render-bridge/live_bridge.py -- surface bridge render errors, flush the status
line; reverted glance-swap note.

Also vendored HUD (dpl2d) renderer work in vrboard/vrview_gl and RENDERER-COLLAB
/ RIO notes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-07 09:53:14 -05:00
co-authored by Claude Opus 4.8
parent c18c253658
commit 2bb2ff7302
8 changed files with 909 additions and 51 deletions
+27
View File
@@ -106,6 +106,8 @@ class VirtualBoard:
self.morphs = {} # morphed geom -> (a, b, alpha)
self.names = {} # 0x8000xxxx name id -> handle (0x22)
self.sfx = {} # SPECIALFX defs by code (0x1c)
self.dl2d = {} # 2D display lists (0x29/0x2b): handle -> [tag words]
self._dl2d_acc = None # in-progress chunked 0x2b flush
self.munga = False # game-build dialect (rpl4opt/btl4opt)
self.frames = 0
self.log = []
@@ -238,6 +240,31 @@ class VirtualBoard:
h = struct.unpack_from('<I', msg.payload, 4)[0]
self.anim4[h] = struct.unpack_from('<16f', msg.payload, 8)
return b''
if msg.action == 0x29 and len(msg.payload) >= 4:
# dpl2d_NewDisplayList: [remote handle][0] (create/reset a 2D
# overlay display list -- the HUD layer; fire-and-forget)
h = struct.unpack_from('<I', msg.payload, 0)[0]
self.dl2d[h] = []
return b''
if msg.action == 0x2b and len(msg.payload) >= 20:
# dpl2d_FlushDisplayList: the host streams the dpl2d_DISPLAY
# chunk structs verbatim (DPL_2D.H: remote, next, tail, size,
# open, data[30]). First chunk carries remote=handle and
# tail=total chunk count; continuation chunks have remote=0.
# Data words concatenate across chunks (tag args split freely
# at chunk boundaries); tags per DPL2DTAG.H.
hdr, _nxt, tail, size = struct.unpack_from('<4I', msg.payload, 0)
size = min(size, (len(msg.payload) - 20) // 4)
dat = list(struct.unpack_from(f'<{size}I', msg.payload, 20))
if hdr:
self._dl2d_acc = [hdr, max(tail, 1) - 1, dat]
elif self._dl2d_acc is not None:
self._dl2d_acc[1] -= 1
self._dl2d_acc[2] += dat
if self._dl2d_acc is not None and self._dl2d_acc[1] <= 0:
self.dl2d[self._dl2d_acc[0]] = self._dl2d_acc[2]
self._dl2d_acc = None
return b''
self.log.append(f"EXTRA action {msg.action:#x} ({len(msg.payload)}B) -- no reply")
return b''
p = msg.payload