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