| 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 | |
| 8 | namespace 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 | |