Skip to main content

flow_ngin/resources/
animation.rs

1use std::ops::BitAnd;
2
3use gltf::animation;
4use instant::{Duration, Instant};
5
6use cgmath::{AbsDiffEq, num_traits::Float};
7
8use crate::data_structures::{instance::Instance, scene_graph::SceneNode};
9
10const EPSILON: f32 = 1e-2;
11
12#[derive(Clone, Debug)]
13pub enum Keyframes {
14    Translation(Vec<cgmath::Vector3<f32>>),
15    Rotation(Vec<cgmath::Quaternion<f32>>),
16    Scale(Vec<cgmath::Vector3<f32>>),
17    Other,
18}
19
20pub struct Animation {
21    speed: f32,
22    rep_after_sec: f32,
23    time: Instant,
24}
25
26impl<'a> Animation {
27    pub fn new(speed: f32, rep_after_sec: f32) -> Self {
28        let time = Instant::now();
29        Self {
30            speed,
31            time,
32            rep_after_sec,
33        }
34    }
35
36    pub fn set_rep_time(&mut self, new_time: f32) {
37        self.rep_after_sec = new_time;
38    }
39
40    /**
41     * This function checks whether the passed Scene Graph contains animation data and plays it
42     * according to the time passed since this `Animation` struct was initialized.
43     *
44     * Repeats the animation after 20s (TODO: make this a parameter)
45     *
46     * TODO: interpolate similar to `animate_with(...args)`
47     */
48    pub fn animate(
49        &mut self,
50        graph: &'a mut Box<dyn SceneNode>,
51        anim_idx: usize,
52        instance_idx: usize,
53    ) {
54        let current_time = &mut self.time;
55        let duration = animate_graph(graph, instance_idx, anim_idx, current_time, self.speed);
56        self.set_rep_time(duration);
57
58        if self.time.elapsed().as_secs_f32() > self.rep_after_sec {
59            self.time = Instant::now();
60        }
61    }
62
63    /**
64     * This function animates a single frame with interpolation between the current position of
65     * the screne_graph and the position given in the animation List.
66     *
67     * `graph` is the Scene Graph of the object to model
68     * `current` is a mapping between Scene Graph nodes and the passed animation positions
69     * `reference` is the desired position
70     * `idx` is the index of the instance to animate
71     * `dt` the duration since the last rendered frame
72     */
73    pub fn animate_with(
74        &mut self,
75        graph: &'a mut Box<dyn SceneNode>,
76        current: &[&[usize]],
77        reference: &[&Instance],
78        idx: usize,
79        dt: Duration,
80    ) -> bool {
81        let mut all_lts = Vec::new();
82        let speed = self.speed.clone();
83
84        for (curr, ref_pos) in current.iter().zip(reference) {
85            let scene_node = curr
86                .into_iter()
87                .fold(&mut *graph, |g, &i| &mut g.get_children_mut()[i]);
88
89            if let Some(local_transform) = scene_node.get_local_transform(idx) {
90                let lt_epsilon = diff_lt_epsilon(&local_transform, ref_pos);
91                all_lts.push(lt_epsilon);
92                if !lt_epsilon {
93                    let new_transform: Instance =
94                        step(&local_transform, ref_pos, dt.as_secs_f32(), speed);
95                    scene_node.set_local_transform(idx, new_transform);
96                }
97            } else {
98                log::warn!("Warning, animation with index {} not found.", idx)
99            }
100        }
101        all_lts.into_iter().fold(true, BitAnd::bitand)
102    }
103}
104
105/// Animates a given `SceneNode` and returns the duration of the longest sub-animation.
106fn animate_graph(
107    graph: &mut Box<dyn SceneNode>,
108    instance_idx: usize,
109    anim_idx: usize,
110    time: &mut Instant,
111    speed: f32,
112) -> f32 {
113    let current_time = time.elapsed().as_secs_f32();
114    let animations = graph.get_animation();
115    let mut longest_anim_duration = 0.0;
116    let mut current_keyframe_index = 0;
117    // pick desired animation
118    if let Some(animation) = &animations.get(anim_idx) {
119        if let Some(timestamp) = animation.timestamps.last() {
120            longest_anim_duration = longest_anim_duration.max(*timestamp)
121        }
122        // invariant: timestamp matches animations
123        for timestamp in &animation.timestamps {
124            // keyframe overdue
125            if timestamp > &current_time {
126                break;
127            }
128            if &current_keyframe_index < &(&animation.timestamps.len() - 1) {
129                current_keyframe_index += 1;
130            }
131        }
132
133        // Update locals with current animation
134        let ref_pos = &animation.instances[current_keyframe_index];
135        graph.set_local_transform(instance_idx, ref_pos.clone());
136    }
137
138    for child in graph.get_children_mut() {
139        let duration = animate_graph(child, instance_idx, anim_idx, time, speed);
140        longest_anim_duration = longest_anim_duration.max(duration);
141    }
142    longest_anim_duration
143}
144
145// linear interpolation between two positions
146fn step(fst: &Instance, snd: &Instance, dt: f32, speed: f32) -> Instance {
147    let position = fst.position + (snd.position - fst.position) * dt * speed;
148    let rotation = fst.rotation.nlerp(snd.rotation, dt * speed);
149    let scale = fst.scale + (snd.scale - fst.scale) * dt * speed;
150
151    Instance {
152        position,
153        rotation,
154        scale,
155    }
156}
157
158fn diff_lt_epsilon(fst: &Instance, snd: &Instance) -> bool {
159    let pos_diff = fst.position.abs_diff_eq(&snd.position, EPSILON);
160    let rot_diff = fst.rotation.abs_diff_eq(&snd.rotation, EPSILON);
161    let scale_diff = fst.scale.abs_diff_eq(&snd.scale, EPSILON);
162    pos_diff && rot_diff && scale_diff
163}