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/scalar.h"
34 #include "foundation/math/vector.h"
35 #include "foundation/utility/poison.h"
36 
37 // Standard headers.
38 #include <cassert>
39 #include <cmath>
40 
41 namespace foundation
42 {
43 
44 //
45 // The Basis3 class represents a 3-dimensional orthonormal basis, and allows
46 // transformation of vectors from parent space to local space and from local
47 // space to parent space.
48 //
49 
50 template <typename T>
51 class Basis3
52 {
53   public:
54     // Vector and basis types.
55     typedef Vector<T, 3> VectorType;
56     typedef Basis3<T> BasisType;
57 
58     // Constructors.
59 #if APPLESEED_COMPILER_CXX_DEFAULTED_FUNCTIONS
60     Basis3() = default;                             // leave all components uninitialized
61 #else
Basis3()62     Basis3() {}                                     // leave all components uninitialized
63 #endif
64     explicit Basis3(const VectorType& normal);      // normal must be unit-length
65     Basis3(
66         const VectorType&   normal,                 // must be unit-length
67         const VectorType&   u);                     // does not need to be unit-length
68     Basis3(
69         const VectorType&   normal,                 // must be unit-length
70         const VectorType&   u,                      // must be unit-length
71         const VectorType&   v);                     // must be unit-length
72 
73     // Construct a basis from another basis of a different type.
74     template <typename U>
75     explicit Basis3(const Basis3<U>& rhs);
76 
77     // Rebuild the basis for a given unit-length normal vector.
78     void build(const VectorType& normal);           // normal must be unit-length
79 
80     // Rebuild the basis for a given unit-length normal and U tangent vector.
81     void build(
82         const VectorType&   normal,                 // must be unit-length
83         const VectorType&   u);                     // does not need to be unit-length
84 
85     // Rebuild the basis for a given unit-length normal and tangent vectors.
86     void build(
87         const VectorType&   normal,                 // must be unit-length
88         const VectorType&   u,                      // must be unit-length
89         const VectorType&   v);                     // must be unit-length
90 
91     // Transform a 3D vector. The vector is not required to be unit-length,
92     // and is not normalized after transformation. However the length of the
93     // vector is preserved by the transformation.
94     VectorType transform_to_local(const VectorType& v) const;
95     VectorType transform_to_parent(const VectorType& v) const;
96 
97     // Transform a 3D vector of a different type.
98     template <typename U>
99     Vector<U, 3> transform_to_local(const Vector<U, 3>& v) const;
100     template <typename U>
101     Vector<U, 3> transform_to_parent(const Vector<U, 3>& v) const;
102 
103     // Retrieve the individual basis vectors.
104     const VectorType& get_normal() const;
105     const VectorType& get_tangent_u() const;
106     const VectorType& get_tangent_v() const;
107 
108   private:
109     template <typename> friend class Basis3;
110     template <typename> friend class PoisonImpl;
111 
112     VectorType m_n, m_u, m_v;
113 
114 #ifndef NDEBUG
115     // Run a bunch of assertions on the basis vectors.
116     void checks();
117 #endif
118 };
119 
120 // Poisoning.
121 template <typename T>
122 class PoisonImpl<Basis3<T>>
123 {
124   public:
125     static void do_poison(Basis3<T>& basis);
126 };
127 
128 
129 //
130 // Specializations for single and double precision orthonormal basis.
131 //
132 
133 typedef Basis3<float>  Basis3f;
134 typedef Basis3<double> Basis3d;
135 
136 
137 //
138 // Basis3 class implementation.
139 //
140 
141 template <typename T>
Basis3(const VectorType & normal)142 inline Basis3<T>::Basis3(const VectorType& normal)
143 {
144     build(normal);
145 }
146 
147 template <typename T>
Basis3(const VectorType & normal,const VectorType & u)148 inline Basis3<T>::Basis3(
149     const VectorType&   normal,
150     const VectorType&   u)
151 {
152     build(normal, u);
153 }
154 
155 template <typename T>
Basis3(const VectorType & normal,const VectorType & u,const VectorType & v)156 inline Basis3<T>::Basis3(
157     const VectorType&   normal,
158     const VectorType&   u,
159     const VectorType&   v)
160 {
161     build(normal, u, v);
162 }
163 
164 template <typename T>
165 template <typename U>
Basis3(const Basis3<U> & rhs)166 inline Basis3<T>::Basis3(const Basis3<U>& rhs)
167   : m_n(rhs.m_n)
168   , m_u(rhs.m_u)
169   , m_v(rhs.m_v)
170 {
171 }
172 
173 template <typename T>
build(const VectorType & normal)174 inline void Basis3<T>::build(const VectorType& normal)
175 {
176     //
177     // Reference:
178     //
179     //   Tom Duff, James Burgess, Per Christensen, Christophe Hery, Andrew Kensler,
180     //   Max Liani, and Ryusuke Villemin, Building an Orthonormal Basis, Revisited,
181     //   Journal of Computer Graphics Techniques (JCGT), vol. 6, no. 1, 1-8, 2017
182     //   http://jcgt.org/published/0006/01/01/paper-lowres.pdf
183     //
184 
185     assert(is_normalized(normal));
186 
187     m_n = normal;
188 
189 #if APPLESEED_COMPILER_CXX_DEFAULTED_FUNCTIONS
190     const T sign = std::copysign(T(1.0), m_n[2]);
191 #else
192     const T sign = m_n[2] < T(0.0) ? T(-1.0) : T(1.0);
193 #endif
194 
195     const T a = T(-1.0) / (sign + m_n[2]);
196     const T b = m_n[0] * m_n[1] * a;
197 
198     m_u = VectorType(b, sign + m_n[1] * m_n[1] * a, -m_n[1]);
199     m_v = VectorType(T(1.0) + sign * m_n[0] * m_n[0] * a, sign * b, -sign * m_n[0]);
200 
201 #ifndef NDEBUG
202     checks();
203 #endif
204 }
205 
206 template <typename T>
build(const VectorType & normal,const VectorType & u)207 inline void Basis3<T>::build(
208     const VectorType&   normal,
209     const VectorType&   u)
210 {
211     assert(is_normalized(normal));
212 
213     m_n = normal;
214     m_v = normalize(cross(u, m_n));
215     m_u = cross(m_n, m_v);
216 
217 #ifndef NDEBUG
218     checks();
219 #endif
220 }
221 
222 template <typename T>
build(const VectorType & normal,const VectorType & u,const VectorType & v)223 inline void Basis3<T>::build(
224     const VectorType&   normal,
225     const VectorType&   u,
226     const VectorType&   v)
227 {
228     assert(is_normalized(normal));
229     assert(is_normalized(u));
230     assert(is_normalized(v));
231 
232     m_n = normal;
233     m_u = u;
234     m_v = v;
235 
236 #ifndef NDEBUG
237     checks();
238 #endif
239 }
240 
241 template <typename T>
transform_to_local(const VectorType & v)242 inline Vector<T, 3> Basis3<T>::transform_to_local(const VectorType& v) const
243 {
244     return
245         Vector<T, 3>(
246             dot(v, m_u),
247             dot(v, m_n),
248             dot(v, m_v));
249 }
250 
251 template <typename T>
transform_to_parent(const VectorType & v)252 inline Vector<T, 3> Basis3<T>::transform_to_parent(const VectorType& v) const
253 {
254     return
255         v[0] * m_u +
256         v[1] * m_n +
257         v[2] * m_v;
258 }
259 
260 template <typename T>
261 template <typename U>
transform_to_local(const Vector<U,3> & v)262 inline Vector<U, 3> Basis3<T>::transform_to_local(const Vector<U, 3>& v) const
263 {
264     return
265         Vector<U, 3>(
266             dot(v, Vector<U, 3>(m_u)),
267             dot(v, Vector<U, 3>(m_n)),
268             dot(v, Vector<U, 3>(m_v)));
269 }
270 
271 template <typename T>
272 template <typename U>
transform_to_parent(const Vector<U,3> & v)273 inline Vector<U, 3> Basis3<T>::transform_to_parent(const Vector<U, 3>& v) const
274 {
275     return
276         v[0] * Vector<U, 3>(m_u) +
277         v[1] * Vector<U, 3>(m_n) +
278         v[2] * Vector<U, 3>(m_v);
279 }
280 
281 template <typename T>
get_normal()282 inline const Vector<T, 3>& Basis3<T>::get_normal() const
283 {
284     return m_n;
285 }
286 
287 template <typename T>
get_tangent_u()288 inline const Vector<T, 3>& Basis3<T>::get_tangent_u() const
289 {
290     return m_u;
291 }
292 
293 template <typename T>
get_tangent_v()294 inline const Vector<T, 3>& Basis3<T>::get_tangent_v() const
295 {
296     return m_v;
297 }
298 
299 template <typename T>
do_poison(Basis3<T> & basis)300 void PoisonImpl<Basis3<T>>::do_poison(Basis3<T>& basis)
301 {
302     always_poison(basis.m_n);
303     always_poison(basis.m_u);
304     always_poison(basis.m_v);
305 }
306 
307 #ifndef NDEBUG
308 
309 template <typename T>
checks()310 void Basis3<T>::checks()
311 {
312     // Make sure (m_u, m_n, m_v) forms an orthonormal basis.
313     assert(is_normalized(m_u));
314     assert(is_normalized(m_n));
315     assert(is_normalized(m_v));
316     assert(fz(dot(m_u, m_n), make_eps<T>(1.0e-4f, 1.0e-6)));
317     assert(fz(dot(m_u, m_v), make_eps<T>(1.0e-4f, 1.0e-6)));
318     assert(fz(dot(m_n, m_v), make_eps<T>(1.0e-4f, 1.0e-6)));
319 
320     // Make sure (m_u, m_n, m_v) is right-handed.
321     assert(feq(dot(cross(m_u, m_n), m_v), T(1.0), make_eps<T>(1.0e-4f, 1.0e-5)));
322 }
323 
324 #endif
325 
326 }   // namespace foundation
327