Motivation
The bind-referenced retarget (#839, PR #843) generates animations that reference the target rig's bind pose, but retarget quality still varies with body proportions: bulky/cartoon characters (short arms, wide torsos) end up with arms clipping into the body or floating too wide, because the retarget aims bones at the source clip's directions with no awareness of the target's volume.
Mixamo solves this with a "Character Arm-Space" slider — a user-controlled post-process that swings the whole arm chain outward/inward. It is cheap, intuitive, and rescues most intersection cases without touching the retarget math. This issue plans the same idea as a generation post-process.
Design
Core: AnimationMerger::adjustArmSpace(skel, animName, degrees) — a pure post-process on an existing generated animation:
- For every keyframe of bones mapped to the arm roles (canonical 7/8/9 right, 11/12/13 left; collars 6/10 get a fractional share), pre-multiply a swing rotation about the torso's forward axis (derived from the target bind frame
Ct, the same one the retarget uses), sign mirrored per side: +deg swings both arms away from the body, −deg tucks them in.
- Only the shoulder role needs the full angle — elbows/hands inherit through the hierarchy since keyframes are parent-relative deltas. Distribute across duplicate-role bones like the retarget's
canonDup split.
- Idempotent absolute application: store the applied angle on the animation via
UserObjectBindings (qtme.armspace); re-applying with a new slider value first reverts the stored one, so scrubbing the slider never accumulates error.
- Undoable via a small
QUndoCommand (UndoManager pattern).
Surfaces:
- GUI: a compact "Arm space" slider (−30°…+45°, default 0) in the Animations panel, visible when a
generated_* clip is selected; live preview while dragging (apply on release; slider maps to the absolute stored value).
- CLI:
qtmesh anim <file> --generate "<prompt>" --arm-space <deg> and standalone qtmesh anim <file> --arm-space <deg> --animation <name> -o out for post-adjusting existing clips.
- MCP:
arm_space argument on generate_motion + a standalone adjust_arm_space tool.
- Sentry breadcrumb
ai.assist.text_to_motion message arm_space.
Testing: pure-data unit test on a synthetic 22-role skeleton — verify (a) shoulder world direction swings by the requested angle in the torso plane, (b) idempotence (apply 20 then 10 ≡ apply 10), (c) elbows/hands unchanged in parent-relative terms.
Follow-up knobs the same mechanism enables (out of scope here, listed for the design to keep the door open)
Acceptance criteria
- Slider visibly widens/narrows the arms of a generated clip on the Hip Hop fox without breaking legs/spine.
- Idempotent (scrubbing the slider back to 0 restores the original clip bit-near-exactly).
- CLI/MCP parity + unit tests green.
Related: epic #837 (t2m-v2), #839 (canonical clip extraction), PR #843 (bind-referenced retarget).
Motivation
The bind-referenced retarget (#839, PR #843) generates animations that reference the target rig's bind pose, but retarget quality still varies with body proportions: bulky/cartoon characters (short arms, wide torsos) end up with arms clipping into the body or floating too wide, because the retarget aims bones at the source clip's directions with no awareness of the target's volume.
Mixamo solves this with a "Character Arm-Space" slider — a user-controlled post-process that swings the whole arm chain outward/inward. It is cheap, intuitive, and rescues most intersection cases without touching the retarget math. This issue plans the same idea as a generation post-process.
Design
Core:
AnimationMerger::adjustArmSpace(skel, animName, degrees)— a pure post-process on an existing generated animation:Ct, the same one the retarget uses), sign mirrored per side:+degswings both arms away from the body,−degtucks them in.canonDupsplit.UserObjectBindings(qtme.armspace); re-applying with a new slider value first reverts the stored one, so scrubbing the slider never accumulates error.QUndoCommand(UndoManager pattern).Surfaces:
generated_*clip is selected; live preview while dragging (apply on release; slider maps to the absolute stored value).qtmesh anim <file> --generate "<prompt>" --arm-space <deg>and standaloneqtmesh anim <file> --arm-space <deg> --animation <name> -o outfor post-adjusting existing clips.arm_spaceargument ongenerate_motion+ a standaloneadjust_arm_spacetool.ai.assist.text_to_motionmessagearm_space.Testing: pure-data unit test on a synthetic 22-role skeleton — verify (a) shoulder world direction swings by the requested angle in the torso plane, (b) idempotence (apply 20 then 10 ≡ apply 10), (c) elbows/hands unchanged in parent-relative terms.
Follow-up knobs the same mechanism enables (out of scope here, listed for the design to keep the door open)
Acceptance criteria
Related: epic #837 (t2m-v2), #839 (canonical clip extraction), PR #843 (bind-referenced retarget).