Update up, forward, right vectors.

This topic is 2433 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

Recommended Posts

I am trying to write my first game, and want to create a simple airplane / flight type of game. I have been brushing up on my vector math but am getting stuck. At this point I'm just working out some pseudocode so I can just get an airplane flying around the world. Here's the basic idea of what I am trying to do:

My airplane will start out flying level and I will be able to start with up, right, and forward/heading vectors. On each frame the plane will move along the forward vector to its new position. If the user inputs up/down the pitch will be affected by "n" degrees, and the same for left/right and the roll affecting the up vector. I am planning ahead for yaw, though I haven't worked out the user control yet.

So I am trying to figure out how I update the up/right/forward vectors based on the degree of rotation. I have found lots of information about how to calculate the axis or the angle of rotation. Perhaps I am going about this in a harder way than necessary, but I know how I want to use these vectors and am sure it will work once I figure out how to calculate the new vectors.

I am using Opengl (Android) and want to use the gluLookat function. It simply accepts values of eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ. Obviously I will need a new up vector here for each frame, and of course my airplane will not always be aligned with the world up vector. Calculating the other values I have solved, based on knowing the updated vector.

I will simplify my question to just one rotation. I have a forward, right, and up vector. I receive input to move pitch up and I know the number of degrees. My rotation axis is the right vector. Given the rotation axis and amount of rotation (in degrees), how do I find the new up & foward vectors?

Share on other sites
The 3 vectors together build up the basis of a rotation matrix. Using e.g. column vectors and the 4th component for the homogeneous coefficients, the first 3 column vectors of a rotation matrix are just the vectors you're looking for. So applying a (delta) rotation matrix onto a (orientation) rotation matrix will yield in another (orientation) rotation matrix where you can simply pick the resulting vectors from. In fact the whole thing is nothing else but rotating the current state of vectors to yield in the next state of vectors.

With a rotation axis and angle you have all parameters of the so-called axis/angle rotation representation. The internet (e.g. [url="http://www.euclideanspace.com/maths/geometry/rotations/axisAngle/index.htm"]euclideanspace.com[/url]) tells about conversion, e.g. to a rotation matrix.

Share on other sites
Thank you haegarr,

I will need to brush up on my matrices before I get going. Several of the sites that I found were leading me toward using rotation matrices and I guess I just was avoiding them. The opengl documentation is what has made me shy away from using matrices. I suppose I can setup a class for the matrix math similar to the one I am using for my 3d vectors.

So if I setup a rotation matrix do you know if I can just apply it to my code with an opengl call? I was looking at glMultimatrix and it seems like that may work. Or would it be better to just compute the matrices and extract the vectors, then use them in my code as I was already intending?

Well, I will get in some studying and probably test out both ways. I will report back if I have any serious struggles. Thanks again for your help.

Share on other sites
[quote name='JM33' timestamp='1304537448' post='4806542']
So if I setup a rotation matrix do you know if I can just apply it to my code with an opengl call? I was looking at glMultimatrix and it seems like that may work. Or would it be better to just compute the matrices and extract the vectors, then use them in my code as I was already intending?[/quote]
Provided it's arranged in memory the way OpenGL expects (that is, the data occupies a contiguous block of 16 elements and the elements of each basis vector appear consecutively), you can upload the matrix directly using glLoadMatrix*() or glMultMatrix*().

Share on other sites
Thanks, I think I will try both to help me grasp how each will work, and decide which works best. Now I'm goong to read some more. I appreciate the help guys, thanks a lot.

Share on other sites
Sorry, double post

Share on other sites
Sorry, triple post

Share on other sites
Well, I got the matrices figured out and was able to build some basic rotation matrices. I built one for each axis and they work when I call glMultimatrixf. I am still running into the same problem which is where my original question lies. Basically, if I were to say pitch up/down to start, everything works fine because my rotations are around the x axis. However, if I try to then roll, I rotate around the z axis. But the z axis is no longer the roll axis/vector, it is still the world z axis. From lots of reading I believe this is gimbal lock, which I understand to an extent.

