// // Lol Engine // // Copyright: (c) 2010-2014 Sam Hocevar // This program is free software; you can redistribute it and/or // modify it under the terms of the Do What The Fuck You Want To // Public License, Version 2, as published by Sam Hocevar. See // http://www.wtfpl.net/ for more details. // #pragma once // // The matrix classes // ------------------ // #include #include #include namespace lol { #if !LOL_FEATURE_CXX11_CONSTEXPR # define constexpr /* */ #endif /* * The generic “mat_t” type, which is fixed-size */ template struct mat_t : public linear_ops::base> { static int const count = COLS; typedef vec_t element; typedef mat_t type; inline mat_t() {} explicit inline mat_t(T const &val) { T const zero = T(0); for (int i = 0; i < COLS; ++i) for (int j = 0; j < ROWS; ++j) m_data[i][j] = i == j ? val : zero; } /* Explicit constructor for type conversion */ template explicit inline mat_t(mat_t const &m) { for (int i = 0; i < COLS; ++i) m_data[i] = (vec_t)m[i]; } inline vec_t& operator[](size_t n) { return m_data[n]; } inline vec_t const& operator[](size_t n) const { return m_data[n]; } private: vec_t m_data[COLS]; }; /* * 2×2-element matrices */ template struct mat_t : public linear_ops::base> { static int const count = 2; typedef vec_t element; typedef mat_t type; inline mat_t() {} inline mat_t(vec_t v0, vec_t v1) #if LOL_FEATURE_CXX11_ARRAY_INITIALIZERS : m_data{ v0, v1 } {} #else : m_v0(v0), m_v1(v1) {} #endif explicit inline mat_t(T const &val) #if LOL_FEATURE_CXX11_ARRAY_INITIALIZERS : m_data{ vec_t(val, T(0)), vec_t(T(0), val) } {} #else : m_v0(val, T(0)), m_v1(T(0), val) {} #endif explicit inline mat_t(mat_t const &m) #if LOL_FEATURE_CXX11_ARRAY_INITIALIZERS : m_data{ m[0].xy, m[1].xy } {} #else : m_v0(m[0].xy), m_v1(m[1].xy) {} #endif /* Explicit constructor for type conversion */ template explicit inline mat_t(mat_t const &m) #if LOL_FEATURE_CXX11_ARRAY_INITIALIZERS : m_data{ (element)m[0], (element)m[1] } {} #else : m_v0((element)m[0]), m_v1((element)m[1]) {} #endif #if LOL_FEATURE_CXX11_ARRAY_INITIALIZERS inline vec_t& operator[](size_t n) { return m_data[n]; } inline vec_t const& operator[](size_t n) const { return m_data[n]; } #else inline vec_t& operator[](size_t n) { return (&m_v0)[n]; } inline vec_t const& operator[](size_t n) const { return (&m_v0)[n]; } #endif /* Helpers for transformation matrices */ static mat_t rotate(T degrees); static inline mat_t rotate(mat_t m, T degrees) { return rotate(degrees) * m; } void printf() const; String tostring() const; template friend std::ostream &operator<<(std::ostream &stream, mat_t const &m); static const mat_t identity; private: #if LOL_FEATURE_CXX11_ARRAY_INITIALIZERS vec_t m_data[2]; #else vec_t m_v0, m_v1; #endif }; static_assert(sizeof(imat2) == 16, "sizeof(imat2) == 16"); #if LOL_FEATURE_CXX11_UNRESTRICTED_UNIONS static_assert(sizeof(f16mat2) == 8, "sizeof(f16mat2) == 8"); #endif static_assert(sizeof(mat2) == 16, "sizeof(mat2) == 16"); static_assert(sizeof(dmat2) == 32, "sizeof(dmat2) == 32"); /* * 3×3-element matrices */ template struct mat_t : public linear_ops::base> { static int const count = 3; typedef vec_t element; typedef mat_t type; inline mat_t() {} inline mat_t(vec_t v0, vec_t v1, vec_t v2) #if LOL_FEATURE_CXX11_ARRAY_INITIALIZERS : m_data{ v0, v1, v2 } {} #else : m_v0(v0), m_v1(v1), m_v2(v2) {} #endif explicit inline mat_t(T const &val) #if LOL_FEATURE_CXX11_ARRAY_INITIALIZERS : m_data{ vec_t(val, (T)0, (T)0), vec_t((T)0, val, (T)0), vec_t((T)0, (T)0, val) } {} #else : m_v0(val, (T)0, (T)0), m_v1((T)0, val, (T)0), m_v2((T)0, (T)0, val) {} #endif explicit inline mat_t(mat_t m, T const &val = T(1)) #if LOL_FEATURE_CXX11_ARRAY_INITIALIZERS : m_data{ vec_t(m[0], (T)0), vec_t(m[1], (T)0), vec_t((T)0, (T)0, val) } {} #else : m_v0(m[0], (T)0), m_v1(m[1], (T)0), m_v2((T)0, (T)0, val) {} #endif explicit inline mat_t(mat_t const &m) #if LOL_FEATURE_CXX11_ARRAY_INITIALIZERS : m_data{ m[0].xyz, m[1].xyz, m[2].xyz } {} #else : m_v0(m[0].xyz), m_v1(m[1].xyz), m_v2(m[2].xyz) {} #endif /* Explicit constructor for type conversion */ template explicit inline mat_t(mat_t const &m) #if LOL_FEATURE_CXX11_ARRAY_INITIALIZERS : m_data{ (element)m[0], (element)m[1], (element)m[2] } {} #else : m_v0((element)m[0]), m_v1((element)m[1]), m_v2((element)m[2]) {} #endif explicit mat_t(quat_t const &q); #if LOL_FEATURE_CXX11_ARRAY_INITIALIZERS inline vec_t& operator[](size_t n) { return m_data[n]; } inline vec_t const& operator[](size_t n) const { return m_data[n]; } #else inline vec_t& operator[](size_t n) { return (&m_v0)[n]; } inline vec_t const& operator[](size_t n) const { return (&m_v0)[n]; } #endif /* Helpers for transformation matrices */ static mat_t scale(T x); static mat_t scale(T x, T y, T z); static mat_t scale(vec_t v); static mat_t rotate(T degrees, T x, T y, T z); static mat_t rotate(T degrees, vec_t v); static mat_t fromeuler_xyz(vec_t const &v); static mat_t fromeuler_xzy(vec_t const &v); static mat_t fromeuler_yxz(vec_t const &v); static mat_t fromeuler_yzx(vec_t const &v); static mat_t fromeuler_zxy(vec_t const &v); static mat_t fromeuler_zyx(vec_t const &v); static mat_t fromeuler_xyz(T phi, T theta, T psi); static mat_t fromeuler_xzy(T phi, T theta, T psi); static mat_t fromeuler_yxz(T phi, T theta, T psi); static mat_t fromeuler_yzx(T phi, T theta, T psi); static mat_t fromeuler_zxy(T phi, T theta, T psi); static mat_t fromeuler_zyx(T phi, T theta, T psi); static mat_t fromeuler_xyx(vec_t const &v); static mat_t fromeuler_xzx(vec_t const &v); static mat_t fromeuler_yxy(vec_t const &v); static mat_t fromeuler_yzy(vec_t const &v); static mat_t fromeuler_zxz(vec_t const &v); static mat_t fromeuler_zyz(vec_t const &v); static mat_t fromeuler_xyx(T phi, T theta, T psi); static mat_t fromeuler_xzx(T phi, T theta, T psi); static mat_t fromeuler_yxy(T phi, T theta, T psi); static mat_t fromeuler_yzy(T phi, T theta, T psi); static mat_t fromeuler_zxz(T phi, T theta, T psi); static mat_t fromeuler_zyz(T phi, T theta, T psi); static inline mat_t rotate(mat_t m, T degrees, vec_t v) { return rotate(degrees, v) * m; } void printf() const; String tostring() const; template friend std::ostream &operator<<(std::ostream &stream, mat_t const &m); static const mat_t identity; private: #if LOL_FEATURE_CXX11_ARRAY_INITIALIZERS vec_t m_data[3]; #else vec_t m_v0, m_v1, m_v2; #endif }; static_assert(sizeof(imat3) == 36, "sizeof(imat3) == 36"); #if LOL_FEATURE_CXX11_UNRESTRICTED_UNIONS static_assert(sizeof(f16mat3) == 18, "sizeof(f16mat3) == 18"); #endif static_assert(sizeof(mat3) == 36, "sizeof(mat3) == 36"); static_assert(sizeof(dmat3) == 72, "sizeof(dmat3) == 72"); /* * 4×4-element matrices */ template struct mat_t : public linear_ops::base> { static int const count = 4; typedef vec_t element; typedef mat_t type; inline mat_t() {} inline mat_t(vec_t v0, vec_t v1, vec_t v2, vec_t v3) #if LOL_FEATURE_CXX11_ARRAY_INITIALIZERS : m_data{ v0, v1, v2, v3 } {} #else : m_v0(v0), m_v1(v1), m_v2(v2), m_v3(v3) {} #endif explicit inline mat_t(T const &val) #if LOL_FEATURE_CXX11_ARRAY_INITIALIZERS : m_data{ vec_t(val, (T)0, (T)0, (T)0), vec_t((T)0, val, (T)0, (T)0), vec_t((T)0, (T)0, val, (T)0), vec_t((T)0, (T)0, (T)0, val) } {} #else : m_v0(val, (T)0, (T)0, (T)0), m_v1((T)0, val, (T)0, (T)0), m_v2((T)0, (T)0, val, (T)0), m_v3((T)0, (T)0, (T)0, val) {} #endif explicit inline mat_t(mat_t m, T const &val = T(1)) #if LOL_FEATURE_CXX11_ARRAY_INITIALIZERS : m_data{ vec_t(m[0], (T)0, (T)0), vec_t(m[1], (T)0, (T)0), vec_t((T)0, (T)0, val, (T)0), vec_t((T)0, (T)0, (T)0, val) } {} #else : m_v0(m[0], (T)0, (T)0), m_v1(m[1], (T)0, (T)0), m_v2((T)0, (T)0, val, (T)0), m_v3((T)0, (T)0, (T)0, val) {} #endif explicit inline mat_t(mat_t m, T const &val = T(1)) #if LOL_FEATURE_CXX11_ARRAY_INITIALIZERS : m_data{ vec_t(m[0], (T)0), vec_t(m[1], (T)0), vec_t(m[2], (T)0), vec_t((T)0, (T)0, (T)0, val) } {} #else : m_v0(m[0], (T)0), m_v1(m[1], (T)0), m_v2(m[2], (T)0), m_v3((T)0, (T)0, (T)0, val) {} #endif /* Explicit constructor for type conversion */ template explicit inline mat_t(mat_t const &m) #if LOL_FEATURE_CXX11_ARRAY_INITIALIZERS : m_data{ (element)m[0], (element)m[1], (element)m[2], (element)m[3] } {} #else : m_v0((element)m[0]), m_v1((element)m[1]), m_v2((element)m[2]), m_v3((element)m[3]) {} #endif explicit mat_t(quat_t const &q); #if LOL_FEATURE_CXX11_ARRAY_INITIALIZERS inline vec_t& operator[](size_t n) { return m_data[n]; } inline vec_t const& operator[](size_t n) const { return m_data[n]; } #else inline vec_t& operator[](size_t n) { return (&m_v0)[n]; } inline vec_t const& operator[](size_t n) const { return (&m_v0)[n]; } #endif /* Helpers for transformation matrices */ static mat_t translate(T x, T y, T z); static mat_t translate(vec_t v); static inline mat_t scale(T x) { return mat_t(mat_t::scale(x), (T)1); } static inline mat_t scale(T x, T y, T z) { return mat_t(mat_t::scale(x, y, z), (T)1); } static inline mat_t scale(vec_t v) { return mat_t(mat_t::scale(v), (T)1); } static inline mat_t translate(mat_t const &m, vec_t v) { return translate(v) * m; } static inline mat_t rotate(T degrees, T x, T y, T z) { return mat_t(mat_t::rotate(degrees, x, y, z), (T)1); } static inline mat_t rotate(T degrees, vec_t v) { return mat_t(mat_t::rotate(degrees, v), (T)1); } static inline mat_t rotate(mat_t &m, T degrees, vec_t v) { return rotate(degrees, v) * m; } static mat_t fromeuler_xyz(vec_t const &v); static mat_t fromeuler_xzy(vec_t const &v); static mat_t fromeuler_yxz(vec_t const &v); static mat_t fromeuler_yzx(vec_t const &v); static mat_t fromeuler_zxy(vec_t const &v); static mat_t fromeuler_zyx(vec_t const &v); static mat_t fromeuler_xyz(T phi, T theta, T psi); static mat_t fromeuler_xzy(T phi, T theta, T psi); static mat_t fromeuler_yxz(T phi, T theta, T psi); static mat_t fromeuler_yzx(T phi, T theta, T psi); static mat_t fromeuler_zxy(T phi, T theta, T psi); static mat_t fromeuler_zyx(T phi, T theta, T psi); static mat_t fromeuler_xyx(vec_t const &v); static mat_t fromeuler_xzx(vec_t const &v); static mat_t fromeuler_yxy(vec_t const &v); static mat_t fromeuler_yzy(vec_t const &v); static mat_t fromeuler_zxz(vec_t const &v); static mat_t fromeuler_zyz(vec_t const &v); static mat_t fromeuler_xyx(T phi, T theta, T psi); static mat_t fromeuler_xzx(T phi, T theta, T psi); static mat_t fromeuler_yxy(T phi, T theta, T psi); static mat_t fromeuler_yzy(T phi, T theta, T psi); static mat_t fromeuler_zxz(T phi, T theta, T psi); static mat_t fromeuler_zyz(T phi, T theta, T psi); /* Helpers for view matrices */ static mat_t lookat(vec_t eye, vec_t center, vec_t up); /* Helpers for projection matrices */ static mat_t ortho(T left, T right, T bottom, T top, T near, T far); static mat_t ortho(T width, T height, T near, T far); static mat_t frustum(T left, T right, T bottom, T top, T near, T far); static mat_t perspective(T fov_y, T width, T height, T near, T far); static mat_t shifted_perspective(T fov_y, T screen_size, T screen_ratio_yx, T near, T far); void printf() const; String tostring() const; template friend std::ostream &operator<<(std::ostream &stream, mat_t const &m); static const mat_t identity; private: #if LOL_FEATURE_CXX11_ARRAY_INITIALIZERS vec_t m_data[4]; #else vec_t m_v0, m_v1, m_v2, m_v3; #endif }; static_assert(sizeof(imat4) == 64, "sizeof(imat4) == 64"); #if LOL_FEATURE_CXX11_UNRESTRICTED_UNIONS static_assert(sizeof(f16mat4) == 32, "sizeof(f16mat4) == 32"); #endif static_assert(sizeof(mat4) == 64, "sizeof(mat4) == 64"); static_assert(sizeof(dmat4) == 128, "sizeof(dmat4) == 128"); /* * Transpose any matrix */ template static inline mat_t transpose(mat_t const &m) { mat_t ret; for (int i = 0; i < COLS; ++i) for (int j = 0; j < ROWS; ++j) ret[j][i] = m[i][j]; return ret; } /* * Compute a square submatrix, useful for minors and cofactor matrices */ template mat_t submatrix(mat_t const &m, int i, int j) { ASSERT(i >= 0); ASSERT(j >= 0); ASSERT(i < N); ASSERT(j < N); mat_t ret; for (int i2 = 0; i2 < N - 1; ++i2) for (int j2 = 0; j2 < N - 1; ++j2) ret[i2][j2] = m[i2 + (i2 >= i)][j2 + (j2 >= j)]; return ret; } /* * Compute square matrix cofactor */ template T cofactor(mat_t const &m, int i, int j) { ASSERT(i >= 0); ASSERT(j >= 0); ASSERT(i < N); ASSERT(j < N); T tmp = determinant(submatrix(m, i, j)); return ((i + j) & 1) ? -tmp : tmp; } template T cofactor(mat_t const &m, int i, int j) { /* This specialisation shouldn't be needed, but Visual Studio. */ ASSERT(i >= 0); ASSERT(j >= 0); ASSERT(i < 2); ASSERT(j < 2); T tmp = m[1 - i][1 - j]; return (i ^ j) ? -tmp : tmp; } /* * Compute LU-decomposition */ template void lu_decomposition(mat_t const &m, mat_t & L, mat_t & U) { for (int i = 0; i < N; ++i) { for (int j = 0; j < N; ++j) { T sum = 0; for (int k = 0 ; k < min(i, j) ; ++k) sum += L[k][j] * U[i][k]; if (i < j) { U[i][j] = 0; L[i][j] = (m[i][j] - sum) / U[i][i]; } else /* if (i >= j) */ { L[i][j] = i == j ? 1 : 0; U[i][j] = (m[i][j] - sum) / L[j][j]; } } } } /* * Compute square matrix determinant, with a specialisation for 1×1 matrices */ template T determinant(mat_t const &m) { mat_t L, U; lu_decomposition(m, L, U); T det = 1; for (int i = 0 ; i < N ; ++i) det *= U[i][i]; return det; } /* * Compute square matrix inverse */ template mat_t inverse(mat_t const &m) { mat_t ret; T d = determinant(m); if (d) { /* Transpose result matrix on the fly */ for (int i = 0; i < N; ++i) for (int j = 0; j < N; ++j) ret[i][j] = cofactor(m, j, i) / d; } return ret; } /* * Matrix-vector and vector-matrix multiplication */ template static inline vec_t operator *(mat_t const &m, vec_t const &v) { vec_t ret(T(0)); for (int i = 0; i < COLS; ++i) ret += m[i] * v[i]; return ret; } template static inline vec_t operator *(vec_t const &v, mat_t const &m) { vec_t ret(T(0)); for (int i = 0; i < COLS; ++i) ret[i] = dot(v, m[i]); return ret; } /* * Matrix-matrix multiplication */ template static inline mat_t operator *(mat_t const &a, mat_t const &b) { mat_t ret; for (int i = 0; i < COLS; ++i) ret[i] = a * b[i]; return ret; } template static inline mat_t &operator *=(mat_t &a, mat_t const &b) { return a = a * b; } /* * Vector-vector outer product */ template static inline mat_t outer(vec_t const &a, vec_t const &b) { /* Valid cast because mat_t and vec_t have similar layouts */ return *reinterpret_cast const *>(&a) * *reinterpret_cast const *>(&b); } /* * Matrix-matrix outer product (Kronecker product) */ template static inline mat_t outer(mat_t const &a, mat_t const &b) { mat_t ret; for (int i1 = 0; i1 < COLS1; ++i1) for (int i2 = 0; i2 < COLS2; ++i2) { /* Valid cast because mat_t and vec_t have similar layouts */ *reinterpret_cast *>(&ret[i1 * COLS2 + i2]) = outer(b[i2], a[i1]); } return ret; } #if !LOL_FEATURE_CXX11_CONSTEXPR #undef constexpr #endif } /* namespace lol */