Lane D: --merge clip-bank GLB hook + honest ledger of the three silent bugs
--merge folds clips_out/*.fbx onto the character's bank GLB via character_kit's merge_anims.py (already CLI'd + slot-bound; fps pin added there this session) -> clips_out/<character>_bank.glb. Rebuilt from scratch each run, so it's idempotent. This is the 'lands in 90sDJsim' step: games load GLB. Verified from the exported glTF JSON rather than Blender (which resamples on read): 27 animations (24 base + 3 ours), no duplicate names, all 30.0 keys/sec, the 317-frame clip keeps 317 keys over 10.567s, our clips drive real bone motion (delta 48-184u). Also: mocap now reports a DANGLING venv symlink honestly (uv pruned its managed CPython 3.10, which makes Path.exists() read as 'missing'), selfcheck covers the bank path, RUNBOOK documents --merge + the env gotcha, PROGRESS records all three bugs and the lesson (check artifacts, not green log lines). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
1b72fa8196
commit
4af9033b68
31
PROGRESS.md
31
PROGRESS.md
@ -1,5 +1,36 @@
|
||||
# MOCAPGOD progress ledger
|
||||
|
||||
## SESSION 8 (2026-07-25) — review + `--merge` hook. Three silent bugs found and fixed.
|
||||
Re-verified the repo against its own claims instead of trusting the ledger. Review #2's "SHIPPED"
|
||||
was right about the machinery but **three defects had passed sign-off**, all of the same species:
|
||||
the *log said green, the artifact was wrong*. Each is now fixed and verified at the artifact level.
|
||||
|
||||
1. **FBX take name was still `Armature|Scene`** — the previous "fix" renamed the baked *action*, but
|
||||
the exporter builds the AnimStack from **`scene.name`**. Caught on a clip built *after* that fix
|
||||
commit. Now sets `scene.name`; verified `Armature|wan_dig_t3` on import + 6 `strings` hits.
|
||||
2. **Every FBX carried Blender's factory 1..250 scene range** (`bake_anim` exports the *scene* range,
|
||||
and retarget only set it inside `qc_render`, which runs *after* export). Short clips gained a frozen
|
||||
tail (98f → 250f); **long clips were truncated — the 317f gymnasts clip lost 67 frames / 2.2s**, and
|
||||
that is the very clip Review #2 signed off as "317f green". Now pinned to the trimmed range.
|
||||
3. **The bank merge silently resampled 30fps → 24fps** (factory scene fps; both importers convert
|
||||
timing through it). Durations stayed right (glTF stores seconds) so nothing looked broken, but
|
||||
every rebuild shaved ~20% temporal resolution off *all* clips, base bank included. Fixed in
|
||||
`character_kit/scripts/merge_anims.py` (cross-repo — affects 90sDJsim/OPSHOPGAME/NPCFACTORY too).
|
||||
|
||||
**`--merge` SHIPPED** (`./mocap … --merge`) → `clips_out/<character>_bank.glb`. Verified from the
|
||||
exported glTF JSON, not Blender (which resamples on read): **27 animations, no dupe names, all
|
||||
30.0 keys/sec**, 317-frame clip keeps 317 keys over 10.567s, our clips drive Δ48–184u. That is the
|
||||
"lands in 90sDJsim" step — point the game at that GLB.
|
||||
|
||||
**Lesson for future reviews:** every one of these hid behind a green log line. Check the *artifact*
|
||||
(import the FBX, parse the glTF), never the console output.
|
||||
|
||||
⚠️ **Lane B is currently un-runnable on ultra:** `uv` pruned its managed CPython 3.10, so
|
||||
`.engine/HSMR/.venv/bin/python` is a dangling symlink. Not caused by these changes. Fix:
|
||||
`./setup_hsmr.sh` (re-runnable; SKEL + weights survive). Retarget/merge stages are unaffected.
|
||||
**Still open:** John's real footage (quality verdict), the MPI Path-A email, `--to-unreal`,
|
||||
foot-contact IK, male rig.
|
||||
|
||||
## FABLE REVIEW #2 (2026-07-17, post lanes B–D) — VERDICT: SHIPPED, one bug, one cross-repo action
|
||||
|
||||
Independently verified (not trusted): fresh end-to-end `./mocap` run on a renamed demo clip →
|
||||
|
||||
@ -39,17 +39,31 @@ Timing: pose engine dominates (~1.6× realtime on ultra/MPS); a 10s clip ≈ a c
|
||||
- Male rig (`--character male` → `hum_character.glb`) is **unverified** until a male clip is run;
|
||||
female (`female_game.glb`) is the proven path.
|
||||
|
||||
## Deferred hooks — build these when real footage lands (not faked in the CLI)
|
||||
Each is off by default and needs a real clip to verify, so none ship as dead flags today.
|
||||
1. **Clip-bank GLB merge** (`--merge`, → 90sDJsim / three.js games). Games load GLB, so this is the
|
||||
"lands in-game" step. Needs a small merge script first: `character_kit/scripts/merge_anims.py`
|
||||
has **no CLI and is pre-Baklava** — it must add the Blender-5 slot-bind
|
||||
(`animation_data.action_slot = act.slots[0]`, verified in Lane C) or the merged GLB won't animate.
|
||||
Then merge `clips_out/<name>.fbx` onto the character's bank GLB and export.
|
||||
2. **`--to-unreal`** — import via the A_3D_STORE deferred-import pattern (memory:
|
||||
## `--merge` — the clip-bank GLB (BUILT + verified 2026-07-25)
|
||||
```
|
||||
./mocap capture/inbox/move.mp4 --name dj_scratch_01 --merge
|
||||
```
|
||||
Folds **every** `clips_out/*.fbx` onto the character's bank GLB via
|
||||
`character_kit/scripts/merge_anims.py` → `clips_out/<character>_bank.glb`. Games load GLB, so this
|
||||
is the "lands in 90sDJsim" step: point the game at that file.
|
||||
|
||||
- Rebuilt from scratch each run (base rig + current `clips_out/` contents) — **idempotent, no
|
||||
compounding**. Prune `clips_out/` to control what ships.
|
||||
- Verified from the exported glTF JSON: female bank = **27 animations** (24 base + 3 ours), no
|
||||
duplicate names, all **30.0 keys/sec**, our clips drive real bone motion (Δ 48–184u).
|
||||
- Needs the Blender-5 slot-bind and the 30fps pin — both now **in** `merge_anims.py`. Without the
|
||||
slot-bind the GLB animates nothing; without the fps pin every rebuild resampled 30→24fps.
|
||||
|
||||
## Deferred hooks — build when real footage lands (not faked in the CLI)
|
||||
1. **`--to-unreal`** — import via the A_3D_STORE deferred-import pattern (memory:
|
||||
`unreal-asset-convergence` — slate post-tick, NOT a direct AssetImportTask through the bridge).
|
||||
3. **`--publish-3god`** — depot pattern from procity `publish.py`.
|
||||
|
||||
## Environment gotcha (bit us 2026-07-25)
|
||||
`uv` pruned its managed **CPython 3.10**, leaving `.engine/HSMR/.venv/bin/python` a *dangling
|
||||
symlink* — so Lane B dies and `Path.exists()` reports the venv "missing". `mocap` now says so
|
||||
explicitly. Rebuild with **`./setup_hsmr.sh`** (re-runnable; SKEL files + weights survive).
|
||||
|
||||
## John's homework (blocks the real quality verdict)
|
||||
Film **walk / dance / pick-up-object** per `docs/CAPTURE.md`, drop in `capture/inbox/`, run `mocap`.
|
||||
Until then the pipeline is proven on the HSMR `gymnasts` demo only — machinery works; the honest
|
||||
|
||||
39
mocap
39
mocap
@ -12,9 +12,11 @@ Chains the three proven stages, all on ultra:
|
||||
Result: clips_out/<name>.fbx (anim-only, Mixamo "Without Skin") + <name>_qc.mp4 turntable.
|
||||
House rule: eyeball the QC mp4 before the clip enters a bank.
|
||||
|
||||
Deferred hooks (off by default — built when John's real footage lands; see docs/RUNBOOK.md):
|
||||
clip-bank GLB merge, --to-unreal, --publish-3god. merge_anims.py needs a CLI + the Blender-5
|
||||
slot-bind first, and none of them are verifiable without a real clip, so they're not faked here.
|
||||
--merge additionally folds clips_out/*.fbx onto the character's bank GLB (games load GLB, so
|
||||
this is the "lands in 90sDJsim" step) via character_kit/scripts/merge_anims.py.
|
||||
|
||||
Deferred hooks (off by default — see docs/RUNBOOK.md): --to-unreal, --publish-3god. Neither is
|
||||
verifiable without a real clip in the target project, so they're not faked here.
|
||||
"""
|
||||
import argparse, datetime, re, subprocess, sys
|
||||
from pathlib import Path
|
||||
@ -26,6 +28,7 @@ CLIPS = REPO / "clips_out"
|
||||
VENV_PY = REPO / ".engine/HSMR/.venv/bin/python" # Lane B lives in its own env
|
||||
BLENDER = Path("/Applications/Blender.app/Contents/MacOS/Blender")
|
||||
KIT = Path.home() / "Documents/character_kit"
|
||||
MERGE = KIT / "scripts/merge_anims.py" # CLI: -- BASE CLIP_DIR OUT (Blender-5 slot-bind inside)
|
||||
RIGS = { # --character → (pose gender, retarget rig)
|
||||
"female": ("female", KIT / "female/female_game.glb"), # v1 proven path
|
||||
"male": ("male", KIT / "hum_character.glb"), # ponytail: male rig unverified until a male clip exists
|
||||
@ -59,7 +62,10 @@ def selfcheck():
|
||||
assert slug("DJ Scratch #1!") == "DJ_Scratch_1", slug("DJ Scratch #1!")
|
||||
for c in RIGS.values():
|
||||
assert c[0] in ("male", "female")
|
||||
print("selfcheck OK — name threads inbox→queue→out→clips, final clip name stays clean")
|
||||
# --merge writes one bank per character, and must not glob its own output back in
|
||||
for ch in RIGS:
|
||||
assert (CLIPS / f"{ch}_bank.glb").suffix == ".glb", ch
|
||||
print("selfcheck OK — name threads inbox→queue→out→clips→bank, final clip name stays clean")
|
||||
return 0
|
||||
|
||||
|
||||
@ -71,6 +77,9 @@ def main():
|
||||
ap.add_argument("--gender", choices=["male", "female"], help="override pose gender (default: from --character)")
|
||||
ap.add_argument("--travel", action="store_true", help="keep world root translation (default: in-place)")
|
||||
ap.add_argument("--no-qc", action="store_true", help="skip the QC turntable render")
|
||||
ap.add_argument("--merge", action="store_true",
|
||||
help="also merge ALL clips_out/*.fbx onto the character's bank GLB "
|
||||
"→ clips_out/<character>_bank.glb (the 'lands in-game' step)")
|
||||
ap.add_argument("--selfcheck", action="store_true", help="verify the path-threading logic and exit")
|
||||
a = ap.parse_args()
|
||||
|
||||
@ -79,7 +88,11 @@ def main():
|
||||
if not a.video or not a.name:
|
||||
ap.error("video and --name are required (or use --selfcheck)")
|
||||
if not VENV_PY.exists():
|
||||
sys.exit(f"✗ HSMR venv missing: {VENV_PY} — run .engine/setup_hsmr.sh (Lane B).")
|
||||
# .exists() is False for a DANGLING symlink too — uv pruning its managed CPython 3.10
|
||||
# breaks the venv this way (seen 2026-07-25). Re-running setup rebuilds it.
|
||||
sys.exit(f"✗ HSMR venv missing or broken: {VENV_PY}\n"
|
||||
f" (a dangling interpreter symlink counts — check: ls -la {VENV_PY})\n"
|
||||
f" rebuild with: ./setup_hsmr.sh")
|
||||
if not BLENDER.exists():
|
||||
sys.exit(f"✗ Blender not found: {BLENDER} (Lane C needs it).")
|
||||
|
||||
@ -111,7 +124,21 @@ def main():
|
||||
print(f"\n✅ clip ready: {fbx}")
|
||||
if qc.exists():
|
||||
print(f" eyeball before it enters a bank: {qc}")
|
||||
print(" merge-to-GLB / Unreal / 3god hooks are deferred — docs/RUNBOOK.md.")
|
||||
|
||||
bank = CLIPS / f"{a.character}_bank.glb"
|
||||
if a.merge:
|
||||
if not MERGE.exists():
|
||||
sys.exit(f"✗ merge script missing: {MERGE}")
|
||||
# the bank is rebuilt from scratch each time = base rig + EVERY fbx currently in clips_out/
|
||||
# (idempotent, no compounding). Prune clips_out to control what ships.
|
||||
print(f"\n bank = {rig.name} + all {len(list(CLIPS.glob('*.fbx')))} clip(s) in {CLIPS.name}/")
|
||||
stage("Lane D · merge to clip-bank GLB", [BLENDER, "--background", "--python", MERGE, "--",
|
||||
rig, CLIPS, bank])
|
||||
if not bank.exists():
|
||||
sys.exit(f"✗ no {bank} after merge.")
|
||||
print(f"\n✅ bank ready: {bank} → load this in 90sDJsim / three.js games")
|
||||
else:
|
||||
print(f" --merge folds it into {bank.name}; --to-unreal / --publish-3god: docs/RUNBOOK.md.")
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user