#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2015 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License #ifndef NUCLEX_GEOMETRY_MATRIX44_H #define NUCLEX_GEOMETRY_MATRIX44_H #include "Nuclex/Geometry/Config.h" #include "Nuclex/Geometry/Math.h" #include "Nuclex/Geometry/Point3.h" #include "Nuclex/Geometry/Vector3.h" #include "Nuclex/Geometry/Vector4.h" #include "Nuclex/Geometry/Areas/Rectangle2.h" #include namespace Nuclex { namespace Geometry { // ------------------------------------------------------------------------------------------- // /// Matrix that holds an orientation and translation in 4D space /// /// This kind of matrix is typically used to store an object's position, scale /// and orientation in 3D space (leaving the 4th dimension, aka W coordinates /// unused and set to the identity matrix values). /// template struct Matrix44 { /// Number of rows in this matrix public: static const std::size_t RowCount = 4; /// Number of columns in this matrix public: static const std::size_t ColumnCount = 4; #pragma region class MatrixRowAccessor /// Accesses a single row in a matrix public: class MatrixRowAccessor { /// Initializes a new matrix row accessor /// Matrix that the row accessor will access /// Index of the row the row accessor will access public: MatrixRowAccessor(Matrix44 &matrix, std::size_t rowIndex) : matrix(matrix), rowIndex(rowIndex) { if(rowIndex >= 4) { throw std::out_of_range("Invalid row index"); } } /// Accesses an entire row of the matrix as a 4D vector public: operator const Vector4() const { switch(this->rowIndex) { case 0: { return Vector4( this->matrix->M11, this->matrix->M12, this->matrix->M13, this->matrix->M14 ); } case 1: { return Vector4( this->matrix->M21, this->matrix->M22, this->matrix->M23, this->matrix->M24 ); } case 2: { return Vector4( this->matrix->M31, this->matrix->M32, this->matrix->M33, this->matrix->M34 ); } case 3: { return Vector4( this->matrix->M41, this->matrix->M42, this->matrix->M43, this->matrix->M44 ); } } } /// Accesses the specified column in the row assigned to the accessor /// Column in which the accessor will access a row /// The matrix element at the specified column public: TScalar &operator[](std::size_t columnIndex) { if(columnIndex >= 4) { throw std::out_of_range("Invalid column index"); } switch((rowIndex << 4) | columnIndex) { case 0x0000: { return matrix->M11; } case 0x0001: { return matrix->M12; } case 0x0002: { return matrix->M13; } case 0x0003: { return matrix->M14; } case 0x0100: { return matrix->M21; } case 0x0101: { return matrix->M22; } case 0x0102: { return matrix->M23; } case 0x0103: { return matrix->M24; } case 0x0200: { return matrix->M31; } case 0x0201: { return matrix->M32; } case 0x0202: { return matrix->M33; } case 0x0203: { return matrix->M34; } case 0x0300: { return matrix->M41; } case 0x0301: { return matrix->M42; } case 0x0302: { return matrix->M43; } case 0x0303: { return matrix->M44; } } } /// Accesses the specified column in the row assigned to the accessor /// Column in which the accessor will access a row /// The matrix element at the specified column public: const TScalar &operator[](std::size_t columnIndex) const { if(columnIndex >= 4) { throw std::out_of_range("Invalid column index"); } switch((rowIndex << 4) | columnIndex) { case 0x0000: { return matrix->M11; } case 0x0001: { return matrix->M12; } case 0x0002: { return matrix->M13; } case 0x0003: { return matrix->M14; } case 0x0100: { return matrix->M21; } case 0x0101: { return matrix->M22; } case 0x0102: { return matrix->M23; } case 0x0103: { return matrix->M24; } case 0x0200: { return matrix->M31; } case 0x0201: { return matrix->M32; } case 0x0202: { return matrix->M33; } case 0x0203: { return matrix->M34; } case 0x0300: { return matrix->M41; } case 0x0301: { return matrix->M42; } case 0x0302: { return matrix->M43; } case 0x0303: { return matrix->M44; } } } /// Matrix the accessor will access private: Matrix44 &matrix; /// Index of the row in which the accessor will access an element private: std::size_t rowIndex; }; #pragma endregion // class MatrixRowAccessor #pragma region class ConstMatrixRowAccessor /// Accesses a single row in an immutable matrix public: class ConstMatrixRowAccessor { /// Initializes a new matrix row accessor /// Matrix that the row accessor will access /// Index of the row the row accessor will access public: ConstMatrixRowAccessor(const Matrix44 &matrix, std::size_t rowIndex) : matrix(matrix), rowIndex(rowIndex) { if(rowIndex >= 4) { throw std::out_of_range("Invalid row index"); } } /// Accesses an entire row of the matrix as a 4D vector public: operator const Vector4() const { switch(this->rowIndex) { case 0: { return Vector4( this->matrix->M11, this->matrix->M12, this->matrix->M13, this->matrix->M14 ); } case 1: { return Vector4( this->matrix->M21, this->matrix->M22, this->matrix->M23, this->matrix->M24 ); } case 2: { return Vector4( this->matrix->M31, this->matrix->M32, this->matrix->M33, this->matrix->M34 ); } case 3: { return Vector4( this->matrix->M41, this->matrix->M42, this->matrix->M43, this->matrix->M44 ); } } } /// Accesses the specified column in the row assigned to the accessor /// Column in which the accessor will access a row /// The matrix element at the specified column public: const TScalar &operator[](std::size_t columnIndex) const { if(columnIndex >= 4) { throw std::out_of_range("Invalid column index"); } switch((rowIndex << 4) | columnIndex) { case 0x0000: { return matrix->M11; } case 0x0001: { return matrix->M12; } case 0x0002: { return matrix->M13; } case 0x0003: { return matrix->M14; } case 0x0100: { return matrix->M21; } case 0x0101: { return matrix->M22; } case 0x0102: { return matrix->M23; } case 0x0103: { return matrix->M24; } case 0x0200: { return matrix->M31; } case 0x0201: { return matrix->M32; } case 0x0202: { return matrix->M33; } case 0x0203: { return matrix->M34; } case 0x0300: { return matrix->M41; } case 0x0301: { return matrix->M42; } case 0x0302: { return matrix->M43; } case 0x0303: { return matrix->M44; } } } /// Matrix the accessor will access private: const Matrix44 &matrix; /// Index of the row in which the accessor will access an element private: std::size_t rowIndex; }; #pragma endregion // class ConstMatrixRowAccessor /// An empty matrix public: static const Matrix44 Empty; /// The identity matrix public: static const Matrix44 Identity; /// Constructs an uninitialized 4x4 matrix public: Matrix44() {} /// Initializes a new 4x4 matrix /// Existing matrix that will be copied public: Matrix44(TScalar m[4][4]) : M11(m[0][0]), M12(m[0][1]), M13(m[0][2]), M14(m[0][3]), M21(m[1][0]), M22(m[1][1]), M23(m[1][2]), M24(m[1][3]), M31(m[2][0]), M32(m[2][1]), M33(m[2][2]), M34(m[2][3]), M41(m[3][0]), M42(m[3][1]), M43(m[3][2]), M44(m[3][3]) {} /// Initializes a new 4x4 matrix /// Value for the element in row 1, column 1 /// Value for the element in row 1, column 2 /// Value for the element in row 1, column 3 /// Value for the element in row 1, column 4 /// Value for the element in row 2, column 1 /// Value for the element in row 2, column 2 /// Value for the element in row 2, column 3 /// Value for the element in row 2, column 4 /// Value for the element in row 3, column 1 /// Value for the element in row 3, column 2 /// Value for the element in row 3, column 3 /// Value for the element in row 3, column 4 /// Value for the element in row 4, column 1 /// Value for the element in row 4, column 2 /// Value for the element in row 4, column 3 /// Value for the element in row 4, column 4 public: Matrix44( TScalar m11, TScalar m12, TScalar m13, TScalar m14, TScalar m21, TScalar m22, TScalar m23, TScalar m24, TScalar m31, TScalar m32, TScalar m33, TScalar m34, TScalar m41, TScalar m42, TScalar m43, TScalar m44 ) : M11(m11), M12(m12), M13(m13), M14(m14), M21(m21), M22(m22), M23(m23), M24(m24), M31(m31), M32(m32), M33(m33), M34(m34), M41(m41), M42(m42), M43(m43), M44(m44) {} /// Creates a right-handed orthogonal projection matrix /// Size of the view shown by the projection matrix /// Near clipping plane distance /// Far clipping plane distance /// An orthogonal projection matrix public: static Matrix44 CreateOrthogonalProjection( const Areas::Rectangle2 &viewSize, TScalar near = TScalar(1) / TScalar(100), TScalar far = TScalar(100) ); /// Creates a right-handed perspective projection matrix /// Field of view in radians /// Aspect ratio of the view area /// Near clipping plane distance /// Far clipping plane distance /// A perspective projection matrix public: static Matrix44 CreatePerspectiveProjection( TScalar fieldOfViewRadians, TScalar aspectRatio, TScalar near = TScalar(1) / TScalar(100), TScalar far = TScalar(100) ); /// Creates a rotation matrix that rotates on the X axis /// Angle by which the matrix rotates /// A rotation matrix public: static Matrix44 CreateXRotation(TScalar angleRadians); /// Creates a rotation matrix that rotates on the Y axis /// Angle by which the matrix rotates /// A rotation matrix public: static Matrix44 CreateYRotation(TScalar angleRadians); /// Creates a rotation matrix that rotates on the Z axis /// Angle by which the matrix rotates /// A rotation matrix public: static Matrix44 CreateZRotation(TScalar angleRadians); /// Creates a translation matrix /// X offset the matrix will translate by /// Y offset the matrix will translate by /// Z offset the matrix will translate by /// A matrix that translates by the specified offset public: static Matrix44 CreateTranslation(TScalar x, TScalar y, TScalar z); /// Creates a translation matrix /// Offset the matrix will translate by /// A matrix that translates by the specified offset public: static Matrix44 CreateTranslation(const Vector3 &translation); /// Changes the matrix orientation so it points at the target /// Target the matrix will point at /// Up axis the camera will be oriented to public: void LookAt( const Point3 &target, const Vector3 &up = Vector3::Up ); /// Transposes the matrix; rows become columns and vice versa public: void Transpose(); /// Multiplies the matrix by another matrix /// Other matrix this matrix will be multiplied with /// This result of the matrix multiplication public: Matrix44 operator *(const Matrix44 &other); /// Matrix row 1 column 1, storing the right vector's X coordinate public: TScalar M11; /// Matrix row 1 column 2, storing the right vector's Y coordinate public: TScalar M12; /// Matrix row 1 column 3, storing the right vector's Z coordinate public: TScalar M13; /// Matrix row 1 column 4, storing the right vector's W coordinate public: TScalar M14; /// Matrix row 2 column 1, storing the up vector's X coordinate public: TScalar M21; /// Matrix row 2 column 2, storing the up vector's Y coordinate public: TScalar M22; /// Matrix row 2 column 3, storing the up vector's Z coordinate public: TScalar M23; /// Matrix row 2 column 4, storing the up vector's W coordinate public: TScalar M24; /// Matrix row 3 column 1, storing the forward vector's X coordinate public: TScalar M31; /// Matrix row 3 column 2, storing the forward vector's Y coordinate public: TScalar M32; /// Matrix row 3 column 3, storing the forward vector's Z coordinate public: TScalar M33; /// Matrix row 3 column 4, storing the forward vector's W coordinate public: TScalar M34; /// Matrix row 4 column 1, storing the translation's X coordinate public: TScalar M41; /// Matrix row 4 column 2, storing the translation's Y coordinate public: TScalar M42; /// Matrix row 4 column 3, storing the translation's Z coordinate public: TScalar M43; /// Matrix row 4 column 4, storing the translation's W coordinate public: TScalar M44; }; // ------------------------------------------------------------------------------------------- // template const Matrix44 Matrix44::Empty( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); // ------------------------------------------------------------------------------------------- // template const Matrix44 Matrix44::Identity( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ); // ------------------------------------------------------------------------------------------- // template Matrix44 Matrix44::CreateOrthogonalProjection( const Areas::Rectangle2 &viewSize, TScalar near /* = TScalar(0.01) */, TScalar far /* = TScalar(100) */ ) { TScalar range = 1 / (near - far); return Matrix44( 2 / viewSize.GetWidth(), 0, 0, 0, 0, 2 / viewSize.GetHeight(), 0, 0, 0, 0, range, 0, 0, 0, range * near, 1 ); } // ------------------------------------------------------------------------------------------- // template Matrix44 Matrix44::CreatePerspectiveProjection( TScalar fieldOfViewRadians, TScalar aspectRatio, TScalar near_ /* = TScalar(0.01) */, TScalar far_ /* = TScalar(100.0) */ ) { fieldOfViewRadians /= 2; TScalar fieldOfViewSine = Math::Sine(fieldOfViewRadians); TScalar fieldOfViewCosine = Math::Cosine(fieldOfViewRadians); TScalar height = fieldOfViewCosine / fieldOfViewSine; TScalar width = height / aspectRatio; TScalar range = far_ / (near_ - far_); return Matrix44( width, 0, 0, 0, 0, height, 0, 0, 0, 0, range, -1, 0, 0, range * near_, 0 ); } // ------------------------------------------------------------------------------------------- // template Matrix44 Matrix44::CreateXRotation(TScalar angleRadians) { TScalar sine = Math::Sine(angleRadians); TScalar cosine = Math::Cosine(angleRadians); return Matrix44( 1, 0, 0, 0, 0, cosine, sine, 0, 0, -sine, cosine, 0, 0, 0, 0, 1 ); } // ------------------------------------------------------------------------------------------- // template Matrix44 Matrix44::CreateYRotation(TScalar angleRadians) { TScalar sine = Math::Sine(angleRadians); TScalar cosine = Math::Cosine(angleRadians); return Matrix44( cosine, 0, -sine, 0, 0, 1, 0, 0, sine, 0, cosine, 0, 0, 0, 0, 1 ); } // ------------------------------------------------------------------------------------------- // template Matrix44 Matrix44::CreateZRotation(TScalar angleRadians) { TScalar sine = Math::Sine(angleRadians); TScalar cosine = Math::Cosine(angleRadians); return Matrix44( cosine, sine, 0, 0, -sine, cosine, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ); } // ------------------------------------------------------------------------------------------- // template Matrix44 Matrix44::CreateTranslation(TScalar x, TScalar y, TScalar z) { return Matrix44( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1 ); } // ------------------------------------------------------------------------------------------- // template Matrix44 Matrix44::CreateTranslation(const Vector3 &translation) { return Matrix44( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, translation.X, translation.Y, translation.Z, 1 ); } // ------------------------------------------------------------------------------------------- // template void Matrix44::LookAt( const Point3 &target, const Vector3 &worldUp /* = Vector3::Up */ ) { // TODO: I think this constructs an already inverted matrix. Change it! // I want this method to construct a normal matrix so it can be used to orient // arbitrary objects towards something. If the user needs a view matrix, he should // explicitly invert it. Vector3 forward( this->M41 - target.X, this->M42 - target.Y, this->M43 - target.Z ); forward.Normalize(); Vector3 right = worldUp.Cross(forward).GetNormalized(); Vector3 up = forward.Cross(right); Vector3 negativePosition(-this->M41, -this->M42, -this->M43); this->M11 = right.X; this->M21 = right.Y; this->M31 = right.Z; this->M41 = right.Dot(negativePosition); this->M12 = up.X; this->M22 = up.Y; this->M32 = up.Z; this->M42 = up.Dot(negativePosition); this->M13 = forward.X; this->M23 = forward.Y; this->M33 = forward.Z; this->M43 = forward.Dot(negativePosition); } // ------------------------------------------------------------------------------------------- // template void Matrix44::Transpose() { float temp; temp = this->M12; this->M12 = this->M21; this->M21 = temp; temp = this->M13; this->M13 = this->M31; this->M31 = temp; temp = this->M14; this->M14 = this->M41; this->M41 = temp; temp = this->M23; this->M23 = this->M32; this->M32 = temp; temp = this->M24; this->M24 = this->M42; this->M42 = temp; temp = this->M34; this->M34 = this->M43; this->M43 = temp; } // ------------------------------------------------------------------------------------------- // template Matrix44 Matrix44::operator *(const Matrix44 &other) { Matrix44 result; TScalar x, y, z, w; x = this->M11; y = this->M12; z = this->M13; w = this->M14; result.M11 = (x * other.M11) + (y * other.M21) + (z * other.M31) + (w * other.M41); result.M12 = (x * other.M12) + (y * other.M22) + (z * other.M32) + (w * other.M42); result.M13 = (x * other.M13) + (y * other.M23) + (z * other.M33) + (w * other.M43); result.M14 = (x * other.M14) + (y * other.M24) + (z * other.M34) + (w * other.M44); x = this->M21; y = this->M22; z = this->M23; w = this->M24; result.M21 = (x * other.M11) + (y * other.M21) + (z * other.M31) + (w * other.M41); result.M22 = (x * other.M12) + (y * other.M22) + (z * other.M32) + (w * other.M42); result.M23 = (x * other.M13) + (y * other.M23) + (z * other.M33) + (w * other.M43); result.M24 = (x * other.M14) + (y * other.M24) + (z * other.M34) + (w * other.M44); x = this->M31; y = this->M32; z = this->M33; w = this->M34; result.M31 = (x * other.M11) + (y * other.M21) + (z * other.M31) + (w * other.M41); result.M32 = (x * other.M12) + (y * other.M22) + (z * other.M32) + (w * other.M42); result.M33 = (x * other.M13) + (y * other.M23) + (z * other.M33) + (w * other.M43); result.M34 = (x * other.M14) + (y * other.M24) + (z * other.M34) + (w * other.M44); x = this->M41; y = this->M42; z = this->M43; w = this->M44; result.M41 = (x * other.M11) + (y * other.M21) + (z * other.M31) + (w * other.M41); result.M42 = (x * other.M12) + (y * other.M22) + (z * other.M32) + (w * other.M42); result.M43 = (x * other.M13) + (y * other.M23) + (z * other.M33) + (w * other.M43); result.M44 = (x * other.M14) + (y * other.M24) + (z * other.M34) + (w * other.M44); return result; } // ------------------------------------------------------------------------------------------- // }} // namespace Nuclex::Geometry #endif // NUCLEX_GEOMETRY_MATRIX44_H