feat(camera): third-person rig, V toggle, visible body

One pawn, both rigs (ADR-9): a skeletal body hidden in first person, a
collision-tested spring arm, and a toggle that swaps the active camera and the
body's visibility. Shipped default stays first person; -thirdperson starts the
pawn in the third-person rig so the review shot is reproducible, and the
screenshot run passes it.

Three defects found and two fixed:
- The skin material had no SkeletalMesh usage flag, so Unreal silently substituted
  the default grey. That is why the body rendered as clay.
- render_screenshots.sh still called ./Engine/... on this branch (the fix lives on
  the facade branch and never reached main); applied here so the shots generate.
- OPEN: the idle clip is bound and PlayAnimation is called - the run logs the clip
  name - but the body still renders in bind pose, arms out. Recorded with that
  evidence in TODO.md rather than called done.

Shot 5 (demo-third-person) added and documented; camera collision is implemented
but unproven, since the spawn has open space behind it.
This commit is contained in:
zwitschi committed 2026-11-09 10:33:06 +01:00
1 parent 96238bdb6a
commit 05c1488c00
49 files changed
+333 -65

No files matched your search

+17 -1
View File
@@ -55,6 +55,20 @@ ELBOW = 0.18
PARTS: list[dict] = []
# The .blend stores the raw A-pose bind: `mpfb_character_export.py --pose idle`
# bakes the arms down only in the exported FBX, not in the file. A clip that
# animates only swing therefore leaves the arms out in an A — which is exactly how
# the first third-person render looked. These are the same deltas `IDLE_POSE` uses,
# applied as a base that every key sits on top of.
BASE = {
"shoulder01.L": (0.0, -0.15, -0.03),
"shoulder01.R": (0.0, 0.15, 0.03),
"upperarm01.L": (0.0, 0.0, -1.55),
"upperarm01.R": (0.0, 0.0, 1.55),
"lowerarm01.L": (0.0, 0.0, -0.10),
"lowerarm01.R": (0.0, 0.0, 0.10),
}
def log(msg):
PARTS.append(msg)
@@ -78,7 +92,9 @@ def pose_bones(arm):
def key_euler(pb, frame, x=0.0, y=0.0, z=0.0):
pb.rotation_euler = (x, y, z)
"""Key a pose-bone rotation on top of the arms-down base."""
bx, by, bz = BASE.get(pb.name, (0.0, 0.0, 0.0))
pb.rotation_euler = (bx + x, by + y, bz + z)
pb.keyframe_insert(data_path="rotation_euler", frame=frame)