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 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 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 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}