OpenGl Matrix Problem

Started by
5 comments, last by enotirab 15 years, 2 months ago
Hi all. I am fairly new to game programming and recently decided to try to include 3d models in a game engine that I'm working on. I bought a good book on the subject by Evan Pipho. I had good success with the md2 model format and now I am trying to work with the Ms3D format so I can use skeletal animation. I am able to load the model and draw it in it's original pose but have had very little success animating the model(it turns into mush). Evan's demo works perfectly so I've been using it as a basis for comparison and I've track down the difference between his code and mind...but that has given me greater confusion. I'm programming the engine in OpenGL and designed a Vector class and Matrix class to allow for manipulation of verts as did he..and there is where the differences have come up. He seems to store the translational data for the matrix in the bottom row, but the translation matrix in the back of the Red Book show the translation matrix as storing the translational data in the last column instead. Using those specs I am able to multiply a rotational and transitional matrix together(much in the was DirectX seems too) and then transform a vector in exactly the way I'd expect. However the resulting matrices I am getting do not match his. This is obviously due to the location of the translational data. I'm really stumped and although I have narrowed the problem down I cannot seem to understand why he placed his transitional data here and I believe that understanding this is key to fixing the problem. I'd be happy to post source code, but there is so much that its impractical for me to post it all. This is my first post here on Gamedev so if anyone has suggestions I'd really appreciate it!
Advertisement
Sound slike your problem is about how matrix and vectors works. OpenGL uses a notation with column vectors and column major matrices. This means a vectors are represented as 4x1 matrices (4 tall, 1 wide). Matrices being column major means that, when stored in linear memory, the 2D array is flattened column by column.

Now, if the book instead uses a notation of row vectors and row major matrices, everything will look different on paper; vectors are now 1x4 (1 tall, 4 wide) instead, and the matrices will look transposed compared to what OpenGL uses. The transpose will effectively put translation in the bottom row instead of the right column (as in OpenGL).

However, once you start working out how matrix multiplication works, and how the matrices are flattened to linear memory, you will see that everything actually end up exactly the same. If you get different result, you are most likely reading something wrong or the book is explaining something wrong.
Well his code doesn't seem to quite line up with the book ideas. The custom matrix class that he creates seems to use row-major matrices for the rotational portion but column-major for transitional data. More specifically here is my code for setting transitional and rotational information:

void Matrix4X4::SetTranslate(float x, float y, float z){	m[3] = x;	m[7] = y;	m[11] = z;	}void Matrix4X4::SetRotation(float x,float y,float z){		float cosX = cosf(x);		float cosY = cosf(y);		float sinX = sinf(x);		float sinY = sinf(y);		float cosZ = cosf(z);		float sinZ = sinf(z);		float cosXsinY = cosX * sinY;		float sinXsinY = sinX * sinY;		m[0] = cosY * cosZ;		m[1] = -cosZ * sinZ;		m[2] = -sinY;		m[4] = -sinXsinY * cosZ + cosX * sinZ;		m[5] = sinXsinY * sinZ + cosX * cosZ;		m[6] = -sinX * cosY;		m[8] = cosXsinY * cosZ + sinX * sinZ;		m[9] = -cosXsinY * sinZ + sinX * cosZ;		m[10] = cosX * cosY;		m[15] = 1.0f;}and his code:inline void CMatrix4X4::SetRotation(float fX, float fY, float fZ){	double cx = cos(fX);	double sx = sin(fX);	double cy = cos(fY);	double sy = sin(fY);	double cz = cos(fZ);	double sz = sin(fZ);	m_fMat[0] = (float)(cy * cz);	m_fMat[1] = (float)(cy * sz);	m_fMat[2] = (float)(-sy);	m_fMat[4] = (float)(sx * sy * cz - cx * sz);	m_fMat[5] = (float)(sx * sy * sz + cx * cz);	m_fMat[6] = (float)(sx * cy);	m_fMat[8] = (float)(cx * sy * cz + sx * sz);	m_fMat[9] = (float)(cx * sy * sz - sx * cz);	m_fMat[10] = (float)(cx * cy);	m_fMat[15] = 1.0f;}inline void CMatrix4X4::SetTranslation(float fX, float fY, float fZ){	m_fMat[12] = fX;	m_fMat[13] = fY;	m_fMat[14] = fZ;}


also, for his matrix multiplication he multiplies them backwards. I thought for awhile that these matrices were simply transposed from each other, but that isn't quite true from what I can tell. Its important to note that I'm not using these matrices directly in OpenGl. I'm not feeding them to the matrix stack at all, but just using them to edit vertex data directly.

[Edited by - enotirab on January 20, 2009 7:41:59 PM]
Having the translation in elements 3, 7 and 11 is unusual. Not that it's wrong, but it implies that you have different vector ordering (row or column vectors) and matrix majorness (row or column major matrix).

As long as you are consistent, everything will be correct. You just have to be very careful that you define all operations correct, and since you're getting wrong result, I believe that you're not defining your operations (multiplication specifically) correct with respect to vector ordering and sides you multiply from.
That must be the case. The reason I placed them there is because much of the literature that I've read seems to place them there. The back of the Red Book, and also this site

When I transform hard-coded verts by multiplying them I SEEM to get the correct result, but my matrices do not match his and the model verts are definitely not coming out correctly. I'm unsure of why this is inconsistent. Its likely that you are correct and I am misunderstanding the way the vector transformations should occur.