So how do I update the actual forward/up/right vectors and rotate around them. I'm sure I can figure out the math behind it, once I know the right steps to take. Also, the matrices I used for rotation seem to be locked to rotate around the world axes. It seems I should be using a matrix that would rotate around an arbitrary axis. I'm just not sure if this would be the right solution, or if more is needed to update the vectors.

Share on other sites
Here is a bit of the code I am using that I think should work. Maybe someone can tell what I am doing wrong. At this time I am just trying to draw my airplane, pop/push the matrix. Then rotate, draw the ground and sky. This way my airplane stays in the center and the world moves around. I have two matrices. One called mOrientMatrix, which stores the current orientation. The other is mArbRotMatrix, which is a rotation matrix for an arbitrary axis. The void setRotPitch() rotates by the angle (pitch/yaw) around the current vectors from the mOrientMatrix. Then the mOrientMatrix is updated with the values produced.

If I only make one rotation, the code works as expected. However, as soon as I make a second rotation the axis is incorrect. (For whichever comes last in the code, pitch or roll)

[code]public class BARenderer implements Renderer {

private float[] mArbRotMatrix = new float[16];
private float[] mOrientMatrix = new float[16];

public static boolean moveLeft;
public static boolean moveRight;
public static boolean moveUp;
public static boolean moveDown;
private Context mContext;

private F22 F22;
private Ground Ground1;
private Sky Sky1;

private GL10 mGl;
private int frame;
private float pitch;
private float yaw;
private float roll;

public BARenderer(Context context) {
mContext = context;

Init();
}

public void Init() {

mOrientMatrix[0] = 1;
mOrientMatrix[1] = 0;
mOrientMatrix[2] = 0;
mOrientMatrix[3] = 0;

mOrientMatrix[4] = 0;
mOrientMatrix[5] = 1;
mOrientMatrix[6] = 0;
mOrientMatrix[7] = 0;

mOrientMatrix[8] = 0;
mOrientMatrix[9] = 0;
mOrientMatrix[10] = 1;
mOrientMatrix[11] = 0;

mOrientMatrix[12] = 0;
mOrientMatrix[13] = 0;
mOrientMatrix[14] = 0;
mOrientMatrix[15] = 1;

pitch = 0;
yaw = 0;
roll = 0;

}

@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
mGl = gl;

gl.glEnable(GL10.GL_TEXTURE_2D); //Enable Texture Mapping ( NEW )
gl.glEnable(GL10.GL_BLEND);
gl.glEnable(GL10.GL_DITHER);
gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f); //Black Background
gl.glClearDepthf(1.0f); //Depth Buffer Setup
gl.glEnable(GL10.GL_DEPTH_TEST); //Enables Depth Testing
gl.glDepthFunc(GL10.GL_LEQUAL); //The Type Of Depth Testing To Do
gl.glEnable(GL10.GL_ALPHA_TEST);
// Enable face culling.
gl.glEnable(GL10.GL_CULL_FACE);

gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
}

@Override
public void onDrawFrame(GL10 gl) {

if (gl == null)
return;

gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

// Replace the current matrix with the identity matrix

gl.glTranslatef(0, 0, -30);

//Plane
gl.glPushMatrix();

gl.glScalef(0.5f, 0.5f, 0.5f);

if(F22 != null)
F22.draw(mGl);

gl.glPopMatrix();

gl.glPushMatrix();

gl.glMultMatrixf(mOrientMatrix, 0);

//Ground

gl.glPushMatrix();

gl.glTranslatef(0, -100, 0);

if(Ground1 != null)
Ground1.draw(mGl);

gl.glPopMatrix();

//Sky
gl.glPushMatrix();

gl.glTranslatef(0, -1000, 0);

if(Sky1 != null)
Sky1.draw(mGl);

gl.glPopMatrix();
gl.glPopMatrix();

frame += 1;
speedframe += speed;

if (moveLeft){
roll += -0.1;
}

if (moveRight){
roll += 0.1;
}

if (moveUp){
pitch += 0.1;
}

if (moveDown){
pitch += -0.1;
}

setRotPitch(pitch, mOrientMatrix[0], mOrientMatrix[1], mOrientMatrix[2]);
mOrientMatrix = mArbRotMatrix;
setRotPitch(pitch, mOrientMatrix[8], mOrientMatrix[9], mOrientMatrix[10]);
mOrientMatrix = mArbRotMatrix;

}

