Sign in to follow this  
daniel_i_l

rotating to a normal

Recommended Posts

daniel_i_l    295
I have a tank whose "base" position is sitting flat on the xz plane (centered at 0,0,0). But the game I have is a on heightmap, and I want the tank to be rotated to fit the terrain, for example I want it to pointing down if it's going downhill). Now I got the normal to the heightmap (x,y,z) were the tank is standing and nomalized it, I also have a normalized vector (x,0,z) that shows the tanks direction(on the xz plane). So how can I use the glRotatef function (1 or more than 1) to both face the tank in the right direction and correctly fit it on the heightmap according to the normal? Thanks.

Share this post


Link to post
Share on other sites
Yann L    1802
The best way to solve this problem is by considering the different coordinate frames. When you say "I have a normal and a direction vector", then you have essentially defined a target coordinate system for your tank.

Now, the idea is to directly construct a rotation matrix that will map the old local coordinate system to the new one. First, you would construct a matrix representing the final coordinate system. You do that by filling the columns of a 3x3 matrix with the three base vectors: NxD, D, N (where N is your normal, and D your direction vector). Make sure both N and D are normalized.

Now, you need to find a matrix which maps the old coordinate system to the one you just created. From the matrix FAQ:


Q40. How do I use matrices to convert one coordinate system to another?
-----------------------------------------------------------------------
Similar to the previous problem, the requirement is to map one
coordinate system onto another. However, instead of just trying to
map one coordinate axis onto another, all three axii have to be matched.
Both coordinate systems are therefore represented as either 3x3 or
4x4 matrices.

The problem is therefore to find the rotation matrix that will map one
matrix onto another. This can be expressed mathematically:

Mfinal = Mrot . Morig

where Mfinal is the final coordinate system matrix,
Morig is the original coordinate system and
Mrot is the desired rotation matrix.

The goal is then to find the matrix Mrot. This can be achieved by
rearranging the equation to give:

-1
Mfinal . Morig = Mrot

-1
Mrot = Mfinal . Morig

Thus, the desired rotation matrix can be by calculatng the inverse of
the original coordinate system and multiplying it with the final
rotation matrix.

As a check, consider the cases when either the original or final
rotation matrices are the identity matrix. In each case, the rotation
matrix should match the final matrix and the inverse of the final
matrix respectively.


You can make this much easier, if the original coordinate system is the identity (which should be true in your case). Then, the rotation matrix equals the matrix constructed from the three base vectors above.

You can load this matrix into OpenGL using one of the glLoadMatrix or glMultMatrix commands.

Share this post


Link to post
Share on other sites
daniel_i_l    295
Thanks. I have a few questions.
First of all are NxD,D and N columns or rows?
Also, glLoadMatrix gets a 4X4 matrix, what do I fill in for the 4th row and column?
And last of all, I read that glLoadMatrix is passed a pointer to a 4X4 matrix. would I do that like this:

double* m;
double matrix[4][4];
matrix[0][0] = ...
...
matrix[3][3] = ...

m = &matrix;
glLoadIdentity();
glLoadMatrix(m);
DrawTank();
glLoadIdentity();
DrawEverythingNormally();


Is that correct?
Thanks.

Share this post


Link to post
Share on other sites
jyk    2094
Quote:
Original post by Yann L
The best way to solve this problem is by considering the different coordinate frames. When you say "I have a normal and a direction vector", then you have essentially defined a target coordinate system for your tank.

Now, the idea is to directly construct a rotation matrix that will map the old local coordinate system to the new one. First, you would construct a matrix representing the final coordinate system. You do that by filling the columns of a 3x3 matrix with the three base vectors: NxD, D, N (where N is your normal, and D your direction vector). Make sure both N and D are normalized.
One problem the OP may have here is that his D and N are not (necessarily) at right angles, and therefore [ NxD | N | D ] will probably not be orthogonal.

@The OP: In my own code I address this problem using incremental rotations, which I think is the best way to do it. However, this requires having your own math library with the appropriate functionality, and so may not be an option for you at the moment.

If not, here's something you might try. I've never done it this way myself, so I really don't know how well it would work in practice:
Vector3 side = normalize(cross(normal, forwardInXZPlane));
Vector3 forward = cross(side, normal);
float m[16];
m[0] = side[0];
m[1] = side[1];
m[2] = side[2];
m[3] = 0.0f;
m[4] = normal[0];
m[5] = normal[1];
m[6] = normal[2];
m[7] = 0.0f;
m[8] = forward[0];
m[9] = forward[1];
m[10] = forward[2];
m[11] = 0.0f;
m[12] = position[0];
m[13] = position[1];
m[14] = position[2];
m[15] = 1.0f;
You can now send 'm' to OpenGL via glMultMatrixf().

