#pragma once #include #include #include #include #include #include #include #include #include #include namespace Scop { template constexpr Mat4::Mat4(T r11, T r12, T r13, T r14, T r21, T r22, T r23, T r24, T r31, T r32, T r33, T r34, T r41, T r42, T r43, T r44) : m11(r11), m12(r12), m13(r13), m14(r14), m21(r21), m22(r22), m23(r23), m24(r24), m31(r31), m32(r32), m33(r33), m34(r34), m41(r41), m42(r42), m43(r43), m44(r44) {} template constexpr Mat4::Mat4(const T matrix[16]) : Mat4(matrix[ 0], matrix[ 1], matrix[ 2], matrix[ 3], matrix[ 4], matrix[ 5], matrix[ 6], matrix[ 7], matrix[ 8], matrix[ 9], matrix[10], matrix[11], matrix[12], matrix[13], matrix[14], matrix[15]) {} template constexpr Mat4& Mat4::ApplyRotation(const Quat& rotation) { return Concatenate(Mat4::Rotate(rotation)); } template constexpr Mat4& Mat4::ApplyScale(const Vec3& scale) { m11 *= scale.x; m12 *= scale.x; m13 *= scale.x; m21 *= scale.y; m22 *= scale.y; m23 *= scale.y; m31 *= scale.z; m32 *= scale.z; m33 *= scale.z; return *this; } template constexpr Mat4& Mat4::ApplyTranslation(const Vec3& translation) { m41 += translation.x; m42 += translation.y; m43 += translation.z; return *this; } template constexpr bool Mat4::ApproxEqual(const Mat4& mat, T maxDifference) const { for(unsigned int i = 0; i < 16; ++i) if(!NumberEquals((&m11)[i], (&mat.m11)[i], maxDifference)) return false; return true; } template constexpr Mat4& Mat4::Concatenate(const Mat4& matrix) { return operator=(Mat4( m11 * matrix.m11 + m12 * matrix.m21 + m13 * matrix.m31 + m14 * matrix.m41, m11 * matrix.m12 + m12 * matrix.m22 + m13 * matrix.m32 + m14 * matrix.m42, m11 * matrix.m13 + m12 * matrix.m23 + m13 * matrix.m33 + m14 * matrix.m43, m11 * matrix.m14 + m12 * matrix.m24 + m13 * matrix.m34 + m14 * matrix.m44, m21 * matrix.m11 + m22 * matrix.m21 + m23 * matrix.m31 + m24 * matrix.m41, m21 * matrix.m12 + m22 * matrix.m22 + m23 * matrix.m32 + m24 * matrix.m42, m21 * matrix.m13 + m22 * matrix.m23 + m23 * matrix.m33 + m24 * matrix.m43, m21 * matrix.m14 + m22 * matrix.m24 + m23 * matrix.m34 + m24 * matrix.m44, m31 * matrix.m11 + m32 * matrix.m21 + m33 * matrix.m31 + m34 * matrix.m41, m31 * matrix.m12 + m32 * matrix.m22 + m33 * matrix.m32 + m34 * matrix.m42, m31 * matrix.m13 + m32 * matrix.m23 + m33 * matrix.m33 + m34 * matrix.m43, m31 * matrix.m14 + m32 * matrix.m24 + m33 * matrix.m34 + m34 * matrix.m44, m41 * matrix.m11 + m42 * matrix.m21 + m43 * matrix.m31 + m44 * matrix.m41, m41 * matrix.m12 + m42 * matrix.m22 + m43 * matrix.m32 + m44 * matrix.m42, m41 * matrix.m13 + m42 * matrix.m23 + m43 * matrix.m33 + m44 * matrix.m43, m41 * matrix.m14 + m42 * matrix.m24 + m43 * matrix.m34 + m44 * matrix.m44 )); } template constexpr Mat4& Mat4::ConcatenateTransform(const Mat4& matrix) { return operator=(Mat4( m11*matrix.m11 + m12*matrix.m21 + m13*matrix.m31, m11*matrix.m12 + m12*matrix.m22 + m13*matrix.m32, m11*matrix.m13 + m12*matrix.m23 + m13*matrix.m33, T(0.0), m21*matrix.m11 + m22*matrix.m21 + m23*matrix.m31, m21*matrix.m12 + m22*matrix.m22 + m23*matrix.m32, m21*matrix.m13 + m22*matrix.m23 + m23*matrix.m33, T(0.0), m31*matrix.m11 + m32*matrix.m21 + m33*matrix.m31, m31*matrix.m12 + m32*matrix.m22 + m33*matrix.m32, m31*matrix.m13 + m32*matrix.m23 + m33*matrix.m33, T(0.0), m41*matrix.m11 + m42*matrix.m21 + m43*matrix.m31 + matrix.m41, m41*matrix.m12 + m42*matrix.m22 + m43*matrix.m32 + matrix.m42, m41*matrix.m13 + m42*matrix.m23 + m43*matrix.m33 + matrix.m43, T(1.0) )); } template constexpr Vec4 Mat4::GetColumn(std::size_t column) const { Assert(column < 4, "column index out of range"); const T* ptr = &m11 + column * 4; return Vec4(ptr[0], ptr[1], ptr[2], ptr[3]); } template constexpr T Mat4::GetDeterminant() const { T A = m22*(m33*m44 - m43*m34) - m32*(m23*m44 - m43*m24) + m42*(m23*m34 - m33*m24); T B = m12*(m33*m44 - m43*m34) - m32*(m13*m44 - m43*m14) + m42*(m13*m34 - m33*m14); T C = m12*(m23*m44 - m43*m24) - m22*(m13*m44 - m43*m14) + m42*(m13*m24 - m23*m14); T D = m12*(m23*m34 - m33*m24) - m22*(m13*m34 - m33*m14) + m32*(m13*m24 - m23*m14); return m11*A - m21*B + m31*C - m41*D; } template constexpr T Mat4::GetDeterminantTransform() const { T A = m22*m33 - m32*m23; T B = m12*m33 - m32*m13; T C = m12*m23 - m22*m13; return m11*A - m21*B + m31*C; } template constexpr bool Mat4::GetInverse(Mat4* dest) const { Assert(dest, "destination matrix must be valid"); T det = GetDeterminant(); if(det == T(0.0)) return false; // http://stackoverflow.com/questions/1148309/inverting-a-4x4-matrix T inv[16]; inv[0] = m22 * m33 * m44 - m22 * m34 * m43 - m32 * m23 * m44 + m32 * m24 * m43 + m42 * m23 * m34 - m42 * m24 * m33; inv[1] = -m12 * m33 * m44 + m12 * m34 * m43 + m32 * m13 * m44 - m32 * m14 * m43 - m42 * m13 * m34 + m42 * m14 * m33; inv[2] = m12 * m23 * m44 - m12 * m24 * m43 - m22 * m13 * m44 + m22 * m14 * m43 + m42 * m13 * m24 - m42 * m14 * m23; inv[3] = -m12 * m23 * m34 + m12 * m24 * m33 + m22 * m13 * m34 - m22 * m14 * m33 - m32 * m13 * m24 + m32 * m14 * m23; inv[4] = -m21 * m33 * m44 + m21 * m34 * m43 + m31 * m23 * m44 - m31 * m24 * m43 - m41 * m23 * m34 + m41 * m24 * m33; inv[5] = m11 * m33 * m44 - m11 * m34 * m43 - m31 * m13 * m44 + m31 * m14 * m43 + m41 * m13 * m34 - m41 * m14 * m33; inv[6] = -m11 * m23 * m44 + m11 * m24 * m43 + m21 * m13 * m44 - m21 * m14 * m43 - m41 * m13 * m24 + m41 * m14 * m23; inv[7] = m11 * m23 * m34 - m11 * m24 * m33 - m21 * m13 * m34 + m21 * m14 * m33 + m31 * m13 * m24 - m31 * m14 * m23; inv[8] = m21 * m32 * m44 - m21 * m34 * m42 - m31 * m22 * m44 + m31 * m24 * m42 + m41 * m22 * m34 - m41 * m24 * m32; inv[9] = -m11 * m32 * m44 + m11 * m34 * m42 + m31 * m12 * m44 - m31 * m14 * m42 - m41 * m12 * m34 + m41 * m14 * m32; inv[10] = m11 * m22 * m44 - m11 * m24 * m42 - m21 * m12 * m44 + m21 * m14 * m42 + m41 * m12 * m24 - m41 * m14 * m22; inv[11] = -m11 * m22 * m34 + m11 * m24 * m32 + m21 * m12 * m34 - m21 * m14 * m32 - m31 * m12 * m24 + m31 * m14 * m22; inv[12] = -m21 * m32 * m43 + m21 * m33 * m42 + m31 * m22 * m43 - m31 * m23 * m42 - m41 * m22 * m33 + m41 * m23 * m32; inv[13] = m11 * m32 * m43 - m11 * m33 * m42 - m31 * m12 * m43 + m31 * m13 * m42 + m41 * m12 * m33 - m41 * m13 * m32; inv[14] = -m11 * m22 * m43 + m11 * m23 * m42 + m21 * m12 * m43 - m21 * m13 * m42 - m41 * m12 * m23 + m41 * m13 * m22; inv[15] = m11 * m22 * m33 - m11 * m23 * m32 - m21 * m12 * m33 + m21 * m13 * m32 + m31 * m12 * m23 - m31 * m13 * m22; T invDet = T(1.0) / det; for(unsigned int i = 0; i < 16; ++i) inv[i] *= invDet; *dest = inv; return true; } template constexpr bool Mat4::GetInverseTransform(Mat4* dest) const { Assert(dest, "destination matrix must be valid"); T det = GetDeterminantTransform(); if(det == T(0.0)) return false; // http://stackoverflow.com/questions/1148309/inverting-a-4x4-matrix T inv[16]; inv[0] = m22 * m33 - m32 * m23; inv[1] = -m12 * m33 + m32 * m13; inv[2] = m12 * m23 - m22 * m13; inv[3] = T(0.0); inv[4] = -m21 * m33 + m31 * m23; inv[5] = m11 * m33 - m31 * m13; inv[6] = -m11 * m23 + m21 * m13; inv[7] = T(0.0); inv[8] = m21 * m32 - m31 * m22; inv[9] = -m11 * m32 + m31 * m12; inv[10] = m11 * m22 - m21 * m12; inv[11] = T(0.0); inv[12] = -m21 * m32 * m43 + m21 * m33 * m42 + m31 * m22 * m43 - m31 * m23 * m42 - m41 * m22 * m33 + m41 * m23 * m32; inv[13] = m11 * m32 * m43 - m11 * m33 * m42 - m31 * m12 * m43 + m31 * m13 * m42 + m41 * m12 * m33 - m41 * m13 * m32; inv[14] = -m11 * m22 * m43 + m11 * m23 * m42 + m21 * m12 * m43 - m21 * m13 * m42 - m41 * m12 * m23 + m41 * m13 * m22; T invDet = T(1.0) / det; for(unsigned int i = 0; i < 16; ++i) inv[i] *= invDet; inv[15] = T(1.0); *dest = inv; return true; } template Quat Mat4::GetRotation() const { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuat/ Quat quat; T trace = m11 + m22 + m33; if(trace > T(0.0)) { T s = T(0.5) / std::sqrt(trace + T(1.0)); quat.w = T(0.25) / s; quat.x = (m23 - m32) * s; quat.y = (m31 - m13) * s; quat.z = (m12 - m21) * s; } else { if(m11 > m22 && m11 > m33) { T s = T(2.0) * std::sqrt(T(1.0) + m11 - m22 - m33); quat.w = (m23 - m32) / s; quat.x = T(0.25) * s; quat.y = (m21 + m12) / s; quat.z = (m31 + m13) / s; } else if(m22 > m33) { T s = T(2.0) * std::sqrt(T(1.0) + m22 - m11 - m33); quat.w = (m31 - m13) / s; quat.x = (m21 + m12) / s; quat.y = T(0.25) * s; quat.z = (m32 + m23) / s; } else { T s = T(2.0) * std::sqrt(T(1.0) + m33 - m11 - m22); quat.w = (m12 - m21) / s; quat.x = (m31 + m13) / s; quat.y = (m32 + m23) / s; quat.z = T(0.25) * s; } } return quat; } template constexpr Vec4 Mat4::GetRow(std::size_t row) const { Assert(row < 4, "row index out of range"); const T* ptr = &m11; return Vec4(ptr[row], ptr[row+4], ptr[row+8], ptr[row+12]); } template constexpr Vec3 Mat4::GetScale() const { Vec3 squaredScale = GetSquaredScale(); return Vec3(std::sqrt(squaredScale.x), std::sqrt(squaredScale.y), std::sqrt(squaredScale.z)); } template constexpr Vec3 Mat4::GetSquaredScale() const { return Vec3(m11 * m11 + m12 * m12 + m13 * m13, m21 * m21 + m22 * m22 + m23 * m23, m31 * m31 + m32 * m32 + m33 * m33); } template constexpr Vec3 Mat4::GetTranslation() const { return Vec3(m41, m42, m43); } template constexpr void Mat4::GetTransposed(Mat4* dest) const { (*dest) = Mat4f( m11, m21, m31, m41, m12, m22, m32, m42, m13, m23, m33, m43, m14, m24, m34, m44 ); } template constexpr bool Mat4::HasNegativeScale() const { return GetDeterminant() < T(0.0); } template constexpr bool Mat4::HasScale() const { T t = m11*m11 + m21*m21 + m31*m31; if(!NumberEquals(t, T(1.0))) return true; t = m12*m12 + m22*m22 + m32*m32; if(!NumberEquals(t, T(1.0))) return true; t = m13*m13 + m23*m23 + m33*m33; if(!NumberEquals(t, T(1.0))) return true; return false; } template constexpr Mat4& Mat4::Inverse(bool* succeeded) { bool result = GetInverse(this); if(succeeded) *succeeded = result; return *this; } template constexpr Mat4& Mat4::InverseTransform(bool* succeeded) { bool result = GetInverseTransform(this); if(succeeded) *succeeded = result; return *this; } template constexpr bool Mat4::IsTransformMatrix() const { return NumberEquals(m14, T(0.0)) && NumberEquals(m24, T(0.0)) && NumberEquals(m34, T(0.0)) && NumberEquals(m44, T(1.0)); } template constexpr bool Mat4::IsIdentity() const { return (NumberEquals(m11, T(1.0)) && NumberEquals(m12, T(0.0)) && NumberEquals(m13, T(0.0)) && NumberEquals(m14, T(0.0)) && NumberEquals(m21, T(0.0)) && NumberEquals(m22, T(1.0)) && NumberEquals(m23, T(0.0)) && NumberEquals(m24, T(0.0)) && NumberEquals(m31, T(0.0)) && NumberEquals(m32, T(0.0)) && NumberEquals(m33, T(1.0)) && NumberEquals(m34, T(0.0)) && NumberEquals(m41, T(0.0)) && NumberEquals(m42, T(0.0)) && NumberEquals(m43, T(0.0)) && NumberEquals(m44, T(1.0))); } template constexpr Mat4& Mat4::SetRotation(const Quat& rotation) { T qw = rotation.w; T qx = rotation.x; T qy = rotation.y; T qz = rotation.z; T qx2 = qx * qx; T qy2 = qy * qy; T qz2 = qz * qz; m11 = T(1.0) - T(2.0) * qy2 - T(2.0) * qz2; m21 = T(2.0) * qx * qy - T(2.0) * qz * qw; m31 = T(2.0) * qx * qz + T(2.0) * qy * qw; m12 = T(2.0) * qx * qy + T(2.0) * qz * qw; m22 = T(1.0) - T(2.0) * qx2 - T(2.0) * qz2; m32 = T(2.0) * qy * qz - T(2.0) * qx * qw; m13 = T(2.0) * qx * qz - T(2.0) * qy * qw; m23 = T(2.0) * qy * qz + T(2.0) * qx * qw; m33 = T(1.0) - T(2.0) * qx2 - T(2.0) * qy2; return *this; } template constexpr Mat4& Mat4::SetScale(const Vec3& scale) { m11 = scale.x; m22 = scale.y; m33 = scale.z; return *this; } template constexpr Mat4& Mat4::SetTranslation(const Vec3& translation) { m41 = translation.x; m42 = translation.y; m43 = translation.z; return *this; } template std::string Mat4::ToString() const { std::ostringstream ss; ss << *this; return ss.str(); } template constexpr Vec2 Mat4::Transform(const Vec2& vector, T z, T w) const { return Vec2(m11 * vector.x + m21 * vector.y + m31 * z + m41 * w, m12 * vector.x + m22 * vector.y + m32 * z + m42 * w); } template constexpr Vec3 Mat4::Transform(const Vec3& vector, T w) const { return Vec3(m11 * vector.x + m21 * vector.y + m31 * vector.z + m41 * w, m12 * vector.x + m22 * vector.y + m32 * vector.z + m42 * w, m13 * vector.x + m23 * vector.y + m33 * vector.z + m43 * w); } template constexpr Vec4 Mat4::Transform(const Vec4& vector) const { return Vec4(m11 * vector.x + m21 * vector.y + m31 * vector.z + m41 * vector.w, m12 * vector.x + m22 * vector.y + m32 * vector.z + m42 * vector.w, m13 * vector.x + m23 * vector.y + m33 * vector.z + m43 * vector.w, m14 * vector.x + m24 * vector.y + m34 * vector.z + m44 * vector.w); } template constexpr Mat4& Mat4::Transpose() { std::swap(m12, m21); std::swap(m13, m31); std::swap(m14, m41); std::swap(m23, m32); std::swap(m24, m42); std::swap(m34, m43); return *this; } template constexpr T& Mat4::operator()(std::size_t x, std::size_t y) { Assert(x <= 3, "index out of range"); Assert(y <= 3, "index out of range"); return (&m11)[y*4 + x]; } template constexpr const T& Mat4::operator()(std::size_t x, std::size_t y) const { Assert(x <= 3, "index out of range"); Assert(y <= 3, "index out of range"); return (&m11)[y*4+x]; } template constexpr T& Mat4::operator[](std::size_t i) { Assert(i <= 16, "index out of range"); return (&m11)[i]; } template constexpr const T& Mat4::operator[](std::size_t i) const { Assert(i <= 16, "index out of range"); return (&m11)[i]; } template constexpr Mat4 Mat4::operator*(const Mat4& matrix) const { Mat4 result(*this); return result.Concatenate(matrix); } template constexpr Vec2 Mat4::operator*(const Vec2& vector) const { return Transform(vector); } template constexpr Vec3 Mat4::operator*(const Vec3& vector) const { return Transform(vector); } template constexpr Vec4 Mat4::operator*(const Vec4& vector) const { return Transform(vector); } template constexpr Mat4 Mat4::operator*(T scalar) const { Mat4 mat; for(unsigned int i = 0; i < 16; ++i) mat[i] = (&m11)[i] * scalar; return mat; } template constexpr Mat4& Mat4::operator*=(const Mat4& matrix) { Concatenate(matrix); return *this; } template constexpr Mat4& Mat4::operator*=(T scalar) { for(unsigned int i = 0; i < 16; ++i) (&m11)[i] *= scalar; return *this; } template constexpr bool Mat4::operator==(const Mat4& mat) const { for(unsigned int i = 0; i < 16; ++i) if((&m11)[i] != (&mat.m11)[i]) return false; return true; } template constexpr bool Mat4::operator!=(const Mat4& mat) const { return !operator==(mat); } template constexpr bool Mat4::ApproxEqual(const Mat4& lhs, const Mat4& rhs, T maxDifference) { return lhs.ApproxEqual(rhs, maxDifference); } template constexpr Mat4 Mat4::Concatenate(const Mat4& left, const Mat4& right) { Mat4 matrix(left); // Copy of left-hand side matrix matrix.Concatenate(right); // Concatenation with right-hand side return matrix; } template constexpr Mat4 Mat4::ConcatenateTransform(const Mat4& left, const Mat4& right) { Mat4 matrix(left); // Copy of left-hand side matrix matrix.ConcatenateTransform(right); // Affine concatenation with right-hand side return matrix; } template constexpr Mat4 Mat4::Identity() { return Mat4( T(1.0), T(0.0), T(0.0), T(0.0), T(0.0), T(1.0), T(0.0), T(0.0), T(0.0), T(0.0), T(1.0), T(0.0), T(0.0), T(0.0), T(0.0), T(1.0) ); } template constexpr Mat4 Mat4::LookAt(const Vec3& eye, const Vec3& target, const Vec3& up) { Vec3 f = Vec3::Normalize(target - eye); Vec3 s = Vec3::Normalize(f.CrossProduct(up)); Vec3 u = s.CrossProduct(f); return Mat4( s.x, u.x, -f.x, T(0.0), s.y, u.y, -f.y, T(0.0), s.z, u.z, -f.z, T(0.0), -s.DotProduct(eye), -u.DotProduct(eye), f.DotProduct(eye), T(1.0) ); } template constexpr Mat4 Mat4::Ortho(T left, T right, T top, T bottom, T zNear, T zFar) { // http://msdn.microsoft.com/en-us/library/windows/desktop/bb204942(v=vs.85).aspx return Mat4( T(2.0) / (right - left), T(0.0), T(0.0), T(0.0), T(0.0), T(2.0) / (top - bottom), T(0.0), T(0.0), T(0.0), T(0.0), T(1.0) / (zNear - zFar), T(0.0), (left + right) / (left - right), (top + bottom) / (bottom - top), zNear / (zNear - zFar), T(1.0) ); } template Mat4 Mat4::Perspective(RadianAngle angle, T ratio, T zNear, T zFar) { angle /= T(2.0); T yScale = angle.GetTan(); return Mat4( T(1.0) / (ratio * yScale), T(0.0), T(0.0), T(0.0), T(0.0), T(-1.0) / (yScale), T(0.0), T(0.0), T(0.0), T(0.0), zFar / (zNear - zFar), T(-1.0), T(0.0), T(0.0), -(zNear * zFar) / (zFar - zNear), T(0.0) ); } template constexpr Mat4 Mat4::Rotate(const Quat& rotation) { Mat4 matrix = Mat4::Identity(); matrix.SetRotation(rotation); return matrix; } template constexpr Mat4 Mat4::Scale(const Vec3& scale) { return Mat4( scale.x, T(0.0), T(0.0), T(0.0), T(0.0), scale.y, T(0.0), T(0.0), T(0.0), T(0.0), scale.z, T(0.0), T(0.0), T(0.0), T(0.0), T(1.0) ); } template constexpr Mat4 Mat4::Translate(const Vec3& translation) { return Mat4( T(1.0), T(0.0), T(0.0), T(0.0), T(0.0), T(1.0), T(0.0), T(0.0), T(0.0), T(0.0), T(1.0), T(0.0), translation.x, translation.y, translation.z, T(1.0) ); } template constexpr Mat4 Mat4::Transform(const Vec3& translation, const Quat& rotation) { Mat4 mat = Mat4f::Identity(); mat.SetRotation(rotation); mat.SetTranslation(translation); return mat; } template constexpr Mat4 Mat4::Transform(const Vec3& translation, const Quat& rotation, const Vec3& scale) { Mat4 mat = Transform(translation, rotation); mat.ApplyScale(scale); return mat; } template constexpr Mat4 Mat4::TransformInverse(const Vec3& translation, const Quat& rotation) { // A view matrix must apply an inverse transformation of the 'world' matrix Quat invRot = rotation.GetConjugate(); // Inverse of the rotation return Transform(-(invRot * translation), invRot); } template constexpr Mat4 Mat4::TransformInverse(const Vec3& translation, const Quat& rotation, const Vec3& scale) { return TransformInverse(translation, rotation).ApplyScale(T(1.0) / scale); } template constexpr Mat4 Mat4::Zero() { return Mat4( T(0.0), T(0.0), T(0.0), T(0.0), T(0.0), T(0.0), T(0.0), T(0.0), T(0.0), T(0.0), T(0.0), T(0.0), T(0.0), T(0.0), T(0.0), T(0.0) ); } template std::ostream& operator<<(std::ostream& out, const Mat4& matrix) { return out << "Mat4(" << matrix.m11 << ", " << matrix.m12 << ", " << matrix.m13 << ", " << matrix.m14 << ",\n" << " " << matrix.m21 << ", " << matrix.m22 << ", " << matrix.m23 << ", " << matrix.m24 << ",\n" << " " << matrix.m31 << ", " << matrix.m32 << ", " << matrix.m33 << ", " << matrix.m34 << ",\n" << " " << matrix.m41 << ", " << matrix.m42 << ", " << matrix.m43 << ", " << matrix.m44 << ')'; } template constexpr Mat4 operator*(T scale, const Mat4& matrix) { return matrix * scale; } }