rotate around own axis

Member
Posts: 94
Joined: 2008.08
Post: #1
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!
Quote this message in a reply
Moderator
Posts: 1,560
Joined: 2003.10
Post: #2
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
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #3
Yes, sounds like you need to use quaternions. If you already are, don't forget to normalize them regularly.
Quote this message in a reply
Member
Posts: 94
Joined: 2008.08
Post: #4
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);
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #5
There are different approaches. Here's one:
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        1e-6f

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;
}
Quote this message in a reply
Member
Posts: 94
Joined: 2008.08
Post: #6
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!!
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #7
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);
Quote this message in a reply
Member
Posts: 94
Joined: 2008.08
Post: #8
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. Sad
Quote this message in a reply
Moderator
Posts: 1,560
Joined: 2003.10
Post: #9
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 e-mail exchange I had a while back trying to explain this to someone:

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);
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #10
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. Sad

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);
}
Quote this message in a reply
Member
Posts: 94
Joined: 2008.08
Post: #11
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.
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #12
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 straight-forward way to use quaternions.

I don't know if this will add to your confusion or not, but here's some pseudo-code 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;
}
Quote this message in a reply
Moderator
Posts: 1,560
Joined: 2003.10
Post: #13
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;
};
...and returns one of these:
Code:
struct Matrix {
    float m[16];
};
You might want to check that what you're assigning to is a struct Matrix and not something else (like struct Matrix * or float[16]).
Quote this message in a reply
Member
Posts: 94
Joined: 2008.08
Post: #14
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.
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #15
Sounds like you didn't list the function declaration.
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Seperating Axis Theorem Code mikey 5 4,603 Oct 9, 2010 03:25 PM
Last Post: Oddity007
  Separating Axis Collision Detection Joseph Duchesne 4 4,081 Dec 22, 2005 10:18 AM
Last Post: Leisure Suit Lurie
  Separation axis collision detection SOUR-Monkey 5 5,935 Mar 24, 2005 01:23 AM
Last Post: DoG