@@ -11,11 +11,12 @@ use bevy_ecs::{
11
11
change_detection:: DetectChanges ,
12
12
entity:: Entity ,
13
13
prelude:: Component ,
14
+ query:: With ,
14
15
reflect:: ReflectComponent ,
15
16
schedule:: IntoSystemDescriptor ,
16
17
system:: { Query , Res } ,
17
18
} ;
18
- use bevy_hierarchy:: Children ;
19
+ use bevy_hierarchy:: { Children , Parent } ;
19
20
use bevy_math:: { Quat , Vec3 } ;
20
21
use bevy_reflect:: { FromReflect , Reflect , TypeUuid } ;
21
22
use bevy_time:: Time ;
@@ -63,17 +64,34 @@ pub struct EntityPath {
63
64
#[ derive( Reflect , FromReflect , Clone , TypeUuid , Debug , Default ) ]
64
65
#[ uuid = "d81b7179-0448-4eb0-89fe-c067222725bf" ]
65
66
pub struct AnimationClip {
66
- curves : HashMap < EntityPath , Vec < VariableCurve > > ,
67
+ curves : Vec < Vec < VariableCurve > > ,
68
+ paths : HashMap < EntityPath , usize > ,
67
69
duration : f32 ,
68
70
}
69
71
70
72
impl AnimationClip {
71
73
#[ inline]
72
- /// Hashmap of the [`VariableCurve`]s per [`EntityPath`] .
73
- pub fn curves ( & self ) -> & HashMap < EntityPath , Vec < VariableCurve > > {
74
+ /// [`VariableCurve`]s for each bone. Indexed by the bone ID .
75
+ pub fn curves ( & self ) -> & Vec < Vec < VariableCurve > > {
74
76
& self . curves
75
77
}
76
78
79
+ /// Gets the curves for a bone.
80
+ ///
81
+ /// Returns `None` if the bone is invalid.
82
+ #[ inline]
83
+ pub fn get_curves ( & self , bone_id : usize ) -> Option < & ' _ Vec < VariableCurve > > {
84
+ self . curves . get ( bone_id)
85
+ }
86
+
87
+ /// Gets the curves by it's [`EntityPath`].
88
+ ///
89
+ /// Returns `None` if the bone is invalid.
90
+ #[ inline]
91
+ pub fn get_curves_by_path ( & self , path : & EntityPath ) -> Option < & ' _ Vec < VariableCurve > > {
92
+ self . paths . get ( path) . and_then ( |id| self . curves . get ( * id) )
93
+ }
94
+
77
95
/// Duration of the clip, represented in seconds
78
96
#[ inline]
79
97
pub fn duration ( & self ) -> f32 {
@@ -86,7 +104,13 @@ impl AnimationClip {
86
104
self . duration = self
87
105
. duration
88
106
. max ( * curve. keyframe_timestamps . last ( ) . unwrap_or ( & 0.0 ) ) ;
89
- self . curves . entry ( path) . or_default ( ) . push ( curve) ;
107
+ if let Some ( bone_id) = self . paths . get ( & path) {
108
+ self . curves [ * bone_id] . push ( curve) ;
109
+ } else {
110
+ let idx = self . curves . len ( ) ;
111
+ self . curves . push ( vec ! [ curve] ) ;
112
+ self . paths . insert ( path, idx) ;
113
+ }
90
114
}
91
115
}
92
116
@@ -99,6 +123,7 @@ pub struct AnimationPlayer {
99
123
speed : f32 ,
100
124
elapsed : f32 ,
101
125
animation_clip : Handle < AnimationClip > ,
126
+ path_cache : Vec < Vec < Option < Entity > > > ,
102
127
}
103
128
104
129
impl Default for AnimationPlayer {
@@ -109,6 +134,7 @@ impl Default for AnimationPlayer {
109
134
speed : 1.0 ,
110
135
elapsed : 0.0 ,
111
136
animation_clip : Default :: default ( ) ,
137
+ path_cache : Vec :: new ( ) ,
112
138
}
113
139
}
114
140
}
@@ -181,117 +207,180 @@ impl AnimationPlayer {
181
207
}
182
208
}
183
209
210
+ fn find_bone (
211
+ root : Entity ,
212
+ path : & EntityPath ,
213
+ children : & Query < & Children > ,
214
+ names : & Query < & Name > ,
215
+ path_cache : & mut Vec < Option < Entity > > ,
216
+ ) -> Option < Entity > {
217
+ // PERF: finding the target entity can be optimised
218
+ let mut current_entity = root;
219
+ path_cache. resize ( path. parts . len ( ) , None ) ;
220
+ // Ignore the first name, it is the root node which we already have
221
+ for ( idx, part) in path. parts . iter ( ) . enumerate ( ) . skip ( 1 ) {
222
+ let mut found = false ;
223
+ let children = children. get ( current_entity) . ok ( ) ?;
224
+ if let Some ( cached) = path_cache[ idx] {
225
+ if children. contains ( & cached) {
226
+ if let Ok ( name) = names. get ( cached) {
227
+ if name == part {
228
+ current_entity = cached;
229
+ found = true ;
230
+ }
231
+ }
232
+ }
233
+ }
234
+ if !found {
235
+ for child in children. deref ( ) {
236
+ if let Ok ( name) = names. get ( * child) {
237
+ if name == part {
238
+ // Found a children with the right name, continue to the next part
239
+ current_entity = * child;
240
+ path_cache[ idx] = Some ( * child) ;
241
+ found = true ;
242
+ break ;
243
+ }
244
+ }
245
+ }
246
+ }
247
+ if !found {
248
+ warn ! ( "Entity not found for path {:?} on part {:?}" , path, part) ;
249
+ return None ;
250
+ }
251
+ }
252
+ Some ( current_entity)
253
+ }
254
+
255
+ /// Verify that there are no ancestors of a given entity that have an `AnimationPlayer`.
256
+ fn verify_no_ancestor_player (
257
+ player_parent : Option < & Parent > ,
258
+ parents : & Query < ( Option < With < AnimationPlayer > > , Option < & Parent > ) > ,
259
+ ) -> bool {
260
+ let Some ( mut current) = player_parent. map ( Parent :: get) else { return true } ;
261
+ loop {
262
+ let Ok ( ( maybe_player, parent) ) = parents. get ( current) else { return true } ;
263
+ if maybe_player. is_some ( ) {
264
+ return false ;
265
+ }
266
+ if let Some ( parent) = parent {
267
+ current = parent. get ( ) ;
268
+ } else {
269
+ return true ;
270
+ }
271
+ }
272
+ }
273
+
184
274
/// System that will play all animations, using any entity with a [`AnimationPlayer`]
185
275
/// and a [`Handle<AnimationClip>`] as an animation root
186
276
pub fn animation_player (
187
277
time : Res < Time > ,
188
278
animations : Res < Assets < AnimationClip > > ,
189
- mut animation_players : Query < ( Entity , & mut AnimationPlayer ) > ,
190
- names : Query < & Name > ,
191
- mut transforms : Query < & mut Transform > ,
192
279
children : Query < & Children > ,
280
+ names : Query < & Name > ,
281
+ transforms : Query < & mut Transform > ,
282
+ parents : Query < ( Option < With < AnimationPlayer > > , Option < & Parent > ) > ,
283
+ mut animation_players : Query < ( Entity , Option < & Parent > , & mut AnimationPlayer ) > ,
193
284
) {
194
- for ( entity, mut player) in & mut animation_players {
195
- if let Some ( animation_clip) = animations. get ( & player. animation_clip ) {
196
- // Continue if paused unless the `AnimationPlayer` was changed
197
- // This allow the animation to still be updated if the player.elapsed field was manually updated in pause
198
- if player. paused && !player. is_changed ( ) {
199
- continue ;
200
- }
201
- if !player. paused {
202
- player. elapsed += time. delta_seconds ( ) * player. speed ;
203
- }
204
- let mut elapsed = player. elapsed ;
205
- if player. repeat {
206
- elapsed %= animation_clip. duration ;
207
- }
208
- if elapsed < 0.0 {
209
- elapsed += animation_clip. duration ;
210
- }
211
- ' entity: for ( path, curves) in & animation_clip. curves {
212
- // PERF: finding the target entity can be optimised
213
- let mut current_entity = entity;
214
- // Ignore the first name, it is the root node which we already have
215
- for part in path. parts . iter ( ) . skip ( 1 ) {
216
- let mut found = false ;
217
- if let Ok ( children) = children. get ( current_entity) {
218
- for child in children. deref ( ) {
219
- if let Ok ( name) = names. get ( * child) {
220
- if name == part {
221
- // Found a children with the right name, continue to the next part
222
- current_entity = * child;
223
- found = true ;
224
- break ;
225
- }
226
- }
285
+ animation_players. par_for_each_mut ( 10 , |( root, maybe_parent, mut player) | {
286
+ let Some ( animation_clip) = animations. get ( & player. animation_clip ) else { return } ;
287
+ // Continue if paused unless the `AnimationPlayer` was changed
288
+ // This allow the animation to still be updated if the player.elapsed field was manually updated in pause
289
+ if player. paused && !player. is_changed ( ) {
290
+ return ;
291
+ }
292
+ if !player. paused {
293
+ player. elapsed += time. delta_seconds ( ) * player. speed ;
294
+ }
295
+ let mut elapsed = player. elapsed ;
296
+ if player. repeat {
297
+ elapsed %= animation_clip. duration ;
298
+ }
299
+ if elapsed < 0.0 {
300
+ elapsed += animation_clip. duration ;
301
+ }
302
+ if player. path_cache . len ( ) != animation_clip. paths . len ( ) {
303
+ player. path_cache = vec ! [ Vec :: new( ) ; animation_clip. paths. len( ) ] ;
304
+ }
305
+ if !verify_no_ancestor_player ( maybe_parent, & parents) {
306
+ warn ! ( "Animation player on {:?} has a conflicting animation player on an ancestor. Cannot safely animate." , root) ;
307
+ return ;
308
+ }
309
+ for ( path, bone_id) in & animation_clip. paths {
310
+ let cached_path = & mut player. path_cache [ * bone_id] ;
311
+ let curves = animation_clip. get_curves ( * bone_id) . unwrap ( ) ;
312
+ let Some ( target) = find_bone ( root, path, & children, & names, cached_path) else { continue } ;
313
+ // SAFETY: The verify_no_ancestor_player check above ensures that two animation players cannot alias
314
+ // any of their descendant Transforms.
315
+ //
316
+ // The system scheduler prevents any other system from mutating Transforms at the same time,
317
+ // so the only way this fetch can alias is if two AnimationPlayers are targetting the same bone.
318
+ // This can only happen if there are two or more AnimationPlayers are ancestors to the same
319
+ // entities. By verifying that there is no other AnimationPlayer in the ancestors of a
320
+ // running AnimationPlayer before animating any entity, this fetch cannot alias.
321
+ //
322
+ // This means only the AnimationPlayers closest to the root of the hierarchy will be able
323
+ // to run their animation. Any players in the children or descendants will log a warning
324
+ // and do nothing.
325
+ let Ok ( mut transform) = ( unsafe { transforms. get_unchecked ( target) } ) else { continue } ;
326
+ for curve in curves {
327
+ // Some curves have only one keyframe used to set a transform
328
+ if curve. keyframe_timestamps . len ( ) == 1 {
329
+ match & curve. keyframes {
330
+ Keyframes :: Rotation ( keyframes) => transform. rotation = keyframes[ 0 ] ,
331
+ Keyframes :: Translation ( keyframes) => {
332
+ transform. translation = keyframes[ 0 ] ;
227
333
}
334
+ Keyframes :: Scale ( keyframes) => transform. scale = keyframes[ 0 ] ,
228
335
}
229
- if !found {
230
- warn ! ( "Entity not found for path {:?} on part {:?}" , path, part) ;
231
- continue ' entity;
232
- }
336
+ continue ;
233
337
}
234
- if let Ok ( mut transform) = transforms. get_mut ( current_entity) {
235
- for curve in curves {
236
- // Some curves have only one keyframe used to set a transform
237
- if curve. keyframe_timestamps . len ( ) == 1 {
238
- match & curve. keyframes {
239
- Keyframes :: Rotation ( keyframes) => transform. rotation = keyframes[ 0 ] ,
240
- Keyframes :: Translation ( keyframes) => {
241
- transform. translation = keyframes[ 0 ] ;
242
- }
243
- Keyframes :: Scale ( keyframes) => transform. scale = keyframes[ 0 ] ,
244
- }
245
- continue ;
246
- }
247
338
248
- // Find the current keyframe
249
- // PERF: finding the current keyframe can be optimised
250
- let step_start = match curve
251
- . keyframe_timestamps
252
- . binary_search_by ( |probe| probe. partial_cmp ( & elapsed) . unwrap ( ) )
253
- {
254
- Ok ( n) if n >= curve. keyframe_timestamps . len ( ) - 1 => continue , // this curve is finished
255
- Ok ( i) => i,
256
- Err ( 0 ) => continue , // this curve isn't started yet
257
- Err ( n) if n > curve. keyframe_timestamps . len ( ) - 1 => continue , // this curve is finished
258
- Err ( i) => i - 1 ,
259
- } ;
260
- let ts_start = curve. keyframe_timestamps [ step_start] ;
261
- let ts_end = curve. keyframe_timestamps [ step_start + 1 ] ;
262
- let lerp = ( elapsed - ts_start) / ( ts_end - ts_start) ;
263
-
264
- // Apply the keyframe
265
- match & curve. keyframes {
266
- Keyframes :: Rotation ( keyframes) => {
267
- let rot_start = keyframes[ step_start] ;
268
- let mut rot_end = keyframes[ step_start + 1 ] ;
269
- // Choose the smallest angle for the rotation
270
- if rot_end. dot ( rot_start) < 0.0 {
271
- rot_end = -rot_end;
272
- }
273
- // Rotations are using a spherical linear interpolation
274
- transform. rotation =
275
- rot_start. normalize ( ) . slerp ( rot_end. normalize ( ) , lerp) ;
276
- }
277
- Keyframes :: Translation ( keyframes) => {
278
- let translation_start = keyframes[ step_start] ;
279
- let translation_end = keyframes[ step_start + 1 ] ;
280
- let result = translation_start. lerp ( translation_end, lerp) ;
281
- transform. translation = result;
282
- }
283
- Keyframes :: Scale ( keyframes) => {
284
- let scale_start = keyframes[ step_start] ;
285
- let scale_end = keyframes[ step_start + 1 ] ;
286
- let result = scale_start. lerp ( scale_end, lerp) ;
287
- transform. scale = result;
288
- }
339
+ // Find the current keyframe
340
+ // PERF: finding the current keyframe can be optimised
341
+ let step_start = match curve
342
+ . keyframe_timestamps
343
+ . binary_search_by ( |probe| probe. partial_cmp ( & elapsed) . unwrap ( ) )
344
+ {
345
+ Ok ( n) if n >= curve. keyframe_timestamps . len ( ) - 1 => continue , // this curve is finished
346
+ Ok ( i) => i,
347
+ Err ( 0 ) => continue , // this curve isn't started yet
348
+ Err ( n) if n > curve. keyframe_timestamps . len ( ) - 1 => continue , // this curve is finished
349
+ Err ( i) => i - 1 ,
350
+ } ;
351
+ let ts_start = curve. keyframe_timestamps [ step_start] ;
352
+ let ts_end = curve. keyframe_timestamps [ step_start + 1 ] ;
353
+ let lerp = ( elapsed - ts_start) / ( ts_end - ts_start) ;
354
+
355
+ // Apply the keyframe
356
+ match & curve. keyframes {
357
+ Keyframes :: Rotation ( keyframes) => {
358
+ let rot_start = keyframes[ step_start] ;
359
+ let mut rot_end = keyframes[ step_start + 1 ] ;
360
+ // Choose the smallest angle for the rotation
361
+ if rot_end. dot ( rot_start) < 0.0 {
362
+ rot_end = -rot_end;
289
363
}
364
+ // Rotations are using a spherical linear interpolation
365
+ transform. rotation =
366
+ rot_start. normalize ( ) . slerp ( rot_end. normalize ( ) , lerp) ;
367
+ }
368
+ Keyframes :: Translation ( keyframes) => {
369
+ let translation_start = keyframes[ step_start] ;
370
+ let translation_end = keyframes[ step_start + 1 ] ;
371
+ let result = translation_start. lerp ( translation_end, lerp) ;
372
+ transform. translation = result;
373
+ }
374
+ Keyframes :: Scale ( keyframes) => {
375
+ let scale_start = keyframes[ step_start] ;
376
+ let scale_end = keyframes[ step_start + 1 ] ;
377
+ let result = scale_start. lerp ( scale_end, lerp) ;
378
+ transform. scale = result;
290
379
}
291
380
}
292
381
}
293
382
}
294
- }
383
+ } ) ;
295
384
}
296
385
297
386
/// Adds animation support to an app
0 commit comments