#pragma once #include #include namespace J3ML::LinearAlgebra { /// A 4-by-4 matrix for affine transformations and perspective projections of 3D geometry. /* This matrix can represent the most generic form of transformations for 3D objects, * including perspective projections, which a 4-by-3 cannot store, * and translations, which a 3-by-3 cannot represent. * The elements of this matrix are * m_00, m_01, m_02, m_03 * m_10, m_11, m_12, m_13 * m_20, m_21, m_22, m_23, * m_30, m_31, m_32, m_33 * * The element m_yx is the value on the row y and column x. * You can access m_yx using the double-bracket notation m[y][x] */ class Matrix4x4 { public: // TODO: Implement assertions to ensure matrix bounds are not violated! enum { Rows = 4 }; enum { Cols = 4 }; /// A constant matrix that has zeroes in all its entries static const Matrix4x4 Zero; /// A constant matrix that is the identity. static const Matrix4x4 Identity; /// A compile-time constant float4x4 which has NaN in each element. /// For this constant, each element has the value of quet NaN, or Not-A-Number. /// Never compare a matrix to this value. Due to how IEEE floats work, "nan == nan" returns false! static const Matrix4x4 NaN; /// Creates a new float4x4 with uninitialized member values. Matrix4x4() {} Matrix4x4(float val); /// Constructs this float4x4 to represent the same transformation as the given float3x3. /** This function expands the last row and column of this matrix with the elements from the identity matrix. */ Matrix4x4(const Matrix3x3&); /// Constructs a new float4x4 by explicitly specifying all the matrix elements. /// The elements are specified in row-major format, i.e. the first row first followed by the second and third row. /// E.g. The element _10 denotes the scalar at second (index 1) row, first (index 0) column. Matrix4x4(float m00, float m01, float m02, float m03, float m10, float m11, float m12, float m13, float m20, float m21, float m22, float m23, float m30, float m31, float m32, float m33); /// Constructs the matrix by explicitly specifying the four column vectors. /** @param col0 The first column. If this matrix represents a change-of-basis transformation, this parameter is the world-space direction of the local X axis. @param col1 The second column. If this matrix represents a change-of-basis transformation, this parameter is the world-space direction of the local Y axis. @param col2 The third column. If this matrix represents a change-of-basis transformation, this parameter is the world-space direction of the local Z axis. @param col3 The fourth column. If this matrix represents a change-of-basis transformation, this parameter is the world-space position of the local space pivot. */ Matrix4x4(const Vector4& r1, const Vector4& r2, const Vector4& r3, const Vector4& r4); explicit Matrix4x4(const Quaternion& orientation); /// Constructs this float4x4 from the given quaternion and translation. /// Logically, the translation occurs after the rotation has been performed. Matrix4x4(const Quaternion& orientation, const Vector3 &translation); /// Creates a LookAt matrix from a look-at direction vector. /** A LookAt matrix is a rotation matrix that orients an object to face towards a specified target direction. @param localForward Specifies the forward direction in the local space of the object. This is the direction the model is facing at in its own local/object space, often +X (1,0,0), +Y (0,1,0) or +Z (0,0,1). The vector to pass in here depends on the conventions you or your modeling software is using, and it is best pick one convention for all your objects, and be consistent. This input parameter must be a normalized vector. @param targetDirection Specifies the desired world space direction the object should look at. This function will compute a rotation matrix which will rotate the localForward vector to orient towards this targetDirection vector. This input parameter must be a normalized vector. @param localUp Specifies the up direction in the local space of the object. This is the up direction the model was authored in, often +Y (0,1,0) or +Z (0,0,1). The vector to pass in here depends on the conventions you or your modeling software is using, and it is best to pick one convention for all your objects, and be consistent. This input parameter must be a normalized vector. This vector must be perpendicular to the vector localForward, i.e. localForward.Dot(localUp) == 0. @param worldUp Specifies the global up direction of the scene in world space. Simply rotating one vector to coincide with another (localForward->targetDirection) would cause the up direction of the resulting orientation to drift (e.g. the model could be looking at its target its head slanted sideways). To keep the up direction straight, this function orients the localUp direction of the model to point towards the specified worldUp direction (as closely as possible). The worldUp and targetDirection vectors cannot be collinear, but they do not need to be perpendicular either. @return A matrix that maps the given local space forward direction vector to point towards the given target direction, and the given local up direction towards the given target world up direction. The returned matrix M is orthonormal with a determinant of +1. For the matrix M it holds that M * localForward = targetDirection, and M * localUp lies in the plane spanned by the vectors targetDirection and worldUp. @note The position of (the translation performed by) the resulting matrix will be set to (0,0,0), i.e. the object will be placed to origin. Call SetTranslatePart() on the resulting matrix to set the position of the model. @see RotateFromTo(). */ static Matrix4x4 LookAt(const Vector3& localFwd, const Vector3& targetDir, const Vector3& localUp, const Vector3& worldUp); /// Returns the translation part. /** The translation part is stored in the fourth column of this matrix. This is equivalent to decomposing this matrix in the form M = T * M', i.e. this translation is applied last, after applying rotation and scale. If this matrix represents a local->world space transformation for an object, then this gives the world space position of the object. @note This function assumes that this matrix does not contain projection (the fourth row of this matrix is [0 0 0 1]). */ Vector3 GetTranslatePart() const; /// Returns the top-left 3x3 part of this matrix. This stores the rotation part of this matrix (if this matrix represents a rotation). Matrix3x3 GetRotatePart() const; void SetTranslatePart(float translateX, float translateY, float translateZ); void SetTranslatePart(const Vector3& offset); void SetRotatePart(const Quaternion& q); void Set3x3Part(const Matrix3x3& r); void SetRow(int row, const Vector3& rowVector, float m_r3); void SetRow(int row, const Vector4& rowVector); void SetRow(int row, float m_r0, float m_r1, float m_r2, float m_r3); Vector4 GetRow(int index) const; Vector4 GetColumn(int index) const; Vector3 GetRow3(int index) const; Vector3 GetColumn3(int index) const; Vector3 GetScale() const { } float &At(int row, int col); float At(int x, int y) const; template void Swap(T &a, T &b) { T temp = std::move(a); a = std::move(b); b = std::move(temp); } void SwapColumns(int col1, int col2); /// Swaps two rows. void SwapRows(int row1, int row2); /// Swapsthe xyz-parts of two rows element-by-element void SwapRows3(int row1, int row2); void ScaleRow(int row, float scalar); void ScaleRow3(int row, float scalar); void ScaleColumn(int col, float scalar); void ScaleColumn3(int col, float scalar); /// Algorithm from Eric Lengyel's Mathematics for 3D Game Programming & Computer Graphics, 2nd Ed. void Pivot(); /// Tests if this matrix does not contain any NaNs or infs. /** @return Returns true if the entries of this float4x4 are all finite, and do not contain NaN or infs. */ bool IsFinite() const; /// Tests if this matrix has an inverse. /** @return Returns true if this matrix can be inverted, up to the given epsilon. */ bool IsInvertible(float epsilon = 1e-3f) const; Vector4 Diagonal() const; Vector3 Diagonal3() const; /// Returns the local +X axis in world space. /// This is the same as transforming the vector (1,0,0) by this matrix. Vector3 WorldX() const; /// Returns the local +Y axis in world space. /// This is the same as transforming the vector (0,1,0) by this matrix. Vector3 WorldY() const; /// Returns the local +Z axis in world space. /// This is the same as transforming the vector (0,0,1) by this matrix. Vector3 WorldZ() const; /// Accesses this structure as a float array. /// @return A pointer to the upper-left element. The data is contiguous in memory. /// ptr[0] gives the element [0][0], ptr[1] is [0][1], ptr[2] is [0][2]. /// ptr[4] == [1][0], ptr[5] == [1][1], ..., and finally, ptr[15] == [3][3]. float *ptr() { return &elems[0][0]; } const float *ptr() const { return &elems[0][0]; } float Determinant3x3() const; /// Computes the determinant of this matrix. // If the determinant is nonzero, this matrix is invertible. float Determinant() const; #define SKIPNUM(val, skip) (val >= skip ? (val+1) : val) float Minor(int i, int j) const; Matrix4x4 Inverse() const; Matrix4x4 Transpose() const; Vector2 Transform(float tx, float ty) const; Vector2 Transform(const Vector2& rhs) const; Vector3 Transform(float tx, float ty, float tz) const; Vector3 Transform(const Vector3& rhs) const; Vector4 Transform(float tx, float ty, float tz, float tw) const; Vector4 Transform(const Vector4& rhs) const; Matrix4x4 Translate(const Vector3& rhs) const; static Matrix4x4 FromTranslation(const Vector3& rhs); static Matrix4x4 D3DOrthoProjLH(float nearPlane, float farPlane, float hViewportSize, float vViewportSize); static Matrix4x4 D3DOrthoProjRH(float nearPlane, float farPlane, float hViewportSize, float vViewportSize); static Matrix4x4 D3DPerspProjLH(float nearPlane, float farPlane, float hViewportSize, float vViewportSize); static Matrix4x4 D3DPerspProjRH(float nearPlane, float farPlane, float hViewportSize, float vViewportSize); static Matrix4x4 OpenGLOrthoProjLH(float nearPlane, float farPlane, float hViewportSize, float vViewportSize); static Matrix4x4 OpenGLOrthoProjRH(float nearPlane, float farPlane, float hViewportSize, float vViewportSize); static Matrix4x4 OpenGLPerspProjLH(float nearPlane, float farPlane, float hViewportSize, float vViewportSize); static Matrix4x4 OpenGLPerspProjRH(float nearPlane, float farPlane, float hViewportSize, float vViewportSize); Vector4 operator[](int row); Matrix4x4 operator-() const; Matrix4x4 operator +(const Matrix4x4& rhs) const; Matrix4x4 operator - (const Matrix4x4& rhs) const; Matrix4x4 operator *(float scalar) const; Matrix4x4 operator /(float scalar) const; Vector2 operator * (const Vector2& rhs) const; Vector3 operator * (const Vector3& rhs) const; Vector4 operator * (const Vector4& rhs) const; Matrix4x4 operator * (const Matrix3x3 &rhs) const; Matrix4x4 operator +() const; Matrix4x4 operator * (const Matrix4x4& rhs) const; Matrix4x4 &operator = (const Matrix3x3& rhs); Matrix4x4 &operator = (const Quaternion& rhs); Matrix4x4 &operator = (const Matrix4x4& rhs) = default; protected: float elems[4][4]; }; }