Files
j3ml/include/J3ML/LinearAlgebra/Matrix4x4.hpp
josh 9ecb64a2fe
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m32s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 30s
Messing with matrices.
2025-06-11 08:12:31 -05:00

586 lines
36 KiB
C++

#pragma once
#include <J3ML/LinearAlgebra/Forward.hpp>
#include <J3ML/Algorithm/RNG.hpp>
#include <algorithm>
#include <iostream>
#include <bits/ostream.tcc>
using namespace J3ML::Algorithm;
namespace J3ML::LinearAlgebra {
/// @brief 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: /// Constant Values
enum { Rows = 4 };
enum { Cols = 4 };
public: /// Constant Members
/// A constant matrix that has zeroes in all its entries
static const Matrix4x4 Zero;
/// A constant matrix that is the identity.
/** The identity matrix looks like the following:
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
Transforming a vector by the identity matrix is like multiplying a number by one, i.e. the vector is not changed */
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.
/// @note Never compare a matrix to this value. Due to how IEEE floats work, "nan == nan" returns false!
static const Matrix4x4 NaN;
public: /// Constructors
/// Creates a new Matrix4x4 with uninitialized member values.
Matrix4x4() {}
Matrix4x4(const Matrix4x4 &rhs) = default; // {Set(rhs);}
Matrix4x4(float val);
/// Constructs this Matrix4x4 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& m);
explicit Matrix4x4(const float* data);
/// 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);
/// Constructs this Matrix4x4 from the given quaternion.
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 a random 4x4 matrix with each entry randomized between the range [minElem, maxElem]
/** Warning: The matrices returned by this function do not represent well-formed 3D transformations.
This function is mostly used for testing and debugging purposes only. */
static Matrix4x4 RandomGeneral(RNG& rng, float minElem, float maxElem);
/// Creates a new Matrix4x4 that rotates about one of the principal axes.
/** Calling RotateX, RotateY, or RotateZ is slightly faster than calling the more generic RotateAxisAngle function.
@param radians The angle to rotate by, in radians. For example, Pi/4.f equals 45 degrees.
@param pointOnAxis If specified, the rotation is performed about an axis that passes through this point,
and not through the origin. The returned matrix will not be a pure rotation matrix, but will also contain translation. */
static Matrix4x4 RotateX(float radians, const Vector3 &pointOnAxis);
/// [similarOverload: RotateX] [hideIndex]
static Matrix4x4 RotateX(float radians);
/// [similarOverload: RotateX] [hideIndex]
static Matrix4x4 RotateY(float radians, const Vector3 &pointOnAxis);
/// [similarOverload: RotateX] [hideIndex]
static Matrix4x4 RotateY(float radians);
/// [similarOverload: RotateX] [hideIndex]
static Matrix4x4 RotateZ(float radians, const Vector3 &pointOnAxis);
/// [similarOverload: RotateX] [hideIndex]
static Matrix4x4 RotateZ(float radians);
/// Creates a new Matrix4x4 that rotates about the given axis.
/** @param axisDirection The axis to rotate about. This vector must be normalized.
@param angleRadians The angle to rotate by, in radians.
@param pointOnAxis If specified, the rotation is performed about an axis that passes through this point,
and not through the origin. The returned matrix will not be a pure rotation matrix, but will also contain translation. */
static Matrix4x4 RotateAxisAngle(const Vector3 &axisDirection, float angleRadians, const Vector3& pointOnAxis);
static Matrix4x4 RotateAxisAngle(const Vector3 &axisDirection, float angleRadians);
/// Creates a new Matrix4x4 that rotates sourceDirection vector to coincide with the targetDirection vector.
/** @note There are infinite such rotations - this function returns the rotation that has the shortest angle
(when decomposed to axis-angle notation)
@param sourceDirection The 'from' direction vector. This vector must be normalized.
@param targetDirection The 'to' direction vector. This vector must be normalized.
@param centerPoint If specified, rotation is performed using this point as the coordinate space origin.
If omitted, the rotation is performed about the coordinate system origin (0,0,0).
@return A new rotation matrix R for which R*sourceDirection == targetDirection */
static Matrix4x4 RotateFromTo(const Vector3 &sourceDirection, const Vector3 &targetDirection, const Vector3 &centerPoint);
static Matrix4x4 RotateFromTo(const Vector3 &sourceDirection, const Vector3 &targetDirection);
static Matrix4x4 RotateFromTo(const Vector4 &sourceDirection, const Vector4 &targetDirection);
/// Creates a new Matrix4x4 that rotates one coordinate system to coincide with another.
/** This function rotates the sourceDirection vector to coincide with the targetDirection vector, and then
rotates sourceDirection2 (which was transformed by 1.) to targetDirection2, but keeping the constraint that
sourceDirection must look at targetDirection. */
/** @param sourceDirection The first 'from' direction. This vector must be normalized.
@param targetDirection The first 'to' direction. This vector must be normalized.
@param sourceDirection2 The second 'from' direction. This vector must be normalized.
@param targetDirection2 The second 'to' direction. This vector must be normalized.
@param centerPoint If specified, rotation is performed using this point as the coordinate space origin.
@return The returned matrix maps sourceDirection to targetDirection. Additionally, the returned matrix
rotates sourceDirection2 to point towards targetDirection2 as closely as possible, under the previous constriant.
The returned matrix is a rotation matrix, i.e. it is orthonormal with a determinant of +1, and optionally
has a translation component if the rotation is not performed w.r.t. the coordinate system origin */
static Matrix4x4 RotateFromTo(const Vector3& sourceDirection, const Vector3 &targetDirection,
const Vector3 &sourceDirection2, const Vector3 &targetDirection2,
const Vector3 &centerPoint);
static Matrix4x4 RotateFromTo(const Vector3& sourceDirection, const Vector3 &targetDirection,
const Vector3 &sourceDirection2, const Vector3 &targetDirection2);
/// Produces a matrix that shears along a principal axis.
/** The shear matrix offsets the two other axes according to the
position of the point along the shear axis. */
static Matrix4x4 ShearX(float yFactor, float zFactor);
static Matrix4x4 ShearY(float xFactor, float zFactor);
static Matrix4x4 ShearZ(float xFactor, float yFactor);
/// Identical to D3DXMatrixOrthoLH, except transposed to account for Matrix * vector convention used in J3ML.
/// See http://msdn.microsoft.com/en-us/library/windows/desktop/bb205346(v=vs.85).aspx
/// @note Use the M*v multiplication order to project points with this matrix.
static Matrix4x4 D3DOrthoProjLH(float nearPlane, float farPlane, float hViewportSize, float vViewportSize);
/// Identical to D3DXMatrixOrthoRH, except transposed to account for Matrix * vector convention used in J3ML.
/// See http://msdn.microsoft.com/en-us/library/windows/desktop/bb205349(v=vs.85).aspx
/// @note Use the M*v multiplication order to project points with this matrix.
static Matrix4x4 D3DOrthoProjRH(float nearPlane, float farPlane, float hViewportSize, float vViewportSize);
/// Identical to D3DXMatrixPerspectiveLH, except transposed to account for Matrix * vector convention used in J3ML.
/// See http://msdn.microsoft.com/en-us/library/windows/desktop/bb205352(v=vs.85).aspx
/// @note Use the M*v multiplication order to project points with this matrix.
static Matrix4x4 D3DPerspProjLH(float nearPlane, float farPlane, float hViewportSize, float vViewportSize);
/// Identical to D3DXMatrixPerspectiveRH, except transposed to account for Matrix * vector convention used in J3ML.
/// See http://msdn.microsoft.com/en-us/library/windows/desktop/bb205355(v=vs.85).aspx
/// @note Use the M*v multiplication order to project points with this matrix.
static Matrix4x4 D3DPerspProjRH(float nearPlane, float farPlane, float hViewportSize, float vViewportSize);
/// Computes a left-handled orthographic projection matrix for OpenGL.
/// @note Use the M*v multiplication order to project points with this matrix.
static Matrix4x4 OpenGLOrthoProjLH(float nearPlane, float farPlane, float hViewportSize, float vViewportSize);
/// Computes a right-handled orthographic projection matrix for OpenGL.
/// @note Use the M*v multiplication order to project points with this matrix.
static Matrix4x4 OpenGLOrthoProjRH(float nearPlane, float farPlane, float hViewportSize, float vViewportSize);
/// Computes a left-handed perspective projection matrix for OpenGL.
/// @note Use the M*v multiplication order to project points with this matrix.
/// @param n Near-plane
/// @param f Far-plane
/// @param h Horizontal FOV
/// @param v Vertical FOV
static Matrix4x4 OpenGLPerspProjLH(float n, float f, float h, float v);
/// Identical to http://www.opengl.org/sdk/docs/man/xhtml/gluPerspective.xml , except uses viewport sizes instead of FOV to set up the
/// projection matrix.
/// @note Use the M*v multiplication order to project points with this matrix.
// @param n Near-plane
/// @param f Far-plane
/// @param h Horizontal FOV
/// @param v Vertical FOV
static Matrix4x4 OpenGLPerspProjRH(float n, float f, float h, float v);
/// Creates a new transformation matrix that translates by the given offset.
static Matrix4x4 Translate(const Vector3& translation);
/// Creates a new transformation matrix that scales by the given factors.
static Matrix4x4 Scale(const Vector3& scale);
/// Creates a new Matrix4x4 as a combination of translation, rotation, and scale.
/** This function creates a new Matrix4x4 M of the form M = T * R * S, where T is a translation matrix, R is a
rotation matrix, and S a scale matrix. Transforming a vector v using this matrix computes the vector
v' = M * v = T*R*S*v = (T * (R * (S * v))), which means that the scale operation is applied to the
vector first, followed by rotation, and finally translation. */
static Matrix4x4 FromTRS(const Vector3& translate, const Quaternion& rotate, const Vector3& scale);
static Matrix4x4 FromTRS(const Vector3& translate, const Matrix3x3& rotate, const Vector3& scale);
static Matrix4x4 FromTRS(const Vector3& translate, const Matrix4x4& rotate, const Vector3& scale);
void SetCol3(int col, const Vector3 &xyz);
bool HasNegativeScale() const;
public:
/// 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]). */
[[nodiscard]] 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).
[[nodiscard]] Matrix3x3 GetRotatePart() const;
/// Sets the translation part of this matrix.
/** This function sets the translation part of this matrix. These are the first three elements of the fourth column. All other entries are left untouched. */
void SetTranslatePart(float translateX, float translateY, float translateZ);
void SetTranslatePart(const Vector3& offset);
/// Sets the 3-by-3 part of this matrix to perform rotation about the given axis and angle (in radians). Leaves all other
/// entries of this matrix untouched.
void SetRotatePart(const Quaternion& q);
void SetRotatePart(const Vector3& axisDirection, float angleRadians);
/// Sets the 3-by-3 part of this matrix.
/// @note This is a convenience function which calls Set3x3Part.
/// @note This function erases the previous top-left 3x3 part of this matrix (any previous rotation, scaling and shearing, etc.) Translation is unaffecte.d
void SetRotatePart(const Matrix3x3& rotation) { Set3x3Part(rotation); }
/// Sets the 3-by-3 part of this matrix to perform rotation about the positive X axis which passes through the origin.
/// Leaves all other entries of this matrix untouched.
void SetRotatePartX(float angleRadians);
/// Sets the 3-by-3 part of this matrix to perform the rotation about the positive Y axis.
/// Leaves all other entries of the matrix untouched.
void SetRotatePartY(float angleRadians);
/// Sets the 3-by-3 part of this matrix to perform the rotation about the positive Z axis.
/// Leaves all other entries of the matrix untouched.
void SetRotatePartZ(float angleRadians);
void Set3x3Part(const Matrix3x3& r);
/// Sets the value of a given row.
/// @param row The index of the row to a set, in the range [0-3].
/// @param data A pointer to an array of 4 floats that contain the new x,y,z,w values for the row.
void SetRow(int row, const float* data);
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);
/// Sets the value of a given column.
/// @param column The index of the column to set, in the range [0-3].
/// @param data A pointer to an array of 3 floats that contain the new x,y,z,w values for the column.
void SetCol(int col, const float *data);
void SetCol(int col, const Vector3& colVector, float m_c3);
void SetCol(int col, const Vector4& colVector);
void SetCol(int col, float m_c0, float m_c1, float m_c2, float m_c3);
void SetCol3(int col, float x, float y, float z);
/// Returns the given row.
/** @param The zero-based index [0, 3] of the row to get */
[[nodiscard]] Vector4 GetRow(int row) const;
[[nodiscard]] Vector4 Row(int row) const;
Vector4& Row(int row);
/// Returns only the first-three elements of the given row.
[[nodiscard]] Vector3 GetRow3(int index) const;
[[nodiscard]] Vector3 Row3(int i) const;
/// This method also allows assignment to the retrieved row.
Vector3& Row3(int row);
/// Returns the given column.
/** @param col The zero-based index [0, 3] of the column to get. */
[[nodiscard]] Vector4 GetColumn(int index) const;
[[nodiscard]] Vector4 Column(int index) const;
[[nodiscard]] Vector4 Col(int i) const;
/// Returns only the first three elements of the given column.
[[nodiscard]] Vector3 GetColumn3(int index) const;
[[nodiscard]] Vector3 Column3(int index) const;
[[nodiscard]] Vector3 Col3(int i) const;
/// Returns the scaling performed by this matrix. This function assumes taht the last row is [0 0 0 1].
/// GetScale().x specifies the amount of scaling applied to the local x direction vector when it is transformed by this matrix.
/// i.e. GetScale()[i] equals Col[i].Length();
[[nodiscard]] Vector3 GetScale() const;
float &At(int row, int col);
[[nodiscard]] float At(int x, int y) const;
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);
/// Reduces this matrix to its row-echelon form.
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 is the identity matrix.
bool IsIdentity(float epsilon = 1e-3f) 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;
[[nodiscard]] Vector4 Diagonal() const;
[[nodiscard]] 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.
[[nodiscard]] 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.
[[nodiscard]] 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.
[[nodiscard]] 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]; }
[[nodiscard]] const float *ptr() const { return &elems[0][0]; }
/// Computes the determinant of the upper-left 3x3 submatrix of this matrix.
[[nodiscard]] float Determinant3x3() const;
/// Computes the determinant of this matrix.
// If the determinant is nonzero, this matrix is invertible.
[[nodiscard]] float Determinant() const;
/// Computes the determinant of this matrix.
/** If the determinant is nonzero, this matrix is invertible.
If the determinant is negative, this matrix performs reflection about some axis.
From http://msdn.microsoft.com/en-us/library/bb204853(VS.85).aspx :
"If the determinant is positive, the basis is said to be "positively" oriented (or right-handed).
If the determinant is negative, the basis is said to be "negatively" oriented (or left-handed)." */
float Determinant4() 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;
static Matrix4x4 FromTranslation(const Vector3& rhs);
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;
Vector4 operator[](int row) const;
Vector2 operator * (const Vector2& rhs) const;
Vector3 operator * (const Vector3& rhs) const;
Vector4 operator * (const Vector4& rhs) const;
Vector2 Mul(const Vector2& rhs) const;
Vector3 Mul(const Vector3& rhs) const;
Vector4 Mul(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;
/// Returns the scale component of this matrix.
/** This function decomposes this matrix M into a form M = M' * S, where M' has the unitary column vectors and S is a diagonal matrix.
@return ExtractScale returns the diagonal entries of S, i.e. the scale of the columns of this matrix. If this matrix
represents a local->world space transformation for an object, then this scale represents a 'local scale', i.e.
scaling that is performed before translating and rotating the object from its local coordinate system to its world
position.
@note This function assumes that this matrix does not contain projection (the fourth row of this matrix is [0 0 0 1]). */
Vector3 ExtractScale() const;
/// Removes the scaling performed by this matrix. That is, decomposes this matrix M into a form M = M' * S, where
/// M' has unitary column vectors and S is a diagonal matrix. Then replaces this matrix with M'.
/// @note This function assumes that this matrix does not contain projection (the fourth row of this matrix is [0 0 0 1]).
/// @note This function assumes that this matrix has orthogonal basis vectors (row and column vector sets are orthogonal).
/// @note This function does not remove reflection (-1 scale along some axis).
void RemoveScale();
/// Decomposes this matrix to translate, rotate, and scale parts.
/** This function decomposes this matrix M to a form M = T * R * S, where T is a translation matrix, R is a rotation matrix, and S is a scale matrix
@note Remember that in the convention of this class, transforms are applied in the order M * v, so scale is
applied first, then rotation, and finally the translation last.
@note This function assumes that this matrix does not contain projection (The fourth row of this matrix is [0 0 0 1]).
@param translate [out] This vector receives the translation component this matrix performs. The translation is applied last
after rotation and scaling.
@param rotate [out] This object receives the rotation part of this transform.
@param scale [out] This vector receives the scaling along the local (before transformation by R) X, Y, and Z axes performed by this matrix. */
void Decompose(Vector3& translate, Quaternion& rotate, Vector3& scale) const;
void Decompose(Vector3& translate, Matrix3x3& rotate, Vector3& scale) const;
void Decompose(Vector3& translate, Matrix4x4& rotate, Vector3& scale) const;
/// Returns true if this matrix only contains uniform scaling, compared to the given epsilon.
/// @note If the matrix does not really do any scaling, this function returns true (scaling uniformly by a factor of 1).
/// @note This function only examines the upper 3-by-3 part of this matrix.
/// @note This function assumes that this matrix does not contain projection (the fourth row of this matrix is [0 0 0 1]).
bool HasUniformScale(float epsilon = 1e-3f) const;
/// Returns true if the row vectors of 3x3-top-left submatrix are all perpendicular to each other.
bool IsColOrthogonal3(float epsilon = 1e-3f) const;
/// Returns true if the column vector of 3x3-top-left submatrix are all perpendicular to each other.
bool IsRowOrthogonal3(float epsilon = 1e-3f) const;
bool IsColOrthogonal(float epsilon = 1e-3f) const;
bool IsRowOrthogonal(float epsilon = 1e-3f) const;
/// Returns true if this matrix is seen to contain a "projective" part,
/// i.e. whether the last row of this matrix differs from [0 0 0 1]
bool ContainsProjection(float epsilon = 1e-3f) const;
/// Sets all values of this matrix.
void Set(float _00, float _01, float _02, float _03,
float _10, float _11, float _12, float _13,
float _20, float _21, float _22, float _23,
float _30, float _31, float _32, float _33);
/// Sets this to be a copy of the matrix rhs.
void Set(const Matrix4x4 &rhs);
/// Sets all values of this matrix.
/** @param values The values in this array will be copied over to this matrix. The source must contain 16 floats in row-major order
(the same order as the Set() ufnction above has its input parameters in. */
void Set(const float *p);
/// Sets this matrix to equal the identity.
void SetIdentity();
/// Returns the adjugate of this matrix.
Matrix4x4 Adjugate() const;
/// Computes the Cholesky decomposition of this matrix.
/// The returned matrix L satisfies L * transpose(L) = this;
/// Returns true on success.
bool CholeskyDecompose(Matrix4x4 &outL) const;
/// Computes the LU decomposition of this matrix.
/// This decomposition has the form 'this = L * U'
/// Returns true on success.
bool LUDecompose(Matrix4x4& outLower, Matrix4x4& outUpper) const;
/// Inverts this matrix using the generic Gauss's method.
/// @return Returns true on success, false otherwise.
bool Inverse(float epsilon = 1e-6f);
/// Returns an inverted copy of this matrix.
/// If this matrix does not have an inverse, returns the matrix that was the result of running
/// Gauss's method on the matrix.
Matrix4x4 Inverted() const;
/// Inverts a column-orthogonal matrix.
/// If a matrix is of form M=T*R*S, where T is an affine translation matrix
/// R is a rotation matrix and S is a diagonal matrix with non-zero but pote ntially non-uniform scaling
/// factors (possibly mirroring), then the matrix M is column-orthogonal and this function can be used to compute the inverse.
/// Calling this function is faster than the calling the generic matrix Inverted() function.
/// Returns true on success. On failure, the matrix is not modified. This function fails if any of the
/// elements of this vector are not finite, or if the matrix contains a zero scaling factor on X, Y, or Z.
/// This function may not be called if this matrix contains any projection (last row differs from (0 0 0 1)).
/// @note The returned matrix will be row-orthogonal, but not column-orthogonal in general.
/// The returned matrix will be column-orthogonal if the original matrix M was row-orthogonal as well.
/// (in which case S had uniform scale, InverseOrthogonalUniformScale() could have been used instead).
bool InverseColOrthogonal();
/// Inverts a matrix that is a concatenation of only translate, rotate, and uniform scale operations.
/// If a matrix is of form M = T*R*S, where T is an affine translation matrix,
/// R is a rotation matrix and S is a diagonal matrix with non-zero and uniform scaling factors (possibly mirroring),
/// then the matrix M is both column- and row-orthogonal and this function can be used to compute this inverse.
/// This function is faster than calling InverseColOrthogonal() or the generic Inverted().
/// Returns true on success. On failure, the matrix is not modified. This function fails if any of the
/// elements of this vector are not finite, or if the matrix contains a zero scaling factor on X, Y, or Z.
/// This function may not be called if this matrix contains any shearing or nonuniform scaling.
/// This function may not be called if this matrix contains any projection (last row differs from (0 0 0 1)).
bool InverseOrthogonalUniformScale();
/// Inverts a matrix that is a concatenation of only translate and rotate operations.
/// If a matrix is of form M = T*R*S, where T is an affine translation matrix, R is a rotation
/// matrix and S is either identity or a mirroring matrix, then the matrix M is orthonormal and this function can be used to compute the inverse.
/// This function is faster than calling InverseOrthogonalUniformScale(), InverseColOrthogonal(), or the generic Inverted().
/// This function may not be called if this matrix contains any scaling or shearing, but it may contain mirroring.
/// This function may not be called if this matrix contains any projection (last row differs from (0 0 0 1)).
void InverseOrthonormal();
/// Transposes this matrix.
/// This operation swaps all elements with respect to the diagonal.
void Transpose();
/// Returns a transposed copy of this matrix.
Matrix4x4 Transposed() const;
/// Computes the inverse transpose of this matrix in-place.
/// Use the inverse transpose to transform covariant vectors (normal vectors).
bool InverseTranspose();
/// Returns the inverse transpose of this matrix.
/// Use that matrix to transform covariant vectors (normal vectors).
Matrix4x4 InverseTransposed() const;
/// Returns the sum of the diagonal elements of this matrix.
float Trace() const;
[[nodiscard]] Quaternion ToQuat() const;
/// Returns true if this Matrix4x4 is equal to the given Matrix4x4, up to given per-element epsilon.
bool Equals(const Matrix4x4& other, float epsilon = 1e-3f) const;
[[nodiscard]] std::string ToString() const;
protected:
float elems[4][4];
Vector3 TransformDir(float tx, float ty, float tz) const;
};
std::ostream& operator << (std::ostream& out, const Matrix4x4& rhs);
}