-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement minimal animation graphs in order to support animation blending. #11670
Conversation
transforms: Query<&mut Transform>, | ||
morph_weights: Query<&mut MorphWeights>, | ||
parents: Query<(Has<AnimationPlayer>, Has<AnimationGraph>, Option<&Parent>)>, | ||
mut animation_graphs: Query<(Entity, Option<&Parent>, &mut AnimationGraph)>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to drop the parallelization that is currently supported, and may be a significant performance regression for scenes with a large number of animated rigs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any reason we cannot just use .par_iter_mut().for_each
here? Seems like it would work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does look like par_iter
would bring this in line with how things are currently dispatched in animation_player
. Same exact query more or less.
|
||
let node = &mut self.animation_graph.graph[*node_index]; | ||
if let Some(ref mut playing_animation) = node.animation { | ||
crate::apply_animation( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Each time apply_animation
is called, the query for each bone within the hierarchy is redone, which involves a hierarchy traversal and multiple string comparisons. This works, but the total overhead scales with the number of clips in the graph. This isn't a significant departure from what we currently have, so I don't want to block on this, but we need to address this sooner rather than later.
'mwq, | ||
'mwa, | ||
'mwb, | ||
'mwc, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These lifetimes definitely need to be addressed before we merge this. This is a little ridiculous.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was able to appease the borrow-checker with the following:
struct AnimationContext<'l, 'w, 's, 'a> {
animation_graph: &'l mut AnimationGraph,
time: &'l Time,
root_entity: Entity,
animation_clips: &'l Assets<AnimationClip>,
parent: Option<&'w Parent>,
parents: &'l Query<
'w,
's,
(
Has<AnimationPlayer>,
Has<AnimationGraph>,
Option<&'a Parent>,
),
>,
children: &'l Query<'w, 's, &'a Children>,
names: &'l Query<'w, 's, &'a Name>,
transforms: &'l Query<'w, 's, &'a mut Transform>,
morph_weights: &'l Query<'w, 's, &'a mut MorphWeights>,
}
pub fn evaluate_animation_graphs<'a>(
time: Res<Time>,
animation_clips: Res<Assets<AnimationClip>>,
children: Query<&'a Children>,
names: Query<&'a Name>,
transforms: Query<&'a mut Transform>,
morph_weights: Query<&'a mut MorphWeights>,
parents: Query<(
Has<AnimationPlayer>,
Has<AnimationGraph>,
Option<&'a Parent>,
)>,
mut animation_graphs: Query<(Entity, Option<&'a Parent>, &'a mut AnimationGraph)>,
)
I don't think it is possible to reduce this further, and I'm not 100% confident this ends up the same as the original. Maybe 'a
is actually static idk. Not currently sure what AnimationContext
is for but I trust the process.
I'm closing this as there will need to be a lot of changes. |
# Objective Bevy's animation system currently does tree traversals based on `Name` that aren't necessary. Not only do they require in unsafe code because tree traversals are awkward with parallelism, but they are also somewhat slow, brittle, and complex, which manifested itself as way too many queries in #11670. # Solution Divide animation into two phases: animation *advancement* and animation *evaluation*, which run after one another. *Advancement* operates on the `AnimationPlayer` and sets the current animation time to match the game time. *Evaluation* operates on all animation bones in the scene in parallel and sets the transforms and/or morph weights based on the time and the clip. To do this, we introduce a new component, `AnimationTarget`, which the asset loader places on every bone. It contains the ID of the entity containing the `AnimationPlayer`, as well as a UUID that identifies which bone in the animation the target corresponds to. In the case of glTF, the UUID is derived from the full path name to the bone. The rule that `AnimationTarget`s are descendants of the entity containing `AnimationPlayer` is now just a convention, not a requirement; this allows us to eliminate the unsafe code. # Migration guide * `AnimationClip` now uses UUIDs instead of hierarchical paths based on the `Name` component to refer to bones. This has several consequences: - A new component, `AnimationTarget`, should be placed on each bone that you wish to animate, in order to specify its UUID and the associated `AnimationPlayer`. The glTF loader automatically creates these components as necessary, so most uses of glTF rigs shouldn't need to change. - Moving a bone around the tree, or renaming it, no longer prevents an `AnimationPlayer` from affecting it. - Dynamically changing the `AnimationPlayer` component will likely require manual updating of the `AnimationTarget` components. * Entities with `AnimationPlayer` components may now possess descendants that also have `AnimationPlayer` components. They may not, however, animate the same bones. * As they aren't specific to `TypeId`s, `bevy_reflect::utility::NoOpTypeIdHash` and `bevy_reflect::utility::NoOpTypeIdHasher` have been renamed to `bevy_reflect::utility::NoOpHash` and `bevy_reflect::utility::NoOpHasher` respectively.
# Objective Bevy's animation system currently does tree traversals based on `Name` that aren't necessary. Not only do they require in unsafe code because tree traversals are awkward with parallelism, but they are also somewhat slow, brittle, and complex, which manifested itself as way too many queries in bevyengine#11670. # Solution Divide animation into two phases: animation *advancement* and animation *evaluation*, which run after one another. *Advancement* operates on the `AnimationPlayer` and sets the current animation time to match the game time. *Evaluation* operates on all animation bones in the scene in parallel and sets the transforms and/or morph weights based on the time and the clip. To do this, we introduce a new component, `AnimationTarget`, which the asset loader places on every bone. It contains the ID of the entity containing the `AnimationPlayer`, as well as a UUID that identifies which bone in the animation the target corresponds to. In the case of glTF, the UUID is derived from the full path name to the bone. The rule that `AnimationTarget`s are descendants of the entity containing `AnimationPlayer` is now just a convention, not a requirement; this allows us to eliminate the unsafe code. # Migration guide * `AnimationClip` now uses UUIDs instead of hierarchical paths based on the `Name` component to refer to bones. This has several consequences: - A new component, `AnimationTarget`, should be placed on each bone that you wish to animate, in order to specify its UUID and the associated `AnimationPlayer`. The glTF loader automatically creates these components as necessary, so most uses of glTF rigs shouldn't need to change. - Moving a bone around the tree, or renaming it, no longer prevents an `AnimationPlayer` from affecting it. - Dynamically changing the `AnimationPlayer` component will likely require manual updating of the `AnimationTarget` components. * Entities with `AnimationPlayer` components may now possess descendants that also have `AnimationPlayer` components. They may not, however, animate the same bones. * As they aren't specific to `TypeId`s, `bevy_reflect::utility::NoOpTypeIdHash` and `bevy_reflect::utility::NoOpTypeIdHasher` have been renamed to `bevy_reflect::utility::NoOpHash` and `bevy_reflect::utility::NoOpHasher` respectively.
# Objective Bevy's animation system currently does tree traversals based on `Name` that aren't necessary. Not only do they require in unsafe code because tree traversals are awkward with parallelism, but they are also somewhat slow, brittle, and complex, which manifested itself as way too many queries in bevyengine#11670. # Solution Divide animation into two phases: animation *advancement* and animation *evaluation*, which run after one another. *Advancement* operates on the `AnimationPlayer` and sets the current animation time to match the game time. *Evaluation* operates on all animation bones in the scene in parallel and sets the transforms and/or morph weights based on the time and the clip. To do this, we introduce a new component, `AnimationTarget`, which the asset loader places on every bone. It contains the ID of the entity containing the `AnimationPlayer`, as well as a UUID that identifies which bone in the animation the target corresponds to. In the case of glTF, the UUID is derived from the full path name to the bone. The rule that `AnimationTarget`s are descendants of the entity containing `AnimationPlayer` is now just a convention, not a requirement; this allows us to eliminate the unsafe code. # Migration guide * `AnimationClip` now uses UUIDs instead of hierarchical paths based on the `Name` component to refer to bones. This has several consequences: - A new component, `AnimationTarget`, should be placed on each bone that you wish to animate, in order to specify its UUID and the associated `AnimationPlayer`. The glTF loader automatically creates these components as necessary, so most uses of glTF rigs shouldn't need to change. - Moving a bone around the tree, or renaming it, no longer prevents an `AnimationPlayer` from affecting it. - Dynamically changing the `AnimationPlayer` component will likely require manual updating of the `AnimationTarget` components. * Entities with `AnimationPlayer` components may now possess descendants that also have `AnimationPlayer` components. They may not, however, animate the same bones. * As they aren't specific to `TypeId`s, `bevy_reflect::utility::NoOpTypeIdHash` and `bevy_reflect::utility::NoOpTypeIdHasher` have been renamed to `bevy_reflect::utility::NoOpHash` and `bevy_reflect::utility::NoOpHasher` respectively.
Objective
This is a minimum viable implementation of bevyengine/rfcs#51. It allows Bevy to support animation blending.
Solution
This is just to start the conversation.