I am trying to prototype a space flight sim in OpenGL, but after reading many articles online I still have difficulty with getting the rotations to work correctly (I did have a quaternion camera that I didn't understand well, but it drifts and has other odd behaviors).
I am trying to do the following:
1) Local rotation - when the user presses arrow keys, rotation occurs relative to the viewport (rotating "up" is toward the top of the screen, for example). Two keys, such as Z and X, will control the "roll" of the ship (rotation around the current view).
2) The rotations will be stored in Axis-angle format (which is most natural for OpenGL and a single rotate call with the camera vector should rotate the scene properly). Therefore, given the initial Angle-axis vector, and one or more of the local rotations noted above (we could locally call "X" the left/right axis, "Y" the top/bottom axis, and "Z" the roll axis), I would like the end result to be a new Axis-angle vector.
3) Avoid quarternions and minimize the use of matrices (for some reason I find both unintuitive). Instead of matrix notation please just show in psuedocode the vector components and what's happening.
4) You should be able to rotate in a direction (using the arrow keys) 360 degrees and return to the starting view without drifting. Preferably, if the user presses one combination and then reverses it, they would expect to be able to return to near their original orientation.
5) The starting state for the camera is at coordinates (0,0,0) facing the Axis-angle vector (0,0,1,0 - z-axis with no starting rotation). "up" is (0,1,0).
Using Euler angles approach is wrong with spacesim. I have tried that approach and quickly had to give up. Player wants all degrees of freedom, and Euler's angles don't provide that, or complicate it enormously.
What you really, really want are quaternions. This is a part of my update code.
Quaternion qtmp1, qtmp2, qtmp3;
Rotation r(........);
qtmp1.CreateFromAxisAngle(1., 0., 0., r.j*m_updatediff);
qtmp2.CreateFromAxisAngle(0., 1., 0., r.i*m_updatediff);
qtmp3.CreateFromAxisAngle(0., 0., 1., r.k*m_updatediff);
m_rotq = qtmp1 * qtmp2 * qtmp3 * m_rotq;
r.i, r.j and r.k contain the current speed of rotation around a certain axis. Getting a spacesim-like feel is just a matter of multiplying these quaternions.
Everything else is just a complication. With Euler's angles, you can play all day long -- in fact, all year long -- but you will just make loads of messy code.
Your daily recommendation: quaternions.
Related
I need to make a function that will calculate the degrees necessary to make an NPC look at the center of the player. However, I have not been able to find any results regarding 3 dimensions which is what I need. Only 2 dimensional equations. I'm programming in C++.
Info:
Data Type: Float.
Vertical-Axis: 90 is looking straight up, -90 is looking straight down and 0 is looking straight ahead.
Horizontal-Axis: Positive value between 0 and 360, North is 0, East is 90, South 180, West 270.
See these transformation equations from Wikipedia. But note since you want "elevation" or "vertical-axis" to be zero on the xy-plane, you need to make the changes noted after "if theta measures elevation from the reference plane instead of inclination from the zenith".
First, find a vector from the NPC to the player to get the values x, y, z, where x is positive to the East, y is positive to the North, and z is positive upward.
Then you have:
float r = sqrtf(x*x+y*y+z*z);
float theta = asinf(z/r);
float phi = atan2f(x,y);
Or you might get better precision from replacing the first declaration with
float r = hypotf(hypotf(x,y), z);
Note acosf and atan2f return radians, not degrees. If you need degrees, start with:
theta *= 180./M_PI;
and theta is now your "vertical axis" angle.
Also, Wikipedia's phi = arctan(y/x) assumes an azimuth of zero at the positive x-axis and pi/2 at the positive y-axis. Since you want an azimuth of zero at the North direction and 90 at the East direction, I've switched to atan2f(x,y) (instead of the more common atan2f(y,x)). Also, atan2f returns a value from -pi to pi inclusive, but you want strictly positive values. So:
if (phi < 0) {
phi += 2*M_PI;
}
phi *= 180./M_PI;
and now phi is your desired "horizontal-axis" angle.
I'm not too familiar with math which involves rotation and 3d envionments, but couldn't you draw a line from your coordinates to the NPC's coordinates or vise versa and have a function approximate the proper rotation to that line until within a range of accepted +/-? This way it does this is by just increasing and decreasing the vertical and horizontal values until it falls into the range, it's just a matter of what causes to increase or decrease first and you could determine that based on the position state of the NPC. But I feel like this is the really lame way to go about it.
use 4x4 homogenous transform matrices instead of Euler angles for this. You create the matrix anyway so why not use it ...
create/use NPC transform matrix M
my bet is you got it somewhere near your mesh and you are using it for rendering. In case you use Euler angles you are doing a set of rotations and translation and the result is the M.
convert players GCS Cartesian position to NPC LCS Cartesian position
GCS means global coordinate system and LCS means local coordinate system. So is the position is 3D vector xyz = (x,y,z,1) the transformed position would be one of these (depending on conventions you use)
xyz'=M*xyz
xyz'=Inverse(M)*xyz
xyz'=Transpose(xyz*M)
xyz'=Transpose(xyz*Inverse(M))
either rotate by angle or construct new NPC matrix
You know your NPC's old coordinate system so you can extract X,Y,Z,O vectors from it. And now you just set the axis that is your viewing direction (usually -Z) to direction to player. That is easy
-Z = normalize( xyz' - (0,0,0) )
Z = -xyz' / |xyz'|
Now just exploit cross product and make the other axises perpendicular to Z again so:
X = cross(Y,Z)
Y = cross(Z,X)
And feed the vectors back to your NPC's matrix. This way is also much much easier to move the objects. Also to lock the side rotation you can set one of the vectors to Up prior to this.
If you still want to compute the rotation then it is:
ang = acos(dot(Z,-xyz')/(|Z|*|xyz'|))
axis = cross(Z,-xyz')
but to convert that into Euler angles is another story ...
With transform matrices you can easily make cool stuff like camera follow, easy computation between objects coordinate systems, easy physics motion simulations and much more.
TL;DR
I have a quaternion representing the orientation of a sphere (an Earth globe). From the quaternion I wish to derive a latitude/longitude. I can visualize in my mind the process, but am weak with the math (matrices/quaternions) and not much better with the code (still learning OpenGL/GLM). How can I achieve this? This is for use in OpenGL using c++ and the GLM library.
Long Version
I am making a mapping program based on a globe of the Earth - not unlike Google Earth, but for a customized purpose that GE cannot be adapted to.
I'm doing this in C++ using OpenGL with the GLM library.
I have successfully coded the sphere and am using a quaternion directly to represent it's orientation. No Euler angles involved. I can rotate the globe using mouse motions thus rotating the globe on arbitrary axes depending on the current viewpoint and orientation.
However, I would like to get a latitude and longitude of a point on the sphere, not only for the user, but for some internal program use as well.
I can visualize that this MUST be possible. Imagine a sphere in world space with no rotations applied. Assuming OpenGL's right hand rule, the north pole points up the Y axis with the equator parallel on the X/Z plane. The latitude/longitude up the Y axis is thus 90N and something else E/W (degenerate). The prime meridian would be on the +Z axis.
If the globe/sphere is rotated arbitrarily the globe's north pole is now somewhere else. This point can be mapped to a latitude/longitude of the original sphere before rotation. Imagine two overlaying spheres, one the globe which is rotated, and the other a fixed reference.
(Actually, it would be in reverse. The latitude/longitude I seek is the point on the rotated sphere that correlates to the north pole of the unrotated reference sphere)
In my mind it seems that somehow I should be able to get the vector of the Earth globe's orientation axis from it's quaternion and compare it to that of the unrotated sphere. But I just can't seem to grok how to do that. (I guess I still don't fully understand mats and quats and have only blundered into my success so far)
I'm hoping to achieve this without needing a crash course in the deep math. I'm looking for a solution/understanding/guidance from the point of view of being able to use the GLM library to achieve my goal. Ideally a code sample with some general explanation. I learn best from example.
FYI, in my code the rotation of the globe/sphere is totally independent of the camera (which does use Euler angles) so it can be moved independently. So I can't use anything from the camera to determine this.
Maybe you could try to follow that link (ie. use boost ;) ) from that thread Longitude / Latitude to quaternion and then deduct the inverse of that conversion.
Or you could also go add a step by converting your quaternion into Euler angle?
I'm using Opengl as the foundations for a small car simulation.
I've imported a model car and it drives around fine and the camera follows it. Unfortunately I can't seem to get the camera's position to move (or stay behind the car) when the car turns. It just rotates on the spot.
eg if the car turns left, the camera rotates to follow it but doesn't move to directly behind the car.
I've looked at these questions here and here but still can't seem to get it working.
This is how I calculate the position of the car (it just drives on the XZ plane for now):
velocity.x = sin(rotX_rad);
velocity.z = cos(rotX_rad);
globalPos.x + velocity.x*delta;
globalPos.z + velocity.z*delta;
glTranslatef(globalPos.x,globalPos.y,globalPos.z);
glRotatef(rotX, 0.0, 1.0, 0.0);
And this is currently how I have my lookAt function:
gluLookAt(camX+globalPos.x,
camY+globalPos.y,
camZ+globalPos.z,
lookX+globalPos.x+sin(rotX_rad),
lookY+globalPos.y+cos(rotX_rad),
lookZ+globalPos.z-cos(rotX_rad),
camNX, camNY, camNZ);
I would like the camera to be like this: (where Y is Z)
The way I see it, it should be:
camX + globalPos.x+ sin(rotX_rad)*(distFromCar),
camY,
camZ + globalPos.z- cos(rotX_rad)*(distFromCar),
but it behaves strangely....
What am I not doing?
Give this a shot, it makes sense in my head but it might not in reality :)
Ok, so we can represent your car's position and velocity as 2-element vectors (in the case of position it is a point, and in the case of velocity it is a true vector.)
Now that we have that, to calculate the position of the camera we can just take the negative of your velocity vector (this means just make all elements of the vector negative, yielding a vector of the same magnitude but exactly opposite direction) and add that vector to your car's position.
For example, say your car is at position (1,1) and your speed vector is (1,2). The negative speed vector would be (-1,-2) and the position of your camera would be (1,1) + (-1,-2) to be (0,-1). You will probably want to normalize the negative speed vector so that your camera stays a constant distance from the car, otherwise the faster you go the further the camera will get :)
Now that you have the camera's position, just call gluLookAt:
gluLookAt(camPos.x,camPos.y,camPos.z,carPos.x,carPos.y,carPos.z,0,1,0);
As you can see, we are passing in the camera's position (which we just calculated in the previous step) and telling the camera to look at the car. You may want to tweak some values (for example, have the camera be floating a few units off the ground, instead of directly behind the car, or maybe have the camera look at a point a few units above the car, etc.)
Let me know how this goes! Again, this is just something I thought might work for you, no guarantees that my math is right though :) But this should be faster and easier to understand than messing around with angles and such. One thing to keep in mind: this will not work if the car is completely stationary (no velocity vector)
Im trying to do a simple rotation of a cube about the x and y axis:
I want to always rotate the cube over the x axis by an amount x
and rotate the cube over the yaxis by an amount y independent of the x axis rotation
first i naively did :
glRotatef(x,1,0,0);
glRotatef(y,0,1,0);
then
but that first rotates over x then rotates over y
i want to rotate over the y independently of the x access.
I started looking into quaternions, so i tried :
Quaternion Rotation1;
Rotation1.createFromAxisAngle(0,1, 0, globalRotateY);
Rotation1.normalize();
Quaternion Rotation2;
Rotation2.createFromAxisAngle(1,0, 0, globalRotateX);
Rotation2.normalize();
GLfloat Matrix[16];
Quaternion q=Rotation2 * Rotation1;
q.createMatrix(Matrix);
glMultMatrixf(Matrix);
that just does almost exactly what was accomplished doing 2 consecutive glRotates ...so i think im missing a step or 2.
is quaternions the way to go or should i be using something different? AND if quaternions are the way to go what steps can i add to make the cube rotate independently of each axis.
i think someone else has the same issue:
Rotating OpenGL scene in 2 axes
I got this to work correctly using quaternions: Im sure there are other ways, but afeter some reseatch , this worked perfectly for me. I posted a similar version on another forum. http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=280859&#Post280859
first create the quaternion representation of the angles of change x/y
then each frame multiply the changing angles quaternions to an accumulating quaternion , then finally convert that quaternion to matrix form to multiply the current matrix. Here is the main code of the loop:
Quaternion3D Rotation1=Quaternion3DMakeWithAxisAndAngle(Vector3DMake(-1.0f,0,0), DEGREES_TO_RADIANS(globalRotateX));
Quaternion3DNormalize(&Rotation1);
Quaternion3D Rotation2=Quaternion3DMakeWithAxisAndAngle(Vector3DMake(0.0f,-1.0f,0), DEGREES_TO_RADIANS(globalRotateY));
Quaternion3DNormalize(&Rotation2);
Matrix3D Mat;
Matrix3DSetIdentity(Mat);
Quaternion3DMultiply(&QAccum, &Rotation1);
Quaternion3DMultiply(&QAccum, &Rotation2);
Matrix3DSetUsingQuaternion3D(Mat, QAccum);
globalRotateX=0;
globalRotateY=0;
glMultMatrixf(Mat);
then draw cube
It would help a lot if you could give a more detailed explanation of what you are trying to do and how the results you are getting differ from the results you want. But in general using Euler angles for rotation has some problems, as combining rotations can result in unintuitive behavior (and in the worst case losing a degree of freedom.)
Quaternion slerp might be the way to go for you if you can find a single axis and a single angle that represent the rotation you want. But doing successive rotations around the X and Y axis using quaternions won't help you avoid the problems inherent in composing Euler rotations.
The post you link to seems to involve another problem though. The poster seems to have been translating his object and then doing his rotations, when he should have been rotating first and then translating.
It is not clear what you want to achieve. Perhaps you should think about some points and where you want them to rotate to -- e.g. vertex (1,1,1) should map to (0,1,0). Then, from that information, you can calculate the required rotation.
Quaternions are generally used to interpolate between two rotational 'positions'. So step one is identifying your start and end 'positions', which you don't have yet. Once you have that, you use quaternions to interpolate. It doesn't sound like you have any time-varying aspect here.
Your problem is not the gimbal lock. And effectively, there is no reason why your quaternion version would work better than your matrix (glRotate) version because the quaternions you are using are mathematically identical to your rotation matrices.
If what you want is a mouse control, you probably want to check out arcballs.
i have an object in 3d space that i want to align according to a vector.
i already got the Y-rotation out by doing an atan2 on the x and z component of the vector. but i would also like to have an X-rotation to make the object look downwards or upwards.
imagine a plane that does it's pitch yaw roll, just without the roll.
i am using openGL to set the rotations so i will need an Y-angle and an X-angle.
I would not use Euler angles, but rather a Euler axis/angle. For that matter, this is what Opengl glRotate uses as input.
If all you want is to map a vector to another vector, there are an infinite number of rotations to do that. For the shortest one, (the one with the smallest angle of rotation), you can use the vector found by the cross product of your from and to unit vectors.
axis = from X to
from there, the angle of rotation can be found from from.to = cos(theta) (assuming unit vectors)
theta = arccos(from.to)
glRotate(axis, theta) will then transform from to to.
But as I said, this is only one of many rotations that can do the job. You need a full referencial to define better how you want the transform done.
You should use some form of quaternion interpolation (Spherical Linear Interpolation) to animate your object going from its current orientation to this new orientation.
If you store the orientations using Quaternions (vector space math), then you can get the shortest path between two orientations very easily. For a great article, please read Understanding Slerp, Then Not Using It.
If you use Euler angles, you will be subject to gimbal lock and some really weird edge cases.
Actually...take a look at this article. It describes Euler Angles which I believe is what you want here.