public void setRotPitch(double angle, double x, double y, double z) {

double c = Math.cos(angle);
double s = Math.sin(angle);
double t = 1 - Math.cos(angle);

mArbRotMatrix[0] = (float) ((t*x*x) + c);
mArbRotMatrix[1] = (float) ((t*x*y) - (s*z));
mArbRotMatrix[2] = (float) ((t*x*y) + (s*y));
mArbRotMatrix[3] = 0;

mArbRotMatrix[4] = (float) ((t*x*y) + (s*z));
mArbRotMatrix[5] = (float) ((t*y*y) + c);
mArbRotMatrix[6] = (float) ((t*y*z) - (s*x));
mArbRotMatrix[7] = 0;

mArbRotMatrix[8] = (float) ((t*x*z) - (s*y));
mArbRotMatrix[9] = (float) ((t*y*z) + (s*x));
mArbRotMatrix[10] = (float) ((t*z*z) + c);
mArbRotMatrix[11] = 0;

mArbRotMatrix[12] = 0; // x movement
mArbRotMatrix[13] = 0; // y movement
mArbRotMatrix[14] = 0; // z movement
mArbRotMatrix[15] = 1;
}

}
[/code]

Any suggestions?

Share on other sites
I got it working now! There were two main problems with the code above. First the pitch/roll values were not needed to be added on every input. They should be =0.01f or =0 dependent on input. (not +=0.01f) The second problem was that I was making the mOrientMatrix = mArbRotMatrix, when I should have been multiplying them together. Though I had tried this, I finally realized that I had transposed my matrix multiplication, therefore getting incorrect values in return. Coupled with the first problem any time I tried a multiplied matrix I was getting very strange rotations. Once I fixed both of these everything fell into place. I actually ended up removing the two rotation on arbitrary axes, and used on combined matrix for rotation on x,y,z axes. I added a translation matrix and got that working as well. Finally I am able to fly around my world, even straight through the ground. I guess I am ready for some collision detection.

Thanks again for the pointing me in the right direction!

Share on other sites
[quote name='JM33' timestamp='1305679498' post='4812181']
I got it working now! There were two main problems with the code above. First the pitch/roll values were not needed to be added on every input. They should be =0.01f or =0 dependent on input. (not +=0.01f) The second problem was that I was making the mOrientMatrix = mArbRotMatrix, when I should have been multiplying them together. Though I had tried this, I finally realized that I had transposed my matrix multiplication, therefore getting incorrect values in return. Coupled with the first problem any time I tried a multiplied matrix I was getting very strange rotations. Once I fixed both of these everything fell into place. I actually ended up removing the two rotation on arbitrary axes, and used on combined matrix for rotation on x,y,z axes. I added a translation matrix and got that working as well. Finally I am able to fly around my world, even straight through the ground. I guess I am ready for some collision detection.

Thanks again for the pointing me in the right direction!
[/quote]

Coolness, I'm glad you got it to work!

I have a tremendous favor to ask you... Could you post the code that made it work? I'm stuck on the same thing for my own game and I can't figure out how to get the rotations to work correctly. Right now I'm getting your 'very strange rotations' (I've been stuck on that for a week), and I'm doing the yaw/pitch/roll arbitrary axis matrices while multiplying all the matrices together. Thanks for the info!

Share on other sites
Nevermind about posting your code, I just got it to work. Thanks for the explanation, it really helped out! It's pretty cool how things go together when one thing is changed, heh! I had a few numbers completely wrong (treated a column matrix like a row matrix - ooooops!) but once I played around with it, it all worked out. Thanks for sharing your success!

Share on other sites
Glad you got it working too!

