Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Is there a way to extract rootmotion curves from a clip

Discussion in 'Animation' started by dibdab, Jan 10, 2019.

  1. dibdab

    dibdab

    Joined:
    Jul 5, 2011
    Posts:
    976
    I would like to use hip curve's root motion data
    (for playing it regularly and/or at a normalized time of the clip)

    and on a humanoid that has root motion switch off (something like a requirement)
    probably could get the hip data when playing the anim clip, not sure

    probably this function did that, which is now obsolete
    https://docs.unity3d.com/ScriptReference/AnimationUtility.GetAllCurves.html

    this says on any animated properties (but is there a way to get that data?)
    https://docs.unity3d.com/ScriptReference/AnimationClip.SampleAnimation.html

    this sound good, but questionable now if will be able to use it
    Thus array of keyframes is returned instead of a singular curve object.
    https://docs.unity3d.com/ScriptReference/AnimationUtility.GetObjectReferenceCurve.html
    Code (CSharp):
    1. public static ObjectReferenceKeyframe[] GetObjectReferenceCurve(AnimationClip clip, EditorCurveBinding binding);

    there has been some discussions about this, but didn't find an answer to
    to don't want to remove or add curve
    here's how to add rootmotion
    https://forum.unity.com/threads/getting-mecanim-root-motion-translation.241312/
    https://forum.unity.com/threads/make-root-motion-in-animation-clip.250723/
    but MecanimAnimator says
    so MotionT and MotionQ is there somewhere, isn't it

    I'm not sure if this example.cs is still working or if would get hip's rootmotion
    https://forum.unity.com/threads/how-to-use-animation-sample.39647/
     
    TigerHix likes this.
  2. teutonicus

    teutonicus

    Joined:
    Jul 4, 2012
    Posts:
    70
    _clip is the clip you're looking at and rootMotionNodeName would be your hip/root motion joint's path:

    Code (CSharp):
    1. var curveBindings = UnityEditor.AnimationUtility.GetCurveBindings(_clip);
    2. foreach (var curveBinding in curveBindings)
    3. {
    4.   if (curveBinding.path == rootMotionNodeName)
    5.   {
    6.     //Debug.Log(curveBinding.path + ", " + curveBinding.propertyName);
    7.     var editorCurve = UnityEditor.AnimationUtility.GetEditorCurve(_clip, curveBinding);
    8.   }
    9. }
     
  3. dibdab

    dibdab

    Joined:
    Jul 5, 2011
    Posts:
    976
    hey, thanks.
    maybe this could lead to something.

    one thing. the clip cannot be humanoid, it must be generic.
    if humanoid, curveBinding.path returns empty.

    for the record, this Debug.Log(curveBinding.path + ", " + curveBinding.propertyName);
    looks like this for RunForward_NtrlFaceFwd.fbx
    clipbindingsx0.jpg
    clipbindingsx.jpg

    now would need this var editorCurve turn into AnimationCurve what it is, but that is not so simple either
    ah, yeah, if just AnimationUtility.GetEditorCurve without UnityEditor I'm getting some curve

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEditor;
    5.  
    6. public class getACurves : MonoBehaviour {
    7.  
    8.     public AnimationClip _clip;
    9.     public string boneName;
    10.  
    11.     public AnimationCurve curve;
    12.  
    13.     void Start () {
    14.  
    15.         var curveBindings = UnityEditor.AnimationUtility.GetCurveBindings(_clip);
    16.  
    17.  
    18.         foreach (var curveBinding in curveBindings)
    19.         {
    20.  
    21.             //Debug.Log(curveBinding.path + ", " + curveBinding.propertyName);
    22.  
    23.             if (curveBinding.path == boneName)
    24.             {
    25.                 curve = AnimationUtility.GetEditorCurve(_clip, curveBinding);
    26.                 Debug.Log("curve> " + curveBinding.path + ", " + curveBinding.propertyName);
    27.             }
    28.         }
    29.  
    30.     }
    31.  
    32. }
    would need to filter them by propertyName yet
     
    Last edited: Jan 10, 2019
    MagiJedi likes this.
  4. teutonicus

    teutonicus

    Joined:
    Jul 4, 2012
    Posts:
    70
    Ahh, it hadn't occurred to me that root motion might work differently for humanoid rig clips as I don't use any of the built in mecanim retargeting.

    So given the root motion node's animation curves from above, to get total root motion for a clip at time t, I do something along the lines of:
    for curve localPosition.x, localPosition.y, etc.
    p0 = evaluate curve at time 0 (AnimationCurve::Evaluate)
    p1 = evaluate curve at time t
    motion = p1 - p0

    Same deal with rotation, etc. I also strip out a bunch of unneeded curves including root motion on import, storing stripped animation clip, root motion curves and other metadata in another asset which I use internally with custom Playables mixer. Requires more scaffolding around your animation setup but also allows complete control over how root motion is treated.
     
  5. dibdab

    dibdab

    Joined:
    Jul 5, 2011
    Posts:
    976
    yeah, it can be usable for many thing: anim rootmotion modif, between-states, poses, animating etc
    I guess unity's Kinematica does this too, first it extracts all curves from the clips

    humanoid/generic is not a problem with mixamo 'cause the gen version got the same animation and same root curves

    the hip/root curves
    clipcurves.jpg

    and if I use these curves to move a transform
    Code (CSharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. public class moveByCurves : MonoBehaviour {
    6.     public getACurves getACurves;
    7.     public Transform mover;
    8.     public float time;
    9.     //for root there's 10 curves: rotation[4], position[3], scale[3]
    10.     //all other bones 7: rotation[4], scale[3]
    11.     public int curvN = 10;
    12.     int i;
    13.     public bool isRoot;
    14.     void Update () {
    15.         time = Time.time;
    16.         if (isRoot) {
    17.             mover.rotation = new Quaternion (
    18.                 getACurves.curve [0].Evaluate (time),
    19.                 getACurves.curve [1].Evaluate (time),
    20.                 getACurves.curve [2].Evaluate (time),
    21.                 getACurves.curve [3].Evaluate (time)
    22.             );
    23.             mover.position = new Vector3 (
    24.                 getACurves.curve [4].Evaluate (time),
    25.                 getACurves.curve [5].Evaluate (time),
    26.                 getACurves.curve [6].Evaluate (time)
    27.             );
    28.         }
    29.         if (!isRoot) {
    30.             mover.localRotation = new Quaternion (
    31.                 getACurves.curve [0].Evaluate (time),
    32.                 getACurves.curve [1].Evaluate (time),
    33.                 getACurves.curve [2].Evaluate (time),
    34.                 getACurves.curve [3].Evaluate (time)
    35.             );
    36.         }
    37.     }
    38. }
    39.  
    then it looks alright
    roothip.gif

    but doesn't match up with legs

    which might be a localRotation (*not rotation) issue
    will test later
    bodycrv.gif
     
    Last edited: Jan 11, 2019
    MagiJedi and samuelmorais like this.
  6. dibdab

    dibdab

    Joined:
    Jul 5, 2011
    Posts:
    976
    okay, had to check it now

    it is localRotation
    bodycrv2.gif

    updated the script above
     
    MagiJedi, SenseEater and samuelmorais like this.
  7. dibdab

    dibdab

    Joined:
    Jul 5, 2011
    Posts:
    976
    used these scripts to get and modify root motion curves
    frog had only one jump, and wanted to have blendtree of short and long jump

    frogrootmotion.gif
    the original animation has been rotated (not lookin the direction of the "T-pose")
    so had used just the root curves (and added rotation)
    and other bones has been played by animator (sync.state)

    playBonesCurves.jpg
    getBonesPath.jpg
     

    Attached Files:

    Last edited: Jul 29, 2020
    FlavioIT likes this.
  8. xarcius

    xarcius

    Joined:
    Dec 3, 2014
    Posts:
    8
    sorry if I write under this old post. I have a list of AnimationCurve. Can I merge them to export to fbx to use this new animation?
     
  9. bobadi

    bobadi

    Joined:
    Jan 3, 2019
    Posts:
    660
    there's no script/package that would do that to my knowledge.

    you can record the animation being played in unity (like w playBonesCurves.cs)
    there's couple possibilities for that:
    - Scene Motion Capture (.anim)
    - Final IK's baker (.anim)
    - Skele's DAE exporter (.anim > .dae)
    - UnityTech's fbx exporter
     
  10. Whatever560

    Whatever560

    Joined:
    Jan 5, 2016
    Posts:
    505
    Thanks guys for digging into this, will be much helpfull in our case !
    I'm binding this into the validate step so I can extract the curves to use while in play mode.