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
//! Animation components and systems
use std::time::{ Duration };
use dotrix_math::{ SquareMatrix, Mat4 };

use crate::{ Pose, Id, Assets, Frame, World };
use crate::assets::Animation;
use crate::ecs::Const;

/// Animation playback state
///
/// [`Duration`] contains current time offset from the beginning of the animation
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum State {
    /// Animation is playing
    Play(Duration),
    /// Animation is looped
    Loop(Duration),
    /// Animation is stopped
    Stop,
}

/// Component to control model animation
pub struct Animator {
    animation: Id<Animation>,
    state: State,
    /// animation speed
    pub speed: f32,
}

impl Animator {
    /// creates new component instance with specified animation with [`State::Stop`]
    pub fn new(animation: Id<Animation>) -> Self {
        Self {
            animation,
            state: State::Stop,
            speed: 1.0,
        }
    }

    /// creates new component instance with specified animation with [`State::Play`]
    pub fn play(animation: Id<Animation>) -> Self {
        Self {
            animation,
            state: State::Play(Duration::from_secs(0)),
            speed: 1.0,
        }
    }

    /// creates new component instance with specified animation with [`State::Loop`]
    pub fn looped(animation: Id<Animation>) -> Self {
        Self {
            animation,
            state: State::Loop(Duration::from_secs(0)),
            speed: 1.0,
        }
    }

    /// Starts current animation
    pub fn start(&mut self) {
        self.state = State::Play(Duration::from_secs(0));
    }

    /// Starts current animation looped
    pub fn start_loop(&mut self) {
        self.state = State::Loop(Duration::from_secs(0));
    }

    /// Stops current animation
    pub fn stop(&mut self) {
        self.state = State::Stop;
    }

    /// Changes current animation
    pub fn animate(&mut self, animation: Id<Animation>) {
        self.animation = animation;
        self.state = State::Stop;
    }

    /// Returns current animation [`Id`]
    pub fn animation(&self) -> Id<Animation> {
        self.animation
    }

    /// Returns current [`State`]
    pub fn state(&self) -> State {
        self.state
    }

    fn update(&mut self, delta: Duration, duration: Duration) -> Option<Duration> {
        self.state = match self.state {
            State::Play(current) => {
                let new_duration = current + delta.mul_f32(self.speed);
                if new_duration < duration {
                    State::Play(new_duration)
                } else {
                    State::Stop
                }
            },
            State::Loop(current) => {
                let new_duration = current + delta.mul_f32(self.speed);
                State::Loop(
                    if new_duration < duration {
                        new_duration
                    } else {
                        Duration::from_secs_f32(new_duration.as_secs_f32() % duration.as_secs_f32())
                    }
                )
            },
            State::Stop => State::Stop
        };

        match self.state {
            State::Play(current) => Some(current),
            State::Loop(current) => Some(current),
            State::Stop => None,
        }
    }

}

/// System handling skeletal animation
pub fn skeletal(frame: Const<Frame>, world: Const<World>, assets: Const<Assets>) {
    for (animator, pose) in world.query::<(&mut Animator, &mut Pose)>() {
        let global_transform = Mat4::identity(); // model.transform.matrix();
        if let Some(skin) = assets.get(pose.skin) {

            let mut local_transforms = None;

            if let Some(animation) = assets.get::<Animation>(animator.animation) {
                if let Some(duration) = animator.update(frame.delta(), animation.duration()) {
                    local_transforms = Some(animation.sample(duration.as_secs_f32()));
                }
            }

            skin.transform(pose, &global_transform, local_transforms);
        }
    }
}