| /* |
| * Copyright 2014 Google Inc. All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| #ifndef MATHFU_MATRIX_H_ |
| #define MATHFU_MATRIX_H_ |
| |
| #include "mathfu/utilities.h" |
| #include "mathfu/vector.h" |
| |
| #include <cmath> |
| |
| #include <assert.h> |
| |
| /// @file mathfu/matrix.h |
| /// @brief Matrix class and functions. |
| /// @addtogroup mathfu_matrix |
| /// |
| /// MathFu provides a generic Matrix implementation which is specialized |
| /// for 4x4 matrices to take advantage of optimization opportunities using |
| /// SIMD instructions. |
| |
| #ifdef _MSC_VER |
| #pragma warning(push) |
| // The following disables warnings for MATHFU_MAT_OPERATION. |
| // The buffer overrun warning must be disabled as MSVC doesn't treat |
| // "columns" as constant and therefore assumes that it's possible |
| // to overrun arrays indexed by "i". |
| // The conditional expression is constant warning is disabled since |
| // MSVC decides that "columns" *is* constant when unrolling the operation |
| // loop. |
| #pragma warning(disable : 4127) // conditional expression is constant |
| #pragma warning(disable : 4789) // buffer overrun |
| #if _MSC_VER >= 1900 // MSVC 2015 |
| #pragma warning(disable : 4456) // allow shadowing in unrolled loops |
| #pragma warning(disable : 4723) // suppress "potential divide by 0" warning |
| #endif // _MSC_VER >= 1900 |
| #endif // _MSC_VER |
| |
| /// @cond MATHFU_INTERNAL |
| /// The stride of a vector (e.g Vector<T, 3>) when cast as an array of floats. |
| #define MATHFU_VECTOR_STRIDE_FLOATS(vector) (sizeof(vector) / sizeof(float)) |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| /// This will unroll loops for matrices with <= 4 columns |
| #define MATHFU_MAT_OPERATION(OP) MATHFU_UNROLLED_LOOP(i, columns, OP) |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| /// This will perform a given OP on each matrix column and return the result |
| #define MATHFU_MAT_OPERATOR(OP) \ |
| { \ |
| Matrix<T, rows, columns> result; \ |
| MATHFU_MAT_OPERATION(result.data_[i] = (OP)); \ |
| return result; \ |
| } |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| /// This will perform a given OP on each matrix column |
| #define MATHFU_MAT_SELF_OPERATOR(OP) \ |
| { \ |
| MATHFU_MAT_OPERATION(OP); \ |
| return *this; \ |
| } |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| /// This macro will take the dot product for a row from data1 and a column from |
| /// data2. |
| #define MATHFU_MATRIX_4X4_DOT(data1, data2, r) \ |
| ((data1)[r] * (data2)[0] + (data1)[(r) + 4] * (data2)[1] + \ |
| (data1)[(r) + 8] * (data2)[2] + (data1)[(r) + 12] * (data2)[3]) |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| #define MATHFU_MATRIX_3X3_DOT(data1, data2, r, size) \ |
| ((data1)[r] * (data2)[0] + (data1)[(r) + (size)] * (data2)[1] + \ |
| (data1)[(r) + 2 * (size)] * (data2)[2]) |
| /// @endcond |
| |
| namespace mathfu { |
| |
| /// @cond MATHFU_INTERNAL |
| template <class T, int rows, int columns = rows> |
| class Matrix; |
| template <class T, int rows, int columns> |
| inline Matrix<T, rows, columns> IdentityHelper(); |
| template <bool check_invertible, class T, int rows, int columns> |
| inline bool InverseHelper(const Matrix<T, rows, columns>& m, |
| Matrix<T, rows, columns>* const inverse); |
| template <class T, int rows, int columns> |
| inline void TimesHelper(const Matrix<T, rows, columns>& m1, |
| const Matrix<T, rows, columns>& m2, |
| Matrix<T, rows, columns>* out_m); |
| template <class T, int rows, int columns> |
| static inline Matrix<T, rows, columns> OuterProductHelper( |
| const Vector<T, rows>& v1, const Vector<T, columns>& v2); |
| template <class T> |
| inline Matrix<T, 4, 4> PerspectiveHelper(T fovy, T aspect, T znear, T zfar, |
| T handedness); |
| template <class T> |
| static inline Matrix<T, 4, 4> OrthoHelper(T left, T right, T bottom, T top, |
| T znear, T zfar, T handedness); |
| template <class T> |
| static inline Matrix<T, 4, 4> LookAtHelper(const Vector<T, 3>& at, |
| const Vector<T, 3>& eye, |
| const Vector<T, 3>& up, |
| T handedness); |
| template <class T> |
| static inline bool UnProjectHelper(const Vector<T, 3>& window_coord, |
| const Matrix<T, 4, 4>& model_view, |
| const Matrix<T, 4, 4>& projection, |
| const float window_width, |
| const float window_height, |
| Vector<T, 3>& result); |
| |
| template <typename T, int rows, int columns, typename CompatibleT> |
| static inline Matrix<T, rows, columns> FromTypeHelper(const CompatibleT& compatible); |
| |
| template <typename T, int rows, int columns, typename CompatibleT> |
| static inline CompatibleT ToTypeHelper(const Matrix<T, rows, columns>& m); |
| /// @endcond |
| |
| /// @addtogroup mathfu_matrix |
| /// @{ |
| /// @class Matrix |
| /// @brief Matrix stores a set of "rows" by "columns" elements of type T |
| /// and provides functions that operate on the set of elements. |
| /// |
| /// @tparam T type of each element in the matrix. |
| /// @tparam rows Number of rows in the matrix. |
| /// @tparam columns Number of columns in the matrix. |
| template <class T, int rows, int columns> |
| class Matrix { |
| public: |
| /// @brief Construct a Matrix of uninitialized values. |
| inline Matrix() {} |
| |
| /// @brief Construct a Matrix from another Matrix copying each element. |
| //// |
| /// @param m Matrix that the data will be copied from. |
| inline Matrix(const Matrix<T, rows, columns>& m) { |
| MATHFU_MAT_OPERATION(data_[i] = m.data_[i]); |
| } |
| |
| /// @brief Construct a Matrix from a single float. |
| /// |
| /// @param s Scalar value used to initialize each element of the matrix. |
| explicit inline Matrix(const T& s) { |
| MATHFU_MAT_OPERATION((data_[i] = Vector<T, rows>(s))); |
| } |
| |
| /// @brief Construct a Matrix from four floats. |
| /// |
| /// @note This method only works with a 2x2 Matrix. |
| /// |
| /// @param s00 Value of the first row and column. |
| /// @param s10 Value of the second row, first column. |
| /// @param s01 Value of the first row, second column. |
| /// @param s11 Value of the second row and column. |
| inline Matrix(const T& s00, const T& s10, const T& s01, const T& s11) { |
| MATHFU_STATIC_ASSERT(rows == 2 && columns == 2); |
| data_[0] = Vector<T, rows>(s00, s10); |
| data_[1] = Vector<T, rows>(s01, s11); |
| } |
| |
| /// @brief Create a Matrix from nine floats. |
| /// |
| /// @note This method only works with a 3x3 Matrix. |
| /// |
| /// @param s00 Value of the first row and column. |
| /// @param s10 Value of the second row, first column. |
| /// @param s20 Value of the third row, first column. |
| /// @param s01 Value of the first row, second column. |
| /// @param s11 Value of the second row and column. |
| /// @param s21 Value of the third row, second column. |
| /// @param s02 Value of the first row, third column. |
| /// @param s12 Value of the second row, third column. |
| /// @param s22 Value of the third row and column. |
| inline Matrix(const T& s00, const T& s10, const T& s20, const T& s01, |
| const T& s11, const T& s21, const T& s02, const T& s12, |
| const T& s22) { |
| MATHFU_STATIC_ASSERT(rows == 3 && columns == 3); |
| data_[0] = Vector<T, rows>(s00, s10, s20); |
| data_[1] = Vector<T, rows>(s01, s11, s21); |
| data_[2] = Vector<T, rows>(s02, s12, s22); |
| } |
| |
| /// @brief Creates a Matrix from twelve floats. |
| /// |
| /// @note This method only works with Matrix<float, 4, 3>. |
| /// |
| /// |
| /// @param s00 Value of the first row and column. |
| /// @param s10 Value of the second row, first column. |
| /// @param s20 Value of the third row, first column. |
| /// @param s30 Value of the fourth row, first column. |
| /// @param s01 Value of the first row, second column. |
| /// @param s11 Value of the second row and column. |
| /// @param s21 Value of the third row, second column. |
| /// @param s31 Value of the fourth row, second column. |
| /// @param s02 Value of the first row, third column. |
| /// @param s12 Value of the second row, third column. |
| /// @param s22 Value of the third row and column. |
| /// @param s32 Value of the fourth row, third column. |
| inline Matrix(const T& s00, const T& s10, const T& s20, const T& s30, |
| const T& s01, const T& s11, const T& s21, const T& s31, |
| const T& s02, const T& s12, const T& s22, const T& s32) { |
| MATHFU_STATIC_ASSERT(rows == 4 && columns == 3); |
| data_[0] = Vector<T, rows>(s00, s10, s20, s30); |
| data_[1] = Vector<T, rows>(s01, s11, s21, s31); |
| data_[2] = Vector<T, rows>(s02, s12, s22, s32); |
| } |
| |
| /// @brief Create a Matrix from sixteen floats. |
| /// |
| /// @note This method only works with a 4x4 Matrix. |
| /// |
| /// @param s00 Value of the first row and column. |
| /// @param s10 Value of the second row, first column. |
| /// @param s20 Value of the third row, first column. |
| /// @param s30 Value of the fourth row, first column. |
| /// @param s01 Value of the first row, second column. |
| /// @param s11 Value of the second row and column. |
| /// @param s21 Value of the third row, second column. |
| /// @param s31 Value of the fourth row, second column. |
| /// @param s02 Value of the first row, third column. |
| /// @param s12 Value of the second row, third column. |
| /// @param s22 Value of the third row and column. |
| /// @param s32 Value of the fourth row, third column. |
| /// @param s03 Value of the first row, fourth column. |
| /// @param s13 Value of the second row, fourth column. |
| /// @param s23 Value of the third row, fourth column. |
| /// @param s33 Value of the fourth row and column. |
| inline Matrix(const T& s00, const T& s10, const T& s20, const T& s30, |
| const T& s01, const T& s11, const T& s21, const T& s31, |
| const T& s02, const T& s12, const T& s22, const T& s32, |
| const T& s03, const T& s13, const T& s23, const T& s33) { |
| MATHFU_STATIC_ASSERT(rows == 4 && columns == 4); |
| data_[0] = Vector<T, rows>(s00, s10, s20, s30); |
| data_[1] = Vector<T, rows>(s01, s11, s21, s31); |
| data_[2] = Vector<T, rows>(s02, s12, s22, s32); |
| data_[3] = Vector<T, rows>(s03, s13, s23, s33); |
| } |
| |
| /// @brief Create 4x4 Matrix from 4, 4 element vectors. |
| /// |
| /// @note This method only works with a 4x4 Matrix. |
| /// |
| /// @param column0 Vector used for the first column. |
| /// @param column1 Vector used for the second column. |
| /// @param column2 Vector used for the third column. |
| /// @param column3 Vector used for the fourth column. |
| inline Matrix(const Vector<T, 4>& column0, const Vector<T, 4>& column1, |
| const Vector<T, 4>& column2, const Vector<T, 4>& column3) { |
| MATHFU_STATIC_ASSERT(rows == 4 && columns == 4); |
| data_[0] = column0; |
| data_[1] = column1; |
| data_[2] = column2; |
| data_[3] = column3; |
| } |
| |
| /// @brief Create a Matrix from the first row * column elements of an array. |
| /// |
| /// @param a Array of values that the matrix will be iniitlized to. |
| explicit inline Matrix(const T* const a) { |
| MATHFU_MAT_OPERATION((data_[i] = Vector<T, rows>(&a[i * columns]))); |
| } |
| |
| /// @brief Create a Matrix from an array of "columns", "rows" element packed |
| /// vectors. |
| /// |
| /// @param vectors Array of "columns", "rows" element packed vectors. |
| explicit inline Matrix(const VectorPacked<T, rows>* const vectors) { |
| MATHFU_MAT_OPERATION((data_[i] = Vector<T, rows>(vectors[i]))); |
| } |
| |
| /// @brief Access an element of the matrix. |
| /// |
| /// @param row Index of the row to access. |
| /// @param column Index of the column to access. |
| /// @return Const reference to the element. |
| inline const T& operator()(const int row, const int column) const { |
| return data_[column][row]; |
| } |
| |
| /// @brief Access an element of the Matrix. |
| /// |
| /// @param row Index of the row to access. |
| /// @param column Index of the column to access. |
| /// @return Reference to the data that can be modified by the caller. |
| inline T& operator()(const int row, const int column) { |
| return data_[column][row]; |
| } |
| |
| /// @brief Access an element of the Matrix. |
| /// |
| /// @param i Index of the element to access in flattened memory. Where |
| /// the column accessed is i / rows and the row is i % rows. |
| /// @return Reference to the data that can be modified by the caller. |
| inline const T& operator()(const int i) const { return operator[](i); } |
| |
| /// @brief Access an element of the Matrix. |
| /// |
| /// @param i Index of the element to access in flattened memory. Where |
| /// the column accessed is i / rows and the row is i % rows. |
| /// @return Reference to the data that can be modified by the caller. |
| inline T& operator()(const int i) { return operator[](i); } |
| |
| /// @brief Access an element of the Matrix. |
| /// |
| /// @param i Index of the element to access in flattened memory. Where |
| /// the column accessed is i / rows and the row is i % rows. |
| /// @return Const reference to the data. |
| inline const T& operator[](const int i) const { |
| return const_cast<Matrix<T, rows, columns>*>(this)->operator[](i); |
| } |
| |
| /// @brief Access an element of the Matrix. |
| /// |
| /// @param i Index of the element to access in flattened memory. Where |
| /// the column accessed is i / rows and the row is i % rows. |
| /// @return Reference to the data that can be modified by the caller. |
| inline T& operator[](const int i) { |
| #if defined(MATHFU_COMPILE_WITH_PADDING) |
| // In this case Vector<T, 3> is padded, so the element offset must be |
| // accessed using the array operator. |
| if (rows == 3) { |
| const int row = i % rows; |
| const int col = i / rows; |
| return data_[col][row]; |
| } else { |
| return reinterpret_cast<T*>(data_)[i]; |
| } |
| #else |
| return reinterpret_cast<T*>(data_)[i]; |
| #endif // defined(MATHFU_COMPILE_WITH_PADDING) |
| } |
| |
| /// @brief Pack the matrix to an array of "rows" element vectors, |
| /// one vector per matrix column. |
| /// |
| /// @param vector Array of "columns" entries to write to. |
| inline void Pack(VectorPacked<T, rows>* const vector) const { |
| MATHFU_MAT_OPERATION(GetColumn(i).Pack(&vector[i])); |
| } |
| |
| /// @cond MATHFU_INTERNAL |
| /// @brief Access a column vector of the Matrix. |
| /// |
| /// @param i Index of the column to access. |
| /// @return Reference to the data that can be modified by the caller. |
| inline Vector<T, rows>& GetColumn(const int i) { return data_[i]; } |
| |
| /// @brief Access a column vector of the Matrix. |
| /// |
| /// @param i Index of the column to access. |
| /// @return Const reference to the data. |
| inline const Vector<T, rows>& GetColumn(const int i) const { |
| return data_[i]; |
| } |
| /// @endcond |
| |
| /// @brief Negate this Matrix. |
| /// |
| /// @return Matrix containing the result. |
| inline Matrix<T, rows, columns> operator-() const { |
| MATHFU_MAT_OPERATOR(-data_[i]); |
| } |
| |
| /// @brief Add a Matrix to this Matrix. |
| /// |
| /// @param m Matrix to add to this Matrix. |
| /// @return Matrix containing the result. |
| inline Matrix<T, rows, columns> operator+( |
| const Matrix<T, rows, columns>& m) const { |
| MATHFU_MAT_OPERATOR(data_[i] + m.data_[i]); |
| } |
| |
| /// @brief Subtract a Matrix from this Matrix. |
| /// |
| /// @param m Matrix to subtract from this Matrix. |
| /// @return Matrix containing the result. |
| inline Matrix<T, rows, columns> operator-( |
| const Matrix<T, rows, columns>& m) const { |
| MATHFU_MAT_OPERATOR(data_[i] - m.data_[i]); |
| } |
| |
| /// @brief Add a scalar to each element of this Matrix. |
| /// |
| /// @param s Scalar to add to this Matrix. |
| /// @return Matrix containing the result. |
| inline Matrix<T, rows, columns> operator+(const T& s) const { |
| MATHFU_MAT_OPERATOR(data_[i] + s); |
| } |
| |
| /// @brief Subtract a scalar from each element of this Matrix. |
| /// |
| /// @param s Scalar to subtract from this matrix. |
| /// @return Matrix containing the result. |
| inline Matrix<T, rows, columns> operator-(const T& s) const { |
| MATHFU_MAT_OPERATOR(data_[i] - s); |
| } |
| |
| /// @brief Multiply each element of this Matrix with a scalar. |
| /// |
| /// @param s Scalar to multiply with this Matrix. |
| /// @return Matrix containing the result. |
| inline Matrix<T, rows, columns> operator*(const T& s) const { |
| MATHFU_MAT_OPERATOR(data_[i] * s); |
| } |
| |
| /// @brief Divide each element of this Matrix with a scalar. |
| /// |
| /// @param s Scalar to divide this Matrix with. |
| /// @return Matrix containing the result. |
| inline Matrix<T, rows, columns> operator/(const T& s) const { |
| return (*this) * (1 / s); |
| } |
| |
| /// @brief Multiply this Matrix with another Matrix. |
| /// |
| /// @param m Matrix to multiply with this Matrix. |
| /// @return Matrix containing the result. |
| inline Matrix<T, rows, columns> operator*( |
| const Matrix<T, rows, columns>& m) const { |
| Matrix<T, rows, columns> result; |
| TimesHelper(*this, m, &result); |
| return result; |
| } |
| |
| /// @brief Add a Matrix to this Matrix (in-place). |
| /// |
| /// @param m Matrix to add to this Matrix. |
| /// @return Reference to this class. |
| inline Matrix<T, rows, columns>& operator+=( |
| const Matrix<T, rows, columns>& m) { |
| MATHFU_MAT_SELF_OPERATOR(data_[i] += m.data_[i]); |
| } |
| |
| /// @brief Subtract a Matrix from this Matrix (in-place). |
| /// |
| /// @param m Matrix to subtract from this Matrix. |
| /// @return Reference to this class. |
| inline Matrix<T, rows, columns>& operator-=( |
| const Matrix<T, rows, columns>& m) { |
| MATHFU_MAT_SELF_OPERATOR(data_[i] -= m.data_[i]); |
| } |
| |
| /// @brief Add a scalar to each element of this Matrix (in-place). |
| /// |
| /// @param s Scalar to add to each element of this Matrix. |
| /// @return Reference to this class. |
| inline Matrix<T, rows, columns>& operator+=(const T& s) { |
| MATHFU_MAT_SELF_OPERATOR(data_[i] += s); |
| } |
| |
| /// @brief Subtract a scalar from each element of this Matrix (in-place). |
| /// |
| /// @param s Scalar to subtract from each element of this Matrix. |
| /// @return Reference to this class. |
| inline Matrix<T, rows, columns>& operator-=(const T& s) { |
| MATHFU_MAT_SELF_OPERATOR(data_[i] -= s); |
| } |
| |
| /// @brief Multiply each element of this Matrix with a scalar (in-place). |
| /// |
| /// @param s Scalar to multiply with each element of this Matrix. |
| /// @return Reference to this class. |
| inline Matrix<T, rows, columns>& operator*=(const T& s) { |
| MATHFU_MAT_SELF_OPERATOR(data_[i] *= s); |
| } |
| |
| /// @brief Divide each element of this Matrix by a scalar (in-place). |
| /// |
| /// @param s Scalar to divide this Matrix by. |
| /// @return Reference to this class. |
| inline Matrix<T, rows, columns>& operator/=(const T& s) { |
| return (*this) *= (1 / s); |
| } |
| |
| /// @brief Multiply this Matrix with another Matrix (in-place). |
| /// |
| /// @param m Matrix to multiply with this Matrix. |
| /// @return Reference to this class. |
| inline Matrix<T, rows, columns>& operator*=( |
| const Matrix<T, rows, columns>& m) { |
| const Matrix<T, rows, columns> copy_of_this(*this); |
| TimesHelper(copy_of_this, m, this); |
| return *this; |
| } |
| |
| /// @brief Calculate the inverse of this Matrix. |
| /// |
| /// This calculates the inverse Matrix such that |
| /// <code>(m * m).Inverse()</code> is the identity. |
| /// @return Matrix containing the result. |
| inline Matrix<T, rows, columns> Inverse() const { |
| Matrix<T, rows, columns> inverse; |
| InverseHelper<false>(*this, &inverse); |
| return inverse; |
| } |
| |
| /// @brief Calculate the inverse of this Matrix. |
| /// |
| /// This calculates the inverse Matrix such that |
| /// <code>(m * m).Inverse()</code> is the identity. |
| /// By contrast to Inverse() this returns whether the matrix is invertible. |
| /// |
| /// The invertible check simply compares the calculated determinant with |
| /// Constants<T>::GetDeterminantThreshold() to roughly determine whether the |
| /// matrix is invertible. This simple check works in common cases but will |
| /// fail for corner cases where the matrix is a combination of huge and tiny |
| /// values that can't be accurately represented by the floating point |
| /// datatype T. More extensive checks (relative to the input values) are |
| /// possible but <b>far</b> more expensive, complicated and difficult to |
| /// test. |
| /// @return Whether the matrix is invertible. |
| inline bool InverseWithDeterminantCheck( |
| Matrix<T, rows, columns>* const inverse) const { |
| return InverseHelper<true>(*this, inverse); |
| } |
| |
| /// @brief Calculate the transpose of this Matrix. |
| /// |
| /// @return The transpose of the specified Matrix. |
| inline Matrix<T, columns, rows> Transpose() const { |
| Matrix<T, columns, rows> transpose; |
| MATHFU_UNROLLED_LOOP( |
| i, columns, MATHFU_UNROLLED_LOOP( |
| j, rows, transpose.GetColumn(j)[i] = GetColumn(i)[j])) |
| return transpose; |
| } |
| |
| /// @brief Get the 2-dimensional translation of a 2-dimensional affine |
| /// transform. |
| /// |
| /// @note 2-dimensional affine transforms are represented by 3x3 matrices. |
| /// @return Vector with the first two components of column 2 of this Matrix. |
| inline Vector<T, 2> TranslationVector2D() const { |
| MATHFU_STATIC_ASSERT(rows == 3 && columns == 3); |
| return Vector<T, 2>(data_[2][0], data_[2][1]); |
| } |
| |
| /// @brief Get the 3-dimensional translation of a 3-dimensional affine |
| /// transform. |
| /// |
| /// @note 3-dimensional affine transforms are represented by 4x4 matrices. |
| /// @return Vector with the first three components of column 3. |
| inline Vector<T, 3> TranslationVector3D() const { |
| MATHFU_STATIC_ASSERT(rows == 4 && columns == 4); |
| return Vector<T, 3>(data_[3][0], data_[3][1], data_[3][2]); |
| } |
| |
| /// @brief Load from any byte-wise compatible external matrix. |
| /// |
| /// Format should be `columns` vectors, each holding `rows` values of type T. |
| /// |
| /// Use this for safe conversion from external matrix classes. |
| /// Often, external libraries will have their own matrix types that are, |
| /// byte-for-byte, exactly the same as mathfu::Matrix. This function allows |
| /// you to load a mathfu::Matrix from those external types, without potential |
| /// aliasing bugs that are caused by casting. |
| /// |
| /// @note If your external type gives you access to a T*, then you can |
| /// equivalently use the Matrix(const T*) constructor. |
| /// |
| /// @param compatible reference to a byte-wise compatible matrix structure; |
| /// array of columns x rows Ts. |
| /// @returns `compatible` loaded as a mathfu::Matrix. |
| template <typename CompatibleT> |
| static inline Matrix<T, rows, columns> FromType(const CompatibleT& compatible) { |
| return FromTypeHelper<T, rows, columns, CompatibleT>(compatible); |
| } |
| |
| /// @brief Load into any byte-wise compatible external matrix. |
| /// |
| /// Format should be `columns` vectors, each holding `rows` values of type T. |
| /// |
| /// Use this for safe conversion to external matrix classes. |
| /// Often, external libraries will have their own matrix types that are, |
| /// byte-for-byte, exactly the same as mathfu::Matrix. This function allows |
| /// you to load an external type from a mathfu::Matrix, without potential |
| /// aliasing bugs that are caused by casting. |
| /// |
| /// @param m reference to mathfu::Matrix to convert. |
| /// @returns CompatibleT loaded from m. |
| template <typename CompatibleT> |
| static inline CompatibleT ToType(const Matrix<T, rows, columns>& m) { |
| return ToTypeHelper<T, rows, columns, CompatibleT>(m); |
| } |
| |
| /// @brief Calculate the outer product of two Vectors. |
| /// |
| /// @return Matrix containing the result. |
| static inline Matrix<T, rows, columns> OuterProduct( |
| const Vector<T, rows>& v1, const Vector<T, columns>& v2) { |
| return OuterProductHelper(v1, v2); |
| } |
| |
| /// @brief Calculate the hadamard / component-wise product of two matrices. |
| /// |
| /// @param m1 First Matrix. |
| /// @param m2 Second Matrix. |
| /// @return Matrix containing the result. |
| static inline Matrix<T, rows, columns> HadamardProduct( |
| const Matrix<T, rows, columns>& m1, const Matrix<T, rows, columns>& m2) { |
| MATHFU_MAT_OPERATOR(m1[i] * m2[i]); |
| } |
| |
| /// @brief Calculate the identity Matrix. |
| /// |
| /// @return Matrix containing the result. |
| static inline Matrix<T, rows, columns> Identity() { |
| return IdentityHelper<T, rows, columns>(); |
| } |
| |
| /// @brief Create a 3x3 translation Matrix from a 2-dimensional Vector. |
| /// |
| /// This matrix will have an empty or zero rotation component. |
| /// |
| /// @param v Vector of size 2. |
| /// @return Matrix containing the result. |
| static inline Matrix<T, 3> FromTranslationVector(const Vector<T, 2>& v) { |
| return Matrix<T, 3>(1, 0, 0, 0, 1, 0, v[0], v[1], 1); |
| } |
| |
| /// @brief Create a 4x4 translation Matrix from a 3-dimensional Vector. |
| /// |
| /// This matrix will have an empty or zero rotation component. |
| /// |
| /// @param v The vector of size 3. |
| /// @return Matrix containing the result. |
| static inline Matrix<T, 4> FromTranslationVector(const Vector<T, 3>& v) { |
| return Matrix<T, 4>(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, v[0], v[1], v[2], |
| 1); |
| } |
| |
| /// @brief Create a square Matrix with the diagonal component set to v. |
| /// |
| /// This is an affine transform matrix, so the dimension of the vector is |
| /// one less than the dimension of the matrix. |
| /// |
| /// @param v Vector containing components for scaling. |
| /// @return Matrix with v along the diagonal, and 1 in the bottom right. |
| static inline Matrix<T, rows> FromScaleVector(const Vector<T, rows - 1>& v) { |
| // TODO OPT: Use a helper function in a similar way to Identity to |
| // construct the matrix for the specialized cases 2, 3, 4, and only run |
| // this method in the general case. This will also allow you to use the |
| // helper methods from specialized classes like Matrix<T, 4, 4>. |
| Matrix<T, rows> return_matrix(Identity()); |
| for (int i = 0; i < rows - 1; ++i) return_matrix(i, i) = v[i]; |
| return return_matrix; |
| } |
| |
| /// @brief Create a 4x4 Matrix from a 3x3 rotation Matrix. |
| /// |
| /// This Matrix will have an empty or zero translation component. |
| /// |
| /// @param m 3x3 rotation Matrix. |
| /// @return Matrix containing the result. |
| static inline Matrix<T, 4> FromRotationMatrix(const Matrix<T, 3>& m) { |
| return Matrix<T, 4>(m[0], m[1], m[2], 0, m[3], m[4], m[5], 0, m[6], m[7], |
| m[8], 0, 0, 0, 0, 1); |
| } |
| |
| /// @brief Constructs a Matrix<float, 4> from an AffineTransform. |
| /// |
| /// @param affine An AffineTransform reference to be used to construct |
| /// a Matrix<float, 4> by adding in the 'w' row of [0, 0, 0, 1]. |
| static inline Matrix<T, 4> FromAffineTransform( |
| const Matrix<T, 4, 3>& affine) { |
| return Matrix<T, 4>(affine[0], affine[4], affine[8], static_cast<T>(0), |
| affine[1], affine[5], affine[9], static_cast<T>(0), |
| affine[2], affine[6], affine[10], static_cast<T>(0), |
| affine[3], affine[7], affine[11], static_cast<T>(1)); |
| } |
| |
| /// @brief Converts a Matrix<float, 4> into an AffineTransform. |
| /// |
| /// @param m A Matrix<float, 4> reference to be converted into an |
| /// AffineTransform by dropping the fixed 'w' row. |
| /// |
| /// @return Returns an AffineTransform that contains the essential |
| /// transformation data from the Matrix<float, 4>. |
| static inline Matrix<T, 4, 3> ToAffineTransform(const Matrix<T, 4>& m) { |
| return Matrix<T, 4, 3>(m[0], m[4], m[8], m[12], m[1], m[5], m[9], m[13], |
| m[2], m[6], m[10], m[14]); |
| } |
| |
| /// @brief Create a 3x3 rotation Matrix from a 2D normalized directional |
| /// Vector around the X axis. |
| /// |
| /// @param v 2D normalized directional Vector. |
| /// @return Matrix containing the result. |
| static inline Matrix<T, 3> RotationX(const Vector<T, 2>& v) { |
| return Matrix<T, 3>(1, 0, 0, 0, v.x, v.y, 0, -v.y, v.x); |
| } |
| |
| /// @brief Create a 3x3 rotation Matrix from a 2D normalized directional |
| /// Vector around the Y axis. |
| /// |
| /// @param v 2D normalized directional Vector. |
| /// @return Matrix containing the result. |
| static inline Matrix<T, 3> RotationY(const Vector<T, 2>& v) { |
| return Matrix<T, 3>(v.x, 0, -v.y, 0, 1, 0, v.y, 0, v.x); |
| } |
| |
| /// @brief Create a 3x3 rotation Matrix from a 2D normalized directional |
| /// Vector around the Z axis. |
| /// |
| /// @param v 2D normalized directional Vector. |
| /// @return Matrix containing the result. |
| static inline Matrix<T, 3> RotationZ(const Vector<T, 2>& v) { |
| return Matrix<T, 3>(v.x, v.y, 0, -v.y, v.x, 0, 0, 0, 1); |
| } |
| |
| /// @brief Create a 3x3 rotation Matrix from an angle (in radians) around |
| /// the X axis. |
| /// |
| /// @param angle Angle (in radians). |
| /// @return Matrix containing the result. |
| static inline Matrix<T, 3> RotationX(T angle) { |
| return RotationX(Vector<T, 2>(cosf(angle), sinf(angle))); |
| } |
| |
| /// @brief Create a 3x3 rotation Matrix from an angle (in radians) around |
| /// the Y axis. |
| /// |
| /// @param angle Angle (in radians). |
| /// @return Matrix containing the result. |
| static inline Matrix<T, 3> RotationY(T angle) { |
| return RotationY(Vector<T, 2>(cosf(angle), sinf(angle))); |
| } |
| |
| /// @brief Create a 3x3 rotation Matrix from an angle (in radians) |
| /// around the Z axis. |
| /// |
| /// @param angle Angle (in radians). |
| /// @return Matrix containing the result. |
| static inline Matrix<T, 3> RotationZ(T angle) { |
| return RotationZ(Vector<T, 2>(cosf(angle), sinf(angle))); |
| } |
| |
| /// @brief Create a 4x4 perspective Matrix. |
| /// |
| /// @param fovy Field of view. |
| /// @param aspect Aspect ratio. |
| /// @param znear Near plane location. |
| /// @param zfar Far plane location. |
| /// @param handedness 1.0f for RH, -1.0f for LH |
| /// @return 4x4 perspective Matrix. |
| static inline Matrix<T, 4, 4> Perspective(T fovy, T aspect, T znear, T zfar, |
| T handedness = 1) { |
| return PerspectiveHelper(fovy, aspect, znear, zfar, handedness); |
| } |
| |
| /// @brief Create a 4x4 orthographic Matrix. |
| /// |
| /// @param left Left extent. |
| /// @param right Right extent. |
| /// @param bottom Bottom extent. |
| /// @param top Top extent. |
| /// @param znear Near plane location. |
| /// @param zfar Far plane location. |
| /// @param handedness 1.0f for RH, -1.0f for LH |
| /// @return 4x4 orthographic Matrix. |
| static inline Matrix<T, 4, 4> Ortho(T left, T right, T bottom, T top, T znear, |
| T zfar, T handedness = 1) { |
| return OrthoHelper(left, right, bottom, top, znear, zfar, handedness); |
| } |
| |
| /// @brief Create a 3-dimensional camera Matrix. |
| /// |
| /// @param at The look-at target of the camera. |
| /// @param eye The position of the camera. |
| /// @param up The up vector in the world, for example (0, 1, 0) if the |
| /// y-axis is up. |
| /// @param handedness 1.0f for RH, -1.0f for LH. |
| /// @return 3-dimensional camera Matrix. |
| /// TODO: Change default handedness to +1 so that it matches Perspective(). |
| static inline Matrix<T, 4, 4> LookAt(const Vector<T, 3>& at, |
| const Vector<T, 3>& eye, |
| const Vector<T, 3>& up, |
| T handedness = -1) { |
| return LookAtHelper(at, eye, up, handedness); |
| } |
| |
| /// @brief Get the 3D position in object space from a window coordinate. |
| /// |
| /// @param window_coord The window coordinate. The z value is for depth. |
| /// A window coordinate on the near plane will have 0 as the z value. |
| /// And a window coordinate on the far plane will have 1 as the z value. |
| /// z value should be with in [0, 1] here. |
| /// @param model_view The Model View matrix. |
| /// @param projection The projection matrix. |
| /// @param window_width Width of the window. |
| /// @param window_height Height of the window. |
| /// @return the mapped 3D position in object space. |
| static inline Vector<T, 3> UnProject(const Vector<T, 3>& window_coord, |
| const Matrix<T, 4, 4>& model_view, |
| const Matrix<T, 4, 4>& projection, |
| const float window_width, |
| const float window_height) { |
| Vector<T, 3> result; |
| UnProjectHelper(window_coord, model_view, projection, window_width, |
| window_height, result); |
| return result; |
| } |
| |
| /// @brief Multiply a Vector by a Matrix. |
| /// |
| /// @param v Vector to multiply. |
| /// @param m Matrix to multiply. |
| /// @return Matrix containing the result. |
| friend inline Vector<T, columns> operator*( |
| const Vector<T, rows>& v, const Matrix<T, rows, columns>& m) { |
| const int d = columns; |
| MATHFU_VECTOR_OPERATOR((Vector<T, rows>::DotProduct(m.data_[i], v))); |
| } |
| |
| // Dimensions of the matrix. |
| /// Number of rows in the matrix. |
| static const int kRows = rows; |
| /// Number of columns in the matrix. |
| static const int kColumns = columns; |
| /// Total number of elements in the matrix. |
| static const int kElements = rows * columns; |
| |
| MATHFU_DEFINE_CLASS_SIMD_AWARE_NEW_DELETE |
| |
| private: |
| Vector<T, rows> data_[columns]; |
| }; |
| /// @} |
| |
| /// @addtogroup mathfu_matrix |
| /// @{ |
| |
| /// @brief Multiply each element of a Matrix by a scalar. |
| /// |
| /// @param s Scalar to multiply by. |
| /// @param m Matrix to multiply. |
| /// @return Matrix containing the result. |
| /// @tparam T Type of each element in the Matrix and the scalar type. |
| /// @tparam rows Number of rows in the matrix. |
| /// @tparam columns Number of columns in the matrix. |
| /// |
| /// @related mathfu::Matrix |
| template <class T, int rows, int columns> |
| inline Matrix<T, rows, columns> operator*(const T& s, |
| const Matrix<T, columns, rows>& m) { |
| return m * s; |
| } |
| |
| /// @brief Multiply a Matrix by a Vector. |
| /// |
| /// @note Template specialized versions are implemented for 2x2, 3x3, and 4x4 |
| /// matrices to increase performance. The 3x3 float is also specialized |
| /// to supported padded the 3-dimensional Vector in SIMD build configurations. |
| /// |
| /// @param m Matrix to multiply. |
| /// @param v Vector to multiply. |
| /// @return Vector containing the result. |
| /// |
| /// @related mathfu::Matrix |
| template <class T, int rows, int columns> |
| inline Vector<T, rows> operator*(const Matrix<T, rows, columns>& m, |
| const Vector<T, columns>& v) { |
| const Vector<T, rows> result(0); |
| int offset = 0; |
| for (int column = 0; column < columns; column++) { |
| for (int row = 0; row < rows; row++) { |
| result[row] += m[offset + row] * v[column]; |
| } |
| offset += rows; |
| } |
| return result; |
| } |
| |
| /// @cond MATHFU_INTERNAL |
| template <class T> |
| inline Vector<T, 2> operator*(const Matrix<T, 2, 2>& m, const Vector<T, 2>& v) { |
| return Vector<T, 2>(m[0] * v[0] + m[2] * v[1], m[1] * v[0] + m[3] * v[1]); |
| } |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| template <class T> |
| inline Vector<T, 3> operator*(const Matrix<T, 3, 3>& m, const Vector<T, 3>& v) { |
| return Vector<T, 3>(MATHFU_MATRIX_3X3_DOT(&m[0], v, 0, 3), |
| MATHFU_MATRIX_3X3_DOT(&m[0], v, 1, 3), |
| MATHFU_MATRIX_3X3_DOT(&m[0], v, 2, 3)); |
| } |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| template <> |
| inline Vector<float, 3> operator*(const Matrix<float, 3, 3>& m, |
| const Vector<float, 3>& v) { |
| return Vector<float, 3>( |
| MATHFU_MATRIX_3X3_DOT(&m[0], v, 0, MATHFU_VECTOR_STRIDE_FLOATS(v)), |
| MATHFU_MATRIX_3X3_DOT(&m[0], v, 1, MATHFU_VECTOR_STRIDE_FLOATS(v)), |
| MATHFU_MATRIX_3X3_DOT(&m[0], v, 2, MATHFU_VECTOR_STRIDE_FLOATS(v))); |
| } |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| template <class T> |
| inline Vector<T, 4> operator*(const Matrix<T, 4, 4>& m, const Vector<T, 4>& v) { |
| return Vector<T, 4>( |
| MATHFU_MATRIX_4X4_DOT(&m[0], v, 0), MATHFU_MATRIX_4X4_DOT(&m[0], v, 1), |
| MATHFU_MATRIX_4X4_DOT(&m[0], v, 2), MATHFU_MATRIX_4X4_DOT(&m[0], v, 3)); |
| } |
| /// @endcond |
| |
| /// @brief Multiply a 4x4 Matrix by a 3-dimensional Vector. |
| /// |
| /// This is provided as a convenience and assumes the vector has a fourth |
| /// component equal to 1. |
| /// |
| /// @param m 4x4 Matrix. |
| /// @param v 3-dimensional Vector. |
| /// @return 3-dimensional Vector result. |
| /// |
| /// @related mathfu::Matrix |
| template <class T> |
| inline Vector<T, 3> operator*(const Matrix<T, 4, 4>& m, const Vector<T, 3>& v) { |
| Vector<T, 4> v4(v[0], v[1], v[2], 1); |
| v4 = m * v4; |
| return Vector<T, 3>(v4[0] / v4[3], v4[1] / v4[3], v4[2] / v4[3]); |
| } |
| |
| /// @cond MATHFU_INTERNAL |
| /// @brief Multiply a Matrix with another Matrix. |
| /// |
| /// @note Template specialized versions are implemented for 2x2, 3x3, and 4x4 |
| /// matrices to improve performance. 3x3 float is also specialized because if |
| /// SIMD is used the vectors of this type of length 4. |
| /// |
| /// @param m1 Matrix to multiply. |
| /// @param m2 Matrix to multiply. |
| /// @param out_m Pointer to a Matrix which receives the result. |
| /// |
| /// @tparam T Type of each element in the returned Matrix. |
| /// @tparam size1 Number of rows in the returned Matrix and columns in m1. |
| /// @tparam size2 Number of columns in the returned Matrix and rows in m2. |
| /// @tparam size3 Number of columns in m3. |
| template <class T, int size1, int size2, int size3> |
| inline void TimesHelper(const Matrix<T, size1, size2>& m1, |
| const Matrix<T, size2, size3>& m2, |
| Matrix<T, size1, size3>* out_m) { |
| for (int i = 0; i < size1; i++) { |
| for (int j = 0; j < size3; j++) { |
| Vector<T, size2> row; |
| for (int k = 0; k < size2; k++) { |
| row[k] = m1(i, k); |
| } |
| (*out_m)(i, j) = Vector<T, size2>::DotProduct(m2.GetColumn(j), row); |
| } |
| } |
| } |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| template <class T> |
| inline void TimesHelper(const Matrix<T, 2, 2>& m1, const Matrix<T, 2, 2>& m2, |
| Matrix<T, 2, 2>* out_m) { |
| Matrix<T, 2, 2>& out = *out_m; |
| out[0] = m1[0] * m2[0] + m1[2] * m2[1]; |
| out[1] = m1[1] * m2[0] + m1[3] * m2[1]; |
| out[2] = m1[0] * m2[2] + m1[2] * m2[3]; |
| out[3] = m1[1] * m2[2] + m1[3] * m2[3]; |
| } |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| template <typename T> |
| inline void TimesHelper(const Matrix<T, 3, 3>& m1, const Matrix<T, 3, 3>& m2, |
| Matrix<T, 3, 3>* out_m) { |
| Matrix<T, 3, 3>& out = *out_m; |
| { |
| Vector<T, 3> row(m1[0], m1[3], m1[6]); |
| out[0] = Vector<T, 3>::DotProduct(m2.GetColumn(0), row); |
| out[3] = Vector<T, 3>::DotProduct(m2.GetColumn(1), row); |
| out[6] = Vector<T, 3>::DotProduct(m2.GetColumn(2), row); |
| } |
| { |
| Vector<T, 3> row(m1[1], m1[4], m1[7]); |
| out[1] = Vector<T, 3>::DotProduct(m2.GetColumn(0), row); |
| out[4] = Vector<T, 3>::DotProduct(m2.GetColumn(1), row); |
| out[7] = Vector<T, 3>::DotProduct(m2.GetColumn(2), row); |
| } |
| { |
| Vector<T, 3> row(m1[2], m1[5], m1[8]); |
| out[2] = Vector<T, 3>::DotProduct(m2.GetColumn(0), row); |
| out[5] = Vector<T, 3>::DotProduct(m2.GetColumn(1), row); |
| out[8] = Vector<T, 3>::DotProduct(m2.GetColumn(2), row); |
| } |
| } |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| template <class T> |
| inline void TimesHelper(const Matrix<T, 4, 4>& m1, const Matrix<T, 4, 4>& m2, |
| Matrix<T, 4, 4>* out_m) { |
| Matrix<T, 4, 4>& out = *out_m; |
| { |
| Vector<T, 4> row(m1[0], m1[4], m1[8], m1[12]); |
| out[0] = Vector<T, 4>::DotProduct(m2.GetColumn(0), row); |
| out[4] = Vector<T, 4>::DotProduct(m2.GetColumn(1), row); |
| out[8] = Vector<T, 4>::DotProduct(m2.GetColumn(2), row); |
| out[12] = Vector<T, 4>::DotProduct(m2.GetColumn(3), row); |
| } |
| { |
| Vector<T, 4> row(m1[1], m1[5], m1[9], m1[13]); |
| out[1] = Vector<T, 4>::DotProduct(m2.GetColumn(0), row); |
| out[5] = Vector<T, 4>::DotProduct(m2.GetColumn(1), row); |
| out[9] = Vector<T, 4>::DotProduct(m2.GetColumn(2), row); |
| out[13] = Vector<T, 4>::DotProduct(m2.GetColumn(3), row); |
| } |
| { |
| Vector<T, 4> row(m1[2], m1[6], m1[10], m1[14]); |
| out[2] = Vector<T, 4>::DotProduct(m2.GetColumn(0), row); |
| out[6] = Vector<T, 4>::DotProduct(m2.GetColumn(1), row); |
| out[10] = Vector<T, 4>::DotProduct(m2.GetColumn(2), row); |
| out[14] = Vector<T, 4>::DotProduct(m2.GetColumn(3), row); |
| } |
| { |
| Vector<T, 4> row(m1[3], m1[7], m1[11], m1[15]); |
| out[3] = Vector<T, 4>::DotProduct(m2.GetColumn(0), row); |
| out[7] = Vector<T, 4>::DotProduct(m2.GetColumn(1), row); |
| out[11] = Vector<T, 4>::DotProduct(m2.GetColumn(2), row); |
| out[15] = Vector<T, 4>::DotProduct(m2.GetColumn(3), row); |
| } |
| } |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| /// @brief Compute the identity matrix. |
| /// |
| /// @note There are template specializations for 2x2, 3x3, and 4x4 matrices to |
| /// increase performance. |
| /// |
| /// @return Identity Matrix. |
| /// @tparam T Type of each element in the returned Matrix. |
| /// @tparam rows Number of rows in the returned Matrix. |
| /// @tparam columns Number of columns in the returned Matrix. |
| template <class T, int rows, int columns> |
| inline Matrix<T, rows, columns> IdentityHelper() { |
| Matrix<T, rows, columns> return_matrix(0.f); |
| int min_d = rows < columns ? rows : columns; |
| for (int i = 0; i < min_d; ++i) return_matrix(i, i) = 1; |
| return return_matrix; |
| } |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| template <class T> |
| inline Matrix<T, 2, 2> IdentityHelper() { |
| return Matrix<T, 2, 2>(1, 0, 0, 1); |
| } |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| template <class T> |
| inline Matrix<T, 3, 3> IdentityHelper() { |
| return Matrix<T, 3, 3>(1, 0, 0, 0, 1, 0, 0, 0, 1); |
| } |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| template <class T> |
| inline Matrix<T, 4, 4> IdentityHelper() { |
| return Matrix<T, 4, 4>(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); |
| } |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| /// @brief Compute the outer product of two vectors. |
| /// |
| /// @note There are template specialization for 2x2, 3x3, and 4x4 matrices to |
| /// increase performance. |
| template <class T, int rows, int columns> |
| static inline Matrix<T, rows, columns> OuterProductHelper( |
| const Vector<T, rows>& v1, const Vector<T, columns>& v2) { |
| Matrix<T, rows, columns> result(0); |
| int offset = 0; |
| for (int column = 0; column < columns; column++) { |
| for (int row = 0; row < rows; row++) { |
| result[row + offset] = v1[row] * v2[column]; |
| } |
| offset += rows; |
| } |
| return result; |
| } |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| template <class T> |
| static inline Matrix<T, 2, 2> OuterProductHelper(const Vector<T, 2>& v1, |
| const Vector<T, 2>& v2) { |
| return Matrix<T, 2, 2>(v1[0] * v2[0], v1[1] * v2[0], v1[0] * v2[1], |
| v1[1] * v2[1]); |
| } |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| template <class T> |
| static inline Matrix<T, 3, 3> OuterProductHelper(const Vector<T, 3>& v1, |
| const Vector<T, 3>& v2) { |
| return Matrix<T, 3, 3>(v1[0] * v2[0], v1[1] * v2[0], v1[2] * v2[0], |
| v1[0] * v2[1], v1[1] * v2[1], v1[2] * v2[1], |
| v1[0] * v2[2], v1[1] * v2[2], v1[2] * v2[2]); |
| } |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| template <class T> |
| static inline Matrix<T, 4, 4> OuterProductHelper(const Vector<T, 4>& v1, |
| const Vector<T, 4>& v2) { |
| return Matrix<T, 4, 4>( |
| v1[0] * v2[0], v1[1] * v2[0], v1[2] * v2[0], v1[3] * v2[0], v1[0] * v2[1], |
| v1[1] * v2[1], v1[2] * v2[1], v1[3] * v2[1], v1[0] * v2[2], v1[1] * v2[2], |
| v1[2] * v2[2], v1[3] * v2[2], v1[0] * v2[3], v1[1] * v2[3], v1[2] * v2[3], |
| v1[3] * v2[3]); |
| } |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| /// Struct used for template specialization for functions that |
| /// returns constants. |
| template <class T> |
| class Constants { |
| public: |
| /// Minimum absolute value of the determinant of an invertible matrix. |
| static T GetDeterminantThreshold() { |
| // No constant defined for the general case. |
| assert(false); |
| return 0; |
| } |
| }; |
| /// @endcond |
| |
| /// Functions that return constants for <code>float</code> values. |
| template <> |
| class Constants<float> { |
| public: |
| /// @brief Minimum absolute value of the determinant of an invertible |
| /// <code>float</code> Matrix. |
| /// |
| /// <code>float</code> values have 23 bits of precision which is roughly |
| /// 1e7f, given that the final step of matrix inversion is multiplication |
| /// with the inverse of the determinant, the minimum value of the |
| /// determinant is 1e-7f before the precision too low to accurately |
| /// calculate the inverse. |
| /// @returns Minimum absolute value of the determinant of an invertible |
| /// <code>float</code> Matrix. |
| /// |
| /// @related mathfu::Matrix::InverseWithDeterminantCheck() |
| static float GetDeterminantThreshold() { return 1e-7f; } |
| }; |
| |
| /// Functions that return constants for <code>double</code> values. |
| template <> |
| class Constants<double> { |
| public: |
| /// @brief Minimum absolute value of the determinant of an invertible |
| /// <code>double</code> Matrix. |
| /// |
| /// <code>double</code> values have 46 bits of precision which is roughly |
| /// 1e15, given that the final step of matrix inversion is multiplication |
| /// with the inverse of the determinant, the minimum value of the |
| /// determinant is 1e-15 before the precision too low to accurately |
| /// calculate the inverse. |
| /// @returns Minimum absolute value of the determinant of an invertible |
| /// <code>double</code> Matrix. |
| /// |
| /// @related mathfu::Matrix::InverseWithDeterminantCheck() |
| static double GetDeterminantThreshold() { return 1e-15; } |
| }; |
| |
| /// @cond MATHFU_INTERNAL |
| /// @brief Compute the inverse of a matrix. |
| /// |
| /// There is template specialization for 2x2, 3x3, and 4x4 matrices to |
| /// increase performance. Inverse is not implemented for dense matrices that |
| /// are not of size 2x2, 3x3, and 4x4. If check_invertible is true the |
| /// determine of the matrix is compared with |
| /// Constants<T>::GetDeterminantThreshold() to roughly determine whether the |
| /// Matrix is invertible. |
| template <bool check_invertible, class T, int rows, int columns> |
| inline bool InverseHelper(const Matrix<T, rows, columns>& m, |
| Matrix<T, rows, columns>* const inverse) { |
| assert(false); |
| (void)m; |
| *inverse = T::Identity(); |
| return false; |
| } |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| template <bool check_invertible, class T> |
| inline bool InverseHelper(const Matrix<T, 2, 2>& m, |
| Matrix<T, 2, 2>* const inverse) { |
| T determinant = m[0] * m[3] - m[1] * m[2]; |
| if (check_invertible && |
| fabs(determinant) < Constants<T>::GetDeterminantThreshold()) { |
| return false; |
| } |
| T inverseDeterminant = 1 / determinant; |
| (*inverse)[0] = inverseDeterminant * m[3]; |
| (*inverse)[1] = -inverseDeterminant * m[1]; |
| (*inverse)[2] = -inverseDeterminant * m[2]; |
| (*inverse)[3] = inverseDeterminant * m[0]; |
| return true; |
| } |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| template <bool check_invertible, class T> |
| inline bool InverseHelper(const Matrix<T, 3, 3>& m, |
| Matrix<T, 3, 3>* const inverse) { |
| // Find determinant of matrix. |
| T sub11 = m[4] * m[8] - m[5] * m[7], sub12 = -m[1] * m[8] + m[2] * m[7], |
| sub13 = m[1] * m[5] - m[2] * m[4]; |
| T determinant = m[0] * sub11 + m[3] * sub12 + m[6] * sub13; |
| if (check_invertible && |
| fabs(determinant) < Constants<T>::GetDeterminantThreshold()) { |
| return false; |
| } |
| // Find determinants of 2x2 submatrices for the elements of the inverse. |
| *inverse = Matrix<T, 3, 3>( |
| sub11, sub12, sub13, m[6] * m[5] - m[3] * m[8], m[0] * m[8] - m[6] * m[2], |
| m[3] * m[2] - m[0] * m[5], m[3] * m[7] - m[6] * m[4], |
| m[6] * m[1] - m[0] * m[7], m[0] * m[4] - m[3] * m[1]); |
| *(inverse) *= 1 / determinant; |
| return true; |
| } |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| template <class T> |
| inline int FindLargestPivotElem(const Matrix<T, 4, 4>& m) { |
| Vector<T, 4> fabs_column(fabs(m[0]), fabs(m[1]), fabs(m[2]), fabs(m[3])); |
| if (fabs_column[0] > fabs_column[1]) { |
| if (fabs_column[0] > fabs_column[2]) { |
| if (fabs_column[0] > fabs_column[3]) { |
| return 0; |
| } else { |
| return 3; |
| } |
| } else if (fabs_column[2] > fabs_column[3]) { |
| return 2; |
| } else { |
| return 3; |
| } |
| } else if (fabs_column[1] > fabs_column[2]) { |
| if (fabs_column[1] > fabs_column[3]) { |
| return 1; |
| } else { |
| return 3; |
| } |
| } else if (fabs_column[2] > fabs_column[3]) { |
| return 2; |
| } else { |
| return 3; |
| } |
| } |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| template <bool check_invertible, class T> |
| bool InverseHelper(const Matrix<T, 4, 4>& m, Matrix<T, 4, 4>* const inverse) { |
| // This will find the pivot element. |
| int pivot_elem = FindLargestPivotElem(m); |
| // This will perform the pivot and find the row, column, and 3x3 submatrix |
| // for this pivot. |
| Vector<T, 3> row, column; |
| Matrix<T, 3> matrix; |
| if (pivot_elem == 0) { |
| row = Vector<T, 3>(m[4], m[8], m[12]); |
| column = Vector<T, 3>(m[1], m[2], m[3]); |
| matrix = |
| Matrix<T, 3>(m[5], m[6], m[7], m[9], m[10], m[11], m[13], m[14], m[15]); |
| } else if (pivot_elem == 1) { |
| row = Vector<T, 3>(m[5], m[9], m[13]); |
| column = Vector<T, 3>(m[0], m[2], m[3]); |
| matrix = |
| Matrix<T, 3>(m[4], m[6], m[7], m[8], m[10], m[11], m[12], m[14], m[15]); |
| } else if (pivot_elem == 2) { |
| row = Vector<T, 3>(m[6], m[10], m[14]); |
| column = Vector<T, 3>(m[0], m[1], m[3]); |
| matrix = |
| Matrix<T, 3>(m[4], m[5], m[7], m[8], m[9], m[11], m[12], m[13], m[15]); |
| } else { |
| row = Vector<T, 3>(m[7], m[11], m[15]); |
| column = Vector<T, 3>(m[0], m[1], m[2]); |
| matrix = |
| Matrix<T, 3>(m[4], m[5], m[6], m[8], m[9], m[10], m[12], m[13], m[14]); |
| } |
| T pivot_value = m[pivot_elem]; |
| if (check_invertible && |
| fabs(pivot_value) < Constants<T>::GetDeterminantThreshold()) { |
| return false; |
| } |
| // This will compute the inverse using the row, column, and 3x3 submatrix. |
| T inv = -1 / pivot_value; |
| row *= inv; |
| matrix += Matrix<T, 3>::OuterProduct(column, row); |
| Matrix<T, 3> mat_inverse; |
| if (!InverseHelper<check_invertible>(matrix, &mat_inverse) && |
| check_invertible) { |
| return false; |
| } |
| Vector<T, 3> col_inverse = mat_inverse * (column * inv); |
| Vector<T, 3> row_inverse = row * mat_inverse; |
| T pivot_inverse = Vector<T, 3>::DotProduct(row, col_inverse) - inv; |
| if (pivot_elem == 0) { |
| *inverse = Matrix<T, 4, 4>( |
| pivot_inverse, col_inverse[0], col_inverse[1], col_inverse[2], |
| row_inverse[0], mat_inverse[0], mat_inverse[1], mat_inverse[2], |
| row_inverse[1], mat_inverse[3], mat_inverse[4], mat_inverse[5], |
| row_inverse[2], mat_inverse[6], mat_inverse[7], mat_inverse[8]); |
| } else if (pivot_elem == 1) { |
| *inverse = Matrix<T, 4, 4>( |
| row_inverse[0], mat_inverse[0], mat_inverse[1], mat_inverse[2], |
| pivot_inverse, col_inverse[0], col_inverse[1], col_inverse[2], |
| row_inverse[1], mat_inverse[3], mat_inverse[4], mat_inverse[5], |
| row_inverse[2], mat_inverse[6], mat_inverse[7], mat_inverse[8]); |
| } else if (pivot_elem == 2) { |
| *inverse = Matrix<T, 4, 4>( |
| row_inverse[0], mat_inverse[0], mat_inverse[1], mat_inverse[2], |
| row_inverse[1], mat_inverse[3], mat_inverse[4], mat_inverse[5], |
| pivot_inverse, col_inverse[0], col_inverse[1], col_inverse[2], |
| row_inverse[2], mat_inverse[6], mat_inverse[7], mat_inverse[8]); |
| } else { |
| *inverse = Matrix<T, 4, 4>( |
| row_inverse[0], mat_inverse[0], mat_inverse[1], mat_inverse[2], |
| row_inverse[1], mat_inverse[3], mat_inverse[4], mat_inverse[5], |
| row_inverse[2], mat_inverse[6], mat_inverse[7], mat_inverse[8], |
| pivot_inverse, col_inverse[0], col_inverse[1], col_inverse[2]); |
| } |
| return true; |
| } |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| /// Create a 4x4 perpective matrix. |
| template <class T> |
| inline Matrix<T, 4, 4> PerspectiveHelper(T fovy, T aspect, T znear, T zfar, |
| T handedness) { |
| const T y = 1 / std::tan(fovy * static_cast<T>(.5)); |
| const T x = y / aspect; |
| const T zdist = (znear - zfar); |
| const T zfar_per_zdist = zfar / zdist; |
| return Matrix<T, 4, 4>(x, 0, 0, 0, 0, y, 0, 0, 0, 0, |
| zfar_per_zdist * handedness, -1 * handedness, 0, 0, |
| 2.0f * znear * zfar_per_zdist, 0); |
| } |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| /// Create a 4x4 orthographic matrix. |
| template <class T> |
| static inline Matrix<T, 4, 4> OrthoHelper(T left, T right, T bottom, T top, |
| T znear, T zfar, T handedness) { |
| return Matrix<T, 4, 4>(static_cast<T>(2) / (right - left), 0, 0, 0, 0, |
| static_cast<T>(2) / (top - bottom), 0, 0, 0, 0, |
| -handedness * static_cast<T>(2) / (zfar - znear), 0, |
| -(right + left) / (right - left), |
| -(top + bottom) / (top - bottom), |
| -(zfar + znear) / (zfar - znear), static_cast<T>(1)); |
| } |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| /// Calculate the axes required to construct a 3-dimensional camera matrix that |
| /// looks at "at" from eye position "eye" with the up vector "up". The axes |
| /// are returned in a 4 element "axes" array. |
| template <class T> |
| static void LookAtHelperCalculateAxes(const Vector<T, 3>& at, |
| const Vector<T, 3>& eye, |
| const Vector<T, 3>& up, T handedness, |
| Vector<T, 3>* const axes) { |
| // Notice that y-axis is always the same regardless of handedness. |
| axes[2] = (at - eye).Normalized(); |
| axes[0] = Vector<T, 3>::CrossProduct(up, axes[2]).Normalized(); |
| axes[1] = Vector<T, 3>::CrossProduct(axes[2], axes[0]); |
| axes[3] = Vector<T, 3>(handedness * Vector<T, 3>::DotProduct(axes[0], eye), |
| -Vector<T, 3>::DotProduct(axes[1], eye), |
| handedness * Vector<T, 3>::DotProduct(axes[2], eye)); |
| |
| // Default calculation is left-handed (i.e. handedness=-1). |
| // Negate x and z axes for right-handed (i.e. handedness=+1) case. |
| const T neg = -handedness; |
| axes[0] *= neg; |
| axes[2] *= neg; |
| } |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| /// Create a 3-dimensional camera matrix. |
| template <class T> |
| static inline Matrix<T, 4, 4> LookAtHelper(const Vector<T, 3>& at, |
| const Vector<T, 3>& eye, |
| const Vector<T, 3>& up, |
| T handedness) { |
| Vector<T, 3> axes[4]; |
| LookAtHelperCalculateAxes(at, eye, up, handedness, axes); |
| const Vector<T, 4> column0(axes[0][0], axes[1][0], axes[2][0], 0); |
| const Vector<T, 4> column1(axes[0][1], axes[1][1], axes[2][1], 0); |
| const Vector<T, 4> column2(axes[0][2], axes[1][2], axes[2][2], 0); |
| const Vector<T, 4> column3(axes[3], 1); |
| return Matrix<T, 4, 4>(column0, column1, column2, column3); |
| } |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| /// Get the 3D position in object space from a window coordinate. |
| template <class T> |
| static inline bool UnProjectHelper(const Vector<T, 3>& window_coord, |
| const Matrix<T, 4, 4>& model_view, |
| const Matrix<T, 4, 4>& projection, |
| const float window_width, |
| const float window_height, |
| Vector<T, 3>& result) { |
| if (window_coord.z < static_cast<T>(0) || |
| window_coord.z > static_cast<T>(1)) { |
| // window_coord.z should be with in [0, 1] |
| // 0: near plane |
| // 1: far plane |
| return false; |
| } |
| Matrix<T, 4, 4> matrix = (projection * model_view).Inverse(); |
| Vector<T, 4> standardized = Vector<T, 4>( |
| static_cast<T>(2) * (window_coord.x - window_width) / window_width + |
| static_cast<T>(1), |
| static_cast<T>(2) * (window_coord.y - window_height) / window_height + |
| static_cast<T>(1), |
| static_cast<T>(2) * window_coord.z - static_cast<T>(1), |
| static_cast<T>(1)); |
| |
| Vector<T, 4> multiply = matrix * standardized; |
| if (multiply.w == static_cast<T>(0)) { |
| return false; |
| } |
| result = multiply.xyz() / multiply.w; |
| return true; |
| } |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| template <typename T, int rows, int columns, typename CompatibleT> |
| static inline Matrix<T, rows, columns> FromTypeHelper(const CompatibleT& compatible) { |
| // C++11 is required for constructed unions. |
| #if __cplusplus >= 201103L |
| // Use a union instead of reinterpret_cast to avoid aliasing bugs. |
| union ConversionUnion { |
| ConversionUnion() {} // C++11. |
| CompatibleT compatible; |
| VectorPacked<T, rows> packed[columns]; |
| } u; |
| static_assert(sizeof(u.compatible) == sizeof(u.packed), "Conversion size mismatch."); |
| |
| // The read of `compatible` and write to `u.compatible` gets optimized away, |
| // and this becomes essentially a safe reinterpret_cast. |
| u.compatible = compatible; |
| |
| // Call the packed vector constructor with the `compatible` data. |
| return Matrix<T, rows, columns>(u.packed); |
| #else |
| // Use the less-desirable memcpy technique if C++11 is not available. |
| // Most compilers understand memcpy deep enough to avoid replace the function |
| // call with a series of load/stores, which should then get optimized away, |
| // however in the worst case the optimize away may not happen. |
| // Note: Memcpy avoids aliasing bugs because it operates via unsigned char*, |
| // which is allowed to alias any type. |
| // See: |
| // http://stackoverflow.com/questions/15745030/type-punning-with-void-without-breaking-the-strict-aliasing-rule-in-c99 |
| Matrix<T, rows, columns> m; |
| assert(sizeof(m) == sizeof(compatible)); |
| memcpy(&m, &compatible, sizeof(m)); |
| return m; |
| #endif // __cplusplus >= 201103L |
| } |
| /// @endcond |
| |
| /// @cond MATHFU_INTERNAL |
| template <typename T, int rows, int columns, typename CompatibleT> |
| static inline CompatibleT ToTypeHelper(const Matrix<T, rows, columns>& m) { |
| // See FromTypeHelper() for comments. |
| #if __cplusplus >= 201103L |
| union ConversionUnion { |
| ConversionUnion() {} |
| CompatibleT compatible; |
| VectorPacked<T, rows> packed[columns]; |
| } u; |
| static_assert(sizeof(u.compatible) == sizeof(u.packed), "Conversion size mismatch."); |
| m.Pack(u.packed); |
| return u.compatible; |
| #else |
| CompatibleT compatible; |
| assert(sizeof(m) == sizeof(compatible)); |
| memcpy(&compatible, &m, sizeof(compatible)); |
| return compatible; |
| #endif // __cplusplus >= 201103L |
| } |
| /// @endcond |
| |
| /// @typedef AffineTransform |
| /// |
| /// @brief A typedef representing a 4x3 float affine transformation. |
| /// Since the last row ('w' row) of an affine transformation is fixed, |
| /// this data type only includes the variable information for the transform. |
| typedef Matrix<float, 4, 3> AffineTransform; |
| /// @} |
| |
| } // namespace mathfu |
| |
| #ifdef _MSC_VER |
| #pragma warning(pop) |
| #endif |
| |
| // Include the specializations to avoid template errors. |
| // See includes at bottom of vector.h for further explanation. |
| #include "mathfu/matrix_4x4.h" |
| |
| #endif // MATHFU_MATRIX_H_ |