@@ -21,7 +21,8 @@ use bevy_utils::{tracing::warn, HashMap};
21
21
pub mod prelude {
22
22
#[ doc( hidden) ]
23
23
pub use crate :: {
24
- AnimationClip , AnimationPlayer , AnimationPlugin , EntityPath , Keyframes , VariableCurve ,
24
+ AnimationClip , AnimationPlayer , AnimationPlugin , EntityPath , Interpolation , Keyframes ,
25
+ VariableCurve ,
25
26
} ;
26
27
}
27
28
@@ -53,7 +54,27 @@ pub struct VariableCurve {
53
54
/// Timestamp for each of the keyframes.
54
55
pub keyframe_timestamps : Vec < f32 > ,
55
56
/// List of the keyframes.
57
+ ///
58
+ /// The representation will depend on the interpolation type of this curve:
59
+ ///
60
+ /// - for `Interpolation::Step` and `Interpolation::Linear`, each keyframe is a single value
61
+ /// - for `Interpolation::CubicSpline`, each keyframe is made of three values for `tangent_in`,
62
+ /// `keyframe_value` and `tangent_out`
56
63
pub keyframes : Keyframes ,
64
+ /// Interpolation method to use between keyframes.
65
+ pub interpolation : Interpolation ,
66
+ }
67
+
68
+ /// Interpolation method to use between keyframes.
69
+ #[ derive( Reflect , Clone , Debug ) ]
70
+ pub enum Interpolation {
71
+ /// Linear interpolation between the two closest keyframes.
72
+ Linear ,
73
+ /// Step interpolation, the value of the start keyframe is used.
74
+ Step ,
75
+ /// Cubic spline interpolation. The value of the two closest keyframes is used, with the out
76
+ /// tangent of the start keyframe and the in tangent of the end keyframe.
77
+ CubicSpline ,
57
78
}
58
79
59
80
/// Path to an entity, with [`Name`]s. Each entity in a path must have a name.
@@ -591,6 +612,18 @@ fn get_keyframe(target_count: usize, keyframes: &[f32], key_index: usize) -> &[f
591
612
& keyframes[ start..end]
592
613
}
593
614
615
+ // Helper macro for cubic spline interpolation
616
+ // it needs to work on `f32`, `Vec3` and `Quat`
617
+ // TODO: replace by a function if the proper trait bounds can be figured out
618
+ macro_rules! cubic_spline_interpolation {
619
+ ( $value_start: expr, $tangent_out_start: expr, $tangent_in_end: expr, $value_end: expr, $lerp: expr, $step_duration: expr, ) => {
620
+ $value_start * ( 2.0 * $lerp. powi( 3 ) - 3.0 * $lerp. powi( 2 ) + 1.0 )
621
+ + $tangent_out_start * ( $step_duration) * ( $lerp. powi( 3 ) - 2.0 * $lerp. powi( 2 ) + $lerp)
622
+ + $value_end * ( -2.0 * $lerp. powi( 3 ) + 3.0 * $lerp. powi( 2 ) )
623
+ + $tangent_in_end * ( $step_duration) * ( $lerp. powi( 3 ) - $lerp. powi( 2 ) )
624
+ } ;
625
+ }
626
+
594
627
#[ allow( clippy:: too_many_arguments) ]
595
628
fn apply_animation (
596
629
weight : f32 ,
@@ -645,7 +678,7 @@ fn apply_animation(
645
678
continue ;
646
679
} ;
647
680
// SAFETY: As above, there can't be other AnimationPlayers with this target so this fetch can't alias
648
- let mut morphs = unsafe { morphs. get_unchecked ( target) } ;
681
+ let mut morphs = unsafe { morphs. get_unchecked ( target) } . ok ( ) ;
649
682
for curve in curves {
650
683
// Some curves have only one keyframe used to set a transform
651
684
if curve. keyframe_timestamps . len ( ) == 1 {
@@ -661,7 +694,7 @@ fn apply_animation(
661
694
transform. scale = transform. scale . lerp ( keyframes[ 0 ] , weight) ;
662
695
}
663
696
Keyframes :: Weights ( keyframes) => {
664
- if let Ok ( morphs) = & mut morphs {
697
+ if let Some ( morphs) = & mut morphs {
665
698
let target_count = morphs. weights ( ) . len ( ) ;
666
699
lerp_morph_weights (
667
700
morphs. weights_mut ( ) ,
@@ -690,44 +723,15 @@ fn apply_animation(
690
723
let ts_end = curve. keyframe_timestamps [ step_start + 1 ] ;
691
724
let lerp = ( animation. seek_time - ts_start) / ( ts_end - ts_start) ;
692
725
693
- // Apply the keyframe
694
- match & curve. keyframes {
695
- Keyframes :: Rotation ( keyframes) => {
696
- let rot_start = keyframes[ step_start] ;
697
- let mut rot_end = keyframes[ step_start + 1 ] ;
698
- // Choose the smallest angle for the rotation
699
- if rot_end. dot ( rot_start) < 0.0 {
700
- rot_end = -rot_end;
701
- }
702
- // Rotations are using a spherical linear interpolation
703
- let rot = rot_start. normalize ( ) . slerp ( rot_end. normalize ( ) , lerp) ;
704
- transform. rotation = transform. rotation . slerp ( rot, weight) ;
705
- }
706
- Keyframes :: Translation ( keyframes) => {
707
- let translation_start = keyframes[ step_start] ;
708
- let translation_end = keyframes[ step_start + 1 ] ;
709
- let result = translation_start. lerp ( translation_end, lerp) ;
710
- transform. translation = transform. translation . lerp ( result, weight) ;
711
- }
712
- Keyframes :: Scale ( keyframes) => {
713
- let scale_start = keyframes[ step_start] ;
714
- let scale_end = keyframes[ step_start + 1 ] ;
715
- let result = scale_start. lerp ( scale_end, lerp) ;
716
- transform. scale = transform. scale . lerp ( result, weight) ;
717
- }
718
- Keyframes :: Weights ( keyframes) => {
719
- if let Ok ( morphs) = & mut morphs {
720
- let target_count = morphs. weights ( ) . len ( ) ;
721
- let morph_start = get_keyframe ( target_count, keyframes, step_start) ;
722
- let morph_end = get_keyframe ( target_count, keyframes, step_start + 1 ) ;
723
- let result = morph_start
724
- . iter ( )
725
- . zip ( morph_end)
726
- . map ( |( a, b) | * a + lerp * ( * b - * a) ) ;
727
- lerp_morph_weights ( morphs. weights_mut ( ) , result, weight) ;
728
- }
729
- }
730
- }
726
+ apply_keyframe (
727
+ curve,
728
+ step_start,
729
+ weight,
730
+ lerp,
731
+ ts_end - ts_start,
732
+ & mut transform,
733
+ & mut morphs,
734
+ ) ;
731
735
}
732
736
}
733
737
@@ -737,6 +741,143 @@ fn apply_animation(
737
741
}
738
742
}
739
743
744
+ #[ inline( always) ]
745
+ fn apply_keyframe (
746
+ curve : & VariableCurve ,
747
+ step_start : usize ,
748
+ weight : f32 ,
749
+ lerp : f32 ,
750
+ duration : f32 ,
751
+ transform : & mut Mut < Transform > ,
752
+ morphs : & mut Option < Mut < MorphWeights > > ,
753
+ ) {
754
+ match ( & curve. interpolation , & curve. keyframes ) {
755
+ ( Interpolation :: Step , Keyframes :: Rotation ( keyframes) ) => {
756
+ transform. rotation = transform. rotation . slerp ( keyframes[ step_start] , weight) ;
757
+ }
758
+ ( Interpolation :: Linear , Keyframes :: Rotation ( keyframes) ) => {
759
+ let rot_start = keyframes[ step_start] ;
760
+ let mut rot_end = keyframes[ step_start + 1 ] ;
761
+ // Choose the smallest angle for the rotation
762
+ if rot_end. dot ( rot_start) < 0.0 {
763
+ rot_end = -rot_end;
764
+ }
765
+ // Rotations are using a spherical linear interpolation
766
+ let rot = rot_start. normalize ( ) . slerp ( rot_end. normalize ( ) , lerp) ;
767
+ transform. rotation = transform. rotation . slerp ( rot, weight) ;
768
+ }
769
+ ( Interpolation :: CubicSpline , Keyframes :: Rotation ( keyframes) ) => {
770
+ let value_start = keyframes[ step_start * 3 + 1 ] ;
771
+ let tangent_out_start = keyframes[ step_start * 3 + 2 ] ;
772
+ let tangent_in_end = keyframes[ ( step_start + 1 ) * 3 ] ;
773
+ let value_end = keyframes[ ( step_start + 1 ) * 3 + 1 ] ;
774
+ let result = cubic_spline_interpolation ! (
775
+ value_start,
776
+ tangent_out_start,
777
+ tangent_in_end,
778
+ value_end,
779
+ lerp,
780
+ duration,
781
+ ) ;
782
+ transform. rotation = transform. rotation . slerp ( result. normalize ( ) , weight) ;
783
+ }
784
+ ( Interpolation :: Step , Keyframes :: Translation ( keyframes) ) => {
785
+ transform. translation = transform. translation . lerp ( keyframes[ step_start] , weight) ;
786
+ }
787
+ ( Interpolation :: Linear , Keyframes :: Translation ( keyframes) ) => {
788
+ let translation_start = keyframes[ step_start] ;
789
+ let translation_end = keyframes[ step_start + 1 ] ;
790
+ let result = translation_start. lerp ( translation_end, lerp) ;
791
+ transform. translation = transform. translation . lerp ( result, weight) ;
792
+ }
793
+ ( Interpolation :: CubicSpline , Keyframes :: Translation ( keyframes) ) => {
794
+ let value_start = keyframes[ step_start * 3 + 1 ] ;
795
+ let tangent_out_start = keyframes[ step_start * 3 + 2 ] ;
796
+ let tangent_in_end = keyframes[ ( step_start + 1 ) * 3 ] ;
797
+ let value_end = keyframes[ ( step_start + 1 ) * 3 + 1 ] ;
798
+ let result = cubic_spline_interpolation ! (
799
+ value_start,
800
+ tangent_out_start,
801
+ tangent_in_end,
802
+ value_end,
803
+ lerp,
804
+ duration,
805
+ ) ;
806
+ transform. translation = transform. translation . lerp ( result, weight) ;
807
+ }
808
+ ( Interpolation :: Step , Keyframes :: Scale ( keyframes) ) => {
809
+ transform. scale = transform. scale . lerp ( keyframes[ step_start] , weight) ;
810
+ }
811
+ ( Interpolation :: Linear , Keyframes :: Scale ( keyframes) ) => {
812
+ let scale_start = keyframes[ step_start] ;
813
+ let scale_end = keyframes[ step_start + 1 ] ;
814
+ let result = scale_start. lerp ( scale_end, lerp) ;
815
+ transform. scale = transform. scale . lerp ( result, weight) ;
816
+ }
817
+ ( Interpolation :: CubicSpline , Keyframes :: Scale ( keyframes) ) => {
818
+ let value_start = keyframes[ step_start * 3 + 1 ] ;
819
+ let tangent_out_start = keyframes[ step_start * 3 + 2 ] ;
820
+ let tangent_in_end = keyframes[ ( step_start + 1 ) * 3 ] ;
821
+ let value_end = keyframes[ ( step_start + 1 ) * 3 + 1 ] ;
822
+ let result = cubic_spline_interpolation ! (
823
+ value_start,
824
+ tangent_out_start,
825
+ tangent_in_end,
826
+ value_end,
827
+ lerp,
828
+ duration,
829
+ ) ;
830
+ transform. scale = transform. scale . lerp ( result, weight) ;
831
+ }
832
+ ( Interpolation :: Step , Keyframes :: Weights ( keyframes) ) => {
833
+ if let Some ( morphs) = morphs {
834
+ let target_count = morphs. weights ( ) . len ( ) ;
835
+ let morph_start = get_keyframe ( target_count, keyframes, step_start) ;
836
+ lerp_morph_weights ( morphs. weights_mut ( ) , morph_start. iter ( ) . copied ( ) , weight) ;
837
+ }
838
+ }
839
+ ( Interpolation :: Linear , Keyframes :: Weights ( keyframes) ) => {
840
+ if let Some ( morphs) = morphs {
841
+ let target_count = morphs. weights ( ) . len ( ) ;
842
+ let morph_start = get_keyframe ( target_count, keyframes, step_start) ;
843
+ let morph_end = get_keyframe ( target_count, keyframes, step_start + 1 ) ;
844
+ let result = morph_start
845
+ . iter ( )
846
+ . zip ( morph_end)
847
+ . map ( |( a, b) | * a + lerp * ( * b - * a) ) ;
848
+ lerp_morph_weights ( morphs. weights_mut ( ) , result, weight) ;
849
+ }
850
+ }
851
+ ( Interpolation :: CubicSpline , Keyframes :: Weights ( keyframes) ) => {
852
+ if let Some ( morphs) = morphs {
853
+ let target_count = morphs. weights ( ) . len ( ) ;
854
+ let morph_start = get_keyframe ( target_count, keyframes, step_start * 3 + 1 ) ;
855
+ let tangents_out_start = get_keyframe ( target_count, keyframes, step_start * 3 + 2 ) ;
856
+ let tangents_in_end = get_keyframe ( target_count, keyframes, ( step_start + 1 ) * 3 ) ;
857
+ let morph_end = get_keyframe ( target_count, keyframes, ( step_start + 1 ) * 3 + 1 ) ;
858
+ let result = morph_start
859
+ . iter ( )
860
+ . zip ( tangents_out_start)
861
+ . zip ( tangents_in_end)
862
+ . zip ( morph_end)
863
+ . map (
864
+ |( ( ( value_start, tangent_out_start) , tangent_in_end) , value_end) | {
865
+ cubic_spline_interpolation ! (
866
+ value_start,
867
+ tangent_out_start,
868
+ tangent_in_end,
869
+ value_end,
870
+ lerp,
871
+ duration,
872
+ )
873
+ } ,
874
+ ) ;
875
+ lerp_morph_weights ( morphs. weights_mut ( ) , result, weight) ;
876
+ }
877
+ }
878
+ }
879
+ }
880
+
740
881
fn update_transitions ( player : & mut AnimationPlayer , time : & Time ) {
741
882
player. transitions . retain_mut ( |animation| {
742
883
animation. current_weight -= animation. weight_decline_per_sec * time. delta_seconds ( ) ;
0 commit comments