#144560 - Noda - Sat Nov 03, 2007 9:17 pm
Hi! After my problem concerning that question had been solved (see http://forum.gbadev.org/viewtopic.php?t=14394), I've been told to write a mini-tuto with the solution, and I thought it was a good idea, so here it is ;)
In my case, the goal was to have a ball rolling on a surface, so I'll take that as an example.
MINI-FAQ:
- Q: Why can't I use glRotate() to simple rotate my object?
- A: glRotate() is based on the world's axis, so when it will work for a single axis rotation, but when you want to rotate an object along mulple axis at the same time, the first rotation affects the other axis causing your rotation to be wrong. That's why you need to use object's local axis to do the rotation.
HOW TO:
There's different methods to do this, but I'll explain only the one I used.
First you need to have a matrix for your object, storing its current rotation.
Let's call it the object matrix, O.
At first you initialize it with its original rotation (identity in the general case).
Then you have to build the rotation matrix, R: it's the one that will be used to rotate your object. The rotation that will be applied here is relative to the current rotation state of the object (stored in O), so you have to use relative angles here.
Here is how it is built:
After that, you want to rotate your object: it's done by multiplying your object matrix by the rotation matrix.
O' = O * R.
Note that the order of the multiplication is important, as with matrix, A*B is diiferent from B*A!
Now we have the new rotation applied for our object, let's save it into our object matrix:
Another step that need to be done (but not necessarily every frame), is to re-orthogonalize and re-normalize the matrix. Because we are using here 20.12 fixed point precision (the NDS original format), the matrix gets somewhat distorted due to rounding errors, that's whay you need regulary apply this step to the object matrix.
Now everything is set up to draw our object:
Then you're done!
Note that the rotation is here applied before the rotation, it's strange because it should have been after but if you put it after the point of rotation will be affected (it should be the opposite). Anyways, that's how it's working for me, it's not really a problem but I had to point it.
Here is now for you the code of the functions I used, I did not detailled them before to simplify the explications.
Hope that will be useful to some, enjoy ;)
Thanks to sajiimori, Rajveer & silent_code for helping me getting the solution of this problem.
In my case, the goal was to have a ball rolling on a surface, so I'll take that as an example.
MINI-FAQ:
- Q: Why can't I use glRotate() to simple rotate my object?
- A: glRotate() is based on the world's axis, so when it will work for a single axis rotation, but when you want to rotate an object along mulple axis at the same time, the first rotation affects the other axis causing your rotation to be wrong. That's why you need to use object's local axis to do the rotation.
HOW TO:
There's different methods to do this, but I'll explain only the one I used.
First you need to have a matrix for your object, storing its current rotation.
Let's call it the object matrix, O.
At first you initialize it with its original rotation (identity in the general case).
Code: |
m3x3 objmat;
// initialize ball rotation matLoadIdentity(&matobj); |
Then you have to build the rotation matrix, R: it's the one that will be used to rotate your object. The rotation that will be applied here is relative to the current rotation state of the object (stored in O), so you have to use relative angles here.
Here is how it is built:
Code: |
m3x3 matrot;
int32 cosX = cosf32(xa); int32 cosY = cosf32(ya); int32 cosZ = cosf32(za); int32 sinX = sinf32(xa); int32 sinY = sinf32(ya); int32 sinZ = sinf32(za); matrot.m[0] = mulf32(cosZ, cosY); matrot.m[3] = mulf32(cosZ, mulf32(sinY, sinX)) - mulf32(sinZ, cosX); matrot.m[6] = mulf32(sinZ, sinX) + mulf32(cosZ, mulf32(sinY, cosX)); matrot.m[1] = mulf32(sinZ, cosY); matrot.m[4] = mulf32(cosZ, cosX) + mulf32(sinZ, mulf32(sinY, sinX)); matrot.m[7] = mulf32(sinZ, mulf32(sinY, cosX)) - mulf32(cosZ, sinX); matrot.m[2] = -sinY; matrot.m[5] = mulf32(cosY, sinX); matrot.m[8] = mulf32(cosY, cosX); |
After that, you want to rotate your object: it's done by multiplying your object matrix by the rotation matrix.
O' = O * R.
Note that the order of the multiplication is important, as with matrix, A*B is diiferent from B*A!
Code: |
m3x3 matnew;
matMult(&matobj, &matrot, &matnew); |
Now we have the new rotation applied for our object, let's save it into our object matrix:
Code: |
matCopy(&matobj, &matnew); |
Another step that need to be done (but not necessarily every frame), is to re-orthogonalize and re-normalize the matrix. Because we are using here 20.12 fixed point precision (the NDS original format), the matrix gets somewhat distorted due to rounding errors, that's whay you need regulary apply this step to the object matrix.
Code: |
matOrthogonalize(&matobj); |
Now everything is set up to draw our object:
Code: |
// push the current matrix onto the stack (save state)
glPushMatrix(); glTranslate3f32(x, y, z); // move the object glMultMatrix3x3(&matobj); // rotate the object // draw you model here glPopMatrix(A); |
Then you're done!
Note that the rotation is here applied before the rotation, it's strange because it should have been after but if you put it after the point of rotation will be affected (it should be the opposite). Anyways, that's how it's working for me, it's not really a problem but I had to point it.
Here is now for you the code of the functions I used, I did not detailled them before to simplify the explications.
Code: |
// orthogonalize & normalize the given 3x3 matrix
void matOrthogonalize(m3x3 *mat) { int32 *x = &mat->m[0]; int32 *y = &mat->m[3]; int32 *z = &mat->m[6]; // calculate the 2nd axis from the 1st & the 3rd crossf32(z, x, y); // calculate the 3rd axis from the 1st & the 2nd crossf32(x, y, z); // normalize the axis normalizef32(x); normalizef32(y); normalizef32(z); } // multiply two 3x3 matrix void matMult(m3x3 *mat1, m3x3 *mat2, m3x3 *mat) { int i, j, k; for(i=0; i<9; i++) mat->m[i] = 0; for(i=0; i<3; i++) for(j=0; j<3; j++) for (k=0; k<3; k++) mat->m[3*i + j] += mulf32(mat1->m[3*i + k], mat2->m[3*k + j]); } // reset the given 3x3 matrix to identity void matLoadIdentity(m3x3 *mat) { mat->m[0] = 1 << 12; mat->m[3] = 0; mat->m[6] = 0; mat->m[1] = 0; mat->m[4] = 1 << 12; mat->m[7] = 0; mat->m[2] = 0; mat->m[5] = 0; mat->m[8] = 1 << 12; } // copy the second 3x3 matrix into the first one void matCopy(m3x3 *matdest, m3x3 *matsrc) { int i; for(i=0; i<9; i++) matdest->m[i] = matsrc->m[i]; } |
Hope that will be useful to some, enjoy ;)
Thanks to sajiimori, Rajveer & silent_code for helping me getting the solution of this problem.