rotate around own axis - Printable Version +- iDevGames Forums (http://www.idevgames.com/forums) +-- Forum: Development Zone (/forum-3.html) +--- Forum: Game Programming Fundamentals (/forum-7.html) +--- Thread: rotate around own axis (/thread-2371.html) Pages: 1 2 3 rotate around own axis - wonza - Sep 24, 2008 10:12 AM 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! rotate around own axis - ThemsAllTook - Sep 24, 2008 12:07 PM 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/Quaternions/Quaternions.xhtml http://www.sacredsoftware.net/tutorials/Matrices/Matrices.xhtml rotate around own axis - AnotherJake - Sep 24, 2008 07:04 PM Yes, sounds like you need to use quaternions. If you already are, don't forget to normalize them regularly. rotate around own axis - wonza - Sep 25, 2008 08:15 AM 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); rotate around own axis - AnotherJake - Sep 25, 2008 08:28 AM 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; }``` rotate around own axis - wonza - Sep 27, 2008 07:50 AM 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!! rotate around own axis - AnotherJake - Sep 27, 2008 08:25 AM 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); rotate around own axis - wonza - Sep 27, 2008 08:46 AM 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. rotate around own axis - ThemsAllTook - Sep 27, 2008 08:51 AM 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);``` rotate around own axis - AnotherJake - Sep 27, 2008 09:18 AM 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); }``` rotate around own axis - wonza - Sep 27, 2008 10:04 AM 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. rotate around own axis - AnotherJake - Sep 27, 2008 10:36 AM 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; }``` rotate around own axis - ThemsAllTook - Sep 27, 2008 10:55 AM 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]). rotate around own axis - wonza - Sep 27, 2008 11:03 AM 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. rotate around own axis - AnotherJake - Sep 27, 2008 11:05 AM Sounds like you didn't list the function declaration.