rotate around own axis
Hi there,
Im trying to create an space shooter style game in OpenGL/objectC. Ive not done much opengl before so you'll have to forgive me. Ive created a virtual camera where i move objects in the universe around the central point to make it appear im moving. ive also got it working so that i move in the direction that im pointing. however, say if i move upwards and then look down and try to rotate around the y axis the object in front of me kinda wobbles. I think this is because its rotating me around the real y axis rather than the y axis of where i am now facing.
Is there any easy way to overcome this problem?
Many thanks!
Im trying to create an space shooter style game in OpenGL/objectC. Ive not done much opengl before so you'll have to forgive me. Ive created a virtual camera where i move objects in the universe around the central point to make it appear im moving. ive also got it working so that i move in the direction that im pointing. however, say if i move upwards and then look down and try to rotate around the y axis the object in front of me kinda wobbles. I think this is because its rotating me around the real y axis rather than the y axis of where i am now facing.
Is there any easy way to overcome this problem?
Many thanks!
3D rotations are a pretty complex subject. The answer will depend on how you're representing/applying your rotations. Are you keeping a quaternion or a matrix that accumulates rotations as you move your camera, or doing something else?
These may be helpful:
http://www.sacredsoftware.net/tutorials/...ions.xhtml
http://www.sacredsoftware.net/tutorials/...ices.xhtml
These may be helpful:
http://www.sacredsoftware.net/tutorials/...ions.xhtml
http://www.sacredsoftware.net/tutorials/...ices.xhtml
Yes, sounds like you need to use quaternions. If you already are, don't forget to normalize them regularly.
i think thats exactly what i need, thanks! those tutorials were great too.
i was using individual float variables for the x, y, z position and the x,y,z angles too.
I think I just about understand vectors and quaternions now, which is great. Im just not sure how to apply this to my code. Ie how to do the equivenlent of what im doing now which is:
glRotatef(vax, 1.0f, 0.0f, 0.0f);
glRotatef(vay, 0.0f, 1.0f, 0.0f);
i was using individual float variables for the x, y, z position and the x,y,z angles too.
I think I just about understand vectors and quaternions now, which is great. Im just not sure how to apply this to my code. Ie how to do the equivenlent of what im doing now which is:
glRotatef(vax, 1.0f, 0.0f, 0.0f);
glRotatef(vay, 0.0f, 1.0f, 0.0f);
There are different approaches. Here's one:
You can also get a matrix from your quat and do a glMultMatrix.
[EDIT] Here's my AngleAxisFromQuat function:
Code:
GLfloat angleAxis[4];
AngleAxisFromQuat(angleAxis, myQuat);
glRotatef(angleAxis[0], angleAxis[1], angleAxis[2], angleAxis[3]);
You can also get a matrix from your quat and do a glMultMatrix.
[EDIT] Here's my AngleAxisFromQuat function:
Code:
#define RAD_TO_DEG 57.295779513082f
#define EPSILON 1e6f
void AngleAxisFromQuat(GLfloat *angleAxis, GLfloat *quat)
{
GLfloat sinTheta;
NormalizeQuat(quat);
sinTheta = 1.0f / sinf((sqrt(1.0f  (quat[3] * quat[3]))));
angleAxis[0] = acos(quat[3]) * 2.0f * RAD_TO_DEG;
angleAxis[1] = quat[0] * sinTheta;
angleAxis[2] = quat[1] * sinTheta;
angleAxis[3] = quat[2] * sinTheta;
}
thanks for your replies. I think Im still getting pretty confused about this (sorry for being dumb). It might help to show you the code im using:
if (thrust > 0.0f) {
vsx +=cos((vay+275)/RAD_TO_DEG);
vsy +=cos((vax+90)/RAD_TO_DEG);
vsz +=cos((vay+180)/RAD_TO_DEG);
}
viewPosition.x+=vsx;
viewPosition.y+=vsy;
viewPosition.z+=vsz;
glRotatef(vax, 1.0f, 0.0f, 0.0f);
glRotatef(vay, 0.0f, 1.0f, 0.0f);
glRotatef(vaz, 0.0f, 0.0f, 1.0f);
glTranslatef(viewPosition.x, viewPosition.y, viewPosition.z);
My angles are changed based on where i click in the screen.
From the tutorials, I think I need to create a direction vector about which to rotate and then get the quaternion from this, and then get the x,y,z from this to then pass into the glRotate function. If Im right about any of that, then how should i work out the direction vector to rotate around from the 3 angles i have? Also, what angle should I be passing into the quaternion function? or would that be the difference in angles of where i was pointing before to where im wanting to point now?
Thanks for your patience!!
if (thrust > 0.0f) {
vsx +=cos((vay+275)/RAD_TO_DEG);
vsy +=cos((vax+90)/RAD_TO_DEG);
vsz +=cos((vay+180)/RAD_TO_DEG);
}
viewPosition.x+=vsx;
viewPosition.y+=vsy;
viewPosition.z+=vsz;
glRotatef(vax, 1.0f, 0.0f, 0.0f);
glRotatef(vay, 0.0f, 1.0f, 0.0f);
glRotatef(vaz, 0.0f, 0.0f, 1.0f);
glTranslatef(viewPosition.x, viewPosition.y, viewPosition.z);
My angles are changed based on where i click in the screen.
From the tutorials, I think I need to create a direction vector about which to rotate and then get the quaternion from this, and then get the x,y,z from this to then pass into the glRotate function. If Im right about any of that, then how should i work out the direction vector to rotate around from the 3 angles i have? Also, what angle should I be passing into the quaternion function? or would that be the difference in angles of where i was pointing before to where im wanting to point now?
Thanks for your patience!!
Sounds like you want to do something similar to Apple's Cocoa OpenGL sample. In particular, check out trackball.c. That sample uses quaternions to rotate a cube based on where the user clicks and drags on the screen.
BTW, instead of dividing by RAD_TO_DEG, you should multiply by DEG_TO_RAD, so it's less confusing, and possibly a little faster doing a multiply instead of divide (I think).
#define DEG_TO_RAD 0.0174532925199f
cos((vay+275) * DEG_TO_RAD);
BTW, instead of dividing by RAD_TO_DEG, you should multiply by DEG_TO_RAD, so it's less confusing, and possibly a little faster doing a multiply instead of divide (I think).
#define DEG_TO_RAD 0.0174532925199f
cos((vay+275) * DEG_TO_RAD);
thanks, ive just had a look at that app, and it seems to be using gluLookAt, which would be ideal, but im using opengl es on the iPhone which doesnt have that function.
Ah, it looks like you've fallen into the same pitfall almost everyone does when they're first learning about this stuff. Euler angles (using one rotation angle for X, one for Y, and one for Z) basically don't work no matter what you do. From an email exchange I had a while back trying to explain this to someone:
As for gluLookAt, you could substitute something like this, where you'd be keeping an orientation quaternion and a position vector (see my tutorial for a Quaternion_toMatrix implementaton):
Quote:I understand your confusion. This is exactly the same difficulty I ran into when trying to understand the difference between rotations with quaternions or matrices and rotations with Euler angles. The main difference between doing them one at a time and doing all three at once is the order of operations. Let's see if I can demonstrate with a simple example:
Starting state: Object is at the default orientation.
Local X axis: {1, 0, 0}
Local Y axis: {0, 1, 0}
Local Z axis: {0, 0, 1}
Object is rotated 90 degrees on the Y axis.
Local X axis: {0, 0, 1}
Local Y axis: {0, 1, 0}
Local Z axis: {1, 0, 0}
Now, once I've done that, I want to rotate 90 degrees on the local X axis, which now points at the world's Z axis. So, what I want is this result:
Local X axis: {0, 0, 1}
Local Y axis: {1, 0, 0}
Local Z axis: {0, 1, 0}
Let's look at what actually happens when we apply this rotation as using Euler angles:
X = {1, 0, 0}; Y = {0, 1, 0}; Z = {0, 0, 1}
Apply X axis rotation of 90 degrees.
X = {1, 0, 0}; Y = {0, 0, 1}; Z = {0, 1, 0}
Apply Y axis rotation of 90 degrees.
X = {0, 1, 0}; Y = {0, 0, 1}; Z = {1, 0, 0}
(We'd apply the Z axis rotation here too, but since it's 0, we can skip this step in the example)
If you compare the final rotated values to our expected result in the above example, you'll see that they don't match. What happened here? Well, since we applied our new X axis rotation after we'd already applied our Y axis rotation, we expected it to be on the local X axis, which at the time was {0, 0, 1}. This isn't what happens, though: Since X is applied before Y, we actually end up rotating on the WORLD X axis of {1, 0, 0}! The only way out of this is to rearrange the order of our rotation operations. It might be possible to write the Euler rotation function to rearrange itself so that the most recently rotated axis is the last one applied, but this would involve recalculating the other two axes' values to something that gives the same rotation result when applied in a completely different order, which is a nontrivial problem to solve in code. A much easier thing to do is to only apply one axis's rotation at a time, and apply that to the current cumulative rotation rather than starting from a blank slate.
As for gluLookAt, you could substitute something like this, where you'd be keeping an orientation quaternion and a position vector (see my tutorial for a Quaternion_toMatrix implementaton):
Code:
Matrix matrix;
matrix = Quaternion_toMatrix(orientation);
glMultMatrixf(matrix.m);
glTranslatef(position.x, position.y, position.z);
wonza Wrote:thanks, ive just had a look at that app, and it seems to be using gluLookAt, which would be ideal, but im using opengl es on the iPhone which doesnt have that function.
It's just a UVN rotate and translate. I constructed (er, borrowed) this from the Mesa source. (I haven't tested it much (definitely not on iPhone yet), but it is known to have worked for me in the past).
Code:
void normalize(GLfloat *v)
{
double length;
double lengthInverse;
length = sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
lengthInverse = 1.0f / length;
v[0] *= lengthInverse;
v[1] *= lengthInverse;
v[2] *= lengthInverse;
}
void cross(GLfloat *v1, GLfloat *v2, GLfloat *vProd)
{
vProd[0] = v1[1] * v2[2]  v2[1] * v1[2];
vProd[1] = v2[0] * v1[2]  v1[0] * v2[2];
vProd[2] = v1[0] * v2[1]  v2[0] * v1[1];
}
void myGluLookAt(GLfloat eyeX, GLfloat eyeY, GLfloat eyeZ,
GLfloat centerX, GLfloat centerY, GLfloat centerZ,
GLfloat upX, GLfloat upY, GLfloat upZ)
{
GLfloat forward[3], side[3], up[3];
GLfloat m[4][4] = { 1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f };
forward[0] = centerX  eyeX;
forward[1] = centerY  eyeY;
forward[2] = centerZ  eyeZ;
normalize(forward);
up[0] = upX;
up[1] = upY;
up[2] = upZ;
cross(forward, up, side);
normalize(side);
cross(side, forward, up);
m[0][0] = side[0];
m[1][0] = side[1];
m[2][0] = side[2];
m[0][1] = up[0];
m[1][1] = up[1];
m[2][1] = up[2];
m[0][2] = forward[0];
m[1][2] = forward[1];
m[2][2] = forward[2];
glMultMatrixf(&m[0][0]);
glTranslatef(eyeX, eyeY, eyeZ);
}
The custom gluLookAt function seemed to work great thanks!
Ive realised Im still going to need the quat functionality though, as ill need to rotate other ships around their own axis too.
I've tried the Quaternion_toMatrix(orientation) but seem to be getting a strange compilation error: "incompatible types in assignment", Im passing in a Quaternion and that should be what its expecting. Its annoying when silly errors get in the way.
Ive realised Im still going to need the quat functionality though, as ill need to rotate other ships around their own axis too.
I've tried the Quaternion_toMatrix(orientation) but seem to be getting a strange compilation error: "incompatible types in assignment", Im passing in a Quaternion and that should be what its expecting. Its annoying when silly errors get in the way.
It would be a good idea to completely tear apart trackball.c to fully understand how they're using quaternions there. The reason I mention this is that if you do, they don't use matrices, but rather angle axis conversions and glRotatef to get to and from quaternions. It is a very simple and straightforward way to use quaternions.
I don't know if this will add to your confusion or not, but here's some pseudocode for one way to implement quaternion rotations. It appears to be similar to how they're doing it in trackball.c:
Here are the remaining functions (I already listed AngleAxisFromQuat earlier in this thread):
I don't know if this will add to your confusion or not, but here's some pseudocode for one way to implement quaternion rotations. It appears to be similar to how they're doing it in trackball.c:
Code:
GLfloat myQuat[4];
GLfloat deltaQuatX[4];
GLfloat deltaQuatY[4];
GLfloat deltaQuatZ[4];
// init
EulerToQuat(startingXRot, startingYRot, startingZRot, myQuat);
// each update (or only once during init if you aren't changing rotational velocities)
EulerToQuat(deltaXRot, 0.0f, 0.0f, deltaQuatX);
EulerToQuat(0.0f, deltaYRot, 0.0f, deltaQuatY);
EulerToQuat(0.0f, 0.0f, deltaZRot, deltaQuatZ);
// each update
AddDeltaQuatToQuat(deltaQuatX, myQuat);
AddDeltaQuatToQuat(deltaQuatY, myQuat);
AddDeltaQuatToQuat(deltaQuatZ, myQuat);
NormalizeQuat(myQuat); // < doesn't necessarily have to be done each update
// each draw
GLfloat angleAxis[4];
AngleAxisFromQuat(angleAxis, myQuat);
glRotatef(angleAxis[0], angleAxis[1], angleAxis[2], angleAxis[3]);
Here are the remaining functions (I already listed AngleAxisFromQuat earlier in this thread):
Code:
void EulerToQuat(GLfloat pitch, GLfloat yaw, GLfloat roll, GLfloat *quat)
{
GLfloat rx, ry, rz, tx, ty, tz, cx, cy, cz, sx, sy, sz, cc, cs, sc, ss;
rx = pitch * DEG_TO_RAD;
ry = yaw * DEG_TO_RAD;
rz = roll * DEG_TO_RAD;
tx = rx * 0.5f;
ty = ry * 0.5f;
tz = rz * 0.5f;
cx = cos(tx);
cy = cos(ty);
cz = cos(tz);
sx = sin(tx);
sy = sin(ty);
sz = sin(tz);
cc = cx * cz;
cs = cx * sz;
sc = sx * cz;
ss = sx * sz;
quat[0] = (cy * sc)  (sy * cs);
quat[1] = (cy * ss) + (sy * cc);
quat[2] = (cy * cs)  (sy * sc);
quat[3] = (cy * cc) + (sy * ss);
}
void AddDeltaQuatToQuat(GLfloat *deltaQuat, GLfloat *quat)
{
GLfloat *q0, *q1, q3[4];
q0 = quat;
q1 = deltaQuat;
// q2 = q1 + q0 (quat multiplication, so order matters)
q3[0] = q1[1] * q0[2]  q1[2] * q0[1] + q1[3] * q0[0] + q1[0] * q0[3];
q3[1] = q1[2] * q0[0]  q1[0] * q0[2] + q1[3] * q0[1] + q1[1] * q0[3];
q3[2] = q1[0] * q0[1]  q1[1] * q0[0] + q1[3] * q0[2] + q1[2] * q0[3];
q3[3] = q1[3] * q0[3]  q1[0] * q0[0]  q1[1] * q0[1]  q1[2] * q0[2];
quat[0] = q3[0];
quat[1] = q3[1];
quat[2] = q3[2];
quat[3] = q3[3];
}
void NormalizeQuat(GLfloat *quat)
{
GLfloat distance, square;
square = quat[0] * quat[0]
+ quat[1] * quat[1]
+ quat[2] * quat[2]
+ quat[3] * quat[3];
if (square > 0.0f)
distance = 1.0f / sqrtf(square);
else distance = 1.0f;
quat[0] *= distance;
quat[2] *= distance;
quat[3] *= distance;
quat[3] *= distance;
}
wonza Wrote:I've tried the Quaternion_toMatrix(orientation) but seem to be getting a strange compilation error: "incompatible types in assignment", Im passing in a Quaternion and that should be what its expecting. Its annoying when silly errors get in the way.
That's odd. Well, since it's saying there are incompatible types in assignment, the problem is more likely what you're doing with the function's return value than its parameter. As defined in my example code, it takes one of these:
Code:
struct Quaternion {
float x;
float y;
float z;
float w;
};
Code:
struct Matrix {
float m[16];
};
its strange, i just moved the Quaternion_toMatrix function before my draw function and it worked. Thanks both again for all your help, I'll let you know how I got on.
Sounds like you didn't list the function declaration.
Possibly Related Threads...
Thread:  Author  Replies:  Views:  Last Post  
Seperating Axis Theorem Code  mikey  5  6,038 
Oct 9, 2010 03:25 PM Last Post: Oddity007 

Separating Axis Collision Detection  Joseph Duchesne  4  4,724 
Dec 22, 2005 10:18 AM Last Post: Leisure Suit Lurie 

Separation axis collision detection  SOURMonkey  5  6,893 
Mar 24, 2005 01:23 AM Last Post: DoG 