/* Copyright (C) 2019 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" //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; } void CMatrix3D::SetOrtho(float left, float right, float bottom, float top, float near, float far) { // Based on OpenGL spec SetZero(); _11 = 2 / (right - left); _22 = 2 / (top - bottom); _33 = -2 / (far - near); _44 = 1; _14 = -(right + left) / (right - left); _24 = -(top + bottom) / (top - bottom); _34 = -(far + near) / (far - near); } void CMatrix3D::SetPerspective(float fov, float aspect, float near, float far) { const float f = 1.f / tanf(fov / 2.f); SetZero(); _11 = f / aspect; _22 = f; _33 = -(far + near) / (near - far); _34 = 2 * far * near / (near - far); _43 = 1; } void CMatrix3D::SetPerspectiveTile(float fov, float aspect, float near, float far, int tiles, int tile_x, int tile_y) { const float f = 1.f / tanf(fov / 2.f); SetPerspective(fov, aspect, near, far); _11 = tiles * f / aspect; _22 = tiles * f; _13 = -(1 - tiles + 2 * tile_x); _23 = -(1 - tiles + 2 * tile_y); } //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::PostTranslate(float x, float y, float z) { // Equivalent to "m.SetTranslation(x, y, z); *this = *this * m;" _14 += _11*x + _12*y + _13*z; _24 += _21*x + _22*y + _23*z; _34 += _31*x + _32*y + _33*z; _44 += _41*x + _42*y + _43*z; } 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); } /////////////////////////////////////////////////////////////////////////////// // 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); } float CMatrix3D::GetYRotation() const { // Project the X axis vector onto the XZ plane CVector3D axis = -GetLeft(); axis.Y = 0; // Normalise projected vector float len = axis.Length(); if (len < 0.0001f) return 0.f; axis *= 1.0f/len; // Negate the return angle to match the SetYRotation convention return -atan2(axis.Z, axis.X); }