/* Copyright (C) 2010 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ /* * A Matrix class used for holding and manipulating transformation * info. */ #include "precompiled.h" #include "Matrix3D.h" #include "Quaternion.h" #include "Vector4D.h" CMatrix3D::CMatrix3D () { } CMatrix3D::CMatrix3D( float a11, float a12, float a13, float a14, float a21, float a22, float a23, float a24, float a31, float a32, float a33, float a34, float a41, float a42, float a43, float a44) : _11(a11), _12(a12), _13(a13), _14(a14), _21(a21), _22(a22), _23(a23), _24(a24), _31(a31), _32(a32), _33(a33), _34(a34), _41(a41), _42(a42), _43(a43), _44(a44) { } CMatrix3D::CMatrix3D(float data[]) { for(int i=0; i<16; i++) { _data[i] = data[i]; } } //Matrix multiplication CMatrix3D CMatrix3D::operator*(const CMatrix3D& matrix) const { return CMatrix3D( _11*matrix._11 + _12*matrix._21 + _13*matrix._31 + _14*matrix._41, _11*matrix._12 + _12*matrix._22 + _13*matrix._32 + _14*matrix._42, _11*matrix._13 + _12*matrix._23 + _13*matrix._33 + _14*matrix._43, _11*matrix._14 + _12*matrix._24 + _13*matrix._34 + _14*matrix._44, _21*matrix._11 + _22*matrix._21 + _23*matrix._31 + _24*matrix._41, _21*matrix._12 + _22*matrix._22 + _23*matrix._32 + _24*matrix._42, _21*matrix._13 + _22*matrix._23 + _23*matrix._33 + _24*matrix._43, _21*matrix._14 + _22*matrix._24 + _23*matrix._34 + _24*matrix._44, _31*matrix._11 + _32*matrix._21 + _33*matrix._31 + _34*matrix._41, _31*matrix._12 + _32*matrix._22 + _33*matrix._32 + _34*matrix._42, _31*matrix._13 + _32*matrix._23 + _33*matrix._33 + _34*matrix._43, _31*matrix._14 + _32*matrix._24 + _33*matrix._34 + _34*matrix._44, _41*matrix._11 + _42*matrix._21 + _43*matrix._31 + _44*matrix._41, _41*matrix._12 + _42*matrix._22 + _43*matrix._32 + _44*matrix._42, _41*matrix._13 + _42*matrix._23 + _43*matrix._33 + _44*matrix._43, _41*matrix._14 + _42*matrix._24 + _43*matrix._34 + _44*matrix._44 ); } //Matrix multiplication/assignment CMatrix3D& CMatrix3D::operator*=(const CMatrix3D& matrix) { Concatenate(matrix); return *this; } //Matrix scaling CMatrix3D CMatrix3D::operator*(float f) const { CMatrix3D tmp; for (int i=0;i<16;i++) { tmp._data[i]=_data[i]*f; } return tmp; } //Matrix scaling/assignment CMatrix3D& CMatrix3D::operator*=(float f) { for (int i=0;i<16;i++) { _data[i]*=f; } return *this; } //Matrix addition CMatrix3D CMatrix3D::operator+(const CMatrix3D& m) const { CMatrix3D tmp; for (int i=0;i<16;i++) { tmp._data[i]=_data[i]+m._data[i]; } return tmp; } //Matrix addition/assignment CMatrix3D& CMatrix3D::operator+=(const CMatrix3D& m) { for (int i=0;i<16;i++) { _data[i]+=m._data[i]; } return *this; } bool CMatrix3D::operator==(const CMatrix3D &matrix) const { for (int i = 0; i < 16; ++i) if (matrix._data[i] != _data[i]) return false; return true; } //Sets the identity matrix void CMatrix3D::SetIdentity () { _11=1.0f; _12=0.0f; _13=0.0f; _14=0.0f; _21=0.0f; _22=1.0f; _23=0.0f; _24=0.0f; _31=0.0f; _32=0.0f; _33=1.0f; _34=0.0f; _41=0.0f; _42=0.0f; _43=0.0f; _44=1.0f; } //Sets the zero matrix void CMatrix3D::SetZero () { _11=0.0f; _12=0.0f; _13=0.0f; _14=0.0f; _21=0.0f; _22=0.0f; _23=0.0f; _24=0.0f; _31=0.0f; _32=0.0f; _33=0.0f; _34=0.0f; _41=0.0f; _42=0.0f; _43=0.0f; _44=0.0f; } //The following clear the matrix and set the //rotation of each of the 3 axes void CMatrix3D::SetXRotation (float angle) { const float Cos = cosf (angle); const float Sin = sinf (angle); _11=1.0f; _12=0.0f; _13=0.0f; _14=0.0f; _21=0.0f; _22=Cos; _23=-Sin; _24=0.0f; _31=0.0f; _32=Sin; _33=Cos; _34=0.0f; _41=0.0f; _42=0.0f; _43=0.0f; _44=1.0f; } void CMatrix3D::SetYRotation (float angle) { const float Cos = cosf (angle); const float Sin = sinf (angle); _11=Cos; _12=0.0f; _13=Sin; _14=0.0f; _21=0.0f; _22=1.0f; _23=0.0f; _24=0.0f; _31=-Sin; _32=0.0f; _33=Cos; _34=0.0f; _41=0.0f; _42=0.0f; _43=0.0f; _44=1.0f; } void CMatrix3D::SetZRotation (float angle) { const float Cos = cosf (angle); const float Sin = sinf (angle); _11=Cos; _12=-Sin; _13=0.0f; _14=0.0f; _21=Sin; _22=Cos; _23=0.0f; _24=0.0f; _31=0.0f; _32=0.0f; _33=1.0f; _34=0.0f; _41=0.0f; _42=0.0f; _43=0.0f; _44=1.0f; } //The following apply a rotation to the matrix //about each of the axes; void CMatrix3D::RotateX (float angle) { const float Cos = cosf (angle); const float Sin = sinf (angle); const float tmp_21 = _21; const float tmp_22 = _22; const float tmp_23 = _23; const float tmp_24 = _24; _21 = Cos * _21 - Sin * _31; _22 = Cos * _22 - Sin * _32; _23 = Cos * _23 - Sin * _33; _24 = Cos * _24 - Sin * _34; _31 = Sin * tmp_21 + Cos * _31; _32 = Sin * tmp_22 + Cos * _32; _33 = Sin * tmp_23 + Cos * _33; _34 = Sin * tmp_24 + Cos * _34; } void CMatrix3D::RotateY (float angle) { const float Cos = cosf (angle); const float Sin = sinf (angle); const float tmp_11 = _11; const float tmp_12 = _12; const float tmp_13 = _13; const float tmp_14 = _14; _11 = Cos * _11 + Sin * _31; _12 = Cos * _12 + Sin * _32; _13 = Cos * _13 + Sin * _33; _14 = Cos * _14 + Sin * _34; _31 = -Sin * tmp_11 + Cos * _31; _32 = -Sin * tmp_12 + Cos * _32; _33 = -Sin * tmp_13 + Cos * _33; _34 = -Sin * tmp_14 + Cos * _34; } void CMatrix3D::RotateZ (float angle) { const float Cos = cosf (angle); const float Sin = sinf (angle); const float tmp_11 = _11; const float tmp_12 = _12; const float tmp_13 = _13; const float tmp_14 = _14; _11 = Cos * _11 - Sin * _21; _12 = Cos * _12 - Sin * _22; _13 = Cos * _13 - Sin * _23; _14 = Cos * _14 - Sin * _24; _21 = Sin * tmp_11 + Cos * _21; _22 = Sin * tmp_12 + Cos * _22; _23 = Sin * tmp_13 + Cos * _23; _24 = Sin * tmp_14 + Cos * _24; } //Sets the translation of the matrix void CMatrix3D::SetTranslation (float x, float y, float z) { _11=1.0f; _12=0.0f; _13=0.0f; _14=x; _21=0.0f; _22=1.0f; _23=0.0f; _24=y; _31=0.0f; _32=0.0f; _33=1.0f; _34=z; _41=0.0f; _42=0.0f; _43=0.0f; _44=1.0f; } void CMatrix3D::SetTranslation(const CVector3D& vector) { SetTranslation(vector.X, vector.Y, vector.Z); } //Applies a translation to the matrix void CMatrix3D::Translate(float x, float y, float z) { _14 += x; _24 += y; _34 += z; } void CMatrix3D::Translate(const CVector3D &vector) { _14 += vector.X; _24 += vector.Y; _34 += vector.Z; } void CMatrix3D::Concatenate(const CMatrix3D& m) { (*this)=m*(*this); } CVector3D CMatrix3D::GetTranslation() const { return CVector3D(_14, _24, _34); } //Clears and sets the scaling of the matrix void CMatrix3D::SetScaling (float x_scale, float y_scale, float z_scale) { _11=x_scale; _12=0.0f; _13=0.0f; _14=0.0f; _21=0.0f; _22=y_scale; _23=0.0f; _24=0.0f; _31=0.0f; _32=0.0f; _33=z_scale; _34=0.0f; _41=0.0f; _42=0.0f; _43=0.0f; _44=1.0f; } //Scales the matrix void CMatrix3D::Scale (float x_scale, float y_scale, float z_scale) { _11 *= x_scale; _12 *= x_scale; _13 *= x_scale; _14 *= x_scale; _21 *= y_scale; _22 *= y_scale; _23 *= y_scale; _24 *= y_scale; _31 *= z_scale; _32 *= z_scale; _33 *= z_scale; _34 *= z_scale; } //Returns the transpose of the matrix. For orthonormal //matrices, this is the same is the inverse matrix CMatrix3D CMatrix3D::GetTranspose() const { return CMatrix3D( _11, _21, _31, _41, _12, _22, _32, _42, _13, _23, _33, _43, _14, _24, _34, _44); } //Get a vector which points to the left of the matrix CVector3D CMatrix3D::GetLeft() const { return CVector3D(-_11, -_21, -_31); } //Get a vector which points up from the matrix CVector3D CMatrix3D::GetUp() const { return CVector3D(_12, _22, _32); } //Get a vector which points to front of the matrix CVector3D CMatrix3D::GetIn() const { return CVector3D(_13, _23, _33); } //Transform a vector by this matrix CVector4D CMatrix3D::Transform(const CVector4D &vector) const { CVector4D result; Transform(vector,result); return result; } void CMatrix3D::Transform(const CVector4D& vector,CVector4D& result) const { result[0] = _11*vector[0] + _12*vector[1] + _13*vector[2] + _14*vector[3]; result[1] = _21*vector[0] + _22*vector[1] + _23*vector[2] + _24*vector[3]; result[2] = _31*vector[0] + _32*vector[1] + _33*vector[2] + _34*vector[3]; result[3] = _41*vector[0] + _42*vector[1] + _43*vector[2] + _44*vector[3]; } /////////////////////////////////////////////////////////////////////////////// // RotateTransposed: rotate a vector by the transpose of this matrix CVector3D CMatrix3D::RotateTransposed(const CVector3D& vector) const { CVector3D result; RotateTransposed(vector,result); return result; } /////////////////////////////////////////////////////////////////////////////// // RotateTransposed: rotate a vector by the transpose of this matrix void CMatrix3D::RotateTransposed(const CVector3D& vector,CVector3D& result) const { result.X = _11*vector.X + _21*vector.Y + _31*vector.Z; result.Y = _12*vector.X + _22*vector.Y + _32*vector.Z; result.Z = _13*vector.X + _23*vector.Y + _33*vector.Z; } void CMatrix3D::GetInverse(CMatrix3D& dst) const { float tmp[12]; // temp array for pairs float src[16]; // array of transpose source matrix float det; // determinant // transpose matrix for (int i = 0; i < 4; ++i) { src[i] = _data[i*4]; src[i + 4] = _data[i*4 + 1]; src[i + 8] = _data[i*4 + 2]; src[i + 12] = _data[i*4 + 3]; } // calculate pairs for first 8 elements (cofactors) tmp[0] = src[10] * src[15]; tmp[1] = src[11] * src[14]; tmp[2] = src[9] * src[15]; tmp[3] = src[11] * src[13]; tmp[4] = src[9] * src[14]; tmp[5] = src[10] * src[13]; tmp[6] = src[8] * src[15]; tmp[7] = src[11] * src[12]; tmp[8] = src[8] * src[14]; tmp[9] = src[10] * src[12]; tmp[10] = src[8] * src[13]; tmp[11] = src[9] * src[12]; // calculate first 8 elements (cofactors) dst._data[0] = (tmp[0]-tmp[1])*src[5] + (tmp[3]-tmp[2])*src[6] + (tmp[4]-tmp[5])*src[7]; dst._data[1] = (tmp[1]-tmp[0])*src[4] + (tmp[6]-tmp[7])*src[6] + (tmp[9]-tmp[8])*src[7]; dst._data[2] = (tmp[2]-tmp[3])*src[4] + (tmp[7]-tmp[6])*src[5] + (tmp[10]-tmp[11])*src[7]; dst._data[3] = (tmp[5]-tmp[4])*src[4] + (tmp[8]-tmp[9])*src[5] + (tmp[11]-tmp[10])*src[6]; dst._data[4] = (tmp[1]-tmp[0])*src[1] + (tmp[2]-tmp[3])*src[2] + (tmp[5]-tmp[4])*src[3]; dst._data[5] = (tmp[0]-tmp[1])*src[0] + (tmp[7]-tmp[6])*src[2] + (tmp[8]-tmp[9])*src[3]; dst._data[6] = (tmp[3]-tmp[2])*src[0] + (tmp[6]-tmp[7])*src[1] + (tmp[11]-tmp[10])*src[3]; dst._data[7] = (tmp[4]-tmp[5])*src[0] + (tmp[9]-tmp[8])*src[1] + (tmp[10]-tmp[11])*src[2]; // calculate pairs for second 8 elements (cofactors) tmp[0] = src[2]*src[7]; tmp[1] = src[3]*src[6]; tmp[2] = src[1]*src[7]; tmp[3] = src[3]*src[5]; tmp[4] = src[1]*src[6]; tmp[5] = src[2]*src[5]; tmp[6] = src[0]*src[7]; tmp[7] = src[3]*src[4]; tmp[8] = src[0]*src[6]; tmp[9] = src[2]*src[4]; tmp[10] = src[0]*src[5]; tmp[11] = src[1]*src[4]; // calculate second 8 elements (cofactors) dst._data[8] = (tmp[0]-tmp[1])*src[13] + (tmp[3]-tmp[2])*src[14] + (tmp[4]-tmp[5])*src[15]; dst._data[9] = (tmp[1]-tmp[0])*src[12] + (tmp[6]-tmp[7])*src[14] + (tmp[9]-tmp[8])*src[15]; dst._data[10] = (tmp[2]-tmp[3])*src[12] + (tmp[7]-tmp[6])*src[13] + (tmp[10]-tmp[11])*src[15]; dst._data[11] = (tmp[5]-tmp[4])*src[12] + (tmp[8]-tmp[9])*src[13] + (tmp[11]-tmp[10])*src[14]; dst._data[12] = (tmp[2]-tmp[3])*src[10] + (tmp[5]-tmp[4])*src[11] + (tmp[1]-tmp[0])*src[9]; dst._data[13] = (tmp[7]-tmp[6])*src[10] + (tmp[8]-tmp[9])*src[11] + (tmp[0]-tmp[1])*src[8]; dst._data[14] = (tmp[6]-tmp[7])*src[9] + (tmp[11]-tmp[10])*src[11] + (tmp[3]-tmp[2])*src[8]; dst._data[15] = (tmp[10]-tmp[11])*src[10] + (tmp[4]-tmp[5])*src[8] + (tmp[9]-tmp[8])*src[9]; // calculate matrix inverse det=src[0]*dst._data[0]+src[1]*dst._data[1]+src[2]*dst._data[2]+src[3]*dst._data[3]; det = 1/det; for ( int j = 0; j < 16; j++) { dst._data[j] *= det; } } CMatrix3D CMatrix3D::GetInverse() const { CMatrix3D r; GetInverse(r); return r; } void CMatrix3D::Rotate(const CQuaternion& quat) { CMatrix3D rotationMatrix=quat.ToMatrix(); Concatenate(rotationMatrix); } CQuaternion CMatrix3D::GetRotation() const { float tr = _data2d[0][0] + _data2d[1][1] + _data2d[2][2]; int next[] = { 1, 2, 0 }; float quat[4]; if (tr > 0.f) { float s = sqrtf(tr + 1.f); quat[3] = s * 0.5f; s = 0.5f / s; quat[0] = (_data2d[1][2] - _data2d[2][1]) * s; quat[1] = (_data2d[2][0] - _data2d[0][2]) * s; quat[2] = (_data2d[0][1] - _data2d[1][0]) * s; } else { int i = 0; if (_data2d[1][1] > _data2d[0][0]) i = 1; if (_data2d[2][2] > _data2d[i][i]) i = 2; int j = next[i]; int k = next[j]; float s = sqrtf((_data2d[i][i] - (_data2d[j][j] + _data2d[k][k])) + 1.f); quat[i] = s * 0.5f; if (s != 0.f) s = 0.5f / s; quat[3] = (_data2d[j][k] - _data2d[k][j]) * s; quat[j] = (_data2d[i][j] + _data2d[j][i]) * s; quat[k] = (_data2d[i][k] + _data2d[k][i]) * s; } return CQuaternion(quat[0], quat[1], quat[2], quat[3]); } void CMatrix3D::SetRotation(const CQuaternion& quat) { quat.ToMatrix(*this); }