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
//! Provides an animated transformation that moves an object between a
//! set of specified keyframes.

use std::ops::Mul;

use bspline::BSpline;

use linalg::{self, quaternion, Keyframe, Transform};
use geometry::BBox;

/// An animated transform that blends between the keyframes in its transformation
/// list over time.
#[derive(Clone, Debug)]
pub struct AnimatedTransform {
    /// List of animated transforms in hierarchical order, e.g. the lowest
    /// index is the object's, index 1 holds its direct parent's transform, etc.
    keyframes: Vec<BSpline<Keyframe>>,
}

impl AnimatedTransform {
    /// Create an animated transformation blending between the passed keyframes
    pub fn with_keyframes(mut keyframes: Vec<Keyframe>, knots: Vec<f32>, degree: usize) -> AnimatedTransform {
        // so we know what degree and so on.
        // Step through and make sure all rotations take the shortest path
        for i in 1..keyframes.len() {
            // If the dot product is negative flip the current quaternion to
            // take the shortest path through the rotation
            if quaternion::dot(&keyframes[i - 1].rotation, &keyframes[i].rotation) < 0.0 {
                keyframes[i].rotation = -keyframes[i].rotation;
            }
        }
        AnimatedTransform { keyframes: vec![BSpline::new(degree, keyframes, knots)] }
    }
    pub fn unanimated(transform: &Transform) -> AnimatedTransform {
        let key = Keyframe::new(transform);
        AnimatedTransform { keyframes: vec![BSpline::new(0, vec![key], vec![0.0, 1.0])] }
    }
    /// Compute the transformation matrix for the animation at some time point using B-Spline
    /// interpolation.
    pub fn transform(&self, time: f32) -> Transform {
        let mut transform = Transform::identity();
        // Step through the transform stack, applying each animation transform at this
        // time as we move up
        for spline in &self.keyframes {
            let domain = spline.knot_domain();
            let t =
                if spline.control_points().count() == 1 {
                    spline.control_points().next().unwrap().transform()
                } else {
                    let t_val = linalg::clamp(time, domain.0, domain.1);
                    spline.point(t_val).transform()
                };
            transform = t * transform;
        }
        transform
    }
    /// Compute the bounds of the box moving through the animation sequence by sampling time
    pub fn animation_bounds(&self, b: &BBox, start: f32, end: f32) -> BBox {
        if !self.is_animated() {
            let t = self.transform(start);
            t * *b
        } else {
            let mut ret = BBox::new();
            for i in 0..128 {
                let time = linalg::lerp((i as f32) / 127.0, &start, &end);
                let t = self.transform(time);
                ret = ret.box_union(&(t * *b));
            }
            ret
        }
    }
    /// Check if the transform is actually animated
    pub fn is_animated(&self) -> bool {
        self.keyframes.is_empty() || self.keyframes.iter().fold(true, |b, spline| b && spline.control_points().count() > 1)
    }
}

impl Mul for AnimatedTransform {
    type Output = AnimatedTransform;
    /// Compose the animated transformations
    fn mul(self, mut rhs: AnimatedTransform) -> AnimatedTransform {
        for l in &self.keyframes[..] {
            rhs.keyframes.push(l.clone());
        }
        rhs
    }
}