1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#pragma once
4
5#include "Prerequisites/BsPrerequisitesUtil.h"
6#include "Math/BsVector3.h"
7
8namespace bs
9{
10 /** @addtogroup Math
11 * @{
12 */
13
14 /**
15 * A 3x3 matrix. Can be used for non-homogenous transformations of three dimensional vectors and points. In row major
16 * format.
17 */
18 class BS_UTILITY_EXPORT Matrix3
19 {
20 private:
21 struct EulerAngleOrderData
22 {
23 int a, b, c;
24 float sign;
25 };
26
27 public:
28 Matrix3() = default;
29 constexpr Matrix3(const Matrix3&) = default;
30 constexpr Matrix3& operator=(const Matrix3&) = default;
31
32 constexpr Matrix3(BS_ZERO)
33 :m{ { 0.0f, 0.0f, 0.0f},
34 { 0.0f, 0.0f, 0.0f},
35 { 0.0f, 0.0f, 0.0f} }
36 { }
37
38 constexpr Matrix3(BS_IDENTITY)
39 :m{ {1.0f, 0.0f, 0.0f},
40 {0.0f, 1.0f, 0.0f},
41 {0.0f, 0.0f, 1.0f} }
42 { }
43
44 constexpr Matrix3(float m00, float m01, float m02,
45 float m10, float m11, float m12,
46 float m20, float m21, float m22)
47 :m{{m00, m01, m02}, {m10, m11, m12}, {m20, m21, m22}}
48 { }
49
50 /** Construct a matrix from a quaternion. */
51 explicit Matrix3(const Quaternion& rotation)
52 {
53 fromQuaternion(rotation);
54 }
55
56 /** Construct a matrix that performs rotation and scale. */
57 explicit Matrix3(const Quaternion& rotation, const Vector3& scale)
58 {
59 fromQuaternion(rotation);
60
61 for (int row = 0; row < 3; row++)
62 {
63 for (int col = 0; col < 3; col++)
64 m[row][col] = scale[row]*m[row][col];
65 }
66 }
67
68 /** Construct a matrix from an angle/axis pair. */
69 explicit Matrix3(const Vector3& axis, const Radian& angle)
70 {
71 fromAxisAngle(axis, angle);
72 }
73
74 /** Construct a matrix from 3 orthonormal local axes. */
75 explicit Matrix3(const Vector3& xaxis, const Vector3& yaxis, const Vector3& zaxis)
76 {
77 fromAxes(xaxis, yaxis, zaxis);
78 }
79
80 /**
81 * Construct a matrix from euler angles, YXZ ordering.
82 *
83 * @see Matrix3::fromEulerAngles
84 */
85 explicit Matrix3(const Radian& xAngle, const Radian& yAngle, const Radian& zAngle)
86 {
87 fromEulerAngles(xAngle, yAngle, zAngle);
88 }
89
90 /**
91 * Construct a matrix from euler angles, custom ordering.
92 *
93 * @see Matrix3::fromEulerAngles
94 */
95 explicit Matrix3(const Radian& xAngle, const Radian& yAngle, const Radian& zAngle, EulerAngleOrder order)
96 {
97 fromEulerAngles(xAngle, yAngle, zAngle, order);
98 }
99
100 /** Swaps the contents of this matrix with another. */
101 void swap(Matrix3& other)
102 {
103 std::swap(m[0][0], other.m[0][0]);
104 std::swap(m[0][1], other.m[0][1]);
105 std::swap(m[0][2], other.m[0][2]);
106 std::swap(m[1][0], other.m[1][0]);
107 std::swap(m[1][1], other.m[1][1]);
108 std::swap(m[1][2], other.m[1][2]);
109 std::swap(m[2][0], other.m[2][0]);
110 std::swap(m[2][1], other.m[2][1]);
111 std::swap(m[2][2], other.m[2][2]);
112 }
113
114 /** Returns a row of the matrix. */
115 float* operator[] (UINT32 row) const
116 {
117 assert(row < 3);
118
119 return (float*)m[row];
120 }
121
122 Vector3 getColumn(UINT32 col) const;
123 void setColumn(UINT32 col, const Vector3& vec);
124
125 bool operator== (const Matrix3& rhs) const;
126 bool operator!= (const Matrix3& rhs) const;
127
128 Matrix3 operator+ (const Matrix3& rhs) const;
129 Matrix3 operator- (const Matrix3& rhs) const;
130 Matrix3 operator* (const Matrix3& rhs) const;
131 Matrix3 operator- () const;
132 Matrix3 operator* (float rhs) const;
133
134 friend Matrix3 operator* (float lhs, const Matrix3& rhs);
135
136 /** Transforms the given vector by this matrix and returns the newly transformed vector. */
137 Vector3 multiply(const Vector3& vec) const;
138
139 /** Returns a transpose of the matrix (switched columns and rows). */
140 Matrix3 transpose () const;
141
142 /**
143 * Calculates an inverse of the matrix if it exists.
144 *
145 * @param[out] mat Resulting matrix inverse.
146 * @param[in] fTolerance (optional) Tolerance to use when checking if determinant is zero (or near zero in this case).
147 * Zero determinant means inverse doesn't exist.
148 * @return True if inverse exists, false otherwise.
149 */
150 bool inverse(Matrix3& mat, float fTolerance = 1e-06f) const;
151
152 /**
153 * Calculates an inverse of the matrix if it exists.
154 *
155 * @param[in] fTolerance (optional) Tolerance to use when checking if determinant is zero (or near zero in this case).
156 * Zero determinant means inverse doesn't exist.
157 *
158 * @return Resulting matrix inverse if it exists, otherwise a zero matrix.
159 */
160 Matrix3 inverse(float fTolerance = 1e-06f) const;
161
162 /** Calculates the matrix determinant. */
163 float determinant() const;
164
165 /**
166 * Decompose a Matrix3 to rotation and scale.
167 *
168 * @note
169 * Matrix must consist only of rotation and uniform scale transformations, otherwise accurate results are not
170 * guaranteed. Applying non-uniform scale guarantees rotation portion will not be accurate.
171 */
172 void decomposition(Quaternion& rotation, Vector3& scale) const;
173
174 /**
175 * Decomposes the matrix into various useful values.
176 *
177 * @param[out] matL Unitary matrix. Columns form orthonormal bases. If your matrix is affine and
178 * doesn't use non-uniform scaling this matrix will be a conjugate transpose of the rotation part of the matrix.
179 * @param[out] matS Singular values of the matrix. If your matrix is affine these will be scaling factors of the matrix.
180 * @param[out] matR Unitary matrix. Columns form orthonormal bases. If your matrix is affine and
181 * doesn't use non-uniform scaling this matrix will be the rotation part of the matrix.
182 */
183 void singularValueDecomposition(Matrix3& matL, Vector3& matS, Matrix3& matR) const;
184
185 /**
186 * Decomposes the matrix into a set of values.
187 *
188 * @param[out] matQ Columns form orthonormal bases. If your matrix is affine and
189 * doesn't use non-uniform scaling this matrix will be the rotation part of the matrix.
190 * @param[out] vecD If the matrix is affine these will be scaling factors of the matrix.
191 * @param[out] vecU If the matrix is affine these will be shear factors of the matrix.
192 */
193 void QDUDecomposition(Matrix3& matQ, Vector3& vecD, Vector3& vecU) const;
194
195 /** Gram-Schmidt orthonormalization (applied to columns of rotation matrix) */
196 void orthonormalize();
197
198 /**
199 * Converts an orthonormal matrix to axis angle representation.
200 *
201 * @note Matrix must be orthonormal.
202 */
203 void toAxisAngle(Vector3& axis, Radian& angle) const;
204
205 /** Creates a rotation matrix from an axis angle representation. */
206 void fromAxisAngle(const Vector3& axis, const Radian& angle);
207
208 /**
209 * Converts an orthonormal matrix to quaternion representation.
210 *
211 * @note Matrix must be orthonormal.
212 */
213 void toQuaternion(Quaternion& quat) const;
214
215 /** Creates a rotation matrix from a quaternion representation. */
216 void fromQuaternion(const Quaternion& quat);
217
218 /** Creates a matrix from a three axes. */
219 void fromAxes(const Vector3& xAxis, const Vector3& yAxis, const Vector3& zAxis);
220
221 /**
222 * Converts an orthonormal matrix to euler angle (pitch/yaw/roll) representation.
223 *
224 * @param[in,out] xAngle Rotation about x axis. (AKA Pitch)
225 * @param[in,out] yAngle Rotation about y axis. (AKA Yaw)
226 * @param[in,out] zAngle Rotation about z axis. (AKA Roll)
227 * @return True if unique solution was found, false otherwise.
228 *
229 * @note Matrix must be orthonormal.
230 */
231 bool toEulerAngles(Radian& xAngle, Radian& yAngle, Radian& zAngle) const;
232
233 /**
234 * Creates a rotation matrix from the provided Pitch/Yaw/Roll angles.
235 *
236 * @param[in] xAngle Rotation about x axis. (AKA Pitch)
237 * @param[in] yAngle Rotation about y axis. (AKA Yaw)
238 * @param[in] zAngle Rotation about z axis. (AKA Roll)
239 *
240 * @note Matrix must be orthonormal.
241 * Since different values will be produced depending in which order are the rotations applied, this method assumes
242 * they are applied in YXZ order. If you need a specific order, use the overloaded "fromEulerAngles" method instead.
243 */
244 void fromEulerAngles(const Radian& xAngle, const Radian& yAngle, const Radian& zAngle);
245
246 /**
247 * Creates a rotation matrix from the provided Pitch/Yaw/Roll angles.
248 *
249 * @param[in] xAngle Rotation about x axis. (AKA Pitch)
250 * @param[in] yAngle Rotation about y axis. (AKA Yaw)
251 * @param[in] zAngle Rotation about z axis. (AKA Roll)
252 * @param[in] order The order in which rotations will be applied.
253 * Different rotations can be created depending on the order.
254 *
255 * @note Matrix must be orthonormal.
256 */
257 void fromEulerAngles(const Radian& xAngle, const Radian& yAngle, const Radian& zAngle, EulerAngleOrder order);
258
259 /**
260 * Eigensolver, matrix must be symmetric.
261 *
262 * @note
263 * Eigenvectors are vectors which when transformed by the matrix, only change in magnitude, but not in direction.
264 * Eigenvalue is that magnitude. In other words you will get the same result whether you multiply the vector by the
265 * matrix or by its eigenvalue.
266 */
267 void eigenSolveSymmetric(float eigenValues[3], Vector3 eigenVectors[3]) const;
268
269 static constexpr const float EPSILON = 1e-06f;
270 static const Matrix3 ZERO;
271 static const Matrix3 IDENTITY;
272
273 protected:
274 friend class Matrix4;
275
276 // Support for eigensolver
277 void tridiagonal (float diag[3], float subDiag[3]);
278 bool QLAlgorithm (float diag[3], float subDiag[3]);
279
280 // Support for singular value decomposition
281 static constexpr const float SVD_EPSILON = 1e-04f;;
282 static constexpr const unsigned int SVD_MAX_ITERS = 32;
283 static void bidiagonalize (Matrix3& matA, Matrix3& matL, Matrix3& matR);
284 static void golubKahanStep (Matrix3& matA, Matrix3& matL, Matrix3& matR);
285
286 float m[3][3];
287 };
288
289 /** @} */
290
291 /** @cond SPECIALIZATIONS */
292 BS_ALLOW_MEMCPY_SERIALIZATION(Matrix3);
293 /** @endcond */
294}
295