BT410 5.3.67: pose_chase was upside down -- harness bug, wire exonerated
The operator caught it: the 5.3.65 image showed the mech hanging from its own ground-shadow plate (-y is up in the bridge world). The follow-up pinned down what was and wasn't wrong, and the reconstruction survives untouched. Right, by three independent wire-level proofs: MAD.SKL offsets are a coherent Y-up model (hip +5.29, knees descending, torso ascending -- anatomical only read +Y-up); the BGF vertex extents agree (foot meshes extend -1.0 below their ankle to the sole, torso +3.8 up to the missile pods); and the SHIPPED pod's wire carries the same signs (hip +6.21, knees negative, vehicle root a pure yaw with det +1). Our wire and shipped's are convention-identical -- no flip exists between game and board in either game. Wrong: the ad-hoc chase camera. It borrowed the aerial up-hint from the calibrated live-bridge world (where -y is up) and applied it to a raw scene-space render of Y-up wire data; the symmetric checkerboard ground disguised the inversion. The shipped capture rendered upside-down through the same harness, which is what cleared the wire. Both conventions now documented in the roadmap: wire/scene space is Y-up in both games; the live bridge presents -y-up via its calibrated mirror family (FP_RIGHT_SIGN / CAGE_TWIST_SIGN, COCKPIT-CAGE-NOTES.md). Tooling: pose_probe.py replaces the throwaway harness (updir=+1 scene space default, -1 for the bridge sense). pose_chase_upfixed.png is the corrected milestone image: the MadCat right side up -- chicken-walker legs, purple knee actuators, feet planted, shadow under the feet. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -54,3 +54,40 @@ LOG: ISA Plug & Play BIOS enabled
|
||||
LOG: VGA ROM BIOS init callback
|
||||
LOG: pixratio 1.000, dw false, dh false
|
||||
LOG: Aspect ratio: 640 x 480 xToY=1.333 yToX=0.750
|
||||
LOG: WARNING: No translation support (to host) for code page 0
|
||||
LOG: DOS keyboard layout loaded with main language code US for layout us
|
||||
LOG: XMS: 50 handles allocated for use by the DOS environment
|
||||
LOG: EMS page frame at 0xe000-0xefff
|
||||
LOG: COMMAND.COM env size: 720 bytes
|
||||
LOG: COMMAND.COM environment block: 0x0701 sz=0x002d
|
||||
LOG: COMMAND.COM main body (PSP): 0x072f sz=0x009a
|
||||
LOG: COMMAND.COM stack: 0x0749
|
||||
LOG: pixratio 1.350, dw false, dh false
|
||||
LOG: Aspect ratio: 720 x 540 xToY=1.333 yToX=0.750
|
||||
LOG: SHELL:Redirect output to COM3
|
||||
LOG: VPXLOG: fifosock client connected
|
||||
VPX lights@f1: type6=0 typeE=0 decoded=0 dir=0 amb(0.00,0.00,0.00)
|
||||
LOG: 237273074 ERROR MOUSE:Unhandled videomode 79 on reset
|
||||
LOG: pixratio 1.000, dw false, dh false
|
||||
LOG: Aspect ratio: 640 x 480 xToY=1.333 yToX=0.750
|
||||
VPX lightnode t=6 nb=52: [8]00000000/0 [12]00000000/0 [16]00000001/1.4013e-45 [20]3f800000/1 [24]3f800000/1 [28]3f800000/1 [32]64078678/1e+22 [36]64078678/1e+22 [40]00311d60/4.51048e-39 [44]00326744/4.62882e-39 [48]ffffffff/nan
|
||||
LOG: 684478479 ERROR INT10:PutPixel unhandled mode type 7
|
||||
LOG: SHELL:Redirect output to RC.TXT
|
||||
LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:2 (IF0:0), TX:0, Break 0
|
||||
LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:3 (IF0:0), TX:0, Break 0
|
||||
LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:12 (IF0:0), TX:0, Break 0
|
||||
LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:6 (IF0:0), TX:0, Break 0
|
||||
LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:3 (IF0:0), TX:0, Break 0
|
||||
LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:3 (IF0:0), TX:0, Break 0
|
||||
LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:6 (IF0:0), TX:0, Break 0
|
||||
LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:3 (IF0:0), TX:0, Break 0
|
||||
LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:3 (IF0:0), TX:0, Break 0
|
||||
LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:6 (IF0:0), TX:0, Break 0
|
||||
LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:3 (IF0:0), TX:0, Break 0
|
||||
LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:3 (IF0:0), TX:0, Break 0
|
||||
LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:3 (IF0:0), TX:0, Break 0
|
||||
LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:3 (IF0:0), TX:0, Break 0
|
||||
LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:6 (IF0:0), TX:0, Break 0
|
||||
LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:3 (IF0:0), TX:0, Break 0
|
||||
LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:3 (IF0:0), TX:0, Break 0
|
||||
LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:6 (IF0:0), TX:0, Break 0
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env python3
|
||||
"""pose_probe -- offline chase render of the articulated mech in a fifodump.
|
||||
|
||||
py pose_probe.py <capture.fifodump> <out.png> [updir]
|
||||
|
||||
Frames the camera on the LAST-articulated root (board.anim_abs), a chase
|
||||
offset away, and renders one frame. The proof harness for any "is the mech
|
||||
posed/assembled right?" question -- offline, against a copy of the live dump,
|
||||
zero interference with a running pod.
|
||||
|
||||
UP SENSE (updir, default +1): the wire's model/scene data is Y-UP -- proven
|
||||
three ways on 2026-07-29: MAD.SKL offsets are anatomically coherent only with
|
||||
+Y up (hip +5.29 above ground, knees/ankles descending), the BGF vertex
|
||||
extents agree (foot meshes extend -1.0 below their ankle joint, torso +3.8
|
||||
above shakey), and the SHIPPED pod's wire carries the same signs (hip +6.2,
|
||||
knees negative, yaw-only vehicle root -- no flip anywhere on the wire). So a
|
||||
scene-space render wants screen-up = +Y, which is updir=+1 here.
|
||||
|
||||
The LIVE bridge world is the other way around -- "-y is up here" -- because
|
||||
the calibrated live path (fp_cam / the mirrored det -1 root-basis family,
|
||||
FP_RIGHT_SIGN, COCKPIT-CAGE-NOTES.md) maps the wire into Dave's GL
|
||||
presentation. The first version of this harness copied the aerial camera's
|
||||
up hint from that world and rendered the mech hanging from its own shadow;
|
||||
the operator caught it. updir=-1 reproduces that view if you ever need it.
|
||||
"""
|
||||
import sys, os, struct
|
||||
sys.path.insert(0, r'C:\VWE\TeslaRel410\dpl3-revive\patha')
|
||||
os.environ.setdefault('SDL_VIDEODRIVER', 'dummy')
|
||||
import numpy as np
|
||||
from vrboard import VirtualBoard, Msg
|
||||
from vrview import Renderer
|
||||
|
||||
cap = sys.argv[1]
|
||||
out = sys.argv[2] if len(sys.argv) > 2 else "pose_probe.png"
|
||||
updir = float(sys.argv[3]) if len(sys.argv) > 3 else 1.0
|
||||
|
||||
data = open(cap, "rb").read()
|
||||
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]
|
||||
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
|
||||
off += 8 + ln
|
||||
|
||||
if not board.anim_abs:
|
||||
print("no articulated roots in this capture"); sys.exit(1)
|
||||
|
||||
r = Renderer(w=832, h=512); c = r.cache; c.maybe_rebuild(board)
|
||||
root = max(board.anim_abs.values(),
|
||||
key=lambda f: float(np.abs(np.array(f[9:12])).sum()))
|
||||
target = np.array(root[9:12]); target[1] += updir * 6.0 # mid-body
|
||||
eye = target + np.array([30.0, updir * 14.0, 40.0]) # above + beside
|
||||
fwd = target - eye; fwd /= np.linalg.norm(fwd); back = -fwd
|
||||
right = np.cross([0.0, updir, 0.0], back); right /= np.linalg.norm(right)
|
||||
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
|
||||
if c.view is not None and c.view in board.nodes:
|
||||
vb = bytearray(board.nodes[c.view].get('body') or b'')
|
||||
if len(vb) >= 56:
|
||||
struct.pack_into('<f', vb, 52, 1.0e6)
|
||||
board.nodes[c.view]['body'] = bytes(vb)
|
||||
r.draw(board)
|
||||
r.pygame.image.save(r.screen, out)
|
||||
print("wrote %s root at %s updir=%+g" % (out, target.round(1), updir))
|
||||
Reference in New Issue
Block a user