pbrt_r3/core/transform/
animated_transform.rs

1use super::decompose::*;
2use super::derivatives::*;
3use super::interval::*;
4use super::matrix4x4::*;
5use super::transform::*;
6use crate::core::base::*;
7use crate::core::geometry::*;
8use crate::core::quaternion::*;
9
10#[derive(Debug, PartialEq, Clone)]
11pub struct AnimatedTransform {
12    pub transforms: [Transform; 2],
13    pub times: [Float; 2],
14    pub actually_animated: bool,
15    pub has_rotation: bool,
16    pub t: [Vector3f; 2],
17    pub r: [Quaternion; 2],
18    pub s: [Vector3f; 2],
19    pub derivatives: Option<[[DerivativeTerm; 3]; 5]>,
20}
21
22impl AnimatedTransform {
23    pub fn new(
24        start_transform: &Transform,
25        start_time: Float,
26        end_transform: &Transform,
27        end_time: Float,
28    ) -> Self {
29        const EPS: Float = Float::EPSILON * 1e+2;
30        let transforms = [*start_transform, *end_transform];
31        let times = [start_time, end_time];
32        let actually_animated = start_transform != end_transform;
33        let (t0, r0, s0) = decompose(&start_transform.m, EPS, 100).unwrap();
34        let (t1, r1, s1) = decompose(&end_transform.m, EPS, 100).unwrap();
35        let t = [t0, t1];
36        let mut r = [r0, r1];
37        let s = [s0, s1];
38        let sm = [
39            Matrix4x4::scale(s0.x, s0.y, s0.z),
40            Matrix4x4::scale(s1.x, s1.y, s1.z),
41        ];
42        if Quaternion::dot(&r[0], &r[1]) < 0.0 {
43            r[1] = -r[1];
44        }
45        let has_rotation = Quaternion::dot(&r[0], &r[1]) < 0.9995;
46        let derivatives = if has_rotation {
47            Some(get_derivatives(&t, &r, &sm))
48        } else {
49            None
50        };
51        AnimatedTransform {
52            transforms,
53            times,
54            actually_animated,
55            has_rotation,
56            t,
57            r,
58            s,
59            derivatives,
60        }
61    }
62
63    pub fn interpolate(&self, time: Float) -> Transform {
64        if !self.actually_animated || time <= self.times[0] {
65            return self.transforms[0];
66        }
67
68        if self.times[1] <= time {
69            return self.transforms[1];
70        }
71
72        let dt = (time - self.times[0]) / (self.times[1] - self.times[0]);
73        //println!("{:?}",dt);
74        let trans = (1.0 - dt) * self.t[0] + dt * self.t[1];
75        let rotate = Quaternion::slerp(dt, &self.r[0], &self.r[1]);
76        let scale = (1.0 - dt) * self.s[0] + dt * self.s[1];
77        let m = Matrix4x4::translate(trans.x, trans.y, trans.z)
78            * rotate.to_matrix()
79            * Matrix4x4::scale(scale.x, scale.y, scale.z);
80        return Transform::from(m);
81    }
82
83    pub fn transform_point(&self, time: Float, p: &Point3f) -> Point3f {
84        if !self.actually_animated || time <= self.times[0] {
85            return self.transforms[0].transform_point(p);
86        }
87
88        if self.times[1] <= time {
89            return self.transforms[1].transform_point(p);
90        }
91
92        let m = self.interpolate(time);
93        return m.transform_point(p);
94    }
95
96    pub fn transform_vector(&self, time: Float, v: &Vector3f) -> Point3f {
97        if !self.actually_animated || time <= self.times[0] {
98            return self.transforms[0].transform_vector(v);
99        }
100
101        if self.times[1] <= time {
102            return self.transforms[1].transform_vector(v);
103        }
104
105        let m = self.interpolate(time);
106        return m.transform_vector(v);
107    }
108
109    pub fn transform_normal(&self, time: Float, n: &Normal3f) -> Point3f {
110        if !self.actually_animated || time <= self.times[0] {
111            return self.transforms[0].transform_normal(n);
112        }
113
114        if self.times[1] <= time {
115            return self.transforms[1].transform_normal(n);
116        }
117
118        let m = self.interpolate(time);
119        return m.transform_normal(n);
120    }
121
122    pub fn transform_ray(&self, r: &Ray) -> (Ray, Vector3f, Vector3f) {
123        if !self.actually_animated || r.time <= self.times[0] {
124            return self.transforms[0].transform_ray(r);
125        } else if self.times[1] <= r.time {
126            return self.transforms[1].transform_ray(r);
127        } else {
128            let t = self.interpolate(r.time);
129            return t.transform_ray(r);
130        }
131    }
132
133    pub fn transform_ray_differential(
134        &self,
135        r: &RayDifferential,
136    ) -> (RayDifferential, Vector3f, Vector3f) {
137        if !self.actually_animated || r.ray.time <= self.times[0] {
138            return self.transforms[0].transform_ray_differential(r);
139        } else if r.ray.time >= self.times[1] {
140            return self.transforms[1].transform_ray_differential(r);
141        } else {
142            let t = self.interpolate(r.ray.time);
143            return t.transform_ray_differential(r);
144        }
145    }
146
147    // pbrt-r3
148    fn expand_bounds(b: &Bounds3f, ratio: Float) -> Bounds3f {
149        let d = b.diagonal() * ratio;
150        return Bounds3f::new(&(b.min - d), &(b.max + d));
151    }
152    // pbrt-r3
153
154    pub fn motion_bounds(&self, b: &Bounds3f) -> Bounds3f {
155        if !self.actually_animated {
156            return self.transforms[0].transform_bounds(b);
157        }
158        if !self.has_rotation {
159            let b0 = self.transforms[0].transform_bounds(b);
160            let b1 = self.transforms[1].transform_bounds(b);
161            return Bounds3f::union(&b0, &b1);
162        }
163        {
164            if true {
165                let count = 64;
166                let b0 = self.transforms[0].transform_bounds(b);
167                let b1 = self.transforms[1].transform_bounds(b);
168                let mut bounds = Bounds3f::union(&b0, &b1);
169                for i in 0..count {
170                    let t = lerp(i as Float / count as Float, self.times[0], self.times[1]);
171                    let tr = self.interpolate(t);
172                    bounds = Bounds3f::union(&bounds, &tr.transform_bounds(b));
173                }
174                return Self::expand_bounds(&bounds, 0.1);
175            } else {
176                let bounds_list: Vec<_> = (0..8)
177                    .map(|i| -> Bounds3f { self.bound_point_motion(&b.corner(i)).unwrap() })
178                    .collect();
179                let bounds = bounds_list[1..8]
180                    .iter()
181                    .fold(bounds_list[0], |a, b| -> Bounds3f {
182                        return a.union(b);
183                    });
184                return Self::expand_bounds(&bounds, 0.1);
185            }
186        }
187    }
188
189    pub fn bound_point_motion(&self, p: &Point3f) -> Option<Bounds3f> {
190        if !self.actually_animated {
191            return None;
192        }
193        if !self.has_rotation {
194            return None;
195        }
196        if let Some(derivatives) = self.derivatives.as_ref() {
197            let p0 = self.transforms[0].transform_point(p);
198            let p1 = self.transforms[1].transform_point(p);
199            let mut bounds = Bounds3f::new(&p0, &p1);
200            let cos_theta = Quaternion::dot(&self.r[0], &self.r[1]);
201            let theta = Float::acos(Float::clamp(cos_theta, -1.0, 1.0));
202            let max_depth = 8;
203            for c in 0..3 {
204                let c1 = derivatives[0][c].eval(p);
205                let c2 = derivatives[1][c].eval(p);
206                let c3 = derivatives[2][c].eval(p);
207                let c4 = derivatives[3][c].eval(p);
208                let c5 = derivatives[4][c].eval(p);
209                let zeros = interval_find_zeros(
210                    c1,
211                    c2,
212                    c3,
213                    c4,
214                    c5,
215                    theta,
216                    Interval::new(0.0, 1.0),
217                    max_depth,
218                );
219                for zero in zeros {
220                    let t = lerp(zero, self.times[0], self.times[1]);
221                    let pz = self.transform_point(t, p);
222                    bounds = bounds.union_p(&pz);
223                }
224            }
225            return Some(bounds);
226        }
227        return None;
228    }
229
230    pub fn is_animated(&self) -> bool {
231        return self.actually_animated;
232    }
233
234    pub fn has_scale(&self) -> bool {
235        return self.transforms[0].has_scale() || self.transforms[1].has_scale();
236    }
237}