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