View Full Version : Mathematics for flying through a 3d space
Lunatic
2006.05.15, 07:58 PM
I've set myself a task to try and replicate the flight model found in space fighting games. I had a basic flight model working over a terrain, however in space things are a bit more complicated, especially if you take into consideration the Z rotation of the player. (Which some space fighters dont seem to - I've seen it working in some games though)
I was hoping that someone else has succeeded because I'm having a few troubles. I'd love some advice, pointers to algorithms, tutorials or source code for examples.
My overall design basically tracks the players x,y,z location and their x,y,z angle/rotation. Is this how others do it?
Current algorithm/concept:
// Take the movement of the trackpad (DeltaHoriz and DeltaVert) to generate the change in X and Y Angles
deltaY = deltaHoriz*cos(Zrot * PI/180) + deltaVert*sin(Zrot * PI/180);
deltaX = deltaVert*cos(Zrot * PI/180) + deltaHoriz*sin(Zrot * PI/180);
(I know this is wrong, but its close and it works in some situations)
//Code to set the X and Y rotation, and cope with the transition over 360 degrees.
// Then theres the basic movement code to move the player based on their rotations (the boolean differentiates between forward and back which may be removed in future - who's ever heard of a spaceship flying backwards? :) )
if (Direction == true)
{
newX = X + sin(Yrot * PI/180)*BASE_SPEED*Ind_SpeedModifier;
newZ = Z - cos(Yrot * PI/180)*BASE_SPEED*Ind_SpeedModifier;
newY = Y - sin(Xrot * PI/180)*BASE_SPEED*Ind_SpeedModifier;
}
else
{
newX = X - sin(Yrot * PI/180)*BASE_SPEED*Ind_SpeedModifier;
newZ = Z + cos(Yrot * PI/180)*BASE_SPEED*Ind_SpeedModifier;
newY = Y + sin(Xrot * PI/180)*BASE_SPEED*Ind_SpeedModifier;
}
It's rather annoying - The trackpad code may work when the Zrot is up, but may be flipped at 90 degrees and fine when at 180 etc. So Im sure theres something wrong with my trackpad code's trig.
OneSadCookie
2006.05.15, 08:15 PM
You want Quaternions. Googling will find you many, many explanations, most of which are completely uninteligible.
This one:
http://sacredsoftware.net/tutorials/Quaternions/Quaternions.xhtml
was written by our resident tutorial writer "ThemsAllTook" -- if you have problems with it, he'll probably be around to help, at least ;)
Lunatic
2006.05.15, 11:34 PM
I thought I'd add to this topic as I learn things in case others want to do something similar.
I found this page useful: http://www.ogre3d.org/wiki/index.php/Quaternion_and_Rotation_Primer (Quaternion and Rotation Primer)
Correct me if I'm wrong, Im still grasping on how to apply this math to practical application: From what this tells me, is that I can use quaternions to choose an arbitrary vector and rotate on that vector (as opposed to only x y z). So what I need to do is store where the player is (in a vector), where they are pointing (in a quaternion?) and create a combined x, y and z quaternion for where they want to point. Using this quaternion create an opengl rotation matrix and then rotate the world?
OneSadCookie
2006.05.15, 11:43 PM
Sounds right.
Store where the player is (vector), their orientation (quaternion). Convert the inputs from Euler angles to quaternions, and multiply quaternions to get a new orientation. Convert the quaternion to a matrix for use with OpenGL.
isgoed
2006.05.16, 05:49 PM
I use the math functions in quesa. www.quesa.org. You can just rip out (with some fixing of the source files) their quaternion functions and they are very complete.
Lunatic
2006.05.16, 10:01 PM
Vector temp;
temp.x = 1;
temp.y = 0;
temp.z = 0;
Quaternion ChangeinX = QuaternionFromAxisAngle(temp, deltaVert);
temp.x = 0;
temp.y = 1;
temp.z = 0;
Quaternion ChangeinY = QuaternionFromAxisAngle(temp, deltaHoriz);
Quaternion NewOrient = QuaternionMultiply(&ChangeinX, &ChangeinY);
Orientation = QuaternionMultiply( &NewOrient, &Orientation);
I applied it as simply as I could but it doesnt seem to work. I did some cout debug statements after each, and it looks like the ChanginX, ChanginY are being created ok, NewOrient is being created, but when I look at the values for Orientation its always 0. Any ideas? Have I buggered it up?
(Orientation being created/defined elsewhere)
Lunatic
2006.05.16, 10:07 PM
addendum: I noticed where I initially set up Orientation, it was set to all zeros. if I put in any numbers the multiplication starts to work. Its wild and erratic, but at least its moving the screen.
OneSadCookie
2006.05.16, 10:55 PM
all zeroes is not a unit quaternion. The identity quaternion (no change in orientation) has 1 in the w component, and 0s in x, y and z.
Lunatic
2006.05.16, 11:54 PM
I'm getting closer:
Once I started converting the trackpad input into radians it started to actually resemble a proper "looking around" function.
There is one strange problem though. As I look around there is an ever so slight change to the z rotation.
e.g. (The debug ouput of when I moved 1 degree on X and 1 degree on Y)
(X Y Z W)
ChangeinX: 0.00872654 0 0 0.999962
ChangeinY: 0 0.00872654 0 0.999962
NewOrient: 0.0087262 0.0087262 7.61524e-05 0.999924
Orientation: 0.0087262 0.0087262 7.61524e-05 0.999924
Since Im still in the dark on how quaternions work (im getting there slowly), I was wondering why the Z was changed (7.61524e-05) ever so slightly.
(Im using the quaternion/vector code from the tutorial OneSadCookie linked to )
(Apologies for the stupid questions)
AnotherJake
2006.05.17, 12:07 AM
Since Im still in the dark on how quaternions work (im getting there slowly), I was wondering why the Z was changed (7.61524e-05) ever so slightly.
A true understanding of how quaternions work in the extra dimension is past my comprehension at this point. Most of us just accept the techniques and math as a black box and go with it.
I admit that I didn't read very carefully what you are describing, but floating point drift over time is pretty common in my experience with quaternions. Depending on what you're trying to do, you can periodically reset them to synch with correct values.
Lunatic
2006.05.17, 03:10 AM
deltaVert = deltaVert * PI/180;
deltaHoriz = deltaHoriz * PI/180;
Quaternion NewOrient;
Quaternion ChangeinX;
Quaternion ChangeinY;
NewOrient.w = 1;
ChangeinY.w = 1;
ChangeinX.w = 1;
Vector temp;
temp.x = 1;
temp.y = 0;
temp.z = 0;
ChangeinX = QuaternionFromAxisAngle(temp, deltaVert);
NewOrient = QuaternionMultiply(&NewOrient, &ChangeinX);
temp.x = 0;
temp.y = 1;
temp.z = 0;
ChangeinY = QuaternionFromAxisAngle(temp, deltaHoriz);
NewOrient = QuaternionMultiply( &NewOrient, &ChangeinY);
NewOrient.z = 0;
Orientation = QuaternionMultiply( &Orientation, &NewOrient);
Orientation.z = 0;
So. This is where I am now.
I create the X Quat, and multiply it with the New Orientation.
I create the Y Quat, and multiply it with the New Orientation.
I then multiply the New Orientation with the Old Orientation.
This seems to go ok, (i.e. I can look around) although Im about to lose the rest of my hair in frustration since I still seem to get the gimble lock effect - I was really hoping that Quaternions would make it so that no matter which way I was pointing, I could move the mouse up/down/left/right and the world would move accordingly :P
ThemsAllTook
2006.05.17, 10:48 AM
Are you trying to maintain a consistent up vector, or do you want the player to be able to rotate freely on any axis, even if they end up upside-down?
I'm not quite sure what you're trying to accomplish by setting the Z component in NewOrient and Orientation to 0. That seems a bit dodgy. Also, if these are structs (as opposed to C++ objects with constructors that initialize everything to 0), you'll need to set the X, Y, and Z components of NewOrient, ChangeInX, and ChangeInY to 0 after you declare them. If you don't, you run the risk that they'll contain random memory garbage at runtime.
This seems to go ok, (i.e. I can look around) although Im about to lose the rest of my hair in frustration since I still seem to get the gimble lock effect - I was really hoping that Quaternions would make it so that no matter which way I was pointing, I could move the mouse up/down/left/right and the world would move accordingly :P
Don't give up hope yet - Quaternions do exhibit gimbal lock when used in certain ways, but used properly, they don't. It can take a little while to wrap your head around how to use them properly, but it's worth it when you get there. :)
Lunatic
2006.05.17, 11:21 AM
They're just structs at the moment, nothing too fancy. Just want to get the simple method working first.
This code is meant to handle simple mouse look. I.e. If the space ship is looking in any direction, you still should be able to look left/right/up/down. I eventually want to incorporate twisting along the Z axis once I get the mouselook working.
The setting of Z to 0 was to counter the strange increments and decrements to the Z axis that were happening when I was multiplying the Quaternians together.
You say that they do exhibit the lock effect in certain ways - is there some sort of set of rules I can use to avoid it? What can I do better to use them "properly"? :) Am I on the right track at least?
ThemsAllTook
2006.05.17, 01:50 PM
You certainly seem to be on the right track. You'll usually only get gimbal lock if you try to use quaternions the same way as euler angles; IE having one quaternion each for pitch, yaw, and roll. I've actually seen people try to do this.
Looking over my own mouselook code, it looks like I rotate first on the Y axis, then on the X axis. Try multiplying NewOrient by ChangeInY before ChangeInX, and see if it helps? Also, I'd suggest commenting out NewOrient.z = 0; and Orientation.z = 0; to make sure they're not having unexpected side-effects.
Lunatic
2006.05.17, 09:43 PM
Once more I am closer...
The mouse look seems to be avoiding any locking effect which is good. However, one last pain exists!
There is something wierd about how the angles are being calculated that causes a Z rotation effect, without there being any Z rotation. Let me explain better..
If I perform little circles in the centre of the track pad, the screen moves around in a circle but also seems to rotate around the Z axis. The debug output of all the quaternions indicates no Z component value for any - yet somehow I can turn upside down :)
I did take on board the comments about applying the Y before the X, I also changed the order of the mulitplication parameters. There was also some fixing of the model view and projection matrixes, I had them in the "wrong order".
monteboyd
2006.05.17, 10:24 PM
I haven't read this entire thread yet but I had a similar problem with flipping upside down and discovered I wasn't applying transformations to the camera's up vector properly.
ThemsAllTook
2006.05.18, 12:15 AM
If you think about it, it actually makes some amount of sense that moving the mouse in a circle would result in your camera facing the same direction, but with a different Z axis rotation. Since this is usually undesirable, though, there's a reasonably simple solution. Before you compute ChangeinY from temp and deltaHoriz, multiply temp (your up vector) by the inverse of your current orientation.
Something like this:
Quaternion InverseOrientation;
...
temp.x = 0;
temp.y = 1;
temp.z = 0;
InverseOrientation = Orientation;
QuaternionInvert(&InverseOrientation);
temp = QuaternionMultiplyVector(&InverseOrientation, &temp);
ChangeinY = QuaternionFromAxisAngle(temp, deltaHoriz);
...
Hope this helps!
Lunatic
2006.05.18, 12:50 AM
I tried the Inverse trick. This seemed to make the slight z rotations worse.
I also tried to multiply temp by the Orientation, and that fixed the mouselook BUT re-introduced the gimble lock! :(
I.e. its perfect movement, unless I look straight down/up and the left/right action becomes a rotation around the y axis. (Also when I turn completely upside down)
So Im still rather stuck. Could there be anything else that would effect it? i.e. model view matrix, etc.
unknown
2006.05.18, 07:11 AM
if you are getting undesired rotation on the z axis one thing you could do is create a quaternion from an axis angle pair, where the axis is z and the angle is that between the world up vector and your local up vector.
dividing your orientation quaternion by this should give you a new orientation to use which will have the correct up vector.
edit: please note I could be wrong, I haven't tried this its just a guess. :)
NCarter
2006.05.18, 10:26 AM
You can't both maintain a constant up vector and eliminate gimbal lock. The two things are contradictory.
In my game Rescue (http://www.nether.org.uk/bad_mac_code.html#rescue) (feel free to look at and use my source code), I use the cursor keys to pitch and yaw, and A and D to roll. This allows the player to rotate the ship to any orientation easily. I'm also assuming that beginners might feel that this is too many controls, and for them, it's entirely reasonable to play the game only using the cursor keys. As you say, this means you can end up 'upside down' if you rotate in certain ways, but it doesn't really matter... after all, you're in space, there is no 'up'. ;)
The roll keys are really just there so that players who care about such things can use them to roll the ship so that it 'feels' the right way up.
Also...
The setting of Z to 0 was to counter the strange increments and decrements to the Z axis that were happening when I was multiplying the Quaternians together.
This is just a numeric accuracy limitation, and it isn't something you should worry too much about. Especially, there's no need to clamp individual values to zero when they're not quite zero.
Instead, you can normalise the quaternion's length periodically to stop it from drifting too far out of range. Use ThemsAllTook's QuaternionNormalize() function for this purpose.
vBulletin® v3.6.8, Copyright ©2000-2008, Jelsoft Enterprises Ltd.