I'm using my own classes and mathematical functions in C++, and I am making a dire mistake but cannot see where it is. I am representing a 4x4 matrix with a linear array, here are my matrix generator functions:
float& Mat4x4::getRef(unsigned int row, unsigned int column)
{
return this->data[(4 * (column)) + row];
}
void Mat4x4::set(unsigned int row, unsigned int column, float value)
{
this->getRef(row, column) = value;
}
Mat4x4 Mat4x4::getRotationalXMatrix(Vec3 rot, bool oglform)
{
/*
| 1 0 0 0 |
M = | 0 cos(A) -sin(A) 0 |
| 0 sin(A) cos(A) 0 |
| 0 0 0 1 |
standard mathematical format -- opengl wants the transposed.
*/
Mat4x4 res = Mat4x4::identity();
res.set(1, 1, cos(rot.getX()));
res.set(2, 1, (-sin(rot.getX())));
res.set(1, 2, sin(rot.getX()));
res.set(2, 2, cos(rot.getX()));
return oglform ? res.transposed() : res;
}
Mat4x4 Mat4x4::getRotationalYMatrix(Vec3 rot, bool oglform)
{
/*
| cos(A) 0 sin(A) 0 |
M = | 0 1 0 0 |
| -sin(A) 0 cos(A) 0 |
| 0 0 0 1 |
standard mathematical format -- opengl wants the transposed.
*/
Mat4x4 res = Mat4x4::identity();
res.set(0, 0, cos(rot.getY()));
res.set(2, 0, sin(rot.getY()));
res.set(0, 2, -sin(rot.getY()));
res.set(2, 2, cos(rot.getY()));
return oglform ? res.transposed() : res;
}
Mat4x4 Mat4x4::getRotationalZMatrix(Vec3 rot, bool oglform)
{
/*
| cos(A) -sin(A) 0 0 |
M = | sin(A) cos(A) 0 0 |
| 0 0 1 0 |
| 0 0 0 1 |
standard mathematical format -- opengl wants the transposed.
*/
Mat4x4 res = Mat4x4::identity();
res.set(0, 0, cos(rot.getZ()));
res.set(1, 0, -sin(rot.getZ()));
res.set(0, 1, sin(rot.getZ()));
res.set(1, 1, cos(rot.getZ()));
return oglform ? res.transposed() : res;
}
Mat4x4 Mat4x4::getRotationalMatrix(Vec3 pos, bool oglform)
{
return getRotationalXMatrix(pos, oglform) * getRotationalYMatrix(pos, oglform) * getRotationalZMatrix(pos, oglform);
}
Mat4x4 Mat4x4::getTranslationMatrix(Vec3 pos, bool oglform)
{
Mat4x4 res = Mat4x4::identity();
res.set(3, 0, pos.getX());
res.set(3, 1, pos.getY());
res.set(3, 2, pos.getZ());
return oglform ? res.transposed() : res;
}
Mat4x4 Mat4x4::getScaleMatrix(Vec3 scale, bool oglform)
{
Mat4x4 res = Mat4x4::identity();
res.set(0, 0, scale.getX());
res.set(1, 1, scale.getY());
res.set(2, 2, scale.getZ());
return oglform ? res.transposed() : res;
}
Mat4x4 Mat4x4::lookatOGL(Vec3 eye, Vec3 centre, Vec3 up)
{
Vec3 zaxis = (centre - eye).normalised();
Vec3 xaxis = (up.cross(zaxis)).normalised();
Vec3 yaxis = zaxis.cross(xaxis);
float orient[16] = {0.0f};
orient[0] = xaxis.getX();
orient[1] = yaxis.getX();
orient[2] = zaxis.getX();
orient[3] = 0.0f;
orient[4] = xaxis.getY();
orient[5] = yaxis.getY();
orient[6] = zaxis.getY();
orient[7] = 0.0f;
orient[8] = xaxis.getZ();
orient[9] = yaxis.getZ();
orient[10] = zaxis.getZ();
orient[11] = 0.0f;
orient[12] = 0.0f; orient[13] = 0.0f; orient[14] = 0.0f; orient[15] = 1.0f;
Mat4x4 orientation(orient);
float trans[16] = {0.0f};
trans[0] = 1.0f;
trans[1] = 0.0f;
trans[2] = 0.0f;
trans[3] = 0.0f;
trans[4] = 0.0f;
trans[5] = 1.0f;
trans[6] = 0.0f;
trans[7] = 0.0f;
trans[8] = 0.0f;
trans[9] = 0.0f;
trans[10] = 1.0f;
trans[11] = 0.0f;
trans[12] = -eye.getX();
trans[13] = -eye.getY();
trans[14] = -eye.getZ();
trans[15] = 1.0f;
Mat4x4 translation(trans);
return (translation * orientation);
}
And the five important OpenGL matrices:
Mat4x4 Mat4x4::getModelMatrix(Vec3 pos, Vec3 rot, Vec3 scale, bool oglform)
{
return Mat4x4::getTranslationMatrix(pos, oglform) * Mat4x4::getRotationalMatrix(rot, oglform) * Mat4x4::getScaleMatrix(scale, oglform);
}
Mat4x4 Mat4x4::getViewMatrix(Vec3 eye, Vec3 centre, Vec3 up, bool oglform)
{
return Mat4x4::lookatOGL(eye, centre, up);
}
Mat4x4 Mat4x4::getProjectionMatrix(float fovRad, float aspectRatio, float near, float far, bool oglformat)
{
float tanHalfFOV = tan(fovRad / 2);
float data[16] = {0.0f};
Mat4x4 res(data);
res.set(0, 0, (1/(aspectRatio * tanHalfFOV)));
res.set(1, 1, (1/tanHalfFOV));
res.set(2, 3, -1);
float twotwo = -(far + near) / (far - near);
float threetwo = -(2 * far * near) / (far - near);
res.set(2, 2, twotwo);
res.set(3, 2, threetwo);
return res;
}
Mat4x4 Mat4x4::getMVMatrixOGL(Vec3 pos, Vec3 rot, Vec3 scale, Vec3 eye, Vec3 centre, Vec3 up)
{
Mat4x4 m = Mat4x4::getModelMatrix(pos, rot, scale, true);
Mat4x4 v = Mat4x4::getViewMatrix(eye, centre, up, true);
return v * m;
}
Mat4x4 Mat4x4::getMVPMatrixOGL(Vec3 pos, Vec3 rot, Vec3 scale, Vec3 eye, Vec3 centre, Vec3 up, float fovRad, float aspectRatio, float near, float far)
{
Mat4x4 m = Mat4x4::getModelMatrix(pos, rot, scale, true);
Mat4x4 v = Mat4x4::getViewMatrix(eye, centre, up, true);
Mat4x4 p = Mat4x4::getProjectionMatrix(fovRad, aspectRatio, near, far, true);
return p * v * m;
}
I see several observational issues with the resulting matrices. When the camera is very close to the model, it appears fairly normal, rotating on the y-axis as normal. However, as the camera gets further away from the model, the model rotates around the y axis in a circular fashion with an increasing radius. Also, the model appears to appear slightly flatter toward the apex of rotation, but that could be due to the nature of the view matrix?
This observation is with using the MV matrix and not the projection matrix at all. When I use an MVP matrix, things get alot worse. The model appears almost completely flat, with some vertices stretching as long as my far-clip, which is normally 1000.0f. My first thought was that I had a few transposing issues, but I know for a fact that my transposing function worked correctly. My Mat4x4 class is natively row-major, as mathematics suggests is the normal format. The oglformat booleans transpose to column major. What did I do so wrong?