@@ -11,11 +11,12 @@ use bevy_ecs::{
1111 change_detection:: DetectChanges ,
1212 entity:: Entity ,
1313 prelude:: Component ,
14+ query:: With ,
1415 reflect:: ReflectComponent ,
1516 schedule:: IntoSystemDescriptor ,
1617 system:: { Query , Res } ,
1718} ;
18- use bevy_hierarchy:: Children ;
19+ use bevy_hierarchy:: { Children , Parent } ;
1920use bevy_math:: { Quat , Vec3 } ;
2021use bevy_reflect:: { FromReflect , Reflect , TypeUuid } ;
2122use bevy_time:: Time ;
@@ -63,17 +64,34 @@ pub struct EntityPath {
6364#[ derive( Reflect , FromReflect , Clone , TypeUuid , Debug , Default ) ]
6465#[ uuid = "d81b7179-0448-4eb0-89fe-c067222725bf" ]
6566pub struct AnimationClip {
66- curves : HashMap < EntityPath , Vec < VariableCurve > > ,
67+ curves : Vec < Vec < VariableCurve > > ,
68+ paths : HashMap < EntityPath , usize > ,
6769 duration : f32 ,
6870}
6971
7072impl AnimationClip {
7173 #[ 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 > > {
7476 & self . curves
7577 }
7678
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+
7795 /// Duration of the clip, represented in seconds
7896 #[ inline]
7997 pub fn duration ( & self ) -> f32 {
@@ -86,7 +104,13 @@ impl AnimationClip {
86104 self . duration = self
87105 . duration
88106 . 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+ }
90114 }
91115}
92116
@@ -99,6 +123,7 @@ pub struct AnimationPlayer {
99123 speed : f32 ,
100124 elapsed : f32 ,
101125 animation_clip : Handle < AnimationClip > ,
126+ path_cache : Vec < Vec < Option < Entity > > > ,
102127}
103128
104129impl Default for AnimationPlayer {
@@ -109,6 +134,7 @@ impl Default for AnimationPlayer {
109134 speed : 1.0 ,
110135 elapsed : 0.0 ,
111136 animation_clip : Default :: default ( ) ,
137+ path_cache : Vec :: new ( ) ,
112138 }
113139 }
114140}
@@ -181,117 +207,180 @@ impl AnimationPlayer {
181207 }
182208}
183209
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+
184274/// System that will play all animations, using any entity with a [`AnimationPlayer`]
185275/// and a [`Handle<AnimationClip>`] as an animation root
186276pub fn animation_player (
187277 time : Res < Time > ,
188278 animations : Res < Assets < AnimationClip > > ,
189- mut animation_players : Query < ( Entity , & mut AnimationPlayer ) > ,
190- names : Query < & Name > ,
191- mut transforms : Query < & mut Transform > ,
192279 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 ) > ,
193284) {
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 ] ;
227333 }
334+ Keyframes :: Scale ( keyframes) => transform. scale = keyframes[ 0 ] ,
228335 }
229- if !found {
230- warn ! ( "Entity not found for path {:?} on part {:?}" , path, part) ;
231- continue ' entity;
232- }
336+ continue ;
233337 }
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- }
247338
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;
289363 }
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;
290379 }
291380 }
292381 }
293382 }
294- }
383+ } ) ;
295384}
296385
297386/// Adds animation support to an app
0 commit comments