Well, I thought everything was working right, hopefully you are not running in to this same problem I now have. Hopefully someone is still listening. Everything looks fine except for the speed. I am using a constant value for speed, but of course I want to be able to change the speed. Even with the constant speed, after about 30 seconds I notice speed building up. After about 1 minute it gets really fast and soon its uncontrollable. Maybe I still don't have something quite right.

Here is some code-

At the end of the onDrawFrame I call this sequence to create a newRotMatrix, which is called with glMultMatrixf(newRotmatrix, 0) just before I draw the ground and sky.
[code]setmXYZrotMatrix(); //creates the rotation matrix mXYZrotmatrix

dirx = mXYZrotMatrix[8]; //get the forward vector from the rotation matrix
diry = mXYZrotMatrix[9];
dirz = mXYZrotMatrix[10];

setTransMatrix(speed, dirx, diry, dirz); //creates the translation matrix mTranslate matrix

multMatrix4x4(mXYZrotMatrix, mTranslateMatrix, newRotMatrix2); //combine the rotation and translation matrices into one transform matrix (newRotMatrix2)

multMatrix4x4(newRotMatrix, newRotMatrix2, newRotMatrix); //Apply the new transform to the current orientation

[/code]

My rotation matrix and translation matrix builders:
[code]
public void setTransMatrix(double dist, double x, double y, double z) {
mTranslateMatrix[0] = 1;
mTranslateMatrix[1] = 0;
mTranslateMatrix[2] = 0;
mTranslateMatrix[3] = 0;

mTranslateMatrix[4] = 0;
mTranslateMatrix[5] = 1;
mTranslateMatrix[6] = 0;
mTranslateMatrix[7] = 0;

mTranslateMatrix[8] = 0;
mTranslateMatrix[9] = 0;
mTranslateMatrix[10] = 1;
mTranslateMatrix[11] = 0;

mTranslateMatrix[12] = (float) (mTranslateMatrix[12] + (x * dist));
mTranslateMatrix[13] = (float) (mTranslateMatrix[13] + (y * dist));
mTranslateMatrix[14] = (float) (mTranslateMatrix[14] + (z * dist));
mTranslateMatrix[15] = 1;
}
public void setmXYZrotMatrix() {
double cA = Math.cos(pitch);
double sA = Math.sin(pitch);
double cB = Math.cos(yaw);
double sB = Math.sin(yaw);
double cC = Math.cos(roll);
double sC = Math.sin(roll);

mXYZrotMatrix[0] = (float) (cB*cC);
mXYZrotMatrix[1] = (float) ((sA*sB*cC)+(cA*sC));
mXYZrotMatrix[2] = (float) ((-cA*sB*cC)+(sA*sC));
mXYZrotMatrix[3] = 0;

mXYZrotMatrix[4] = (float) (-cB*sC);
mXYZrotMatrix[5] = (float) ((-sA*sB*sC) + (cA*cC));
mXYZrotMatrix[6] = (float) ((cA*sB*sC)+(sA*cC));
mXYZrotMatrix[7] = 0;

mXYZrotMatrix[8] = (float) sB;
mXYZrotMatrix[9] = (float) (-sA*cB);
mXYZrotMatrix[10] = (float) (cA*cB);
mXYZrotMatrix[11] = 0;

mXYZrotMatrix[12] = 0;
mXYZrotMatrix[13] = 0;
mXYZrotMatrix[14] = 0;
mXYZrotMatrix[15] = 1;
}
[/code]

