// Copyright (C) 2002-2006 Nikolaus Gebhardt // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h #pragma once #include "irrMath.h" #include "..\\..\\include\\matrix4.h" #include "Vector3D.h" namespace Irrlicht { namespace Core { /// /// 4x4 matrix. Mostly used as transformation matrix for 3d calculations. /// Matrix4 is mainly used by the Irrlicht engine for doing transformations. /// The matrix is a D3D style matrix, row major with translations in the 4th row. /// This class has been ported directly from the native C++ Irrlicht Engine, so it may not /// be 100% complete yet and the design may not be 100% .NET like. /// public __value struct Matrix4 { public: /// Constructor Matrix4() { //Members = new float __gc[16]; MakeIdentity(); } /// Set matrix to identity. void MakeIdentity() { for (int i=0; i<16; ++i) Members[i] = 0.0f; Members[0] = Members[5] = Members[10] = Members[15] = 1; } /// Direct accessing every row and colum of the matrix values __property inline float get_M(int row, int col) { if (row < 0 || row >= 4 || col < 0 || col >= 4) throw new System::Exception(new System::String("Invalid index for accessing matrix members")); return Members[col * 4 + row]; } /// Direct accessing every row and colum of the matrix values inline void set_M(int row, int col, float v) { if (row < 0 || row >= 4 || col < 0 || col >= 4) throw new System::Exception(new System::String("Invalid index for accessing matrix members")); Members[col * 4 + row] = v; } /* /// Sets this matrix equal to the other matrix. matrix4& operator=(const matrix4 &other); /// Returns true if other matrix is equal to this matrix. bool operator==(const matrix4 &other) const; /// Returns true if other matrix is not equal to this matrix. bool operator!=(const matrix4 &other) const; /// Multiply by another matrix. matrix4& operator*=(const matrix4& other); */ /// Multiply by another matrix. static Matrix4 op_Multiply(Matrix4 a, Matrix4 b) { Matrix4 tmtrx; #define m1 a.Members #define m2 b.Members #define m3 tmtrx.Members m3[0] = m1[0]*m2[0] + m1[4]*m2[1] + m1[8]*m2[2] + m1[12]*m2[3]; m3[1] = m1[1]*m2[0] + m1[5]*m2[1] + m1[9]*m2[2] + m1[13]*m2[3]; m3[2] = m1[2]*m2[0] + m1[6]*m2[1] + m1[10]*m2[2] + m1[14]*m2[3]; m3[3] = m1[3]*m2[0] + m1[7]*m2[1] + m1[11]*m2[2] + m1[15]*m2[3]; m3[4] = m1[0]*m2[4] + m1[4]*m2[5] + m1[8]*m2[6] + m1[12]*m2[7]; m3[5] = m1[1]*m2[4] + m1[5]*m2[5] + m1[9]*m2[6] + m1[13]*m2[7]; m3[6] = m1[2]*m2[4] + m1[6]*m2[5] + m1[10]*m2[6] + m1[14]*m2[7]; m3[7] = m1[3]*m2[4] + m1[7]*m2[5] + m1[11]*m2[6] + m1[15]*m2[7]; m3[8] = m1[0]*m2[8] + m1[4]*m2[9] + m1[8]*m2[10] + m1[12]*m2[11]; m3[9] = m1[1]*m2[8] + m1[5]*m2[9] + m1[9]*m2[10] + m1[13]*m2[11]; m3[10] = m1[2]*m2[8] + m1[6]*m2[9] + m1[10]*m2[10] + m1[14]*m2[11]; m3[11] = m1[3]*m2[8] + m1[7]*m2[9] + m1[11]*m2[10] + m1[15]*m2[11]; m3[12] = m1[0]*m2[12] + m1[4]*m2[13] + m1[8]*m2[14] + m1[12]*m2[15]; m3[13] = m1[1]*m2[12] + m1[5]*m2[13] + m1[9]*m2[14] + m1[13]*m2[15]; m3[14] = m1[2]*m2[12] + m1[6]*m2[13] + m1[10]*m2[14] + m1[14]*m2[15]; m3[15] = m1[3]*m2[12] + m1[7]*m2[13] + m1[11]*m2[14] + m1[15]*m2[15]; #undef m1 #undef m2 #undef m3 return tmtrx; } /// Returns true if the matrix is the identity matrix. inline bool IsIdentity() { for (int i=0; i<4; ++i) for (int j=0; j<4; ++j) if (j != i) { if (get_M(i,j) < -0.0000001f || get_M(i,j) > 0.0000001f) return false; } else { if (get_M(i,j) < 0.9999999f || get_M(i,j) > 1.0000001f) return false; } return true; } /// Set the translation of the current matrix. Will erase any previous values. void SetTranslation( const Vector3D translation ) { Members[12] = translation.X; Members[13] = translation.Y; Members[14] = translation.Z; } /// Gets the current translation Vector3D GetTranslation() { return Vector3D(Members[12], Members[13], Members[14]); } /// Set the inverse translation of the current matrix. /// Will erase any previous values. void SetInverseTranslation( const Vector3D translation ) { Members[12] = -translation.X; Members[13] = -translation.Y; Members[14] = -translation.Z; } /// Make a rotation matrix from Euler angles. /// The 4th row and column are unmodified. void SetRotationRadians( const Vector3D rotation ) { double cr = Math::Cos( rotation.X ); double sr = Math::Sin( rotation.X ); double cp = Math::Cos( rotation.Y ); double sp = Math::Sin( rotation.Y ); double cy = Math::Cos( rotation.Z ); double sy = Math::Sin( rotation.Z ); Members[0] = (float)( cp*cy ); Members[1] = (float)( cp*sy ); Members[2] = (float)( -sp ); double srsp = sr*sp; double crsp = cr*sp; Members[4] = (float)( srsp*cy-cr*sy ); Members[5] = (float)( srsp*sy+cr*cy ); Members[6] = (float)( sr*cp ); Members[8] = (float)( crsp*cy+sr*sy ); Members[9] = (float)( crsp*sy-sr*cy ); Members[10] = (float)( cr*cp ); } /// Make a rotation matrix from Euler angles. /// The 4th row and column are unmodified. void SetRotationDegrees( const Vector3D rotation ) { SetRotationRadians( rotation * (float)(3.1415926535897932384626433832795 / 180.0) ); } /// Returns the rotation, as set by setRotation(). /// This code was orginally written by by Chev. Vector3D GetRotationDegrees() { Matrix4 &mat = *this; double Y = -asin(mat.get_M(2,0)); double D = Y; double C = cos(Y); Y *= GRAD_PI; double rotx, roty, X, Z; if (fabs(C) > 0.0005f) { rotx = mat.get_M(2,2) / C; roty = mat.get_M(2,1) / C; X = atan2( roty, rotx ) * GRAD_PI; rotx = mat.get_M(0,0) / C; roty = mat.get_M(1,0) / C; Z = atan2( roty, rotx ) * GRAD_PI; } else { X = 0.0f; rotx = mat.get_M(1,1); roty = -mat.get_M(0,1); Z = atan2( roty, rotx ) * GRAD_PI; } // fix values that get below zero // before it would set (!) values to 360 // that where above 360: if (X < 0.00) X += 360.00; if (Y < 0.00) Y += 360.00; if (Z < 0.00) Z += 360.00; return Vector3D((float)X, (float)Y, (float)Z); } /* /// Make an inverted rotation matrix from Euler angles. The 4th row and column are unmodified. void setInverseRotationRadians( const Vector3D& rotation ); /// Make an inverted rotation matrix from Euler angles. The 4th row and column are unmodified. void setInverseRotationDegrees( const Vector3D& rotation ); */ /// Set Scale void SetScale( Vector3D scale ) { Members[0] = scale.X; Members[5] = scale.Y; Members[10] = scale.Z; } /* /// Translate a vector by the inverse of the translation part of this matrix. void inverseTranslateVect( Vector3D& vect ) const; /// Rotate a vector by the inverse of the rotation part of this matrix. void inverseRotateVect( Vector3D& vect ) const; /// Rotate a vector by the rotation part of this matrix. void rotateVect( Vector3D& vect ) const; */ /// Transforms the vector by this matrix void TransformVect( Vector3D& vect) { float vector[3]; vector[0] = vect.X*Members[0] + vect.Y*Members[4] + vect.Z*Members[8] + Members[12]; vector[1] = vect.X*Members[1] + vect.Y*Members[5] + vect.Z*Members[9] + Members[13]; vector[2] = vect.X*Members[2] + vect.Y*Members[6] + vect.Z*Members[10] + Members[14]; vect.X = vector[0]; vect.Y = vector[1]; vect.Z = vector[2]; } /// Transforms input vector by this matrix and stores result in output vector void TransformVect( const Vector3D& in, Vector3D& out) { out.X = in.X*Members[0] + in.Y*Members[4] + in.Z*Members[8] + Members[12]; out.Y = in.X*Members[1] + in.Y*Members[5] + in.Z*Members[9] + Members[13]; out.Z = in.X*Members[2] + in.Y*Members[6] + in.Z*Members[10] + Members[14]; } /// Translate a vector by the translation part of this matrix. void TranslateVect( Vector3D& vect ) { vect.X = vect.X+Members[12]; vect.Y = vect.Y+Members[13]; vect.Z = vect.Z+Members[14]; } /* /// Transforms a plane by this matrix void transformPlane( core::plane3d &plane) const /// Transforms a plane by this matrix void transformPlane( const core::plane3d &in, core::plane3d &out) const; /// Transforms a axis aligned bounding box void transformBox( core::aabbox3d &box) const; /// Multiplies this matrix by a 1x4 matrix void multiplyWith1x4Matrix(float* matrix) const; */ /// Calculates inverse of matrix. Slow. /// Returns false if there is no inverse matrix. bool MakeInverse() { Matrix4 temp; if (GetInverse(temp)) { *this = temp; return true; } return false; } /// returns the inversed matrix of this one /// where result matrix is written to. /// Returns false if there is no inverse matrix. bool GetInverse(Matrix4& out) { /// Calculates the inverse of this Matrix /// The inverse is calculated using Cramers rule. /// If no inverse exists then 'false' is returned. #define m getMInsecure #define out out.setMInsecure float d = (m(0, 0) * m(1, 1) - m(1, 0) * m(0, 1)) * (m(2, 2) * m(3, 3) - m(3, 2) * m(2, 3)) - (m(0, 0) * m(2, 1) - m(2, 0) * m(0, 1)) * (m(1, 2) * m(3, 3) - m(3, 2) * m(1, 3)) + (m(0, 0) * m(3, 1) - m(3, 0) * m(0, 1)) * (m(1, 2) * m(2, 3) - m(2, 2) * m(1, 3)) + (m(1, 0) * m(2, 1) - m(2, 0) * m(1, 1)) * (m(0, 2) * m(3, 3) - m(3, 2) * m(0, 3)) - (m(1, 0) * m(3, 1) - m(3, 0) * m(1, 1)) * (m(0, 2) * m(2, 3) - m(2, 2) * m(0, 3)) + (m(2, 0) * m(3, 1) - m(3, 0) * m(2, 1)) * (m(0, 2) * m(1, 3) - m(1, 2) * m(0, 3)); if (d == 0.f) return false; d = 1.f / d; out(0, 0, d * (m(1, 1) * (m(2, 2) * m(3, 3) - m(3, 2) * m(2, 3)) + m(2, 1) * (m(3, 2) * m(1, 3) - m(1, 2) * m(3, 3)) + m(3, 1) * (m(1, 2) * m(2, 3) - m(2, 2) * m(1, 3)))); out(1, 0, d * (m(1, 2) * (m(2, 0) * m(3, 3) - m(3, 0) * m(2, 3)) + m(2, 2) * (m(3, 0) * m(1, 3) - m(1, 0) * m(3, 3)) + m(3, 2) * (m(1, 0) * m(2, 3) - m(2, 0) * m(1, 3)))); out(2, 0, d * (m(1, 3) * (m(2, 0) * m(3, 1) - m(3, 0) * m(2, 1)) + m(2, 3) * (m(3, 0) * m(1, 1) - m(1, 0) * m(3, 1)) + m(3, 3) * (m(1, 0) * m(2, 1) - m(2, 0) * m(1, 1)))); out(3, 0, d * (m(1, 0) * (m(3, 1) * m(2, 2) - m(2, 1) * m(3, 2)) + m(2, 0) * (m(1, 1) * m(3, 2) - m(3, 1) * m(1, 2)) + m(3, 0) * (m(2, 1) * m(1, 2) - m(1, 1) * m(2, 2)))); out(0, 1, d * (m(2, 1) * (m(0, 2) * m(3, 3) - m(3, 2) * m(0, 3)) + m(3, 1) * (m(2, 2) * m(0, 3) - m(0, 2) * m(2, 3)) + m(0, 1) * (m(3, 2) * m(2, 3) - m(2, 2) * m(3, 3)))); out(1, 1, d * (m(2, 2) * (m(0, 0) * m(3, 3) - m(3, 0) * m(0, 3)) + m(3, 2) * (m(2, 0) * m(0, 3) - m(0, 0) * m(2, 3)) + m(0, 2) * (m(3, 0) * m(2, 3) - m(2, 0) * m(3, 3)))); out(2, 1, d * (m(2, 3) * (m(0, 0) * m(3, 1) - m(3, 0) * m(0, 1)) + m(3, 3) * (m(2, 0) * m(0, 1) - m(0, 0) * m(2, 1)) + m(0, 3) * (m(3, 0) * m(2, 1) - m(2, 0) * m(3, 1)))); out(3, 1, d * (m(2, 0) * (m(3, 1) * m(0, 2) - m(0, 1) * m(3, 2)) + m(3, 0) * (m(0, 1) * m(2, 2) - m(2, 1) * m(0, 2)) + m(0, 0) * (m(2, 1) * m(3, 2) - m(3, 1) * m(2, 2)))); out(0, 2, d * (m(3, 1) * (m(0, 2) * m(1, 3) - m(1, 2) * m(0, 3)) + m(0, 1) * (m(1, 2) * m(3, 3) - m(3, 2) * m(1, 3)) + m(1, 1) * (m(3, 2) * m(0, 3) - m(0, 2) * m(3, 3)))); out(1, 2, d * (m(3, 2) * (m(0, 0) * m(1, 3) - m(1, 0) * m(0, 3)) + m(0, 2) * (m(1, 0) * m(3, 3) - m(3, 0) * m(1, 3)) + m(1, 2) * (m(3, 0) * m(0, 3) - m(0, 0) * m(3, 3)))); out(2, 2, d * (m(3, 3) * (m(0, 0) * m(1, 1) - m(1, 0) * m(0, 1)) + m(0, 3) * (m(1, 0) * m(3, 1) - m(3, 0) * m(1, 1)) + m(1, 3) * (m(3, 0) * m(0, 1) - m(0, 0) * m(3, 1)))); out(3, 2, d * (m(3, 0) * (m(1, 1) * m(0, 2) - m(0, 1) * m(1, 2)) + m(0, 0) * (m(3, 1) * m(1, 2) - m(1, 1) * m(3, 2)) + m(1, 0) * (m(0, 1) * m(3, 2) - m(3, 1) * m(0, 2)))); out(0, 3, d * (m(0, 1) * (m(2, 2) * m(1, 3) - m(1, 2) * m(2, 3)) + m(1, 1) * (m(0, 2) * m(2, 3) - m(2, 2) * m(0, 3)) + m(2, 1) * (m(1, 2) * m(0, 3) - m(0, 2) * m(1, 3)))); out(1, 3, d * (m(0, 2) * (m(2, 0) * m(1, 3) - m(1, 0) * m(2, 3)) + m(1, 2) * (m(0, 0) * m(2, 3) - m(2, 0) * m(0, 3)) + m(2, 2) * (m(1, 0) * m(0, 3) - m(0, 0) * m(1, 3)))); out(2, 3, d * (m(0, 3) * (m(2, 0) * m(1, 1) - m(1, 0) * m(2, 1)) + m(1, 3) * (m(0, 0) * m(2, 1) - m(2, 0) * m(0, 1)) + m(2, 3) * (m(1, 0) * m(0, 1) - m(0, 0) * m(1, 1)))); out(3, 3, d * (m(0, 0) * (m(1, 1) * m(2, 2) - m(2, 1) * m(1, 2)) + m(1, 0) * (m(2, 1) * m(0, 2) - m(0, 1) * m(2, 2)) + m(2, 0) * (m(0, 1) * m(1, 2) - m(1, 1) * m(0, 2)))); return true; } /// Builds a right-handed perspective projection matrix based on a field of view void buildProjectionMatrixPerspectiveFovRH(float fieldOfViewRadians, float aspectRatio, float zNear, float zFar) { float h = (float)(Math::Cos(fieldOfViewRadians/2) / Math::Sin(fieldOfViewRadians/2)); float w = h / aspectRatio; setMInsecure(0,0,2*zNear/w); setMInsecure(1,0,0); setMInsecure(2,0,0); setMInsecure(3,0,0); setMInsecure(0,1,0); setMInsecure(1,1,2*zNear/h); setMInsecure(2,1,0); setMInsecure(3,1,0); setMInsecure(0,2,0); setMInsecure(1,2,0); setMInsecure(2,2,zFar/(zFar-zNear)); setMInsecure(3,2,-1); setMInsecure(0,3,0); setMInsecure(1,3,0); setMInsecure(2,3,zNear*zFar/(zNear-zFar)); setMInsecure(3,3,0); } /// Builds a left-handed perspective projection matrix based on a field of view void buildProjectionMatrixPerspectiveFovLH(float fieldOfViewRadians, float aspectRatio, float zNear, float zFar) { float h = (float)(Math::Cos(fieldOfViewRadians/2) / Math::Sin(fieldOfViewRadians/2)); float w = h / aspectRatio; setMInsecure(0,0,2*zNear/w); setMInsecure(1,0,0); setMInsecure(2,0,0); setMInsecure(3,0,0); setMInsecure(0,1,0); setMInsecure(1,1,2*zNear/h); setMInsecure(2,1,0); setMInsecure(3,1,0); setMInsecure(0,2,0); setMInsecure(1,2,0); setMInsecure(2,2,zFar/(zFar-zNear)); setMInsecure(3,2,1); setMInsecure(0,3,0); setMInsecure(1,3,0); setMInsecure(2,3,zNear*zFar/(zNear-zFar)); setMInsecure(3,3,0); } /// Builds a right-handed perspective projection matrix. void buildProjectionMatrixPerspectiveRH(float widthOfViewVolume, float heightOfViewVolume, float zNear, float zFar) { setMInsecure(0,0,2*zNear/widthOfViewVolume); setMInsecure(1,0,0); setMInsecure(2,0,0); setMInsecure(3,0,0); setMInsecure(0,1,0); setMInsecure(1,1,2*zNear/heightOfViewVolume); setMInsecure(2,1,0); setMInsecure(3,1,0); setMInsecure(0,2,0); setMInsecure(1,2,0); setMInsecure(2,2,zFar/(zNear-zFar)); setMInsecure(3,2,-1); setMInsecure(0,3,0); setMInsecure(1,3,0); setMInsecure(2,3,zNear*zFar/(zNear-zFar)); setMInsecure(3,3,0); } /// Builds a left-handed perspective projection matrix. void buildProjectionMatrixPerspectiveLH(float widthOfViewVolume, float heightOfViewVolume, float zNear, float zFar) { setMInsecure(0,0,2*zNear/widthOfViewVolume); setMInsecure(1,0,0); setMInsecure(2,0,0); setMInsecure(3,0,0); setMInsecure(0,1,0); setMInsecure(1,1,2*zNear/heightOfViewVolume); setMInsecure(2,1,0); setMInsecure(3,1,0); setMInsecure(0,2,0); setMInsecure(1,2,0); setMInsecure(2,2,zFar/(zNear-zFar)); setMInsecure(3,2,1); setMInsecure(0,3,0); setMInsecure(1,3,0); setMInsecure(2,3,zNear*zFar/(zNear-zFar)); setMInsecure(3,3,0); } /// Builds a left-handed orthogonal projection matrix. void buildProjectionMatrixOrthoLH(float widthOfViewVolume, float heightOfViewVolume, float zNear, float zFar) { setMInsecure(0,0,2/widthOfViewVolume); setMInsecure(1,0,0); setMInsecure(2,0,0); setMInsecure(3,0,0); setMInsecure(0,1,0); setMInsecure(1,1,2/heightOfViewVolume); setMInsecure(2,1,0); setMInsecure(3,1,0); setMInsecure(0,2,0); setMInsecure(1,2,0); setMInsecure(2,2,1/(zNear-zFar)); setMInsecure(3,2,0); setMInsecure(0,3,0); setMInsecure(1,3,0); setMInsecure(2,3,zNear/(zNear-zFar)); setMInsecure(3,3,1); } /// Builds a right-handed orthogonal projection matrix. void buildProjectionMatrixOrthoRH(float widthOfViewVolume, float heightOfViewVolume, float zNear, float zFar) { setMInsecure(0,0,2/widthOfViewVolume); setMInsecure(1,0,0); setMInsecure(2,0,0); setMInsecure(3,0,0); setMInsecure(0,1,0); setMInsecure(1,1,2/heightOfViewVolume); setMInsecure(2,1,0); setMInsecure(3,1,0); setMInsecure(0,2,0); setMInsecure(1,2,0); setMInsecure(2,2,1/(zNear-zFar)); setMInsecure(3,2,0); setMInsecure(0,3,0); setMInsecure(1,3,0); setMInsecure(2,3,zNear/(zNear-zFar)); setMInsecure(3,3,-1); } /// Builds a left-handed look-at matrix. void buildCameraLookAtMatrixLH(Vector3D position, Vector3D target, Vector3D upVector) { Vector3D zaxis = target - position; zaxis.Normalize(); Vector3D xaxis = upVector.CrossProduct(zaxis); xaxis.Normalize(); Vector3D yaxis = zaxis.CrossProduct(xaxis); setMInsecure(0,0,xaxis.X); setMInsecure(1,0,yaxis.X); setMInsecure(2,0,zaxis.X); setMInsecure(3,0,0); setMInsecure(0,1,xaxis.Y); setMInsecure(1,1,yaxis.Y); setMInsecure(2,1,zaxis.Y); setMInsecure(3,1,0); setMInsecure(0,2,xaxis.Z); setMInsecure(1,2,yaxis.Z); setMInsecure(2,2,zaxis.Z); setMInsecure(3,2,0); setMInsecure(0,3,-xaxis.DotProduct(position)); setMInsecure(1,3,-yaxis.DotProduct(position)); setMInsecure(2,3,-zaxis.DotProduct(position)); setMInsecure(3,3,1.0f); } /// Builds a right-handed look-at matrix. void buildCameraLookAtMatrixRH(Vector3D position, Vector3D target, Vector3D upVector) { Vector3D zaxis = position - target; zaxis.Normalize(); Vector3D xaxis = upVector.CrossProduct(zaxis); xaxis.Normalize(); Vector3D yaxis = zaxis.CrossProduct(xaxis); setMInsecure(0,0,xaxis.X); setMInsecure(1,0,yaxis.X); setMInsecure(2,0,zaxis.X); setMInsecure(3,0,0); setMInsecure(0,1,xaxis.Y); setMInsecure(1,1,yaxis.Y); setMInsecure(2,1,zaxis.Y); setMInsecure(3,1,0); setMInsecure(0,2,xaxis.Z); setMInsecure(1,2,yaxis.Z); setMInsecure(2,2,zaxis.Z); setMInsecure(3,2,0); setMInsecure(0,3,-xaxis.DotProduct(position)); setMInsecure(1,3,-yaxis.DotProduct(position)); setMInsecure(2,3,-zaxis.DotProduct(position)); setMInsecure(3,3,1.0f); } /// Returns the transposed matrix. Matrix4 GetTransposed() { Matrix4 t; for (int r=0; r<4; ++r) for (int c=0; c<4; ++c) t.set_M(r, c, this->get_M( c, r )); return t; } /// Returns array of floats representing the matrix float GetFloats() __gc[] { float f __gc[] = new float __gc [16]; for (int i=0; i<16; ++i) f[i] = Members[i]; return f; } /// Matrix data, stored in column-major order float Members __nogc [16]; //float Members __gc []; private: /// /// Direct accessing every row and colum of the matrix values without boundary checking /// inline float getMInsecure(int row, int col) { return Members[col * 4 + row]; } /// /// Direct accessing every row and colum of the matrix values without boundary checking /// inline void setMInsecure(int row, int col, float v) { Members[col * 4 + row] = v; } }; } }