Search Unity

  1. Unity 2018.3 is now released.
    Dismiss Notice
  2. The Unity Pro & Visual Studio Professional Bundle gives you the tools you need to develop faster & collaborate more efficiently. Learn more.
    Dismiss Notice
  3. We've updated our Terms of Service. Please read our blog post from Unity CTO and Co-Founder Joachim Ante here
    Dismiss Notice
  4. Want to provide direct feedback to the Unity team? Join the Unity Advisory Panel.
    Dismiss Notice
  5. Improve your Unity skills with a certified instructor in a private, interactive classroom. Watch the overview now.
    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:
    665
    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/
     
  2. teutonicus

    teutonicus

    Joined:
    Jul 4, 2012
    Posts:
    27
    _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:
    665
    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
  4. teutonicus

    teutonicus

    Joined:
    Jul 4, 2012
    Posts:
    27
    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:
    665
    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 at 2:25 PM
  6. dibdab

    dibdab

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

    it is localRotation
    bodycrv2.gif

    updated the script above