1 
2 //
3 // This source file is part of appleseed.
4 // Visit https://appleseedhq.net/ for additional information and resources.
5 //
6 // This software is released under the MIT license.
7 //
8 // Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
9 // Copyright (c) 2014-2018 Francois Beaune, The appleseedhq Organization
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining a copy
12 // of this software and associated documentation files (the "Software"), to deal
13 // in the Software without restriction, including without limitation the rights
14 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 // copies of the Software, and to permit persons to whom the Software is
16 // furnished to do so, subject to the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be included in
19 // all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 // THE SOFTWARE.
28 //
29 
30 #pragma once
31 
32 // appleseed.foundation headers.
33 #include "foundation/math/aabb.h"
34 #include "foundation/math/matrix.h"
35 #include "foundation/math/quaternion.h"
36 #include "foundation/math/ray.h"
37 #include "foundation/math/scalar.h"
38 #include "foundation/math/vector.h"
39 #include "foundation/utility/poison.h"
40 
41 // Standard headers.
42 #include <cassert>
43 
44 namespace foundation
45 {
46 
47 //
48 // The Transform class represents a 3D affine transformation, and allows
49 // transformation of points, vectors, normals, rays and bounding boxes
50 // from parent space to local space (as defined by the transform) and
51 // from local space to parent space.
52 //
53 // Note: normals and ray directions are not required to be unit-length,
54 // and are never normalized after transformation.
55 //
56 
57 template <typename T>
58 class Transform
59 {
60   public:
61     // Matrix, vector and transform types.
62     typedef Matrix<T, 4, 4> MatrixType;
63     typedef Vector<T, 3> VectorType;
64     typedef Transform<T> TransformType;
65 
66     // Constructors.
67 #if APPLESEED_COMPILER_CXX_DEFAULTED_FUNCTIONS
68     Transform() = default;                      // leave the transformation uninitialized
69 #else
Transform()70     Transform() {}                              // leave the transformation uninitialized
71 #endif
72     explicit Transform(                         // throws a foundation::ExceptionSingularMatrix exception if local_to_parent is singular
73         const MatrixType& local_to_parent);
74     Transform(
75         const MatrixType& local_to_parent,
76         const MatrixType& parent_to_local);     // must be equal to inverse(local_to_parent)
77 
78     // Construct and return the identity transform.
79     static TransformType make_identity();
80 
81     // Return the identity transform.
82     static const Transform& identity();
83 
84     // Build a transform using a given local-to-parent transform.
85     // The parent-to-local transform will be computed using matrix inversion.
86     static Transform from_local_to_parent(const MatrixType& m);
87 
88     // Set the transformation matrices.
89     void set_local_to_parent(const MatrixType& m);
90     void set_parent_to_local(const MatrixType& m);
91 
92     // Retrieve the transformation matrices.
93     const MatrixType& get_local_to_parent() const;
94     const MatrixType& get_parent_to_local() const;
95 
96     // Compose two transformations.
97     TransformType operator*(const TransformType& rhs) const;
98 
99     // Transform a 3D point.
100     template <typename U> Vector<U, 3> point_to_local(const Vector<U, 3>& p) const;
101     template <typename U> Vector<U, 3> point_to_parent(const Vector<U, 3>& p) const;
102 
103     // Transform a 3D vector.
104     template <typename U> Vector<U, 3> vector_to_local(const Vector<U, 3>& v) const;
105     template <typename U> Vector<U, 3> vector_to_parent(const Vector<U, 3>& v) const;
106 
107     // Transform a 3D normal.
108     template <typename U> Vector<U, 3> normal_to_local(const Vector<U, 3>& n) const;
109     template <typename U> Vector<U, 3> normal_to_parent(const Vector<U, 3>& n) const;
110 
111     // Transform a 3D ray.
112     template <typename U> Ray<U, 3> to_local(const Ray<U, 3>& r) const;
113     template <typename U> Ray<U, 3> to_parent(const Ray<U, 3>& r) const;
114 
115     // Transform a 3D axis-aligned bounding box.
116     // If the bounding box is invalid, it is returned unmodified.
117     template <typename U> AABB<U, 3> to_local(const AABB<U, 3>& b) const;
118     template <typename U> AABB<U, 3> to_parent(const AABB<U, 3>& b) const;
119 
120     // Retrieve the origin in parent space.
121     VectorType get_parent_origin() const;
122 
123     // Retrieve the primary axes in parent space.
124     VectorType get_parent_x() const;
125     VectorType get_parent_y() const;
126     VectorType get_parent_z() const;
127 
128     // Returns true if the transform swaps the handedness.
129     bool swaps_handedness() const;
130 
131   private:
132     template <typename> friend class TransformInterpolator;
133     template <typename> friend class PoisonImpl;
134 
135     MatrixType  m_local_to_parent;
136     MatrixType  m_parent_to_local;
137 
138     // The identity transform returned by identity().
139     static const TransformType m_identity;
140 };
141 
142 // Poisoning.
143 template <typename T>
144 class PoisonImpl<Transform<T>>
145 {
146   public:
147     static void do_poison(Transform<T>& transform);
148 };
149 
150 // Exact inequality and equality tests.
151 template <typename T> bool operator!=(const Transform<T>& lhs, const Transform<T>& rhs);
152 template <typename T> bool operator==(const Transform<T>& lhs, const Transform<T>& rhs);
153 
154 // Approximate equality tests.
155 template <typename T> bool feq(const Transform<T>& lhs, const Transform<T>& rhs);
156 template <typename T> bool feq(const Transform<T>& lhs, const Transform<T>& rhs, const T eps);
157 
158 
159 //
160 // Specializations for single and double precision transformations.
161 //
162 
163 typedef Transform<float>  Transformf;
164 typedef Transform<double> Transformd;
165 
166 
167 //
168 // The TransformInterpolator class allows to interpolate between two rigid
169 // transformations (any combination of rotations, translations and scalings).
170 //
171 
172 template <typename T>
173 class TransformInterpolator
174 {
175   public:
176     // Matrix and transform types.
177     typedef Matrix<T, 4, 4> MatrixType;
178     typedef Transform<T> TransformType;
179 
180     // Constructors.
181 #if APPLESEED_COMPILER_CXX_DEFAULTED_FUNCTIONS
182     TransformInterpolator() = default;          // leave the interpolator uninitialized
183 #else
TransformInterpolator()184     TransformInterpolator() {}                  // leave the interpolator uninitialized
185 #endif
186     TransformInterpolator(
187         const TransformType&    from,
188         const TransformType&    to);
189 
190     // Set the initial and final transformations.
191     // Returns true on success, false otherwise.
192     bool set_transforms(
193         const TransformType&    from,
194         const TransformType&    to);
195 
196     // Return the start and end scaling, rotation and translation.
197     const Vector<T, 3>& get_s0() const;
198     const Vector<T, 3>& get_s1() const;
199     const Quaternion<T>& get_q0() const;
200     const Quaternion<T>& get_q1() const;
201     const Vector<T, 3>& get_t0() const;
202     const Vector<T, 3>& get_t1() const;
203 
204     // Compute the transform for any value of the interpolation parameter t.
205     // Returns the initial transform for t == 0 and the final transform for t == 1.
206     void evaluate(
207         const T                 t,
208         TransformType&          result) const;
209 
210   private:
211     Vector<T, 3>  m_s0, m_s1;
212     Quaternion<T> m_q0, m_q1;
213     Vector<T, 3>  m_t0, m_t1;
214 };
215 
216 
217 //
218 // Specializations for single and double precision transformation interpolators.
219 //
220 
221 typedef TransformInterpolator<float>  TransformInterpolatorf;
222 typedef TransformInterpolator<double> TransformInterpolatord;
223 
224 
225 //
226 // Transform class implementation.
227 //
228 
229 template <typename T>
230 const Transform<T> Transform<T>::m_identity(Transform<T>::make_identity());
231 
232 template <typename T>
Transform(const MatrixType & local_to_parent)233 inline Transform<T>::Transform(const MatrixType& local_to_parent)
234   : m_local_to_parent(local_to_parent)
235   , m_parent_to_local(inverse(local_to_parent))
236 {
237 }
238 
239 template <typename T>
Transform(const MatrixType & local_to_parent,const MatrixType & parent_to_local)240 inline Transform<T>::Transform(
241     const MatrixType& local_to_parent,
242     const MatrixType& parent_to_local)
243   : m_local_to_parent(local_to_parent)
244   , m_parent_to_local(parent_to_local)
245 {
246     assert(
247         feq(
248             m_local_to_parent * m_parent_to_local,
249             MatrixType::make_identity(),
250             make_eps<T>(1.0e-4f, 1.0e-6)));
251 }
252 
253 template <typename T>
do_poison(Transform<T> & transform)254 void PoisonImpl<Transform<T>>::do_poison(Transform<T>& transform)
255 {
256     always_poison(transform.m_local_to_parent);
257     always_poison(transform.m_parent_to_local);
258 }
259 
260 template <typename T>
make_identity()261 Transform<T> Transform<T>::make_identity()
262 {
263     return Transform(MatrixType::make_identity(), MatrixType::make_identity());
264 }
265 
266 template <typename T>
identity()267 inline const Transform<T>& Transform<T>::identity()
268 {
269     return m_identity;
270 }
271 
272 template <typename T>
from_local_to_parent(const MatrixType & m)273 inline Transform<T> Transform<T>::from_local_to_parent(const MatrixType& m)
274 {
275     return Transform(m, inverse(m));
276 }
277 
278 template <typename T>
set_local_to_parent(const MatrixType & m)279 inline void Transform<T>::set_local_to_parent(const MatrixType& m)
280 {
281     m_local_to_parent = m;
282 }
283 
284 template <typename T>
set_parent_to_local(const MatrixType & m)285 inline void Transform<T>::set_parent_to_local(const MatrixType& m)
286 {
287     m_parent_to_local = m;
288 }
289 
290 template <typename T>
get_local_to_parent()291 inline const Matrix<T, 4, 4>& Transform<T>::get_local_to_parent() const
292 {
293     return m_local_to_parent;
294 }
295 
296 template <typename T>
get_parent_to_local()297 inline const Matrix<T, 4, 4>& Transform<T>::get_parent_to_local() const
298 {
299     return m_parent_to_local;
300 }
301 
302 template <typename T>
303 inline Transform<T> Transform<T>::operator*(const TransformType& rhs) const
304 {
305     return
306         Transform<T>(
307             rhs.m_local_to_parent * m_local_to_parent,
308             m_parent_to_local * rhs.m_parent_to_local);
309 }
310 
311 template <typename T>
312 template <typename U>
point_to_local(const Vector<U,3> & p)313 inline Vector<U, 3> Transform<T>::point_to_local(const Vector<U, 3>& p) const
314 {
315     Vector<U, 3> res;
316 
317     res.x = static_cast<U>(m_parent_to_local[ 0] * T(p.x) +
318                            m_parent_to_local[ 1] * T(p.y) +
319                            m_parent_to_local[ 2] * T(p.z) +
320                            m_parent_to_local[ 3]);
321 
322     res.y = static_cast<U>(m_parent_to_local[ 4] * T(p.x) +
323                            m_parent_to_local[ 5] * T(p.y) +
324                            m_parent_to_local[ 6] * T(p.z) +
325                            m_parent_to_local[ 7]);
326 
327     res.z = static_cast<U>(m_parent_to_local[ 8] * T(p.x) +
328                            m_parent_to_local[ 9] * T(p.y) +
329                            m_parent_to_local[10] * T(p.z) +
330                            m_parent_to_local[11]);
331 
332     const U w =
333             static_cast<U>(m_parent_to_local[12] * T(p.x) +
334                            m_parent_to_local[13] * T(p.y) +
335                            m_parent_to_local[14] * T(p.z) +
336                            m_parent_to_local[15]);
337 
338     assert(w != U(0.0));
339 
340     if (w != U(1.0))
341         res /= w;
342 
343     return res;
344 }
345 
346 template <typename T>
347 template <typename U>
point_to_parent(const Vector<U,3> & p)348 inline Vector<U, 3> Transform<T>::point_to_parent(const Vector<U, 3>& p) const
349 {
350     Vector<U, 3> res;
351 
352     res.x = static_cast<U>(m_local_to_parent[ 0] * T(p.x) +
353                            m_local_to_parent[ 1] * T(p.y) +
354                            m_local_to_parent[ 2] * T(p.z) +
355                            m_local_to_parent[ 3]);
356 
357     res.y = static_cast<U>(m_local_to_parent[ 4] * T(p.x) +
358                            m_local_to_parent[ 5] * T(p.y) +
359                            m_local_to_parent[ 6] * T(p.z) +
360                            m_local_to_parent[ 7]);
361 
362     res.z = static_cast<U>(m_local_to_parent[ 8] * T(p.x) +
363                            m_local_to_parent[ 9] * T(p.y) +
364                            m_local_to_parent[10] * T(p.z) +
365                            m_local_to_parent[11]);
366 
367     const U w =
368             static_cast<U>(m_local_to_parent[12] * T(p.x) +
369                            m_local_to_parent[13] * T(p.y) +
370                            m_local_to_parent[14] * T(p.z) +
371                            m_local_to_parent[15]);
372 
373     assert(w != U(0.0));
374 
375     if (w != U(1.0))
376         res /= w;
377 
378     return res;
379 }
380 
381 template <typename T>
382 template <typename U>
vector_to_local(const Vector<U,3> & v)383 inline Vector<U, 3> Transform<T>::vector_to_local(const Vector<U, 3>& v) const
384 {
385     Vector<U, 3> res;
386 
387     res.x = static_cast<U>(m_parent_to_local[ 0] * T(v.x) +
388                            m_parent_to_local[ 1] * T(v.y) +
389                            m_parent_to_local[ 2] * T(v.z));
390 
391     res.y = static_cast<U>(m_parent_to_local[ 4] * T(v.x) +
392                            m_parent_to_local[ 5] * T(v.y) +
393                            m_parent_to_local[ 6] * T(v.z));
394 
395     res.z = static_cast<U>(m_parent_to_local[ 8] * T(v.x) +
396                            m_parent_to_local[ 9] * T(v.y) +
397                            m_parent_to_local[10] * T(v.z));
398 
399     return res;
400 }
401 
402 template <typename T>
403 template <typename U>
vector_to_parent(const Vector<U,3> & v)404 inline Vector<U, 3> Transform<T>::vector_to_parent(const Vector<U, 3>& v) const
405 {
406     Vector<U, 3> res;
407 
408     res.x = static_cast<U>(m_local_to_parent[ 0] * T(v.x) +
409                            m_local_to_parent[ 1] * T(v.y) +
410                            m_local_to_parent[ 2] * T(v.z));
411 
412     res.y = static_cast<U>(m_local_to_parent[ 4] * T(v.x) +
413                            m_local_to_parent[ 5] * T(v.y) +
414                            m_local_to_parent[ 6] * T(v.z));
415 
416     res.z = static_cast<U>(m_local_to_parent[ 8] * T(v.x) +
417                            m_local_to_parent[ 9] * T(v.y) +
418                            m_local_to_parent[10] * T(v.z));
419 
420     return res;
421 }
422 
423 template <typename T>
424 template <typename U>
normal_to_local(const Vector<U,3> & n)425 inline Vector<U, 3> Transform<T>::normal_to_local(const Vector<U, 3>& n) const
426 {
427     Vector<U, 3> res;
428 
429     res.x = static_cast<U>(m_local_to_parent[ 0] * T(n.x) +
430                            m_local_to_parent[ 4] * T(n.y) +
431                            m_local_to_parent[ 8] * T(n.z));
432 
433     res.y = static_cast<U>(m_local_to_parent[ 1] * T(n.x) +
434                            m_local_to_parent[ 5] * T(n.y) +
435                            m_local_to_parent[ 9] * T(n.z));
436 
437     res.z = static_cast<U>(m_local_to_parent[ 2] * T(n.x) +
438                            m_local_to_parent[ 6] * T(n.y) +
439                            m_local_to_parent[10] * T(n.z));
440 
441     return res;
442 }
443 
444 template <typename T>
445 template <typename U>
normal_to_parent(const Vector<U,3> & n)446 inline Vector<U, 3> Transform<T>::normal_to_parent(const Vector<U, 3>& n) const
447 {
448     Vector<U, 3> res;
449 
450     res.x = static_cast<U>(m_parent_to_local[ 0] * T(n.x) +
451                            m_parent_to_local[ 4] * T(n.y) +
452                            m_parent_to_local[ 8] * T(n.z));
453 
454     res.y = static_cast<U>(m_parent_to_local[ 1] * T(n.x) +
455                            m_parent_to_local[ 5] * T(n.y) +
456                            m_parent_to_local[ 9] * T(n.z));
457 
458     res.z = static_cast<U>(m_parent_to_local[ 2] * T(n.x) +
459                            m_parent_to_local[ 6] * T(n.y) +
460                            m_parent_to_local[10] * T(n.z));
461 
462     return res;
463 }
464 
465 template <typename T>
466 template <typename U>
to_local(const Ray<U,3> & r)467 inline Ray<U, 3> Transform<T>::to_local(const Ray<U, 3>& r) const
468 {
469     Ray<U, 3> res;
470 
471     res.m_org = point_to_local(r.m_org);
472     res.m_dir = vector_to_local(r.m_dir);
473     res.m_tmin = r.m_tmin;
474     res.m_tmax = r.m_tmax;
475 
476     return res;
477 }
478 
479 template <typename T>
480 template <typename U>
to_parent(const Ray<U,3> & r)481 inline Ray<U, 3> Transform<T>::to_parent(const Ray<U, 3>& r) const
482 {
483     Ray<U, 3> res;
484 
485     res.m_org = point_to_parent(r.m_org);
486     res.m_dir = vector_to_parent(r.m_dir);
487     res.m_tmin = r.m_tmin;
488     res.m_tmax = r.m_tmax;
489 
490     return res;
491 }
492 
493 template <typename T>
494 template <typename U>
to_local(const AABB<U,3> & b)495 inline AABB<U, 3> Transform<T>::to_local(const AABB<U, 3>& b) const
496 {
497     //
498     // Note: there are more efficient (although possibly less precise) ways
499     // of transforming a bounding box. Here are some references:
500     //
501     //   http://www.cs.unc.edu/~zhangh/technotes/bbox.pdf
502     //   http://www.ics.uci.edu/~arvo/code/TransformingBoxes.c
503     //
504     //   "Transforming Axis-Aligned Bounding Boxes",
505     //   by Jim Arvo, in "Graphics Gems", Academic Press, 1990.
506     //
507 
508     if (!b.is_valid())
509         return b;
510 
511     AABB<U, 3> res;
512     res.invalidate();
513 
514     res.insert(point_to_local(Vector<U, 3>(b[0][0], b[0][1], b[0][2])));
515     res.insert(point_to_local(Vector<U, 3>(b[0][0], b[0][1], b[1][2])));
516     res.insert(point_to_local(Vector<U, 3>(b[0][0], b[1][1], b[1][2])));
517     res.insert(point_to_local(Vector<U, 3>(b[0][0], b[1][1], b[0][2])));
518     res.insert(point_to_local(Vector<U, 3>(b[1][0], b[1][1], b[0][2])));
519     res.insert(point_to_local(Vector<U, 3>(b[1][0], b[1][1], b[1][2])));
520     res.insert(point_to_local(Vector<U, 3>(b[1][0], b[0][1], b[1][2])));
521     res.insert(point_to_local(Vector<U, 3>(b[1][0], b[0][1], b[0][2])));
522 
523     return res;
524 }
525 
526 template <typename T>
527 template <typename U>
to_parent(const AABB<U,3> & b)528 inline AABB<U, 3> Transform<T>::to_parent(const AABB<U, 3>& b) const
529 {
530     if (!b.is_valid())
531         return b;
532 
533     AABB<U, 3> res;
534     res.invalidate();
535 
536     res.insert(point_to_parent(Vector<U, 3>(b[0][0], b[0][1], b[0][2])));
537     res.insert(point_to_parent(Vector<U, 3>(b[0][0], b[0][1], b[1][2])));
538     res.insert(point_to_parent(Vector<U, 3>(b[0][0], b[1][1], b[1][2])));
539     res.insert(point_to_parent(Vector<U, 3>(b[0][0], b[1][1], b[0][2])));
540     res.insert(point_to_parent(Vector<U, 3>(b[1][0], b[1][1], b[0][2])));
541     res.insert(point_to_parent(Vector<U, 3>(b[1][0], b[1][1], b[1][2])));
542     res.insert(point_to_parent(Vector<U, 3>(b[1][0], b[0][1], b[1][2])));
543     res.insert(point_to_parent(Vector<U, 3>(b[1][0], b[0][1], b[0][2])));
544 
545     return res;
546 }
547 
548 template <typename T>
get_parent_origin()549 inline typename Transform<T>::VectorType Transform<T>::get_parent_origin() const
550 {
551     VectorType res(
552         m_local_to_parent[ 3],
553         m_local_to_parent[ 7],
554         m_local_to_parent[11]);
555 
556     const T w = m_local_to_parent[15];
557 
558     assert(w != T(0.0));
559 
560     if (w != T(1.0))
561         res /= w;
562 
563     return res;
564 }
565 
566 template <typename T>
get_parent_x()567 inline typename Transform<T>::VectorType Transform<T>::get_parent_x() const
568 {
569     return
570         VectorType(
571             m_local_to_parent[ 0],
572             m_local_to_parent[ 4],
573             m_local_to_parent[ 8]);
574 }
575 
576 template <typename T>
get_parent_y()577 inline typename Transform<T>::VectorType Transform<T>::get_parent_y() const
578 {
579     return
580         VectorType(
581             m_local_to_parent[ 1],
582             m_local_to_parent[ 5],
583             m_local_to_parent[ 9]);
584 }
585 
586 template <typename T>
get_parent_z()587 inline typename Transform<T>::VectorType Transform<T>::get_parent_z() const
588 {
589     return
590         VectorType(
591             m_local_to_parent[ 2],
592             m_local_to_parent[ 6],
593             m_local_to_parent[10]);
594 }
595 
596 template <typename T>
swaps_handedness()597 inline bool Transform<T>::swaps_handedness() const
598 {
599     // We can test any of the matrices because if a matrix swaps the handedness, its inverse does too.
600     // gcc 4.8 needs the extra foundation qualifier.
601     return foundation::swaps_handedness(m_local_to_parent);
602 }
603 
604 template <typename T>
605 inline bool operator!=(const Transform<T>& lhs, const Transform<T>& rhs)
606 {
607     return lhs.get_local_to_parent() != rhs.get_local_to_parent();
608 }
609 
610 template <typename T>
611 inline bool operator==(const Transform<T>& lhs, const Transform<T>& rhs)
612 {
613     return lhs.get_local_to_parent() == rhs.get_local_to_parent();
614 }
615 
616 template <typename T>
feq(const Transform<T> & lhs,const Transform<T> & rhs)617 inline bool feq(const Transform<T>& lhs, const Transform<T>& rhs)
618 {
619     return feq(lhs.get_local_to_parent(), rhs.get_local_to_parent());
620 }
621 
622 template <typename T>
feq(const Transform<T> & lhs,const Transform<T> & rhs,const T eps)623 inline bool feq(const Transform<T>& lhs, const Transform<T>& rhs, const T eps)
624 {
625     return feq(lhs.get_local_to_parent(), rhs.get_local_to_parent(), eps);
626 }
627 
628 
629 //
630 // TransformInterpolator class implementation.
631 //
632 
633 template <typename T>
TransformInterpolator(const TransformType & from,const TransformType & to)634 inline TransformInterpolator<T>::TransformInterpolator(
635     const TransformType& from,
636     const TransformType& to)
637 {
638     set_transforms(from, to);
639 }
640 
641 template <typename T>
set_transforms(const TransformType & from,const TransformType & to)642 bool TransformInterpolator<T>::set_transforms(
643     const TransformType& from,
644     const TransformType& to)
645 {
646     from.get_local_to_parent().decompose(m_s0, m_q0, m_t0);
647     to.get_local_to_parent().decompose(m_s1, m_q1, m_t1);
648 
649     // Handle double-cover.
650     if (dot(m_q0, m_q1) < T(0.0))
651         m_q1 = -m_q1;
652 
653     const T Eps = make_eps<T>(1.0e-4f, 1.0e-6);
654     return is_normalized(m_q0, Eps) && is_normalized(m_q1, Eps);
655 }
656 
657 template <typename T>
get_s0()658 inline const Vector<T, 3>& TransformInterpolator<T>::get_s0() const
659 {
660     return m_s0;
661 }
662 
663 template <typename T>
get_s1()664 inline const Vector<T, 3>& TransformInterpolator<T>::get_s1() const
665 {
666     return m_s1;
667 }
668 
669 template <typename T>
get_q0()670 inline const Quaternion<T>& TransformInterpolator<T>::get_q0() const
671 {
672     return m_q0;
673 }
674 
675 template <typename T>
get_q1()676 inline const Quaternion<T>& TransformInterpolator<T>::get_q1() const
677 {
678     return m_q1;
679 }
680 
681 template <typename T>
get_t0()682 inline const Vector<T, 3>& TransformInterpolator<T>::get_t0() const
683 {
684     return m_t0;
685 }
686 
687 template <typename T>
get_t1()688 inline const Vector<T, 3>& TransformInterpolator<T>::get_t1() const
689 {
690     return m_t1;
691 }
692 
693 template <typename T>
evaluate(const T t,Transform<T> & result)694 inline void TransformInterpolator<T>::evaluate(const T t, Transform<T>& result) const
695 {
696     //
697     // Unoptimized implementation:
698     //
699     //     // Interpolate the scaling, rotation and translation components.
700     //     const Vector<T, 3> s = lerp(m_s0, m_s1, t);
701     //     const Quaternion<T> q = slerp(m_q0, m_q1, t);
702     //     const Vector<T, 3> p = lerp(m_t0, m_t1, t);
703     //
704     //     // Compute the local-to-parent matrix.
705     //     const Matrix<T, 4, 4> smat(Matrix<T, 4, 4>::make_scaling(s));
706     //     const Matrix<T, 4, 4> rmat(Matrix<T, 4, 4>::make_rotation(q));
707     //     const Matrix<T, 4, 4> tmat(Matrix<T, 4, 4>::make_translation(p));
708     //     Matrix<T, 4, 4> local_to_parent = smat;
709     //     local_to_parent = rmat * local_to_parent;
710     //     local_to_parent = tmat * local_to_parent;
711     //
712     //     // Compute the parent-to-local matrix.
713     //     const Vector<T, 3> inv_s(T(1.0) / s[0], T(1.0) / s[1], T(1.0) / s[2]);
714     //     const Matrix<T, 4, 4> inv_smat(Matrix<T, 4, 4>::make_scaling(inv_s));
715     //     const Matrix<T, 4, 4> inv_rmat(transpose(rmat));
716     //     const Matrix<T, 4, 4> inv_tmat(Matrix<T, 4, 4>::make_translation(-p));
717     //     Matrix<T, 4, 4> parent_to_local = inv_tmat;
718     //     parent_to_local = inv_rmat * parent_to_local;
719     //     parent_to_local = inv_smat * parent_to_local;
720     //
721 
722     const Quaternion<T> q = fast_slerp(m_q0, m_q1, t);
723 
724     const T rtx = q.v[0] + q.v[0];
725     const T rty = q.v[1] + q.v[1];
726     const T rtz = q.v[2] + q.v[2];
727     const T twx = rtx * q.s;
728     const T twy = rty * q.s;
729     const T twz = rtz * q.s;
730     const T txx = rtx * q.v[0];
731     const T txy = rty * q.v[0];
732     const T txz = rtz * q.v[0];
733     const T tyy = rty * q.v[1];
734     const T tyz = rtz * q.v[1];
735     const T tzz = rtz * q.v[2];
736 
737     result.m_local_to_parent[ 0] = result.m_parent_to_local[ 0] = T(1.0) - (tyy + tzz);
738     result.m_local_to_parent[ 4] = result.m_parent_to_local[ 1] = txy + twz;
739     result.m_local_to_parent[ 8] = result.m_parent_to_local[ 2] = txz - twy;
740     result.m_local_to_parent[ 1] = result.m_parent_to_local[ 4] = txy - twz;
741     result.m_local_to_parent[ 5] = result.m_parent_to_local[ 5] = T(1.0) - (txx + tzz);
742     result.m_local_to_parent[ 9] = result.m_parent_to_local[ 6] = tyz + twx;
743     result.m_local_to_parent[ 2] = result.m_parent_to_local[ 8] = txz + twy;
744     result.m_local_to_parent[ 6] = result.m_parent_to_local[ 9] = tyz - twx;
745     result.m_local_to_parent[10] = result.m_parent_to_local[10] = T(1.0) - (txx + tyy);
746 
747     const Vector<T, 3> s = lerp(m_s0, m_s1, t);
748 
749     result.m_local_to_parent[ 0] *= s.x;
750     result.m_local_to_parent[ 4] *= s.x;
751     result.m_local_to_parent[ 8] *= s.x;
752 
753     result.m_local_to_parent[ 1] *= s.y;
754     result.m_local_to_parent[ 5] *= s.y;
755     result.m_local_to_parent[ 9] *= s.y;
756 
757     result.m_local_to_parent[ 2] *= s.z;
758     result.m_local_to_parent[ 6] *= s.z;
759     result.m_local_to_parent[10] *= s.z;
760 
761     const T rcp_sx = T(1.0) / s[0];
762     result.m_parent_to_local[ 0] *= rcp_sx;
763     result.m_parent_to_local[ 1] *= rcp_sx;
764     result.m_parent_to_local[ 2] *= rcp_sx;
765 
766     const T rcp_sy = T(1.0) / s[1];
767     result.m_parent_to_local[ 4] *= rcp_sy;
768     result.m_parent_to_local[ 5] *= rcp_sy;
769     result.m_parent_to_local[ 6] *= rcp_sy;
770 
771     const T rcp_sz = T(1.0) / s[2];
772     result.m_parent_to_local[ 8] *= rcp_sz;
773     result.m_parent_to_local[ 9] *= rcp_sz;
774     result.m_parent_to_local[10] *= rcp_sz;
775 
776     const Vector<T, 3> p = lerp(m_t0, m_t1, t);
777 
778     result.m_parent_to_local[ 3] = -(result.m_parent_to_local[ 0] * p.x + result.m_parent_to_local[ 1] * p.y + result.m_parent_to_local[ 2] * p.z);
779     result.m_local_to_parent[ 3] = p.x;
780     result.m_parent_to_local[ 7] = -(result.m_parent_to_local[ 4] * p.x + result.m_parent_to_local[ 5] * p.y + result.m_parent_to_local[ 6] * p.z);
781     result.m_local_to_parent[ 7] = p.y;
782     result.m_parent_to_local[11] = -(result.m_parent_to_local[ 8] * p.x + result.m_parent_to_local[ 9] * p.y + result.m_parent_to_local[10] * p.z);
783     result.m_local_to_parent[11] = p.z;
784 
785     result.m_local_to_parent[12] = result.m_parent_to_local[12] = T(0.0);
786     result.m_local_to_parent[13] = result.m_parent_to_local[13] = T(0.0);
787     result.m_local_to_parent[14] = result.m_parent_to_local[14] = T(0.0);
788     result.m_local_to_parent[15] = result.m_parent_to_local[15] = T(1.0);
789 }
790 
791 }   // namespace foundation
792