Matrix multiplication is done here
[code]public void multMatrix4x4(float[] mat1, float[] mat2, float[] newMat) {

newMat [0] = (mat1[0] * mat2[0]) + (mat1[1] * mat2[1]) + (mat1[2] * mat2[2]) + (mat1[3] * mat2[3]);
newMat [1] = (mat1[0] * mat2[4]) + (mat1[1] * mat2[5]) + (mat1[2] * mat2[6]) + (mat1[3] * mat2[7]);
newMat [2] = (mat1[0] * mat2[8]) + (mat1[1] * mat2[9]) + (mat1[2] * mat2[10]) + (mat1[3] * mat2[11]);
newMat [3] = (mat1[0] * mat2[12]) + (mat1[1] * mat2[13]) + (mat1[2] * mat2[14]) + (mat1[3] * mat2[15]);

newMat [4] = (mat1[4] * mat2[0]) + (mat1[5] * mat2[1]) + (mat1[6] * mat2[2]) + (mat1[7] * mat2[3]);
newMat [5] = (mat1[4] * mat2[4]) + (mat1[5] * mat2[5]) + (mat1[6] * mat2[6]) + (mat1[7] * mat2[7]);
newMat [6] = (mat1[4] * mat2[8]) + (mat1[5] * mat2[9]) + (mat1[6] * mat2[10]) + (mat1[7] * mat2[11]);
newMat [7] = (mat1[4] * mat2[12]) + (mat1[5] * mat2[13]) + (mat1[6] * mat2[14]) + (mat1[7] * mat2[15]);

newMat [8] = (mat1[8] * mat2[0]) + (mat1[9] * mat2[1]) + (mat1[10] * mat2[2]) + (mat1[11] * mat2[3]);
newMat [9] = (mat1[8] * mat2[4]) + (mat1[9] * mat2[5]) + (mat1[10] * mat2[6]) + (mat1[11] * mat2[7]);
newMat [10] = (mat1[8] * mat2[8]) + (mat1[9] * mat2[9]) + (mat1[10] * mat2[10]) + (mat1[11] * mat2[11]);
newMat [11] = (mat1[8] * mat2[12]) + (mat1[9] * mat2[13]) + (mat1[10] * mat2[14]) + (mat1[11] * mat2[15]);

newMat [12] = (mat1[12] * mat2[0]) + (mat1[13] * mat2[1]) + (mat1[14] * mat2[2]) + (mat1[15] * mat2[3]);
newMat [13] = (mat1[12] * mat2[4]) + (mat1[13] * mat2[5]) + (mat1[14] * mat2[6]) + (mat1[15] * mat2[7]);
newMat [14] = (mat1[12] * mat2[8]) + (mat1[13] * mat2[9]) + (mat1[14] * mat2[10]) + (mat1[15] * mat2[11]);
newMat [15] = (mat1[12] * mat2[12]) + (mat1[13] * mat2[13]) + (mat1[14] * mat2[14]) + (mat1[15] * mat2[15]);

}
[/code]

The resulting values of my mTranslatematrix give constant values based on the speed. However, when they are combined with the current orientation they multiply slowly, but exponentially.. I logged the value of each, just to make sure I wasn't imagining something, and found the [12],[13],[14] values for the transform matrix to be constant with the speed. The values of the newRotMatrix do increase over time as I was seeing in the application. Everything I have read is telling me to do it this way, and I can't find anything on the web with a similar problem. I'll keep trying but so far everything I change makes something else stop working right.

