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) 2012-2013 Esteban Tovagliari, Jupiter Jazz Limited
9 // Copyright (c) 2014-2018 Esteban Tovagliari, 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/matrix.h"
34 #include "foundation/math/quaternion.h"
35 #include "foundation/math/vector.h"
36 #include "foundation/platform/python.h"
37 #include "foundation/utility/iostreamop.h"
38 
39 // Standard headers.
40 #include <cstddef>
41 
42 namespace foundation
43 {
44 
45 template <typename T>
46 class UnalignedMatrix44
47 {
48   public:
identity()49     static UnalignedMatrix44<T> identity()
50     {
51         return UnalignedMatrix44<T>(Matrix<T, 4, 4>::identity());
52     }
53 
make_translation(const Vector<T,3> & v)54     static UnalignedMatrix44<T> make_translation(const Vector<T, 3>& v)
55     {
56         return UnalignedMatrix44<T>(Matrix<T, 4, 4>::make_translation(v));
57     }
58 
make_scaling(const Vector<T,3> & s)59     static UnalignedMatrix44<T> make_scaling(const Vector<T, 3>& s)
60     {
61         return UnalignedMatrix44<T>(Matrix<T, 4, 4>::make_scaling(s));
62     }
63 
make_rotation_x(const T angle)64     static UnalignedMatrix44<T> make_rotation_x(const T angle)
65     {
66         return UnalignedMatrix44<T>(Matrix<T, 4, 4>::make_rotation_x(angle));
67     }
68 
make_rotation_y(const T angle)69     static UnalignedMatrix44<T> make_rotation_y(const T angle)
70     {
71         return UnalignedMatrix44<T>(Matrix<T, 4, 4>::make_rotation_y(angle));
72     }
73 
make_rotation_z(const T angle)74     static UnalignedMatrix44<T> make_rotation_z(const T angle)
75     {
76         return UnalignedMatrix44<T>(Matrix<T, 4, 4>::make_rotation_z(angle));
77     }
78 
make_rotation(const T yaw,const T pitch,const T roll)79     static UnalignedMatrix44<T> make_rotation(const T yaw, const T pitch, const T roll)
80     {
81         return UnalignedMatrix44<T>(Matrix<T, 4, 4>::make_rotation(yaw, pitch, roll));
82     }
83 
make_rotation(const Vector<T,3> & axis,const T angle)84     static UnalignedMatrix44<T> make_rotation(const Vector<T, 3>& axis, const T angle)
85     {
86         return UnalignedMatrix44<T>(Matrix<T, 4, 4>::make_rotation(axis, angle));
87     }
88 
make_rotation(const Quaternion<T> & q)89     static UnalignedMatrix44<T> make_rotation(const Quaternion<T>& q)
90     {
91         return UnalignedMatrix44<T>(Matrix<T, 4, 4>::make_rotation(q));
92     }
93 
make_lookat(const Vector<T,3> & origin,const Vector<T,3> & target,const Vector<T,3> & up)94     static UnalignedMatrix44<T> make_lookat(const Vector<T, 3>& origin, const Vector<T, 3>& target, const Vector<T, 3>& up)
95     {
96         return UnalignedMatrix44<T>(Matrix<T, 4, 4>::make_lookat(origin, target, up));
97     }
98 
UnalignedMatrix44()99     UnalignedMatrix44() {}
100 
UnalignedMatrix44(const T x)101     explicit UnalignedMatrix44(const T x)
102     {
103         for (size_t i = 0; i < 16; ++i)
104             m_data[i] = x;
105     }
106 
107     template <typename U>
UnalignedMatrix44(const Matrix<U,4,4> & m)108     explicit UnalignedMatrix44(const Matrix<U, 4, 4>& m)
109     {
110         for (size_t i = 0; i < 16; ++i)
111             m_data[i] = static_cast<T>(m[i]);
112     }
113 
114     template <typename U>
UnalignedMatrix44(const UnalignedMatrix44<U> & m)115     explicit UnalignedMatrix44(const UnalignedMatrix44<U>& m)
116     {
117         for (size_t i = 0; i < 16; ++i)
118             m_data[i] = static_cast<T>(m[i]);
119     }
120 
121 #ifdef APPLESEED_ENABLE_IMATH_INTEROP
122 
UnalignedMatrix44(const Imath::Matrix44<T> & rhs)123     UnalignedMatrix44(const Imath::Matrix44<T>& rhs)
124     {
125         T *p = m_data;
126 
127         for (int i = 0; i < 3; ++i)
128         {
129             for (int j = 0; j < 3; ++j)
130                 *p++ = rhs[j][i];
131         }
132     }
133 
134     operator Imath::Matrix44<T>() const
135     {
136         return as_foundation_matrix();
137     }
138 
139 #endif
140 
141     template <typename U>
142     UnalignedMatrix44<T>& operator=(const UnalignedMatrix44<U>& m)
143     {
144         for (size_t i = 0; i < 16; ++i)
145             m_data[i] = static_cast<T>(m[i]);
146 
147         return *this;
148     }
149 
as_foundation_matrix()150     Matrix<T, 4, 4> as_foundation_matrix() const
151     {
152         return Matrix<T, 4, 4>::from_array(m_data);
153     }
154 
155     T operator[](const size_t index) const
156     {
157         return m_data[index];
158     }
159 
160     T& operator[](const size_t index)
161     {
162         return m_data[index];
163     }
164 
operator()165     T operator()(const size_t row, const size_t col) const
166     {
167         return m_data[row * 4 + col];
168     }
169 
operator()170     T& operator()(const size_t row, const size_t col)
171     {
172         return m_data[row * 4 + col];
173     }
174 
extract_matrix3()175     Matrix<T, 3, 3> extract_matrix3() const
176     {
177         return as_foundation_matrix().extract_matrix3();
178     }
179 
extract_translation()180     Vector<T, 3> extract_translation() const
181     {
182         return as_foundation_matrix().extract_translation();
183     }
184 
transform_vector(const Vector<T,3> & v)185     Vector<T, 3> transform_vector(const Vector<T, 3>& v) const
186     {
187         Vector<T, 3> res;
188 
189         res[0] = m_data[0] * v[0] + m_data[1] * v[1] + m_data[ 2] * v[2];
190         res[1] = m_data[4] * v[0] + m_data[5] * v[1] + m_data[ 6] * v[2];
191         res[2] = m_data[8] * v[0] + m_data[9] * v[1] + m_data[10] * v[2];
192 
193         return res;
194     }
195 
transform_normal(const Vector<T,3> & n)196     Vector<T, 3> transform_normal(const Vector<T, 3>& n) const
197     {
198         Vector<T, 3> res;
199 
200         res.x = m_data[ 0] * n.x +
201                 m_data[ 4] * n.y +
202                 m_data[ 8] * n.z;
203 
204         res.y = m_data[ 1] * n.x +
205                 m_data[ 5] * n.y +
206                 m_data[ 9] * n.z;
207 
208         res.z = m_data[ 2] * n.x +
209                 m_data[ 6] * n.y +
210                 m_data[10] * n.z;
211 
212         return res;
213     }
214 
transform_point(const Vector<T,3> & p)215     Vector<T, 3> transform_point(const Vector<T, 3>& p) const
216     {
217         Vector<T, 3> res;
218 
219         res.x = m_data[ 0] * p.x +
220                 m_data[ 1] * p.y +
221                 m_data[ 2] * p.z +
222                 m_data[ 3];
223 
224         res.y = m_data[ 4] * p.x +
225                 m_data[ 5] * p.y +
226                 m_data[ 6] * p.z +
227                 m_data[ 7];
228 
229         res.z = m_data[ 8] * p.x +
230                 m_data[ 9] * p.y +
231                 m_data[10] * p.z +
232                 m_data[11];
233 
234         const T w = m_data[12] * p.x +
235                     m_data[13] * p.y +
236                     m_data[14] * p.z +
237                     m_data[15];
238 
239         if (w == T(0.0))
240         {
241             PyErr_SetString(PyExc_RuntimeError, "Zero homogeneous coordinate in appleseed.Matrix44.transform_point");
242             boost::python::throw_error_already_set();
243             return res;
244         }
245 
246         res /= w;
247 
248         return res;
249     }
250 
251   private:
252     T m_data[16];
253 };
254 
255 template <typename T>
256 UnalignedMatrix44<T> operator*(const UnalignedMatrix44<T>& a, const UnalignedMatrix44<T>& b)
257 {
258     return UnalignedMatrix44<T>(a.as_foundation_matrix() * b.as_foundation_matrix());
259 }
260 
261 template <typename T>
262 Vector<T, 4> operator*(const UnalignedMatrix44<T>& a, const Vector<T, 4>& v)
263 {
264     return a.as_foundation_matrix() * v;
265 }
266 
267 template <typename T>
invert_matrix(const UnalignedMatrix44<T> & mat)268 UnalignedMatrix44<T> invert_matrix(const UnalignedMatrix44<T>& mat)
269 {
270     try
271     {
272         return UnalignedMatrix44<T>(inverse(mat.as_foundation_matrix()));
273     }
274     catch (const ExceptionSingularMatrix&)
275     {
276         PyErr_SetString(PyExc_RuntimeError, "Singular matrix in appleseed.Matrix.inverse");
277         boost::python::throw_error_already_set();
278     }
279 
280     return UnalignedMatrix44<T>();
281 }
282 
283 template <typename T>
284 std::ostream& operator<<(std::ostream& s, const UnalignedMatrix44<T>& matrix)
285 {
286     return s << matrix.as_foundation_matrix();
287 }
288 
289 }   // namespace foundation
290