mbrea-c / bevy_animation_graph

Animation graphs in Bevy!
Apache License 2.0
102 stars 5 forks source link

Root motion physics integration #20

Open ghost opened 9 months ago

ghost commented 9 months ago

In your planning list I saw you were considering using bone masks to specify which bones are kinematically driven and which are simulated. Similarly, there is a concept called root motion that uses a joint (typically pelvis in humanoid) to drive the character in the physics engine using velocity, acceleration, and/or impulses. Have you considered how root motion data could be exposed and zero'd for kinematic motion?

https://github.com/bevyengine/bevy/pull/8102 https://docs.unity3d.com/Manual/RootMotion.html

mbrea-c commented 9 months ago

My plan for the user-facing API was

  1. Add an option to specify a root bone either in the AnimationGraphPlayer component or in an animation graph (open to discussion).
  2. Expose the root motion deltas in the AnimationGraphPlayer component, without automatically applying it. Users can then add a system to read the root motion deltas every frame and apply them as needed, since they may need be applied differently depending on how you structured your character controller and which physics engine (if any) you're using.

That said I haven't developed this idea into anything yet, and in particular I haven't figured out what the best way of extracting the root motion internally would be. Options I can think of are:

  1. Extract root motion in animation clip nodes, and propagate through the graph together with animation data. This has the disadvantage that root motion data will need to be handled anywhere we manipulate a pose, e.g. blending, chaining, etc.
  2. Extract root motion at the end of the graph query, based on the final root bone positions and taking into account looping. Saves the effort of handling root motion in animation nodes.
  3. Add a dedicated "extract root motion" node, and then propagate the motion with animation data. Gives the user more flexibility, but otherwise same disadvantages as option 1.

Currently I'm leaning towards option 2, since it should be easiest to implement and I can't think of a use case for having root motion data available to the animation graph.

Of course, feedback/discussion is appreciated, as it will help to better understand the requirements others have and, honestly, I don't really know that much about this :) In any case, I'm hoping to work on this after the animation graph editor (which I'm currently working on), animation events and state machines.

ghost commented 9 months ago

Neat! I also don't know much about this but it's really fun to think about. I've fallen down the Bobby Anguelov link.

Regarding extracting the root motion internally, it seems like how Bobby is doing it is from the blended pose from the animation graph. Imagine blending a walk and run animation. The blended root motion joint delta is the instantaneous velocity. Minute 38 of the YouTube link talks about how this is configurable in the transition node.

Having said that, calculating the root motion delta for nodes seems tricky and like it's easy to make an incorrect decision.

Bobby has a root motion override node that I think is your option 3 for internal management.

https://github.com/BobbyAnguelov/Esoterica/blob/main/Code/Engine/Animation/AnimationRootMotion.cpp

https://github.com/BobbyAnguelov/Esoterica/blob/bed49a68d0dcf7d4863a8ab652e8825d7638a515/Code/Engine/Animation/Graph/Nodes/Animation_RuntimeGraphNode_RootMotionOverride.cpp

Around minute 21, 28, and 38, and 1:34 https://www.youtube.com/live/R-T3Mk5oDHI?feature=shared

Edit:

1:34 of the video talks about getting the "root motion from the graph and not calculated by the pose". Reading into this more with the below links it turns out that it's configurable based on the graph behavior. it's a little confusing to follow, but it can blend or not blend, and then it can override behavior after that.

https://github.com/BobbyAnguelov/Esoterica/blob/bed49a68d0dcf7d4863a8ab652e8825d7638a515/Code/EngineTools/Animation/ResourceCompilers/ResourceCompiler_AnimationClip.cpp#L485

https://github.com/BobbyAnguelov/Esoterica/blob/bed49a68d0dcf7d4863a8ab652e8825d7638a515/Code/Engine/Animation/Graph/Nodes/Animation_RuntimeGraphNode_Transition.cpp#L65

https://github.com/BobbyAnguelov/Esoterica/blob/bed49a68d0dcf7d4863a8ab652e8825d7638a515/Code/Engine/Animation/Graph/Nodes/Animation_RuntimeGraphNode_Blend2D.cpp#L374

mbrea-c commented 9 months ago

Nice, thank you for looking into this! It seems that Bobby does include root motion deltas as part of the pose data available to the graph, which was what I wasn't sure about. It looks like he does some combination of options 1 and 3? He extracts the root motion from the animation clip and propagates it through the graph. When blending two poses he just does either a linear or additive blend to the root motion deltas of either pose, depending on some configuration:

https://github.com/BobbyAnguelov/Esoterica/blob/bed49a68d0dcf7d4863a8ab652e8825d7638a515/Code/Engine/Animation/AnimationBlender.h#L147

I'll go through the code you sent in a bit more detail this weekend when I have more time, but I think we can get away with a very similar solution here. Very cool :)