Search Unity

  1. Unity Asset Manager is now available in public beta. Try it out now and join the conversation here in the forums.
    Dismiss Notice

DOTS Rootmotion? (also samples outdated doc)

Discussion in 'DOTS Animation' started by thelebaron, Nov 3, 2020.

  1. thelebaron

    thelebaron

    Joined:
    Jun 2, 2013
    Posts:
    857
    What does it take to get rootmotion working on the MyFirstAnimationClip example?

    I've:
    - set the ClipConfiguation when creating a graph to include all the rootmotion types(CycleRootMotion| DeltaRootMotion | RootMotionFromVelocity)
    - added a ProcessDefaultAnimationGraph.AnimatedRootMotion component to the rig entity
    - added both Default/LateAnimationGraphWriteTransformHandle to the character's hips
    - tried changing the ConnectionTypes on the entityNode & ClipPlayerNode to Feedback
    - tried baking the BlobAssetReference<Clip> using UberClipNode.Bake

    None of these things result in rootmotion for me(only https://i.imgur.com/FprYoZr.gifv), what else must one do?

    Secondly I noticed the docs for the HDRP/AnimationController refer to the RootMotionNode which doesnt exist now.
     
    Egad_McDad and florianhanke like this.
  2. pavelmo4alov

    pavelmo4alov

    Joined:
    Mar 13, 2013
    Posts:
    14
    I managed to work root motion some days ago.
    1) Add ProcessDefaultAnimationGraph.AnimatedRootMotion component to entity in MyFirstClip_ClipPlayer near adding DeltaTime component.
    2) Set ClipConfiguration : Mask and MotionID. For MotionName I used name of hips game objects - Hips. You can find example of setting MotionID in example ConfigurableClip.

    Now MyFirstClip_ClipPlayer looks like that:
    Code (CSharp):
    1. public class MyFirstClip_ClipPlayer : MonoBehaviour, IConvertGameObjectToEntity
    2. {
    3.     public AnimationClip Clip;
    4.     public string MotionName;
    5.  
    6.     private StringHash _motionId;
    7.  
    8.     public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    9.     {
    10.         if (Clip == null)
    11.             return;
    12.  
    13.         var rig = GetComponent<RigComponent>();
    14.  
    15.  
    16.         for (var boneIter = 0; boneIter < rig.Bones.Length; boneIter++)
    17.         {
    18.             if (MotionName == rig.Bones[boneIter].name)
    19.             {
    20.                 _motionId = RigGenerator.ComputeRelativePath(rig.Bones[boneIter], rig.transform);
    21.             }
    22.         }
    23.  
    24.  
    25.         conversionSystem.DeclareAssetDependency(gameObject, Clip);
    26.  
    27.         dstManager.AddComponentData(entity, new MyFirstClip_PlayClipComponent
    28.         {
    29.             Clip = conversionSystem.BlobAssetStore.GetClip(Clip),
    30.             MotionId = _motionId
    31.         });
    32.  
    33.         dstManager.AddComponent<ProcessDefaultAnimationGraph.AnimatedRootMotion>(entity);
    34.         dstManager.AddComponent<DeltaTime>(entity);
    35.     }
    36. }


    and part of CreateGraph method:
    Code (CSharp):
    1. set.SendMessage(data.ClipPlayerNode, ClipPlayerNode.SimulationPorts.Configuration,
    2.             new ClipConfiguration
    3.             {
    4.                 Mask = ClipConfigurationMask.LoopTime | ClipConfigurationMask.CycleRootMotion |
    5.                        ClipConfigurationMask.DeltaRootMotion | ClipConfigurationMask.LoopValues | ClipConfigurationMask.RootMotionFromVelocity,
    6.                 MotionID = playClip.MotionId
    7.             });
     
    PhilSA, Egad_McDad and thelebaron like this.
  3. thelebaron

    thelebaron

    Joined:
    Jun 2, 2013
    Posts:
    857
    thanks, a heads up for anyone else testing this myfirstanimationclip example's animation does not have rootmotion in the clip(or at least not sure how to make it work with that specific animation clip/model)
     
  4. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    I can see the rootmotion deltas properly being written in ProcessDefaultAnimationGraph.AnimatedRootMotion, but I haven't figured out how to make the animation not actually move the root (I want to take care of that myself, and make it move with a physics velocity instead of with translation, etc...)

    Has anyone figured out how to do that?
     
    Last edited: Jan 30, 2021
  5. thelebaron

    thelebaron

    Joined:
    Jun 2, 2013
    Posts:
    857
    Not sure if its the actual way of doing it but what I figured out. :)

    Made a PhysicsVelocityNode that modifies the delta and sets that into the physics velocity component. You also need the builtin GetAnimationStreamLocalToRootNode, and connect an output stream(like a mixer node) to it. Its been a while since I did the setup for this but I think the output stream needs to be the final combination of whatever nodes you have connected, ie if two mixers are connected to a third mixer, it should be the third mixers output. Also the order of how you connect nodes and send messages is important.

    Note this also uses the SimpleInput component from my other post and it has a bool in it for enabling/disabling the physics node's behaviour.

    Code (CSharp):
    1.  
    2.         var physicsNode           = graphSystem.CreateNode<PhysicsVelocityNode>(data.Graph);
    3.         var localToRootNode       = graphSystem.CreateNode<GetAnimationStreamLocalToRootNode>(data.Graph);
    4.         set.Connect(entityNode, physicsNode, PhysicsVelocityNode.KernelPorts.InputLocalToWorld, NodeSet.ConnectionType.Feedback);
    5.         set.Connect(entityNode, physicsNode, PhysicsVelocityNode.KernelPorts.InputSimpleData, NodeSet.ConnectionType.Feedback);
    6.         set.Connect(clipToStreamMixerNode, ToggleClipNode.KernelPorts.Output, localToRootNode, GetAnimationStreamLocalToRootNode.KernelPorts.Input);
    7.         set.Connect(localToRootNode, GetAnimationStreamLocalToRootNode.KernelPorts.Translation, physicsNode, PhysicsVelocityNode.KernelPorts.RootTranslationDelta);
    8.         set.Connect(physicsNode, PhysicsVelocityNode.KernelPorts.Ouput, entityNode);
    9.      
    10.         set.SendMessage(localToRootNode, GetAnimationStreamLocalToRootNode.SimulationPorts.Rig, rig);
    11.  
    Code (CSharp):
    1. using Unity.Burst;
    2. using Unity.DataFlowGraph;
    3. using Unity.Mathematics;
    4. using Unity.Physics;
    5. using Unity.Transforms;
    6. using UnityEngine;
    7.  
    8. public class PhysicsVelocityNode : SimulationKernelNodeDefinition<PhysicsVelocityNode.SimPorts,PhysicsVelocityNode.KernelDefs>
    9. {
    10.     public struct SimPorts : ISimulationPortDefinition
    11.     {
    12.         public MessageOutput<PhysicsVelocityNode, float3> OutputData; // manual data access
    13.     }
    14.  
    15.     public struct KernelDefs : IKernelPortDefinition
    16.     {
    17.         //public DataInput<PhysicsVelocityNode, PhysicsVelocity> Input;
    18.         public DataInput<PhysicsVelocityNode, float3>          RootTranslationDelta;
    19.         public DataInput<PhysicsVelocityNode, LocalToWorld>    InputLocalToWorld;
    20.         public DataInput<PhysicsVelocityNode, SimpleInput> InputSimpleData;
    21.      
    22.         //public DataOutput<PhysicsVelocityNode, PhysicsVelocity> Output;
    23.         public DataOutput<PhysicsVelocityNode, float3>          OuputDelta;
    24.         public DataOutput<PhysicsVelocityNode, PhysicsVelocity> Ouput; // data filled in a system
    25.     }
    26.  
    27.     struct KernelData : IKernelData {}
    28.  
    29.  
    30.     [BurstCompile]
    31.     struct Kernel : IGraphKernel<KernelData, KernelDefs>
    32.     {
    33.         public void Execute(RenderContext ctx, in KernelData data, ref KernelDefs ports)
    34.         {
    35.             var transformDelta = ctx.Resolve(ports.RootTranslationDelta);
    36.             var localToWorld   = ctx.Resolve(ports.InputLocalToWorld);
    37.          
    38.             // get feedback about state of input
    39.             var input     = ctx.Resolve(ports.InputSimpleData);            
    40.             var multipliedResult = transformDelta * 30;
    41.             var linear           = math.mul(new quaternion(localToWorld.Value), multipliedResult);
    42.             if(!input.Enabled)
    43.                 linear = float3.zero;
    44.             ctx.Resolve(ref ports.OuputDelta) = ctx.Resolve(ports.RootTranslationDelta);
    45.             ctx.Resolve(ref ports.Ouput) = new PhysicsVelocity{ Linear = linear };
    46.         }
    47.     }
    48. }
     
  6. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    I ended up with an alternative solution (probably not the "official" way of doing things, but works for now):
    • Have a "ProcessDefaultAnimationGraph.AnimatedRootMotion" component on your rig entity
    • Set ClipConfigurationMask.DeltaRootMotion on the clip node
    • Add your own "PostAnimationSystem" that resets the translation of the rig entity to what it was before the animation update
    With this setup, you end up with the root motion deltas in your AnimatedRootMotion component, and your rig stays immobile. Now you have the complete freedom to process the root motion however you want in your own systems later by using the deltas

    EDIT: found a problem: for some weird reason it seems the root motion on the Y axis isn't computed, but on the XZ axis it is (?)
     
    Last edited: Jan 31, 2021
    NotaNaN and Egad_McDad like this.