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
use std::collections::hash_map::IntoIter;
use std::collections::HashMap;

use gltf::Gltf;

use crate::animation::asset::Animation;
use crate::ffi::{loop_animation, stop_animation};

#[derive(PartialEq, Eq)]
enum RepeatMode {
    Loop,
    None,
}

/// An animation asset with additional playback details.
pub struct DetailedAnimation {
    pub animation: Animation,
    pub time_scale: f32,
    pub is_active: bool,
    repeat_mode: RepeatMode,
}

impl DetailedAnimation {
    pub fn new(anim: Animation) -> Self {
        Self {
            animation: anim,
            time_scale: 1.0,
            is_active: false,
            repeat_mode: RepeatMode::None,
        }
    }
}

/// An animation mixer / manager capable of playing multiple simulataneous
/// animations with varying playback rates (including reverse playback).
#[derive(Default)]
pub struct AnimationManager {
    map: HashMap<String, DetailedAnimation>,
}

impl IntoIterator for AnimationManager {
    type Item = (String, DetailedAnimation);
    type IntoIter = IntoIter<String, DetailedAnimation>;
    fn into_iter(self) -> Self::IntoIter {
        self.map.into_iter()
    }
}

impl Extend<(String, DetailedAnimation)> for AnimationManager {
    fn extend<T: IntoIterator<Item = (String, DetailedAnimation)>>(&mut self, iter: T) {
        self.map.extend(iter);
    }
}

impl AnimationManager {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn import_from_gltf(gltf: &Gltf) -> Self {
        let mut manager = Self::new();
        for anim in gltf.animations() {
            let animation_asset = Animation::from_gltf(
                gltf,
                anim.name()
                    .expect("Animation loaded from glTF doesn't have name"),
            )
            .expect("Animation that should exist, doesn't");
            manager.add(animation_asset);
        }
        manager
    }

    pub fn add(&mut self, anim: Animation) -> Option<DetailedAnimation> {
        self.map
            .insert(anim.name().clone(), DetailedAnimation::new(anim))
    }

    pub fn get(&self, name: &str) -> Option<&DetailedAnimation> {
        self.map.get(name)
    }

    pub fn get_mut(&mut self, name: &str) -> Option<&mut DetailedAnimation> {
        self.map.get_mut(name)
    }

    pub fn remove(&mut self, name: &str) -> Option<DetailedAnimation> {
        self.map.remove(name)
    }

    pub fn update(&mut self, delta_seconds: f32) {
        for detailed_animation in self.map.values_mut().filter(|danim| danim.is_active) {
            let animation = &mut detailed_animation.animation;
            if detailed_animation.repeat_mode == RepeatMode::Loop {
                let new_timeline_pos =
                    animation.timeline_position() + delta_seconds * detailed_animation.time_scale;
                if new_timeline_pos > animation.duration() {
                    animation.set_timeline_position(new_timeline_pos - animation.duration());
                }
                if new_timeline_pos <= 0.0 {
                    animation.set_timeline_position(animation.duration() - new_timeline_pos);
                }
            }
            animation.update(delta_seconds * detailed_animation.time_scale);
        }
    }

    pub fn loop_animation(&mut self, anim_name: &str, scene_object_name: Option<&str>) {
        if let Some(detailed_anim) = self.get_mut(anim_name) {
            let scene_object_name = if let Some(name) = scene_object_name {
                name
            } else {
                ""
            };
            detailed_anim.is_active = true;
            detailed_anim.repeat_mode = RepeatMode::Loop;
            loop_animation(scene_object_name, anim_name, detailed_anim.time_scale);
        }
    }

    pub fn stop_animation(&mut self, anim_name: &str, scene_object_name: Option<&str>) {
        if let Some(detailed_anim) = self.get_mut(anim_name) {
            let scene_object_name = if let Some(name) = scene_object_name {
                name
            } else {
                ""
            };
            detailed_anim.is_active = false;
            stop_animation(scene_object_name, anim_name);
        }
    }
}