bevy_animation/graph.rs
1//! The animation graph, which allows animations to be blended together.
2
3use core::{
4 fmt::Write,
5 iter,
6 ops::{Index, IndexMut, Range},
7};
8use std::io;
9
10use bevy_asset::{
11 io::Reader, Asset, AssetEvent, AssetId, AssetLoader, AssetPath, Assets, Handle, LoadContext,
12};
13use bevy_derive::{Deref, DerefMut};
14use bevy_ecs::{
15 component::Component,
16 message::MessageReader,
17 reflect::ReflectComponent,
18 resource::Resource,
19 system::{Res, ResMut},
20};
21use bevy_platform::collections::HashMap;
22use bevy_reflect::{prelude::ReflectDefault, Reflect};
23use derive_more::derive::From;
24use petgraph::{
25 graph::{DiGraph, NodeIndex},
26 Direction,
27};
28use ron::de::SpannedError;
29use serde::{Deserialize, Serialize};
30use smallvec::SmallVec;
31use thiserror::Error;
32use tracing::warn;
33
34use crate::{AnimationClip, AnimationTargetId};
35
36/// A graph structure that describes how animation clips are to be blended
37/// together.
38///
39/// Applications frequently want to be able to play multiple animations at once
40/// and to fine-tune the influence that animations have on a skinned mesh. Bevy
41/// uses an *animation graph* to store this information. Animation graphs are a
42/// directed acyclic graph (DAG) that describes how animations are to be
43/// weighted and combined together. Every frame, Bevy evaluates the graph from
44/// the root and blends the animations together in a bottom-up fashion to
45/// produce the final pose.
46///
47/// There are three types of nodes: *blend nodes*, *add nodes*, and *clip
48/// nodes*, all of which can have an associated weight. Blend nodes and add
49/// nodes have no associated animation clip and combine the animations of their
50/// children according to those children's weights. Clip nodes specify an
51/// animation clip to play. When a graph is created, it starts with only a
52/// single blend node, the root node.
53///
54/// For example, consider the following graph:
55///
56/// ```text
57/// ┌────────────┐
58/// │ │
59/// │ Idle ├─────────────────────┐
60/// │ │ │
61/// └────────────┘ │
62/// │
63/// ┌────────────┐ │ ┌────────────┐
64/// │ │ │ │ │
65/// │ Run ├──┐ ├──┤ Root │
66/// │ │ │ ┌────────────┐ │ │ │
67/// └────────────┘ │ │ Blend │ │ └────────────┘
68/// ├──┤ ├──┘
69/// ┌────────────┐ │ │ 0.5 │
70/// │ │ │ └────────────┘
71/// │ Walk ├──┘
72/// │ │
73/// └────────────┘
74/// ```
75///
76/// In this case, assuming that Idle, Run, and Walk are all playing with weight
77/// 1.0, the Run and Walk animations will be equally blended together, then
78/// their weights will be halved and finally blended with the Idle animation.
79/// Thus the weight of Run and Walk are effectively half of the weight of Idle.
80///
81/// Nodes can optionally have a *mask*, a bitfield that restricts the set of
82/// animation targets that the node and its descendants affect. Each bit in the
83/// mask corresponds to a *mask group*, which is a set of animation targets
84/// (bones). An animation target can belong to any number of mask groups within
85/// the context of an animation graph.
86///
87/// When the appropriate bit is set in a node's mask, neither the node nor its
88/// descendants will animate any animation targets belonging to that mask group.
89/// That is, setting a mask bit to 1 *disables* the animation targets in that
90/// group. If an animation target belongs to multiple mask groups, masking any
91/// one of the mask groups that it belongs to will mask that animation target.
92/// (Thus an animation target will only be animated if *all* of its mask groups
93/// are unmasked.)
94///
95/// A common use of masks is to allow characters to hold objects. For this, the
96/// typical workflow is to assign each character's hand to a mask group. Then,
97/// when the character picks up an object, the application masks out the hand
98/// that the object is held in for the character's animation set, then positions
99/// the hand's digits as necessary to grasp the object. The character's
100/// animations will continue to play but will not affect the hand, which will
101/// continue to be depicted as holding the object.
102///
103/// Animation graphs are assets and can be serialized to and loaded from [RON]
104/// files. Canonically, such files have an `.animgraph.ron` extension.
105///
106/// The animation graph implements [RFC 51]. See that document for more
107/// information.
108///
109/// [RON]: https://github.com/ron-rs/ron
110///
111/// [RFC 51]: https://github.com/bevyengine/rfcs/blob/main/rfcs/51-animation-composition.md
112#[derive(Asset, Reflect, Clone, Debug)]
113#[reflect(Debug, Clone)]
114pub struct AnimationGraph {
115 /// The `petgraph` data structure that defines the animation graph.
116 pub graph: AnimationDiGraph,
117
118 /// The index of the root node in the animation graph.
119 pub root: NodeIndex,
120
121 /// The mask groups that each animation target (bone) belongs to.
122 ///
123 /// Each value in this map is a bitfield, in which 0 in bit position N
124 /// indicates that the animation target doesn't belong to mask group N, and
125 /// a 1 in position N indicates that the animation target does belong to
126 /// mask group N.
127 ///
128 /// Animation targets not in this collection are treated as though they
129 /// don't belong to any mask groups.
130 pub mask_groups: HashMap<AnimationTargetId, AnimationMask>,
131}
132
133/// A [`Handle`] to the [`AnimationGraph`] to be used by the [`AnimationPlayer`](crate::AnimationPlayer) on the same entity.
134#[derive(Component, Clone, Debug, Default, Deref, DerefMut, Reflect, PartialEq, Eq, From)]
135#[reflect(Component, Default, Clone)]
136pub struct AnimationGraphHandle(pub Handle<AnimationGraph>);
137
138impl From<AnimationGraphHandle> for AssetId<AnimationGraph> {
139 fn from(handle: AnimationGraphHandle) -> Self {
140 handle.id()
141 }
142}
143
144impl From<&AnimationGraphHandle> for AssetId<AnimationGraph> {
145 fn from(handle: &AnimationGraphHandle) -> Self {
146 handle.id()
147 }
148}
149
150/// A type alias for the `petgraph` data structure that defines the animation
151/// graph.
152pub type AnimationDiGraph = DiGraph<AnimationGraphNode, (), u32>;
153
154/// The index of either an animation or blend node in the animation graph.
155///
156/// These indices are the way that [animation players] identify each animation.
157///
158/// [animation players]: crate::AnimationPlayer
159pub type AnimationNodeIndex = NodeIndex<u32>;
160
161/// An individual node within an animation graph.
162///
163/// The [`AnimationGraphNode::node_type`] field specifies the type of node: one
164/// of a *clip node*, a *blend node*, or an *add node*. Clip nodes, the leaves
165/// of the graph, contain animation clips to play. Blend and add nodes describe
166/// how to combine their children to produce a final animation.
167#[derive(Clone, Reflect, Debug)]
168#[reflect(Clone)]
169pub struct AnimationGraphNode {
170 /// Animation node data specific to the type of node (clip, blend, or add).
171 ///
172 /// In the case of clip nodes, this contains the actual animation clip
173 /// associated with the node.
174 pub node_type: AnimationNodeType,
175
176 /// A bitfield specifying the mask groups that this node and its descendants
177 /// will not affect.
178 ///
179 /// A 0 in bit N indicates that this node and its descendants *can* animate
180 /// animation targets in mask group N, while a 1 in bit N indicates that
181 /// this node and its descendants *cannot* animate mask group N.
182 pub mask: AnimationMask,
183
184 /// The weight of this node, which signifies its contribution in blending.
185 ///
186 /// Note that this does not propagate down the graph hierarchy; rather,
187 /// each [Blend] and [Add] node uses the weights of its children to determine
188 /// the total animation that is accumulated at that node. The parent node's
189 /// weight is used only to determine the contribution of that total animation
190 /// in *further* blending.
191 ///
192 /// In other words, it is as if the blend node is replaced by a single clip
193 /// node consisting of the blended animation with the weight specified at the
194 /// blend node.
195 ///
196 /// For animation clips, this weight is also multiplied by the [active animation weight]
197 /// before being applied.
198 ///
199 /// [Blend]: AnimationNodeType::Blend
200 /// [Add]: AnimationNodeType::Add
201 /// [active animation weight]: crate::ActiveAnimation::weight
202 pub weight: f32,
203}
204
205/// Animation node data specific to the type of node (clip, blend, or add).
206///
207/// In the case of clip nodes, this contains the actual animation clip
208/// associated with the node.
209#[derive(Clone, Default, Reflect, Debug)]
210#[reflect(Clone)]
211pub enum AnimationNodeType {
212 /// A *clip node*, which plays an animation clip.
213 ///
214 /// These are always the leaves of the graph.
215 Clip(Handle<AnimationClip>),
216
217 /// A *blend node*, which blends its children according to their weights.
218 ///
219 /// The weights of all the children of this node are normalized to 1.0.
220 #[default]
221 Blend,
222
223 /// An *additive blend node*, which combines the animations of its children
224 /// additively.
225 ///
226 /// The weights of all the children of this node are *not* normalized to
227 /// 1.0. Rather, each child is multiplied by its respective weight and
228 /// added in sequence.
229 ///
230 /// Add nodes are primarily useful for superimposing an animation for a
231 /// portion of a rig on top of the main animation. For example, an add node
232 /// could superimpose a weapon attack animation for a character's limb on
233 /// top of a running animation to produce an animation of a character
234 /// attacking while running.
235 Add,
236}
237
238/// An [`AssetLoader`] that can load [`AnimationGraph`]s as assets.
239///
240/// The canonical extension for [`AnimationGraph`]s is `.animgraph.ron`. Plain
241/// `.animgraph` is supported as well.
242#[derive(Default)]
243pub struct AnimationGraphAssetLoader;
244
245/// Errors that can occur when serializing animation graphs to RON.
246#[derive(Error, Debug)]
247pub enum AnimationGraphSaveError {
248 /// An I/O error occurred.
249 #[error(transparent)]
250 Io(#[from] io::Error),
251 /// An error occurred in RON serialization.
252 #[error(transparent)]
253 Ron(#[from] ron::Error),
254 /// An error occurred converting the graph to its serialization form.
255 #[error(transparent)]
256 ConvertToSerialized(#[from] NonPathHandleError),
257}
258
259/// Errors that can occur when deserializing animation graphs from RON.
260#[derive(Error, Debug)]
261pub enum AnimationGraphLoadError {
262 /// An I/O error occurred.
263 #[error(transparent)]
264 Io(#[from] io::Error),
265 /// An error occurred in RON deserialization.
266 #[error(transparent)]
267 Ron(#[from] ron::Error),
268 /// An error occurred in RON deserialization, and the location of the error
269 /// is supplied.
270 #[error(transparent)]
271 SpannedRon(#[from] SpannedError),
272 /// The deserialized graph contained legacy data that we no longer support.
273 #[error(
274 "The deserialized AnimationGraph contained an AnimationClip referenced by an AssetId, \
275 which is no longer supported. Consider manually deserializing the SerializedAnimationGraph \
276 type and determine how to migrate any SerializedAnimationClip::AssetId animation clips"
277 )]
278 GraphContainsLegacyAssetId,
279}
280
281/// Acceleration structures for animation graphs that allows Bevy to evaluate
282/// them quickly.
283///
284/// These are kept up to date as [`AnimationGraph`] instances are added,
285/// modified, and removed.
286#[derive(Default, Reflect, Resource)]
287pub struct ThreadedAnimationGraphs(
288 pub(crate) HashMap<AssetId<AnimationGraph>, ThreadedAnimationGraph>,
289);
290
291/// An acceleration structure for an animation graph that allows Bevy to
292/// evaluate it quickly.
293///
294/// This is kept up to date as the associated [`AnimationGraph`] instance is
295/// added, modified, or removed.
296#[derive(Default, Reflect)]
297pub struct ThreadedAnimationGraph {
298 /// A cached postorder traversal of the graph.
299 ///
300 /// The node indices here are stored in postorder. Siblings are stored in
301 /// descending order. This is because the
302 /// [`AnimationCurveEvaluator`](`crate::animation_curves::AnimationCurveEvaluator`) uses a stack for
303 /// evaluation. Consider this graph:
304 ///
305 /// ```text
306 /// ┌─────┐
307 /// │ │
308 /// │ 1 │
309 /// │ │
310 /// └──┬──┘
311 /// │
312 /// ┌───────┼───────┐
313 /// │ │ │
314 /// ▼ ▼ ▼
315 /// ┌─────┐ ┌─────┐ ┌─────┐
316 /// │ │ │ │ │ │
317 /// │ 2 │ │ 3 │ │ 4 │
318 /// │ │ │ │ │ │
319 /// └──┬──┘ └─────┘ └─────┘
320 /// │
321 /// ┌───┴───┐
322 /// │ │
323 /// ▼ ▼
324 /// ┌─────┐ ┌─────┐
325 /// │ │ │ │
326 /// │ 5 │ │ 6 │
327 /// │ │ │ │
328 /// └─────┘ └─────┘
329 /// ```
330 ///
331 /// The postorder traversal in this case will be (4, 3, 6, 5, 2, 1).
332 ///
333 /// The fact that the children of each node are sorted in reverse ensures
334 /// that, at each level, the order of blending proceeds in ascending order
335 /// by node index, as we guarantee. To illustrate this, consider the way
336 /// the graph above is evaluated. (Interpolation is represented with the ⊕
337 /// symbol.)
338 ///
339 /// | Step | Node | Operation | Stack (after operation) | Blend Register |
340 /// | ---- | ---- | ---------- | ----------------------- | -------------- |
341 /// | 1 | 4 | Push | 4 | |
342 /// | 2 | 3 | Push | 4 3 | |
343 /// | 3 | 6 | Push | 4 3 6 | |
344 /// | 4 | 5 | Push | 4 3 6 5 | |
345 /// | 5 | 2 | Blend 5 | 4 3 6 | 5 |
346 /// | 6 | 2 | Blend 6 | 4 3 | 5 ⊕ 6 |
347 /// | 7 | 2 | Push Blend | 4 3 2 | |
348 /// | 8 | 1 | Blend 2 | 4 3 | 2 |
349 /// | 9 | 1 | Blend 3 | 4 | 2 ⊕ 3 |
350 /// | 10 | 1 | Blend 4 | | 2 ⊕ 3 ⊕ 4 |
351 /// | 11 | 1 | Push Blend | 1 | |
352 /// | 12 | | Commit | | |
353 pub threaded_graph: Vec<AnimationNodeIndex>,
354
355 /// A mapping from each parent node index to the range within
356 /// [`Self::sorted_edges`].
357 ///
358 /// This allows for quick lookup of the children of each node, sorted in
359 /// ascending order of node index, without having to sort the result of the
360 /// `petgraph` traversal functions every frame.
361 pub sorted_edge_ranges: Vec<Range<u32>>,
362
363 /// A list of the children of each node, sorted in ascending order.
364 pub sorted_edges: Vec<AnimationNodeIndex>,
365
366 /// A mapping from node index to a bitfield specifying the mask groups that
367 /// this node masks *out* (i.e. doesn't animate).
368 ///
369 /// A 1 in bit position N indicates that this node doesn't animate any
370 /// targets of mask group N.
371 pub computed_masks: Vec<u64>,
372}
373
374/// A version of [`AnimationGraph`] suitable for serializing as an asset.
375///
376/// Animation nodes can refer to external animation clips, and the [`AssetId`]
377/// is typically not sufficient to identify the clips, since the
378/// [`bevy_asset::AssetServer`] assigns IDs in unpredictable ways. That fact
379/// motivates this type, which replaces the `Handle<AnimationClip>` with an
380/// asset path. Loading an animation graph via the [`bevy_asset::AssetServer`]
381/// actually loads a serialized instance of this type, as does serializing an
382/// [`AnimationGraph`] through `serde`.
383#[derive(Serialize, Deserialize)]
384pub struct SerializedAnimationGraph {
385 /// Corresponds to the `graph` field on [`AnimationGraph`].
386 pub graph: DiGraph<SerializedAnimationGraphNode, (), u32>,
387 /// Corresponds to the `root` field on [`AnimationGraph`].
388 pub root: NodeIndex,
389 /// Corresponds to the `mask_groups` field on [`AnimationGraph`].
390 pub mask_groups: HashMap<AnimationTargetId, AnimationMask>,
391}
392
393/// A version of [`AnimationGraphNode`] suitable for serializing as an asset.
394///
395/// See the comments in [`SerializedAnimationGraph`] for more information.
396#[derive(Serialize, Deserialize)]
397pub struct SerializedAnimationGraphNode {
398 /// Corresponds to the `node_type` field on [`AnimationGraphNode`].
399 pub node_type: SerializedAnimationNodeType,
400 /// Corresponds to the `mask` field on [`AnimationGraphNode`].
401 pub mask: AnimationMask,
402 /// Corresponds to the `weight` field on [`AnimationGraphNode`].
403 pub weight: f32,
404}
405
406/// A version of [`AnimationNodeType`] suitable for serializing as part of a
407/// [`SerializedAnimationGraphNode`] asset.
408#[derive(Serialize, Deserialize)]
409pub enum SerializedAnimationNodeType {
410 /// Corresponds to [`AnimationNodeType::Clip`].
411 Clip(MigrationSerializedAnimationClip),
412 /// Corresponds to [`AnimationNodeType::Blend`].
413 Blend,
414 /// Corresponds to [`AnimationNodeType::Add`].
415 Add,
416}
417
418/// A type to facilitate migration from the legacy format of [`SerializedAnimationGraph`] to the
419/// new format.
420///
421/// By using untagged serde deserialization, we can try to deserialize the modern form, then
422/// fallback to the legacy form. Users must migrate to the modern form by Bevy 0.18.
423// TODO: Delete this after Bevy 0.17.
424#[derive(Serialize, Deserialize)]
425#[serde(untagged)]
426pub enum MigrationSerializedAnimationClip {
427 /// This is the new type of this field.
428 Modern(AssetPath<'static>),
429 /// This is the legacy type of this field. Users must migrate away from this.
430 #[serde(skip_serializing)]
431 Legacy(SerializedAnimationClip),
432}
433
434/// The legacy form of serialized animation clips. This allows raw asset IDs to be deserialized.
435// TODO: Delete this after Bevy 0.17.
436#[derive(Deserialize)]
437pub enum SerializedAnimationClip {
438 /// Records an asset path.
439 AssetPath(AssetPath<'static>),
440 /// The fallback that records an asset ID.
441 ///
442 /// Because asset IDs can change, this should not be relied upon. Prefer to
443 /// use asset paths where possible.
444 AssetId(AssetId<AnimationClip>),
445}
446
447/// The type of an animation mask bitfield.
448///
449/// Bit N corresponds to mask group N.
450///
451/// Because this is a 64-bit value, there is currently a limitation of 64 mask
452/// groups per animation graph.
453pub type AnimationMask = u64;
454
455impl AnimationGraph {
456 /// Creates a new animation graph with a root node and no other nodes.
457 pub fn new() -> Self {
458 let mut graph = DiGraph::default();
459 let root = graph.add_node(AnimationGraphNode::default());
460 Self {
461 graph,
462 root,
463 mask_groups: HashMap::default(),
464 }
465 }
466
467 /// A convenience function for creating an [`AnimationGraph`] from a single
468 /// [`AnimationClip`].
469 ///
470 /// The clip will be a direct child of the root with weight 1.0. Both the
471 /// graph and the index of the added node are returned as a tuple.
472 pub fn from_clip(clip: Handle<AnimationClip>) -> (Self, AnimationNodeIndex) {
473 let mut graph = Self::new();
474 let node_index = graph.add_clip(clip, 1.0, graph.root);
475 (graph, node_index)
476 }
477
478 /// A convenience method to create an [`AnimationGraph`]s with an iterator
479 /// of clips.
480 ///
481 /// All of the animation clips will be direct children of the root with
482 /// weight 1.0.
483 ///
484 /// Returns the graph and indices of the new nodes.
485 pub fn from_clips<'a, I>(clips: I) -> (Self, Vec<AnimationNodeIndex>)
486 where
487 I: IntoIterator<Item = Handle<AnimationClip>>,
488 <I as IntoIterator>::IntoIter: 'a,
489 {
490 let mut graph = Self::new();
491 let indices = graph.add_clips(clips, 1.0, graph.root).collect();
492 (graph, indices)
493 }
494
495 /// Adds an [`AnimationClip`] to the animation graph with the given weight
496 /// and returns its index.
497 ///
498 /// The animation clip will be the child of the given parent. The resulting
499 /// node will have no mask.
500 pub fn add_clip(
501 &mut self,
502 clip: Handle<AnimationClip>,
503 weight: f32,
504 parent: AnimationNodeIndex,
505 ) -> AnimationNodeIndex {
506 let node_index = self.graph.add_node(AnimationGraphNode {
507 node_type: AnimationNodeType::Clip(clip),
508 mask: 0,
509 weight,
510 });
511 self.graph.add_edge(parent, node_index, ());
512 node_index
513 }
514
515 /// Adds an [`AnimationClip`] to the animation graph with the given weight
516 /// and mask, and returns its index.
517 ///
518 /// The animation clip will be the child of the given parent.
519 pub fn add_clip_with_mask(
520 &mut self,
521 clip: Handle<AnimationClip>,
522 mask: AnimationMask,
523 weight: f32,
524 parent: AnimationNodeIndex,
525 ) -> AnimationNodeIndex {
526 let node_index = self.graph.add_node(AnimationGraphNode {
527 node_type: AnimationNodeType::Clip(clip),
528 mask,
529 weight,
530 });
531 self.graph.add_edge(parent, node_index, ());
532 node_index
533 }
534
535 /// A convenience method to add multiple [`AnimationClip`]s to the animation
536 /// graph.
537 ///
538 /// All of the animation clips will have the same weight and will be
539 /// parented to the same node.
540 ///
541 /// Returns the indices of the new nodes.
542 pub fn add_clips<'a, I>(
543 &'a mut self,
544 clips: I,
545 weight: f32,
546 parent: AnimationNodeIndex,
547 ) -> impl Iterator<Item = AnimationNodeIndex> + 'a
548 where
549 I: IntoIterator<Item = Handle<AnimationClip>>,
550 <I as IntoIterator>::IntoIter: 'a,
551 {
552 clips
553 .into_iter()
554 .map(move |clip| self.add_clip(clip, weight, parent))
555 }
556
557 /// Adds a blend node to the animation graph with the given weight and
558 /// returns its index.
559 ///
560 /// The blend node will be placed under the supplied `parent` node. During
561 /// animation evaluation, the descendants of this blend node will have their
562 /// weights multiplied by the weight of the blend. The blend node will have
563 /// no mask.
564 pub fn add_blend(&mut self, weight: f32, parent: AnimationNodeIndex) -> AnimationNodeIndex {
565 let node_index = self.graph.add_node(AnimationGraphNode {
566 node_type: AnimationNodeType::Blend,
567 mask: 0,
568 weight,
569 });
570 self.graph.add_edge(parent, node_index, ());
571 node_index
572 }
573
574 /// Adds a blend node to the animation graph with the given weight and
575 /// returns its index.
576 ///
577 /// The blend node will be placed under the supplied `parent` node. During
578 /// animation evaluation, the descendants of this blend node will have their
579 /// weights multiplied by the weight of the blend. Neither this node nor its
580 /// descendants will affect animation targets that belong to mask groups not
581 /// in the given `mask`.
582 pub fn add_blend_with_mask(
583 &mut self,
584 mask: AnimationMask,
585 weight: f32,
586 parent: AnimationNodeIndex,
587 ) -> AnimationNodeIndex {
588 let node_index = self.graph.add_node(AnimationGraphNode {
589 node_type: AnimationNodeType::Blend,
590 mask,
591 weight,
592 });
593 self.graph.add_edge(parent, node_index, ());
594 node_index
595 }
596
597 /// Adds a blend node to the animation graph with the given weight and
598 /// returns its index.
599 ///
600 /// The blend node will be placed under the supplied `parent` node. During
601 /// animation evaluation, the descendants of this blend node will have their
602 /// weights multiplied by the weight of the blend. The blend node will have
603 /// no mask.
604 pub fn add_additive_blend(
605 &mut self,
606 weight: f32,
607 parent: AnimationNodeIndex,
608 ) -> AnimationNodeIndex {
609 let node_index = self.graph.add_node(AnimationGraphNode {
610 node_type: AnimationNodeType::Add,
611 mask: 0,
612 weight,
613 });
614 self.graph.add_edge(parent, node_index, ());
615 node_index
616 }
617
618 /// Adds a blend node to the animation graph with the given weight and
619 /// returns its index.
620 ///
621 /// The blend node will be placed under the supplied `parent` node. During
622 /// animation evaluation, the descendants of this blend node will have their
623 /// weights multiplied by the weight of the blend. Neither this node nor its
624 /// descendants will affect animation targets that belong to mask groups not
625 /// in the given `mask`.
626 pub fn add_additive_blend_with_mask(
627 &mut self,
628 mask: AnimationMask,
629 weight: f32,
630 parent: AnimationNodeIndex,
631 ) -> AnimationNodeIndex {
632 let node_index = self.graph.add_node(AnimationGraphNode {
633 node_type: AnimationNodeType::Add,
634 mask,
635 weight,
636 });
637 self.graph.add_edge(parent, node_index, ());
638 node_index
639 }
640
641 /// Adds an edge from the edge `from` to `to`, making `to` a child of
642 /// `from`.
643 ///
644 /// The behavior is unspecified if adding this produces a cycle in the
645 /// graph.
646 pub fn add_edge(&mut self, from: NodeIndex, to: NodeIndex) {
647 self.graph.add_edge(from, to, ());
648 }
649
650 /// Removes an edge between `from` and `to` if it exists.
651 ///
652 /// Returns true if the edge was successfully removed or false if no such
653 /// edge existed.
654 pub fn remove_edge(&mut self, from: NodeIndex, to: NodeIndex) -> bool {
655 self.graph
656 .find_edge(from, to)
657 .map(|edge| self.graph.remove_edge(edge))
658 .is_some()
659 }
660
661 /// Returns the [`AnimationGraphNode`] associated with the given index.
662 ///
663 /// If no node with the given index exists, returns `None`.
664 pub fn get(&self, animation: AnimationNodeIndex) -> Option<&AnimationGraphNode> {
665 self.graph.node_weight(animation)
666 }
667
668 /// Returns a mutable reference to the [`AnimationGraphNode`] associated
669 /// with the given index.
670 ///
671 /// If no node with the given index exists, returns `None`.
672 pub fn get_mut(&mut self, animation: AnimationNodeIndex) -> Option<&mut AnimationGraphNode> {
673 self.graph.node_weight_mut(animation)
674 }
675
676 /// Returns an iterator over the [`AnimationGraphNode`]s in this graph.
677 pub fn nodes(&self) -> impl Iterator<Item = AnimationNodeIndex> {
678 self.graph.node_indices()
679 }
680
681 /// Serializes the animation graph to the given [`Write`]r in RON format.
682 ///
683 /// If writing to a file, it can later be loaded with the
684 /// [`AnimationGraphAssetLoader`] to reconstruct the graph.
685 pub fn save<W>(&self, writer: &mut W) -> Result<(), AnimationGraphSaveError>
686 where
687 W: Write,
688 {
689 let mut ron_serializer = ron::ser::Serializer::new(writer, None)?;
690 let serialized_graph: SerializedAnimationGraph = self.clone().try_into()?;
691 Ok(serialized_graph.serialize(&mut ron_serializer)?)
692 }
693
694 /// Adds an animation target (bone) to the mask group with the given ID.
695 ///
696 /// Calling this method multiple times with the same animation target but
697 /// different mask groups will result in that target being added to all of
698 /// the specified groups.
699 pub fn add_target_to_mask_group(&mut self, target: AnimationTargetId, mask_group: u32) {
700 *self.mask_groups.entry(target).or_default() |= 1 << mask_group;
701 }
702}
703
704impl AnimationGraphNode {
705 /// Masks out the mask groups specified by the given `mask` bitfield.
706 ///
707 /// A 1 in bit position N causes this function to mask out mask group N, and
708 /// thus neither this node nor its descendants will animate any animation
709 /// targets that belong to group N.
710 pub fn add_mask(&mut self, mask: AnimationMask) -> &mut Self {
711 self.mask |= mask;
712 self
713 }
714
715 /// Unmasks the mask groups specified by the given `mask` bitfield.
716 ///
717 /// A 1 in bit position N causes this function to unmask mask group N, and
718 /// thus this node and its descendants will be allowed to animate animation
719 /// targets that belong to group N, unless another mask masks those targets
720 /// out.
721 pub fn remove_mask(&mut self, mask: AnimationMask) -> &mut Self {
722 self.mask &= !mask;
723 self
724 }
725
726 /// Masks out the single mask group specified by `group`.
727 ///
728 /// After calling this function, neither this node nor its descendants will
729 /// animate any animation targets that belong to the given `group`.
730 pub fn add_mask_group(&mut self, group: u32) -> &mut Self {
731 self.add_mask(1 << group)
732 }
733
734 /// Unmasks the single mask group specified by `group`.
735 ///
736 /// After calling this function, this node and its descendants will be
737 /// allowed to animate animation targets that belong to the given `group`,
738 /// unless another mask masks those targets out.
739 pub fn remove_mask_group(&mut self, group: u32) -> &mut Self {
740 self.remove_mask(1 << group)
741 }
742}
743
744impl Index<AnimationNodeIndex> for AnimationGraph {
745 type Output = AnimationGraphNode;
746
747 fn index(&self, index: AnimationNodeIndex) -> &Self::Output {
748 &self.graph[index]
749 }
750}
751
752impl IndexMut<AnimationNodeIndex> for AnimationGraph {
753 fn index_mut(&mut self, index: AnimationNodeIndex) -> &mut Self::Output {
754 &mut self.graph[index]
755 }
756}
757
758impl Default for AnimationGraphNode {
759 fn default() -> Self {
760 Self {
761 node_type: Default::default(),
762 mask: 0,
763 weight: 1.0,
764 }
765 }
766}
767
768impl Default for AnimationGraph {
769 fn default() -> Self {
770 Self::new()
771 }
772}
773
774impl AssetLoader for AnimationGraphAssetLoader {
775 type Asset = AnimationGraph;
776
777 type Settings = ();
778
779 type Error = AnimationGraphLoadError;
780
781 async fn load(
782 &self,
783 reader: &mut dyn Reader,
784 _: &Self::Settings,
785 load_context: &mut LoadContext<'_>,
786 ) -> Result<Self::Asset, Self::Error> {
787 let mut bytes = Vec::new();
788 reader.read_to_end(&mut bytes).await?;
789
790 // Deserialize a `SerializedAnimationGraph` directly, so that we can
791 // get the list of the animation clips it refers to and load them.
792 let mut deserializer = ron::de::Deserializer::from_bytes(&bytes)?;
793 let serialized_animation_graph = SerializedAnimationGraph::deserialize(&mut deserializer)
794 .map_err(|err| deserializer.span_error(err))?;
795
796 // Load all `AssetPath`s to convert from a `SerializedAnimationGraph` to a real
797 // `AnimationGraph`. This is effectively a `DiGraph::map`, but this allows us to return
798 // errors.
799 let mut animation_graph = DiGraph::with_capacity(
800 serialized_animation_graph.graph.node_count(),
801 serialized_animation_graph.graph.edge_count(),
802 );
803
804 let mut already_warned = false;
805 for serialized_node in serialized_animation_graph.graph.node_weights() {
806 animation_graph.add_node(AnimationGraphNode {
807 node_type: match serialized_node.node_type {
808 SerializedAnimationNodeType::Clip(ref clip) => match clip {
809 MigrationSerializedAnimationClip::Modern(path) => {
810 AnimationNodeType::Clip(load_context.load(path.clone()))
811 }
812 MigrationSerializedAnimationClip::Legacy(
813 SerializedAnimationClip::AssetPath(path),
814 ) => {
815 if !already_warned {
816 let path = load_context.asset_path();
817 warn!(
818 "Loaded an AnimationGraph asset at \"{path}\" which contains a \
819 legacy-style SerializedAnimationClip. Please re-save the asset \
820 using AnimationGraph::save to automatically migrate to the new \
821 format"
822 );
823 already_warned = true;
824 }
825 AnimationNodeType::Clip(load_context.load(path.clone()))
826 }
827 MigrationSerializedAnimationClip::Legacy(
828 SerializedAnimationClip::AssetId(_),
829 ) => {
830 return Err(AnimationGraphLoadError::GraphContainsLegacyAssetId);
831 }
832 },
833 SerializedAnimationNodeType::Blend => AnimationNodeType::Blend,
834 SerializedAnimationNodeType::Add => AnimationNodeType::Add,
835 },
836 mask: serialized_node.mask,
837 weight: serialized_node.weight,
838 });
839 }
840 for edge in serialized_animation_graph.graph.raw_edges() {
841 animation_graph.add_edge(edge.source(), edge.target(), ());
842 }
843 Ok(AnimationGraph {
844 graph: animation_graph,
845 root: serialized_animation_graph.root,
846 mask_groups: serialized_animation_graph.mask_groups,
847 })
848 }
849
850 fn extensions(&self) -> &[&str] {
851 &["animgraph", "animgraph.ron"]
852 }
853}
854
855impl TryFrom<AnimationGraph> for SerializedAnimationGraph {
856 type Error = NonPathHandleError;
857
858 fn try_from(animation_graph: AnimationGraph) -> Result<Self, NonPathHandleError> {
859 // Convert all the `Handle<AnimationClip>` to AssetPath, so that
860 // `AnimationGraphAssetLoader` can load them. This is effectively just doing a
861 // `DiGraph::map`, except we need to return an error if any handles aren't associated to a
862 // path.
863 let mut serialized_graph = DiGraph::with_capacity(
864 animation_graph.graph.node_count(),
865 animation_graph.graph.edge_count(),
866 );
867 for node in animation_graph.graph.node_weights() {
868 serialized_graph.add_node(SerializedAnimationGraphNode {
869 weight: node.weight,
870 mask: node.mask,
871 node_type: match node.node_type {
872 AnimationNodeType::Clip(ref clip) => match clip.path() {
873 Some(path) => SerializedAnimationNodeType::Clip(
874 MigrationSerializedAnimationClip::Modern(path.clone()),
875 ),
876 None => return Err(NonPathHandleError),
877 },
878 AnimationNodeType::Blend => SerializedAnimationNodeType::Blend,
879 AnimationNodeType::Add => SerializedAnimationNodeType::Add,
880 },
881 });
882 }
883 for edge in animation_graph.graph.raw_edges() {
884 serialized_graph.add_edge(edge.source(), edge.target(), ());
885 }
886 Ok(Self {
887 graph: serialized_graph,
888 root: animation_graph.root,
889 mask_groups: animation_graph.mask_groups,
890 })
891 }
892}
893
894/// Error for when only path [`Handle`]s are supported.
895#[derive(Error, Debug)]
896#[error("AnimationGraph contains a handle to an AnimationClip that does not correspond to an asset path")]
897pub struct NonPathHandleError;
898
899/// A system that creates, updates, and removes [`ThreadedAnimationGraph`]
900/// structures for every changed [`AnimationGraph`].
901///
902/// The [`ThreadedAnimationGraph`] contains acceleration structures that allow
903/// for quick evaluation of that graph's animations.
904pub(crate) fn thread_animation_graphs(
905 mut threaded_animation_graphs: ResMut<ThreadedAnimationGraphs>,
906 animation_graphs: Res<Assets<AnimationGraph>>,
907 mut animation_graph_asset_events: MessageReader<AssetEvent<AnimationGraph>>,
908) {
909 for animation_graph_asset_event in animation_graph_asset_events.read() {
910 match *animation_graph_asset_event {
911 AssetEvent::Added { id }
912 | AssetEvent::Modified { id }
913 | AssetEvent::LoadedWithDependencies { id } => {
914 // Fetch the animation graph.
915 let Some(animation_graph) = animation_graphs.get(id) else {
916 continue;
917 };
918
919 // Reuse the allocation if possible.
920 let mut threaded_animation_graph =
921 threaded_animation_graphs.0.remove(&id).unwrap_or_default();
922 threaded_animation_graph.clear();
923
924 // Recursively thread the graph in postorder.
925 threaded_animation_graph.init(animation_graph);
926 threaded_animation_graph.build_from(
927 &animation_graph.graph,
928 animation_graph.root,
929 0,
930 );
931
932 // Write in the threaded graph.
933 threaded_animation_graphs
934 .0
935 .insert(id, threaded_animation_graph);
936 }
937
938 AssetEvent::Removed { id } => {
939 threaded_animation_graphs.0.remove(&id);
940 }
941 AssetEvent::Unused { .. } => {}
942 }
943 }
944}
945
946impl ThreadedAnimationGraph {
947 /// Removes all the data in this [`ThreadedAnimationGraph`], keeping the
948 /// memory around for later reuse.
949 fn clear(&mut self) {
950 self.threaded_graph.clear();
951 self.sorted_edge_ranges.clear();
952 self.sorted_edges.clear();
953 }
954
955 /// Prepares the [`ThreadedAnimationGraph`] for recursion.
956 fn init(&mut self, animation_graph: &AnimationGraph) {
957 let node_count = animation_graph.graph.node_count();
958 let edge_count = animation_graph.graph.edge_count();
959
960 self.threaded_graph.reserve(node_count);
961 self.sorted_edges.reserve(edge_count);
962
963 self.sorted_edge_ranges.clear();
964 self.sorted_edge_ranges
965 .extend(iter::repeat_n(0..0, node_count));
966
967 self.computed_masks.clear();
968 self.computed_masks.extend(iter::repeat_n(0, node_count));
969 }
970
971 /// Recursively constructs the [`ThreadedAnimationGraph`] for the subtree
972 /// rooted at the given node.
973 ///
974 /// `mask` specifies the computed mask of the parent node. (It could be
975 /// fetched from the [`Self::computed_masks`] field, but we pass it
976 /// explicitly as a micro-optimization.)
977 fn build_from(
978 &mut self,
979 graph: &AnimationDiGraph,
980 node_index: AnimationNodeIndex,
981 mut mask: u64,
982 ) {
983 // Accumulate the mask.
984 mask |= graph.node_weight(node_index).unwrap().mask;
985 self.computed_masks[node_index.index()] = mask;
986
987 // Gather up the indices of our children, and sort them.
988 let mut kids: SmallVec<[AnimationNodeIndex; 8]> = graph
989 .neighbors_directed(node_index, Direction::Outgoing)
990 .collect();
991 kids.sort_unstable();
992
993 // Write in the list of kids.
994 self.sorted_edge_ranges[node_index.index()] =
995 (self.sorted_edges.len() as u32)..((self.sorted_edges.len() + kids.len()) as u32);
996 self.sorted_edges.extend_from_slice(&kids);
997
998 // Recurse. (This is a postorder traversal.)
999 for kid in kids.into_iter().rev() {
1000 self.build_from(graph, kid, mask);
1001 }
1002
1003 // Finally, push our index.
1004 self.threaded_graph.push(node_index);
1005 }
1006}