I've made some regular old Vector2,3, and 4 classes to use as rows in my Matrix2,3, and 4 classes. My matrix classes are row major. My main concern right now is the Matrix4 class. Since all the Vector classes are pretty standard, I don't think it's necessary to put them here. Here's my Matrix4 class:
template <typename ValueType>
class Matrix4
{
public:
Matrix4()
{
Identity(1.0f);
}
Matrix4(const Matrix4 <ValueType>&Mat)
{
*this = Mat;
}
~Matrix4()
{
}
void DebugPrint() const
{
for(int i = 0;i < 4;i++)System::USystem::GetSystem()->Log(Ungine::System::LOG_MESSAGE,"%f\t%f\t%f\t%f\n",Rows[0],Rows[1],Rows[2],Rows[3]);
}
Matrix4 <ValueType>Transpose() const
{
Matrix4 <ValueType>Ret;
Ret[0][0] = Rows[0][0]; Ret[0][1] = Rows[1][0]; Ret[0][2] = Rows[2][0]; Ret[0][3] = Rows[3][0];
Ret[1][0] = Rows[0][1]; Ret[1][1] = Rows[1][1]; Ret[1][2] = Rows[2][1]; Ret[1][3] = Rows[3][1];
Ret[2][0] = Rows[0][2]; Ret[2][1] = Rows[1][2]; Ret[2][2] = Rows[2][2]; Ret[2][3] = Rows[3][2];
Ret[3][0] = Rows[0][3]; Ret[3][1] = Rows[1][3]; Ret[3][2] = Rows[2][3]; Ret[3][3] = Rows[3][3];
return Ret;
}
void operator =(const Matrix4 <ValueType>&Mat)
{
for(int i = 0;i < 4;i++) for(int j = 0;j < 4;j++) Rows[j] = Mat[j];
}
Matrix4 <ValueType>operator *(const Matrix4 <ValueType>&Mat) const
{
Matrix4 Ret;
Ret[0][0] = Rows[0].DotProduct(Mat.GetColumn(0));
Ret[0][1] = Rows[0].DotProduct(Mat.GetColumn(1));
Ret[0][2] = Rows[0].DotProduct(Mat.GetColumn(2));
Ret[0][3] = Rows[0].DotProduct(Mat.GetColumn(3));
Ret[1][0] = Rows[1].DotProduct(Mat.GetColumn(0));
Ret[1][1] = Rows[1].DotProduct(Mat.GetColumn(1));
Ret[1][2] = Rows[1].DotProduct(Mat.GetColumn(2));
Ret[1][3] = Rows[1].DotProduct(Mat.GetColumn(3));
Ret[2][0] = Rows[2].DotProduct(Mat.GetColumn(0));
Ret[2][1] = Rows[2].DotProduct(Mat.GetColumn(1));
Ret[2][2] = Rows[2].DotProduct(Mat.GetColumn(2));
Ret[2][3] = Rows[2].DotProduct(Mat.GetColumn(3));
Ret[3][0] = Rows[3].DotProduct(Mat.GetColumn(0));
Ret[3][1] = Rows[3].DotProduct(Mat.GetColumn(1));
Ret[3][2] = Rows[3].DotProduct(Mat.GetColumn(2));
Ret[3][3] = Rows[3].DotProduct(Mat.GetColumn(3));
return Ret;
}
Matrix4 <ValueType>operator *=(const Matrix4 <ValueType>&Mat)
{
*this = *this * Mat;
return *this;
}
Vector4 <ValueType>operator *(const Vector4<ValueType>&Vec) const
{
Vector4 <ValueType>Ret;
Ret.x = Rows[0].DotProduct(Vec);
Ret.y = Rows[1].DotProduct(Vec);
Ret.z = Rows[2].DotProduct(Vec);
Ret.w = Rows[3].DotProduct(Vec);
return Ret;
}
Vector3 <ValueType>operator *(const Vector3<ValueType>&Vec) const
{
Vector3 <ValueType>Ret;
Ret.x = Rows[0].DotProduct(Vec);
Ret.y = Rows[1].DotProduct(Vec);
Ret.z = Rows[2].DotProduct(Vec);
return Ret;
}
void Identity(ValueType id = 1.0f)
{
Rows[0] = Vector4 <ValueType>(id,0.0f,0.0f,0.0f);
Rows[1] = Vector4 <ValueType>(0.0f,id,0.0f,0.0f);
Rows[2] = Vector4 <ValueType>(0.0f,0.0f,id,0.0f);
Rows[3] = Vector4 <ValueType>(0.0f,0.0f,0.0f,id);
}
void Translate(const Vector3 <ValueType>&Trans)
{
Matrix4 <ValueType> Mat;
Mat[0] = Vector4 <ValueType>(1.0f,0.0f,0.0f,Trans.x);
Mat[1] = Vector4 <ValueType>(0.0f,1.0f,0.0f,Trans.y);
Mat[2] = Vector4 <ValueType>(0.0f,0.0f,1.0f,Trans.z);
Mat[3] = Vector4 <ValueType>(0.0f,0.0f,0.0f,1.0f );
*this = *this * Mat;
}
void Rotate(const Vector3 <ValueType>&Axis,ValueType Angle)
{
Matrix4 Mat;
ValueType hAngle = Angle * 0.5f;
ValueType SinAngle = Sin(hAngle);
ValueType CosAngle = Cos(hAngle);
ValueType w = CosAngle;
ValueType x = Axis.x * SinAngle;
ValueType y = Axis.y * SinAngle;
ValueType z = Axis.z * SinAngle;
ValueType xSq = x * x;
ValueType ySq = y * y;
ValueType zSq = z * z;
ValueType xy = x * y;
ValueType xz = x * z;
ValueType xw = x * w;
ValueType yw = y * w;
ValueType zw = z * w;
ValueType zy = z * y;
Mat[0][0] = 1.0f - 2.0f * (ySq + zSq);
Mat[0][1] = 2.0f * (xy + zw);
Mat[0][2] = 2.0f * (xz - yw);
Mat[0][3] = 0.0f;
Mat[1][0] = 2.0f * (xy - zw);
Mat[1][1] = 1.0f - 2.0f * (xSq + zSq);
Mat[1][2] = 2.0f * (zy + xw);
Mat[1][3] = 0.0f;
Mat[2][0] = 2.0f * (xz + yw);
Mat[2][1] = 2.0f * (zy - xw);
Mat[2][2] = 1.0f - 2.0f * (xSq + ySq);
Mat[2][3] = 0.0f;
Mat[3][0] = 0.0f;
Mat[3][1] = 0.0f;
Mat[3][2] = 0.0f;
Mat[3][3] = 1.0f;
*this = *this * Mat;
}
void Scale(ValueType Scale)
{
Matrix4 <ValueType> Mat;
Mat[0] = Vector4 <ValueType>(Scale,0.0f,0.0f,0.0f);
Mat[1] = Vector4 <ValueType>(0.0f,Scale,0.0f,0.0f);
Mat[2] = Vector4 <ValueType>(0.0f,0.0f,Scale,0.0f);
Mat[3] = Vector4 <ValueType>(0.0f,0.0f,0.0f,Scale);
*this = *this * Mat;
}
void Scale(const Vector4 <ValueType> &Scale)
{
Matrix4 <ValueType> Mat;
Mat[0] = Vector4 <ValueType>(Scale.x,0.0f,0.0f,0.0f);
Mat[1] = Vector4 <ValueType>(0.0f,Scale.y,0.0f,0.0f);
Mat[2] = Vector4 <ValueType>(0.0f,0.0f,Scale.z,0.0f);
Mat[3] = Vector4 <ValueType>(0.0f,0.0f,0.0f,Scale.w);
*this = *this * Mat;
}
ValueType Determinant() const
{
abort();
}
bool Inverse(Matrix4 <ValueType>*Mat)
{
abort();
return false;
}
ValueType *Ptr()
{
return &Rows[0].x;
}
Vector4 <ValueType>GetColumn(Index Idx) const
{
return Vector4 <ValueType>(Rows[0][Idx],Rows[1][Idx],Rows[2][Idx],Rows[3][Idx]);
}
Vector4 <ValueType>&operator[](Index Idx)
{
return Rows[Idx];
}
Vector4 <ValueType>operator[](Index Idx) const
{
return Rows[Idx];
}
protected:
Vector4 <ValueType>Rows[4];
};
I realized I had a problems when I was trying to use this class to build a MVP matrix for a camera. No matter how I do it, what order I multiply in, whether or not the end MVP matrix or the individual matrices are transposed, what I see on the screen is just a very stretched out polygon. I am attempting to render a simple square at 0,0,0,0 with a perspective and orthographic projection.
Here's part of the render function where I am testing the matrix class:
System::USystem::GetSystem()->SetAngleType(Ungine::System::AT_DEGREE);
Matrix4f Model, View, Projection, MVP;
static f32 Val = 0.0f;
Val += 0.01f;
//Model.Translate(Vector3f(768 / 2,1024 / 2,0));
//Model.Rotate(Vector3f(0,1,1).Normalize(),Val / 2.0f);
//Model.Scale(Vector4f((2 + Math::Sin(Val)) * 0.5f,(2 + Math::Sin(Val)) * 0.5f,(2 + Math::Sin(Val)) * 0.5f,1.0f));//;Rotate(Vector3f(0,0,1),Val);
View = LookAt(Vector3f(0,0,-20.0f),Vector3f(0,0,0),Vector3f(0,1,0));
Projection = Perspectivef(50.0f,768.0f / 1024.0f,0.1f,1000.0f);
//Projection = Orthographicf(0.0f,768.0f,0.0f,1024.0f,0.0f,1000.0f);
MVP = Projection * View * Model;
glUniformMatrix4fv(MVP_Loc,1,false,MVP.Ptr());
And here are the projection and LookAt functions:
Matrix4f Perspectivef(f32 FieldOfView,f32 AspectRatio,f32 Near,f32 Far)
{
Matrix4f Mat;
f32 TanFov = Tan(FieldOfView * 0.5f);
f32 a = 1.0f / (AspectRatio * TanFov);
f32 b = 1.0f / TanFov;
f32 c = Far / (Far - Near);
f32 d = -(2.0f * Near * Far) / (Far - Near);
Mat[0] = Vector4f(a , 0.0f , 0.0f , 0.0f);
Mat[1] = Vector4f(0.0f , b , 0.0f , 0.0f);
Mat[2] = Vector4f(0.0f , 0.0f , c , d );
Mat[3] = Vector4f(0.0f , 0.0f , 1.0f , 0.0f);
return Mat;
}
Matrix4f Orthographicf(f32 Left,f32 Right,f32 Top,f32 Bottom,f32 Near,f32 Far)
{
Matrix4f Mat;
f32 dW = Left - Right;
f32 dH = Top - Bottom;
f32 dL = Far - Near;
f32 Tx = (Right + Left) / (Right - Left);
f32 Ty = (Far + Near) / (Far - Near);
f32 Tz = (Top + Bottom) / (Top - Bottom);
Mat[0] = Vector4f(2.0f / (dW),0.0f ,0.0f ,Tx );
Mat[1] = Vector4f(0.0f ,2.0f / (dH),0.0f ,Ty );
Mat[2] = Vector4f(0.0f ,0.0f ,-(2.0f / dL),Tz );
Mat[3] = Vector4f(0.0f ,0.0f ,0.0f ,1.0f);
return Mat;
}
Matrix4f LookAt(const Vector3f &Eye,const Vector3f &Target,const Vector3f &Up)
{
Matrix4f Mat;
Vector3f Forward, Side, _Up;
Forward = (Target - Eye).Normalize();
Side = Forward.CrossProduct(Up).Normalize();
_Up = Side.CrossProduct(Forward).Normalize();
Mat[0] = Vector4f(Side ,0.0f );
Mat[1] = Vector4f(_Up ,0.0f );
Mat[2] = Vector4f(Forward * -1.0f,0.0f );
Mat[3] = Vector4f(0.0f ,0.0f,0.0f,1.0f);
Mat.Translate(Eye * -1.0f);
return Mat;
}
It's a bunch of code to look through, but if anyone has the patience, could you tell me if you see anything that looks wrong?
Matrix4f is just Matrix4 <f32>. Also, sorry about the large amount of whitespace before each line, that's just from the indentation from two namespaces that I didn't include in the code, for some reason they got larger when I submitted this thread.
Thank you for your time!