s&box/field-guide
the symptom, in your words

"Retargeting free Mixamo mocap onto a custom rig"

✓ verified on engine 26.07lane: Rigging & animationposted
▸ SYMPTOM
  • Mixamo clip on your custom rig looks hunched forward in idle (spine/chest/neck stacked on top of hips yaw).
  • Walk/run stride measures absurd (e.g. ~70 m/s) after you "fixed" scale.
  • Ambient idles pop at the loop seam; locomotion clips almost loop but double a frame.
  • Retarget script wipes your armature mid-build because Mixamo import resets the scene.
▸ CAUSE

Mixamo and your rig have different rest poses. Copying world rotations double-counts every ancestor. The fix is rest-delta + parent-relative transfer.

Classic forward-hunch: feeding the Poser a raw world delta means Hips yaw is applied again into Spine/Chest/Neck/Head on top of the pelvis's own motion.

Scale trap: Mixamo imports at 0.01 armature-object scale. matrix_world already folds that in (pose worlds come out in meters). transform_apply(scale=True) leaves LOCATION fcurves in the pre-apply unit → Hips translation blows up ~100×. Rotations are unaffected — only root-motion / hip-bob translation.

▸ FIX

Rest-delta + parent-relative

Per source bone at frame f:

snippet
dq_src = M_anim @ M_rest^-1          # world rotation away from THAT skeleton's rest
dq_src = F @ dq_src @ F^-1           # reframe Mixamo world → your armature world
Q      = dq_parent^-1 @ dq_bone      # PARENT-RELATIVE — not the raw world delta

Frame quat F for the common facing mismatch: Mixamo faces −Y / left +X, your rig faces +X / left +Y → F = Rz(+90). (needs verification) if your rig facing differs — measure once.

Feed Q into your keyer as a world-delta-from-rest (or a key_world_q helper that keeps quaternion hemisphere continuity: negate if dot(prev, new) < 0).

Do not feed raw world deltas into a poser that composes bone_world = dq_parent @ Q @ rest_world — that is the hunch.

Scale

snippet
# Leave Mixamo armature object scale ON
# Read everything through matrix_world — never transform_apply(scale=True)

Loop / root motion

Clip typeLoopRoot motion
Locomotion (walk/run)Mixamo usually loops (frame 1 == N) — drop the duplicate seam frameStrip Hips world XY (runtime moves the char); keep vertical bob × our_hip_z / mixamo_hip_z
Ambient idlePick lowest-seam sub-window (~5–8°/bone residual is OK)Keep full XYZ scaled, window-relative for sit/collapse
Soft seamLet vmdl fade_in/fade_out + Sequence.Blending hide residual

Depicted speed handoff (don't re-time the clip):

snippet
depicted_mps = Mixamo_XY_travel * hip_scale / window_seconds

Use that const for PlaybackRate = actualSpeed / depicted_mps.

Build order

Preload / cache Mixamo quats BEFORE building your rig. Importing a take often read_factory_settings and wipes the scene; cached mathutils quats survive. Pattern: preload_all() at the top of each rig script, then build.

Export with fbx-export-recipe.

▸ WHY IT WORKS

Rest-delta cancels incompatible bind poses: you transfer motion away from rest, not absolute orientations. Parent-relative deltas stop ancestor rotations from being applied twice down the chain. Leaving Mixamo's object scale alone keeps matrix_world and LOCATION fcurves in the same unit, so hip travel stays meters instead of centimetres×100.

Verified on engine 26.07 — seen in a real project.
s&box moves fast; an undated fix is a liability. Spot a stale detail?