Here is my code for it:
Matrix4X4 Matrix4X4::operator*(const Matrix4X4 rhs){  Matrix4X4 temp(	  //first row	  m[0]*rhs.m[0] + m[1]*rhs.m[4] + m[2]*rhs.m[8] + m[3]*rhs.m[12], m[0]*rhs.m[1] + m[1]*rhs.m[5] + m[2]*rhs.m[9] + m[3]*rhs.m[13],	             m[0]*rhs.m[2] + m[1]*rhs.m[6] + m[2]*rhs.m[10]+m[3]*rhs.m[14], m[0]*rhs.m[3] + m[1]*rhs.m[7] + m[2]*rhs.m[11] + m[3]*rhs.m[15],      //second row	  m[4]*rhs.m[0] + m[5]*rhs.m[4] + m[6]*rhs.m[8] + m[7]*rhs.m[12], m[4]*rhs.m[1] + m[5]*rhs.m[5] + m[6]*rhs.m[9] + m[7]*rhs.m[13],	             m[4]*rhs.m[2] + m[5]*rhs.m[6] + m[6]*rhs.m[10]+m[7]*rhs.m[14], m[4]*rhs.m[3] + m[5]*rhs.m[7] + m[6]*rhs.m[11] + m[7]*rhs.m[15],      //third row	  m[8]*rhs.m[0] + m[9]*rhs.m[4] + m[10]*rhs.m[8] + m[11]*rhs.m[12], m[8]*rhs.m[1] + m[9]*rhs.m[5] + m[10]*rhs.m[9] + m[11]*rhs.m[13],	             m[8]*rhs.m[2] + m[9]*rhs.m[6] + m[10]*rhs.m[10]+m[11]*rhs.m[14], m[8]*rhs.m[3] + m[9]*rhs.m[7] + m[10]*rhs.m[11] + m[11]*rhs.m[15],      //fourth row	  m[12]*rhs.m[0] + m[13]*rhs.m[4] + m[14]*rhs.m[8] + m[15]*rhs.m[12], m[12]*rhs.m[1] + m[13]*rhs.m[5] + m[14]*rhs.m[9] + m[15]*rhs.m[13],	             m[12]*rhs.m[2] + m[13]*rhs.m[6] + m[14]*rhs.m[10]+m[15]*rhs.m[14], m[12]*rhs.m[3] + m[13]*rhs.m[7] + m[14]*rhs.m[11] + m[15]*rhs.m[15]);       return temp;}void Vector::Transform(const Matrix4X4& mat) {	 float nx,ny,nz;	 	 nx = mat.Get(0,0) * v[vX] + mat.Get(1,0) *v[vY] + mat.Get(2,0) *v[vZ] + mat.Get(3,0); 	 ny = mat.Get(0,1) *v[vX]+ mat.Get(1,1) *v[vY] + mat.Get(2,1) *v[vZ] + mat.Get(3,1);      nz = mat.Get(0,2) *v[vX]+ mat.Get(1,2) *v[vY] + mat.Get(2,2) *v[vZ] + mat.Get(3,2); 	 v[vX] = nx;	 v[vY] = ny;	 v[vZ] = nz; }


Basically I'm just confused as to why the Red Book has a translation matrix
like this;

1 0 0 dx
0 1 0 dy (which is how mine is meant to be set up.
0 0 1 dz
0 0 0 1

and other matrix classes I have read about seem to be using a translation matrix like this:

1 0 0 0
0 1 0 0
0 0 1 0
dx dy dyz 1

I do understand that you have two options of transforming vectors. As a row from one side of multiplication, or as a column on the other. I'm using(I belive) a column style, so it should look like this:

1 0 0 dx * vx
0 1 0 dy * vy
0 0 1 dz * vz
0 0 0 1 * 1


Please forgive me if I seem like I'm being obtuse....I must be missing something and since this is a key graphical programming issue I really want to fix the error in my logic.

[Edited by - enotirab on January 20, 2009 3:33:22 PM]
Quote:Original post by enotirab
Basically I'm just confused as to why the Red Book has a translation matrix
like this;

1 0 0 dx
0 1 0 dy (which is how mine is meant to be set up.
0 0 1 dz
0 0 0 1

It is the correct notation in OpenGL.

However, keep in mind how OpenGL stores these 16 elements of the matrix into an 1-dimensional array. That is, the vertical elements (columns) are stored first, then move on the next column.

The problem may be that your matrix class stores 16 elements in row-major order; storing the first row elements, then moving to the next line, which is common for math and C++.

I do not know the original intention for OpenGL's way in first place, however, as a result, you can find very interesting fact; 3 consecutive elements in an array represent a meaningful set.

For example, the first column, (m0, m1, m2) is the left(X) axis,
the second column, (m4, m5, m6) is the up(Y) axis,
the third column, (m8, m9, 10) is the forward(Z) axis,
and the right most column, (m12, m13, m14) is for the translation.

Here is an image for better understanding;


For more info, check this link;
OpenGL Matrix

You can keep the own structure in your class, but you need to transpose the matrix data when you pass them to OpenGL. OpenGL also provides functions for this; glLoadTransposeMatrix{fd}() and glMultTransposeMatrix{fd}().
Thanks very much everyone for your help. I finally got it to work as a result. Now my skeletal animation is working like a charm. I basically had columns as rows, and rows as columns (base on the visual diagram). I don't know how long I'd have struggled with this alone. Thanks so much!

This topic is closed to new replies.

Advertisement