Share on other sites
I heard from some of the other math experts (I'm definitely not one of them, lol!) that floating-point imprecision can cause that to happen. The trick is to re-orthogonalize the rotation matrix. I'm using a different sized matrix (3x3 matrix), but you should be able to modify the below code and get it to work in your project:

[code]// Orthogonalize the orientation matrix!
var rfx, rfy, rfz, rfMag;
var rux, ruy, ruz, ruMag;
var rx, ry, rz, rMag;

// Cross-product of right/forward vectors (store in 'up')...
rfx = matrix[5]*matrix[6] - matrix[8]*matrix[3];
rfy = matrix[8]*matrix[0] - matrix[2]*matrix[6];
rfz = matrix[2]*matrix[3] - matrix[5]*matrix[0];
rfMag = sqrt(rfx*rfx + rfy*rfy + rfz*rfz);
rfx = rfx/rfMag;
rfy = rfy/rfMag;
rfz = rfz/rfMag;

// Cross-product of right/up vectors (store in 'forward')...
rux = rfz*matrix[3] - rfy*matrix[6];
ruy = rfx*matrix[6] - rfz*matrix[0];
ruz = rfy*matrix[0] - rfx*matrix[3];
ruMag = sqrt(rux*rux + ruy*ruy + ruz*ruz);
rux = rux/ruMag;
ruy = ruy/ruMag;
ruz = ruz/ruMag;

// Normalize 'x' vector...
rMag = sqrt(matrix[0]*matrix[0] + matrix[3]*matrix[3] + matrix[6]*matrix[6]);
rx = matrix[0]/rMag;
ry = matrix[3]/rMag;
rz = matrix[6]/rMag;

// All's orthaganlized! Store new values into
// ship's 'ma' matrix...
matrix[0] = rx;
matrix[1] = rfx;
matrix[2] = rux;

matrix[3] = ry;
matrix[4] = rfy;
matrix[5] = ruy;

matrix[6] = rz;
matrix[7] = rfz;
matrix[8] = ruz;
[/code]

If you plug in this code and notice nothing except your speed errors are gone, then it worked! If the visuals go all over the place, I probably got a couple of matrix elements inverted or something like that and some simple tweaking should get it to work (I translated from my own variables to something more understandable before posting the code). Anyway, I was told that this code doesn't have to be executed on every frame, so you can make a call to this code every now and then (every ten frames?) or perform some low-cost calculations to see if an axis has gone out of tolerance.

Hope that helps! I'm still pretty new to this, so please let me know if it works!

EDIT: I plugged in this code after I updated the old orientation matrix with the new values, just before I put things on the screen.

Share on other sites
Thanks HobbyDude! I will try this when I get home. Before I left I tried removing the addition of mTranslateMatrix[12],13,14 in my setTranslateMatrix(). It seems to have slowed the speed increase, but not eliminated it. Hopefully combined with the code above I will get rid of it. I also read that this could happen and the need to reorthagonlize is not necessary on every frame, maybe every hundred or so. Some basic code can check for the values being within a tolerance. If you are within tolerance, then don't do it to save some computation time.
Thanks again, I'll update with my results later.

Share on other sites
I will need to sit down and rewrite the reortho myself. For now it seems that my change to setTranslateMatrix() did the trick.

The new code would look like mTranslateMatrix[12] = (float) (x * dist);

The same for [13] & [14], with y,z values. (All other values are just from the identity matrix). I believe the addition I thought needed to be there is actually happening when I multiply the matrix by the current orientation.

The re-orthoganalize code doesn't work if I just plug it in. I think you may be using row major matrices, while mine are column major (openGL). I tried some simple adjustments but no luck. Like I said I'll just rewrite it, its just a matter of multiplication order and matrix order and I'm sure it'll go easier if i do it by hand myself.

Thanks again and happy coding!

Share on other sites
Please notice that inaccuracies ever occur when concatenating enough transformations. You [i]may[/i] consider to switch from concatenating rotation matrices to concatenating quaternions. The are less prone to failure accumulation and their re-orthonormalization (yes, just that) is much more simple. However, it comes with the costs to do a conversion.

Just to clarify: Rotation matrices need not only be "re-orthogonalized" (i.e. their vectors need to be enforced to be pairwise orthogonal) but "re-orthonormalized" (i.e. they also need to be enforced to have unit length). Only a orthonormal matrix is guaranteed to be a pure rotation matrix. The code snippet actually does a re-orthonormalization (AFAIS), and I just wanted to clarify that the term used for it is not exact.

[quote name='JM33' timestamp='1305888840' post='4813413']
...
The re-orthoganalize code doesn't work if I just plug it in. I think you may be using row major matrices, while mine are column major (openGL). I tried some simple adjustments but no luck. Like I said I'll just rewrite it, its just a matter of multiplication order and matrix order and I'm sure it'll go easier if i do it by hand myself.
...
[/quote]
Also for clarification: "Row major" and "column major" are terms to describe how the elements of the 2D matrix are arranged when stored in linear (i.e. 1D) memory. Hence you probably mean "row vector use" (e.g. D3D, like in [b]v[/b] * [b]M[/b]) or "column vector use" (e.g. OpenGL, like in [b]M[/b] * [b]v[/b]) instead, what plays a role how the terms of a matrix product are to be arranged.

Share on other sites

This topic is 2433 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.