Again, this is just a suggestion, but at least now you have a couple of different ideas to work with.

Share this post


Link to post
Share on other sites
Yann L    1802
Quote:
Original post by jyk
One problem the OP may have here is that his D and N are not (necessarily) at right angles, and therefore [ NxD | N | D ] will probably not be orthogonal.

If he does his math right, they should be at a right angle (otherwise, the tank would be pointing upwards into the air, or downwards into the ground). You can still ensure orthogonality by using a double cross product. If we assume N to always be a normalized reference vector, we can do:

T = normalize(N x D)
D = N x T

Then the matrix formed by N, D, and NxD will be guaranteed to be orthonormal.

Quote:

First of all are NxD,D and N columns or rows?

In OpenGL, columns.

Quote:

Also, glLoadMatrix gets a 4X4 matrix, what do I fill in for the 4th row and column?

Everything should be zero, except for the bottom right element, which should be one.

Share this post


Link to post
Share on other sites
jyk    2094
Quote:
Original post by Yann L
If he does his math right, they should be at a right angle (otherwise, the tank would be pointing upwards into the air, or downwards into the ground).
My comment was based on the following from the original post:
Quote:
I also have a normalized vector (x,0,z) that shows the tanks direction(on the xz plane).
Which suggests that N and D will rarely be at right angles to begin with.
Quote:
You can still ensure orthogonality by using a double cross product. If we assume N to always be a normalized reference vector, we can do:

T = normalize(N x D)
D = N x T

Then the matrix formed by N, D, and NxD will be guaranteed to be orthonormal.
And this is (almost) exactly what the code sample I posted does (the only difference being that, I think, it should be D = T x N).

Share this post


Link to post
Share on other sites
daniel_i_l    295
Thanks everyone, I think I'll try that.
And one other thing, should I translate to the position of the tank with glTranslatef() before calling glMultMatrix or after?
Thanks.

Share this post


Link to post
Share on other sites
jyk    2094
Quote:
Original post by daniel_i_l
And one other thing, should I translate to the position of the tank with glTranslatef() before calling glMultMatrix or after?
Most likely the glTranslatef() call should come before the glMultMatrix() call; this corresponds to the transformation order rotate->translate, which is usually what you want for rendering models.

Share this post


Link to post
Share on other sites
daniel_i_l    295
But isn't the glMultMatrix like a rotation call, if that is true then the glTranslate should come after (like you said, rotation then translation)?
Thanks.

Share this post


Link to post
Share on other sites
jyk    2094
Quote:
Original post by daniel_i_l
But isn't the glMultMatrix like a rotation call, if that is true then the glTranslate should come after (like you said, rotation then translation)?
OpenGL uses column vectors, which is reflected in the fact that OpenGL transform function calls must be issued 'backwards' from the order in which you want them to be applied. So if you want rotate->translate, you call glTranslate() first, and the rotation function second.

Share this post


Link to post
Share on other sites
daniel_i_l    295
I implemented it like this:

glLoadIdentity();
Camera.Look();
glColor3f(0.5,1,0.5);
CVector3 vForward2D = {1,0,0};
CVector3 vNormal;
vNormal = NormalToMap(g_HeightMap, ((Map.BestPoint.x)/SCALE_SIDE), ((Map.BestPoint.z)/SCALE_SIDE));

CVector3 vSide = Normalize(Cross(vNormal, vForward2D));
CVector3 vForward = Cross(vSide, vNormal);
CVector3 vPosition = Map.BestPoint;

float m[16];
m[0] = vSide.x;
m[1] = vSide.y;
m[2] = vSide.z;
m[3] = 0.0f;
m[4] = vNormal.x;
m[5] = vNormal.y;
m[6] = vNormal.z;
m[7] = 0.0f;
m[8] = vForward.x;
m[9] = vForward.y;
m[10] = vForward.z;
m[11] = 0.0f;
m[12] = vPosition.x;
m[13] = vPosition.y;
m[14] = vPosition.z;
m[15] = 1.0f;

glMultMatrixf(m);

glLineWidth(2);
glBegin(GL_TRIANGLES);
glVertex3f(-15, 1, 15);
glVertex3f( 20, 1, 20);
glVertex3f( 0, 1, -20);
glEnd();
glLineWidth(1);



And it worked Great! I don't even have to translate cause of the vPosition input.
I just have a little question, when I set the forward to
CVector3 vForward2D = {1,0,0};
the triangle points to the left instead of to the right, only when I set the x to -1 it points right. Also, when the z is set to 1 it point into the screen instead of out of it? Why does this happen?
Thanks.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this