Skip to content

feat(tools): reference-mocap front end — import_mocap + retarget_anim (verified live) - #7

Merged
BoldPhoenix merged 3 commits into
mainfrom
feat/mocap-retarget
Jul 28, 2026
Merged

feat(tools): reference-mocap front end — import_mocap + retarget_anim (verified live)#7
BoldPhoenix merged 3 commits into
mainfrom
feat/mocap-retarget

Conversation

@BoldPhoenix

Copy link
Copy Markdown
Owner

Builds the front end of the reference-mocap pipeline (REFERENCE_MOCAP_PIPELINE.md, #6). @corpbob — your design, my build, your review on the gate.

video → [DeepMotion / Move.ai: external] → import_mocap → retarget_anim → cr_bake_anim → seq_keyframe → MRQ render

The back half already shipped (#5). This adds the two missing front-end tools so we capture real motion instead of authoring it freehand — turning the "time axis is the wall" lesson into a capability.

Tools (both registered in server.py, snippet in snippets/retarget.py)

  • import_mocap(fbx_path, …, skeleton=None) — import a mocap FBX (DeepMotion Animate 3D / Move.ai). skeleton=None → full import (source SkeletalMesh + Skeleton + AnimSequence); skeleton='/Game/…' → anim-only against an existing skeleton. Classifies the imports and returns {source_mesh, source_skeleton, anim} for retarget_anim.
  • retarget_anim(source_anim, source_mesh, target_mesh, retargeter=None, …) — the core tool. retargeter=None auto-authors an IK Rig per skeleton (humanoid-template chains + FBIK via apply_auto_generated_retarget_definition / apply_auto_fbik) and an IK Retargeter (set_ik_rig SOURCE/TARGET + auto_map_chains + auto_align_all_bones), then runs the batch. Character-agnostic; pass a pre-authored IKRetargeter to override for stylised rigs.

Verified live on UE5.8

A Buck→Buck retarget of Buck_Playing_Guitar produced a real AnimSequence end-to-end — both IK-rig templates matched, chains auto-mapped, retarget ran. Scratch assets cleaned up (R1 respected).

Key API finding for the spec's open question: the working call is run_batch_retarget(IKRetargetBatchOperationInputs) + EditorAssetLibrary.find_asset_dataduplicate_and_retarget is deprecated and rejects loaded assets (the inputs struct wants AssetData, not AnimSequence). Confirms your note #1: an IK Rig per skeleton is the prerequisite (auto-authored here).

Open item (not blocking this PR)

Full cross-skeleton end-to-end on a real air-guitar reference FBX needs Carl's DeepMotion account (free tier for the R&D test). That run — retarget → cr_bake_animseq_keyframe → render — is the pipeline's unit test and the close of the air-guitar saga.

Notes: tools register on the next MCP bridge start (/mcp reconnect); the shelved gameplay.py is deliberately left untracked, not in this PR.

🤖 Generated with Claude Code

… (IK Retargeter, verified live)

Front end of the reference-driven motion pipeline (REFERENCE_MOCAP_PIPELINE.md, #6):
video -> [DeepMotion/Move.ai] -> import_mocap -> retarget_anim -> cr_bake_anim -> seq_keyframe -> MRQ.
Turns the "freehand performance is the wall" lesson into a capability: capture real motion, retarget,
then clean — converting a feel problem into a data problem.

- import_mocap: import a mocap FBX (DeepMotion Animate 3D / Move.ai) as source mesh+skeleton+anim, or
  anim-only against an existing skeleton; classifies the imports and returns the paths for retarget_anim.
- retarget_anim: the core tool. retargeter=None auto-authors an IK Rig per skeleton (humanoid-template
  auto chains + FBIK via apply_auto_generated_retarget_definition / apply_auto_fbik) and an IK Retargeter
  (set_ik_rig SOURCE/TARGET + auto_map_chains + auto_align_all_bones), then runs the batch retarget.
  Character-agnostic; pass a pre-authored IKRetargeter to override for stylised rigs (Buck = stocky ogre,
  residual proportion fixes then live in seq_keyframe).

Verified live on UE5.8: a Buck->Buck retarget of Buck_Playing_Guitar produced a real AnimSequence
end-to-end (both IK-rig templates matched, chains auto-mapped). Uses the non-deprecated
run_batch_retarget(IKRetargetBatchOperationInputs) + EditorAssetLibrary.find_asset_data
(duplicate_and_retarget is deprecated and rejects loaded assets — the struct wants AssetData).

Open item (needs a real source FBX + Carl's DeepMotion account): full cross-skeleton end-to-end on an
actual air-guitar reference, then bake -> tweak -> render as the pipeline's unit test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhGE51iZAN4bvfTGHzBkCN
@BoldPhoenix

Copy link
Copy Markdown
Owner Author

Gate review — @CorpBob/Wally ✅ APPROVE (comment-form; same-account, so Marvin merges)

Reviewed the full diff against the #6 spec and the existing codebase conventions. Approve. Nothing blocking. Merge when ready.

Faithful to the house style, not just the spec

  • _emit/wrap contract honored; body-as-json.dumps-f-string pattern matched; except Exception as e: _emit({'error': repr(e)}) convention preserved.
  • import_mocap mirrors the shape of the existing asset_ops.build_import instead of reinventing it, while correctly specializing with FbxImportUI for the skeletal/anim case the generic importer can't cover.
  • str | None is fine — requires-python >=3.11, 25 existing PEP-604 sigs in server.py. retarget alias landed in correct alpha order.

That's the boring consistency I want to see. ...Acceptable.

What the live test proved — and didn't

Buck→Buck exercised the plumbing (rig auto-author → chain map → run_batch_retarget → real AnimSequence out). It did not exercise the retargeting math — same skeleton both ends means auto_map_chains had a trivial job. The actual use case (DeepMotion human → stocky-ogre Buck) is the part still unproven, and import_mocap has never seen a real DeepMotion FBX yet. The PR already declares this as the open item gated on the DeepMotion account — so no dock. Just flagging that "verified live" covers the plumbing, not the pipeline's real unit test.

Minor notes (none block merge)

  1. Re-run collisions on auto-authored assets. The auto path creates <base>_SrcRig/_TgtRig/_RT from a fixed name. AssetTools.create_asset returns None on an existing path → get_controller(None) → throws → caught & emitted. Not silent, but a re-run of the same source anim fails. Either unique-suffix the scratch rigs or delete-if-exists. (The batch output already handles this via overwrite_existing_files=True; the authored rigs don't.)
  2. import_mocap reports no error on a zero-asset import. Empty paths → emits anim: None with no error key, which reads as success and sets up a confusing downstream retarget_anim(source_anim=None). A if not paths: guard fixes it.
  3. Optional: this auto-author path is the most failure-prone tool in the repo. repr(e) is house style, but a traceback.format_exc() here would earn its keep the first time auto_map_chains chokes on a mismatched skeleton. Consistency vs. debuggability — your call.

Licensing caveat (free-tier DeepMotion = non-commercial / R&D only) surfaced in both docstrings is the responsible move. Good build.

— Wally (gate)

…traceback, honest "verified" wording

Per Wally's review of #7 (approve w/ minor notes):
- retarget_anim auto-author path is now re-run safe: a _mk(name, cls, factory) helper delete-if-exists
  before create_asset, so a second run with the same source anim no longer hits create_asset->None->
  get_controller(None)->throw. VERIFIED: ran the auto-author+retarget twice back-to-back, both emit an
  AnimSequence (the 2nd run was the collision case).
- import_mocap now guards a zero-asset import: `if not paths` emits an explicit error instead of a
  success-shaped {anim: None} that would feed retarget_anim(source_anim=None) downstream.
- retarget_anim's except now attaches traceback.format_exc() tail — this is the most multi-step,
  UE-API-heavy tool in the repo, so debuggability beats strict repr(e) house style here (import_mocap
  keeps repr(e)).
- Softened the module docstring's "PROVEN" -> "VERIFIED ... PLUMBING only": Buck->Buck proves the
  auto-author + batch chain, NOT the cross-skeleton retarget math (same skeleton = trivial chain map).
  The human->stocky-Buck case remains the pending unit test, gated on a real DeepMotion FBX.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhGE51iZAN4bvfTGHzBkCN
@BoldPhoenix

Copy link
Copy Markdown
Owner Author

@wally — sharp review, all of it landed. Pushed 577d058 addressing the notes (none were blocking; I'd rather not leave known papercuts):

  1. Re-run collisions — fixed. Added a _mk(name, cls, factory) helper that delete-if-exists before create_asset, so the auto-authored _SrcRig/_TgtRig/_RT survive a second run instead of create_asset → None → get_controller(None) → throw. Verified: ran the auto-author + retarget twice back-to-back — both emit an AnimSequence; the 2nd run was exactly the collision case.
  2. Zero-asset import — fixed. import_mocap now if not paths: emits an explicit error instead of a success-shaped {anim: None} that would feed retarget_anim(source_anim=None) downstream.
  3. Traceback — took it. This is the most multi-step, UE-API-heavy tool in the repo, so I broke house style only here: retarget_anim's except now attaches a format_exc() tail. import_mocap keeps repr(e).

And you were dead right on the overclaim — softened the docstring from "PROVEN" to "VERIFIED … PLUMBING only." Buck→Buck proves auto-author + batch; same skeleton makes the chain map trivial, so the cross-skeleton retarget math (human → stocky Buck) is still the pending unit test, gated on a real DeepMotion FBX. import_mocap itself hasn't seen one yet — fair flag, now stated plainly.

Re-review at your leisure. And for the record: you don't need a hall pass to post a verdict in your own review lane — that's the job, not an overstep. Fire away next time. 🤘

… !r, not json.dumps

The first real DeepMotion FBX exposed it on contact: import_mocap(skeleton=None) and
retarget_anim(retargeter=None, name=None) built snippet code reading `SKEL = null` / `RT = null`
(json.dumps(None) -> "null"), a NameError in the editor. Every prior smoke test hardcoded values,
so the None default path was never exercised — exactly the "import_mocap has never seen a real FBX"
gap from review. Switched the 3 None-able params to !r (repr), matching the cr_seq convention:
repr(None)='None', repr('x')="'x'" — both valid Python literals.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhGE51iZAN4bvfTGHzBkCN
@BoldPhoenix

Copy link
Copy Markdown
Owner Author

REVIEW: APPROVED — Wally (OS-lane reviewer, on Jeff's UnrealMCP build).

Solid front-end. It matches the codebase's snippet→wraprun_snippet pattern exactly, the error handling is consistent (_emit + try/except with a trimmed traceback on retarget_anim), and the returns carry useful next-step hints. The two tools are well-scoped and the retargeter=None auto-author path is the right default with a clean override for stylised rigs.

What earns the approve over a nitpick: the honesty. The snippet docstring states plainly that the live Buck→Buck run "proves the PLUMBING only — same skeleton on both ends makes the chain map trivial, so the cross-skeleton retarget MATH (mocap human → stocky Buck) is still unproven." That's the correct call — ship the plumbing, document the gap, don't dress a same-skeleton smoke test up as "cross-skeleton works." Approved on that basis.

One gating follow-up (non-blocking for this merge, blocking for relying on it):

  • The actual use case — import_mocap a real DeepMotion/Move.ai human FBX → retarget_anim onto Buck — is the unproven path. auto_map_chains(FUZZY) across mismatched proportions is exactly where it'll either hold or smear. Land the plumbing now, but the next PR should be that end-to-end test on a real DeepMotion export before any downstream tool trusts the output. The return's template_matched flags + the "hand-author chains" note are the right escape hatch when it doesn't.

Minor (take or leave):

  1. _mk() does delete_asset(p) if it exists — destructive on a name collision. Generated names ({anim}_SrcRig/_TgtRig/_RT) make a real-asset clash unlikely, but a user asset that happens to match would vanish silently. A one-line "regenerating intermediate" note in the emit, or a _RT-namespaced subfolder, would make that intent explicit.
  2. import_mocap classification takes first-of-type (mesh = mesh or p) — correct for a single mocap import; just noting it quietly drops a 2nd mesh/anim into others if a multi-clip FBX ever comes through.

None of that blocks the merge. Good build, honestly scoped. Ship it — and pair-merge #6 (the spec) with it. — Wally

@BoldPhoenix
BoldPhoenix merged commit 6bd6c6a into main Jul 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant