PDA

View Full Version : rotating around a fixed coordinate system



TJ22
01-19-2005, 05:47 AM
I have seen this question asked here many times but have not found a relative answer. I have a sphere which I rotate around the X an then the Y but I want to do more of a horizontal and vertical rotation. I basically want the axes to stay fixed. so when I rotate around the X xis it does not change the location of the Y axis. I found the following in an OpenGL FAQ but it is still unclear. Thanks in advance.

"9.070 How do I transform my objects around a fixed coordinate system rather than the object's local coordinate system?

If you rotate an object around its Y-axis, you'll find that the X- and Z-axes rotate with the object. A subsequent rotation around one of these axes rotates around the newly transformed axis and not the original axis. It's often desirable to perform transformations in a fixed coordinate system rather than the object’s local coordinate system.

The OpenGL Game Developer’s FAQ contains information on using quaternions to store rotations, which may be useful in solving this problem.

The root cause of the problem is that OpenGL matrix operations postmultiply onto the matrix stack, thus causing transformations to occur in object space. To affect screen space transformations, you need to premultiply. OpenGL doesn't provide a mode switch for the order of matrix multiplication, so you need to premultiply by hand. An application might implement this by retrieving the current matrix after each frame. The application multiplies new transformations for the next frame on top of an identity matrix and multiplies the accumulated current transformations (from the last frame) onto those transformations using glMultMatrix().

You need to be aware that retrieving the ModelView matrix once per frame might have a detrimental impact on your application’s performance. However, you need to benchmark this operation, because the performance will vary from one implementation to the next."

TJ22
01-20-2005, 04:48 AM
Anybody?

TJ22
01-21-2005, 04:39 AM
Please. Thanks.

Aeluned
01-21-2005, 06:33 AM
Did you try and implement what was suggested?

I'm not sure if this will work, but you can try the following (this is to rotate it about the world-y axis after a transformation):

after transforming the object multiply the point
(0,1,0) by the modelview matrix, call it point p.
now the vector defined by (0,0,0) and p define the vector describing the y-axis in the local coordinate system.
To rotate about the world y-axis use:
glRotatef(theta,-p.x,-p.y,-p.z);

TJ22
01-21-2005, 10:24 AM
Thanks but the problem is not rotating around an axis after translation. The problem is when I rotate around the Y glrotated(angle, 0, 1, 0), then I rotate around the X glrotated(angle, 1, 0, 0), the x axis has moved during the y rotation so rotation around the x is akward/incorrect.

I do not understand any of the posts on this subject, none of which seem to be resolved. It seems like it should be commom place and not this difficult.

Aeluned
01-21-2005, 10:55 AM
the method above isn't meant for translation, it should work given any rotation, in fact: i think translation will break it now that you mention it.

The idea was this:
after a rotation, your axes have also been rotated. I was finding the new vector of the y axis after rotation and then rotating around a vector that would have put the y-axis back correctly.

for example, suppose you rotate about the x-axis 45 degrees.
Your new y axis would be no longer defined by (0,1,0); it would be defined by (0,.5,.5).
Given this new coordinate system, rotating an object about the vector (0,-.5,-.5) should compensate for the shift in the y axis.

Like i said, I haven't coded it and it might not work...but it shouldn't take more than a couple of minutes to give it a whirl.

01-24-2005, 08:56 AM
I recently fixed this problem by multiplying all the points that I wanted to draw by the matrix of the appropiate rotation. For example, to rotate all the points c degrees in the x axis,
(x,y,z)=(x,cos(c)*y-sin(c)*z,sin(c)*y+cos(c)*z)
and to do the same in the y axis,
(x,y,z)=(cos(c)*x-sin(c)*z,y,sin(c)*x+cos(c)*z).

The problem is that this method only rotates the points, if you need some figure rotated (a cube, for example), you will have to rotate if after doing this.

dvm
01-24-2005, 12:49 PM
You can always invert the order with which you make your transformations. For example if you need to do a translation and then a rotation then you should first write the rotation and then the translation. Check out the red book on transformations, around the 2nd or 3rd chapter.

TJ22
01-25-2005, 04:54 AM
It seems only Aeluned knows my problem.

I am rotating a sphere doing x then y rotations
glrotated(45, 1, 0, 0)
glrotated(45, 0, 1, 0)

on the first rotation everything seems correct but upon 2nd rotation instead rotating around the vertical it rotates around a skewed axis. This is because the y axis moved with the first rotation.

http://users.adelphia.net/~tejj/axes.jpg

now if you rotate around the y axiz it is like rotating around the original Z.

Any other ideas?

Aeluned
01-25-2005, 05:18 AM
TJ22,
This is what I'm trying to tell you:
Look at your new y-axis after the (90 degree) rotation.
The vector defining your new y-axis is now (0,0,1). It just so happens that if you do a rotation about this axis now, it'll be a rotation on the vertical axis (see how your z-axis is now the vertical one?).
a little something like this:


glrotated(90, 1, 0, 0)
glrotated(90, 0, 0, 1)
that should give you what you're trying to do, albeit it's a simple case - you have an axis swap rather than offsetting the axes.

You need to multiply the vector defining the axis you want to rotate on by the modelview matrix after each rotation, giving you your new vector for the axis. Then rotate about the resulting vector.

TJ22
01-25-2005, 07:30 AM
I thought I had already posted this, but I tried what you had suggested already to no avail. The reason is as follows.

I am listening to the mouse and dependent on its directionI adjust the x or y axis

glrotated(angleX, 1, 0, 0)
glrotated(angleY, 0, cos(angleX), sin(angleX))

mouselistener
if xdir
angleX++
if ydir
angleY++

so if you move alot in the X dir there is a jump when you move in the Y. There is no smooth transition due to the angleX.

dvm
01-25-2005, 03:08 PM
Maybe you want to take a look at spherical (or polar) coordinates.

Also can you try reversing the transformations like

glrotated(angleY, 0, cos(angleX), sin(angleX))
glrotated(angleX, 1, 0, 0)What you describe is normal OpenGL behavior. Try to find a download for the red book (OpenGL Programming Guide) and read the chapter on transformations.

01-25-2005, 11:35 PM
Try with the method that I proposed. I had this very same problem, and I fixed it by moving all the points instead of using the glrotate. The problem is that in theory it takes more CPU time, but to tell the truth, since OpenGL has to multiply all the points per the GL_MODELVIEW matrix, then we are more or less in the same case. With the method that I proposed, all the three axis remain fixed in the same position, so you will no longer have the problem of having "movable" axis.

TJ22
01-26-2005, 06:29 AM
Plyskeen,

I am willing to try your solution but I am not sure I know/understand exactly how to do this in opengl. Could you provide some code?

Thanks in advance.

01-26-2005, 08:48 AM
Sure!

You need to save all the points in an array, say for example "points" [array of structure]
To move all the points an angle alpha in the x-axis:

for [i=0,i<=number_of_points;i++] {
y=points[i].y;
z=points[i].z;
points[i].y=cos[alpha]*y-sin[alpha]*z; points[i].z=sin[alpha]*y+cos[alpha]*z;
}

To move all the points an angle alpha in the y-axis:

for [i=0,i<=number_of_points;i++] {
x=points[i].x;
z=points[i].z;
points[i].x=cos[alpha]*x-sin[alpha]*z; points[i].z=sin[alpha]*x+cos[alpha]*z;
}

This is, using a structure like this:

typedef struct {
double x,y,z;
} XYZ;

and then

XYZ * points;

and when you know the number of points

points = [XYZ*] malloc [number_of_points]*sizeof[XYZ]];

Even then, this only moves the points of the system. If in each point you draw a cube, the cubes will rotate but everyone will face the user, no matter what rotation the central point has given. If you draw a sphere [and the point of rotation is not its centre] this should not be a problem [because unless you attach a texture, you are not going to see the facing].

I couldn't try this exact code because my structure is far more complex, but I think that it should work...

And I am using branquets [] instead of perenthesis because of some strange error while posting... :confused:

TJ22
01-27-2005, 06:10 AM
Actually I am using a texture.

01-27-2005, 06:41 AM
OK, then I must say that that method will not work.
And with the rotations will be the same, even if you try to change the order. Because:
if A is the rotate[angle_x] and
B is the rotate[angle_y],

if you make AB, the B will have the y-axis moved, the same for the BA, and if you try to undo the first rotation, i.e., B=A**-1BA, [A**-1 is the inverse of A]you get:
A[A**-1BA], which is the same as BA, and you have the same. So if anyone knows how to solve the problem, I am really interested too!!

dvm
01-28-2005, 10:10 AM
Ok, supposing that I've understood the problem, I hope the following example will glEnable(GL_LIGHTING) (shed some light :) )
Let's suppose we have a basic render function with glClear in the beginning and buffer swap at the end.
We'll draw a nice teapot.

glutSolidTeapot(1);The above code just draws a teapot in the center of the window (suppose a properly set frustum).
Now let's rotate and translate the model so that we see the teapot from the top, the teapot's brim (the point where tea pours out) is pointing downwards and the teapot is a bit to the right.
1st try: local object model

//Move the teapot to the right
glTranslatef(1.0, 0.0, 0.0);
//Rotate Teapot so that the brim is looking towards us
glRotatef(-90.0, 0.0, 1.0, 0.0);
//Rotate the pot so the brim is pointing downwards.
glRotatef(-90.0, 0.0, 0.0, 1.0);
// Draw the 'pot
glutSolidTeapot(1);In the above code I first translate the teapot a bit to the right, right? Then I rotate the teapot in its y axis so the brim is looking at me. Remember that before any transformations the teapot's x axis is running from the handle to the brim (we're looking at the teapot sideways). Now, to rotate the teapot so as the brim is looking down, I have to rotate the teapot's z-axis.

Ok, I hope you've got the above example. Now on to the interesting stuff. So you want to use a central coordinate system. Remember what I've said above? You need to reverse the order of the transformations. So you would want to rotate along the z-axis for the brim to look down (you still look at the teapot sideways). Then you would want to rotate the central y-axis so that you're looking the teapot from the top. And then you would translate the teapot to it's final position. If you reverse the aforementioned transformations you get:
2nd example

glTranslatef(1.0, 0.0, 0.0);
glRotatef(-90.0, 0.0, 1.0, 0.0);
glRotatef(-90.0, 0.0, 0.0, 1.0);
glutSolidTeapot(1);Are you noticing something? The code is the same. It's just the way of thinking that's changed.
Suppose we have another way of thinking to achieve the same result using the central axis system. I first want to make the brim look at me, then look down, then move to the right. By reversing we get:

glTranslatef(1.0, 0.0, 0.0);
glRotatef(90.0, 1.0, 0.0, 0.0);
glRotatef(-90.0, 0.0, 1.0, 0.0);
glutSolidTeapot(1);After this I hope I've showed you the way, if I've understood your problem. By the way can anyone else tell me if this makes sense? Is it simple enought to be used in a classroom? It's a difficult concept to explain and I've only recently understood it myself.

Late Note: If you don't want your next model to be affected by the transformations, include your first model between a glPushMatrix()-glPopMatrix() call and you should be ok. Another way to do this is by knowing which transformations took place and reverse them after drawing your previous model.

TJ22
01-28-2005, 11:18 AM
Thanks moucard, but I am not sure that you answered the question. Maybe this explanantion will clear up what I am trying to say.

Alright lets stick with the teapot. So you have rendered a teapot the the screen. I have mouse controls that control rotation. So when I click and move the mouse vertical on the screen I want the teapot to rotate around the horizontal which is initial the X axis. And when I click and move the mouse horizontally I want teapot to rotate around the vertical which initial is the Y axis.

glrotate(angleX, 1, 0, 0)
glrotate(angleY, 0, 1, 0)

When you do that rotation around the X, vertical mouse movement, say 90 degrees, you see the top of the teapot, handle and spot still right and left with the screen but from the top of the pot(which I really dont even care about).

The initial Y axis is now where the intial Z axis was and vice vera.

Now I want to do a rotation around the initial Y axis, horizontal mouse movement, but now when rotating around the Y, say 90 degrees, the handle and spout would point top and bottom of the screen. While what I would actual want is the handle and spout to point into and out of the screen.

I hope this clarifies.

dvm
01-29-2005, 02:30 AM
Ok, I've finally got it! I'm holding now... don't let go. :)
Anyway, I think you need to take a look at quaternions or polar coordinates. In essence it would be the same if you rotated your camera around the scene, instead of your models.
Another way to go would be to use spherical coordinates (a.k.a. polar coordinates). Here's some code dump from an old project of mine:

// camera's global variables
float g_afCameraPos[3] = { 0.0, 0.0, 1000.0 };
float g_afCameraLook[3] = { 0.0, 0.0, 0.0 };
float g_afCameraUp[3] = { 0.0, 1.0, 0.0 };
// Then on the keyboard handler...
switch (key) {
case GLUT_KEY_UP:
g_fPolarPhi -= c_fFactor * (M_PI / 180.0f);
break;
case GLUT_KEY_DOWN:
g_fPolarPhi += c_fFactor * (M_PI / 180.0f);
break;
case GLUT_KEY_LEFT:
g_fPolarTheta += c_fFactor * (M_PI / 180.0f);
break;
case GLUT_KEY_RIGHT:
g_fPolarTheta -= c_fFactor * (M_PI / 180.0f);
break;
case GLUT_KEY_PAGE_UP:
g_fPolarR -= c_fFactor * 50;
break;
case GLUT_KEY_PAGE_DOWN:
g_fPolarR += c_fFactor * 50;
break;
}
g_afCameraPos[0] = g_fPolarR * sinf(g_fPolarPhi) * sinf(g_fPolarTheta);
g_afCameraPos[1] = g_fPolarR * cosf(g_fPolarPhi);
g_afCameraPos[2] = g_fPolarR * sinf(g_fPolarPhi) * cosf(g_fPolarTheta);

// Then in render...
gluLookAt(g_afCameraPos[0], g_afCameraPos[1], g_afCameraPos[2],
g_afCameraLook[0], g_afCameraLook[1], g_afCameraLook[2],
g_afCameraUp[0], g_afCameraUp[1], g_afCameraUp[2]);Though there's a catch in the above code. If you move from the positive to the negative axis the display will invert. That's because the up-vector for the camera is still looking up towards (0, 1, 0), while it should point to (0, -1, 0). If I remember correctly it was not as simple as this though. The same would hold true for the other axes so things got complicated. If someone else knows how to detect where the up-vector should point please post. I'd like the answer as well. But the above code should give you a kick start. Just think of the keyboard arrows as vertical-horizontal mouse movement and page up/down as zoom in/out.
I don't know about quaternions though. Maybe with a bit more math they make things a lot simpler. Take a look at both.

TJ22
01-31-2005, 10:17 AM
what is the initial value of c_fFactor?

dvm
02-01-2005, 08:58 AM
2

02-02-2005, 04:21 AM
I tried with the spheric coordinates some time ago and I didn't solve anything (but I don't remember if I was making the change in the up vector). I would have to take a look at the quaternions.... A friend recommended me this webpage about it:

http://en.wikipedia.org/wiki/Qua

Nice story the one about Hamilton and the bridge... :)

02-03-2005, 09:12 AM
You rotate first on the X and then on the Y Axis and not visa versa:

//rotate automatically the sphere on it's Y axis
glTranslatef(0.0f,10.0f,40.0f);
glRotatef( rotateSphere , 0.0f, 1.0f , 0.0f);
glRotatef ((GLfloat)90.0, (GLfloat)1.0, (GLfloat)0.0, (GLfloat)0.0);
glScalef(5.0f, 5.0f, 5.0f);
gluSphere(q, 1.0f, 32, 16); // Draw Sphere

02-03-2005, 10:22 AM
never mind my last statement. It looks as your order of rotations is correct.

Maybe this will give you some ideas on rotating a sphere(without texture):/************************************************** ****
*
*
* OPEN GL
* GLOBE TROT VER 0.00001
*
************************************************** ******/

/*I N C L U D E S*/
#include <windows.h>
#include <GL/gl.h>
#include <GL/glu.h>

/*Defines*/
#define BLACK_INDEX 0 /*black for background*/
#define GREEN_INDEX 14 /*green for wireframe globe object*/
#define WIDTH 640 /*starting window pixel width*/
#define HEIGHT 480 /*starting window pixel height*/
#define GLOBE 1 /*The object to be rendered*/

/*Prototypes */
LONG WINAPI MainWndProc (HWND, UINT, WPARAM, LPARAM);
BOOL bSetupPixelFormat(HDC);
GLvoid resize(GLsizei, GLsizei);
GLvoid initializeGL(GLsizei, GLsizei);
GLvoid drawScene(GLvoid);
void polarView( GLdouble, GLdouble, GLdouble, GLdouble);

/*OpenGL globals*/
GLfloat latitude, longitude, latinc, longinc;
GLdouble radius;
CHAR szAppName[]="Win OpenGL";
HWND ghWnd; /* handle for our window */
HDC ghDC; /* handle for device context */
HGLRC ghRC; /* handle for render context */

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
WNDCLASS wndclass;

/* Register the frame class */
wndclass.style = 0;
wndclass.lpfnWndProc = (WNDPROC)MainWndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon (hInstance, szAppName);
wndclass.hCursor = LoadCursor (NULL,IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wndclass.lpszMenuName = szAppName;
wndclass.lpszClassName = szAppName;

if (!RegisterClass (&wndclass) )
return FALSE;

/* Create the frame */
ghWnd = CreateWindow (szAppName,
"Rotating OpenGL Globe",
WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
CW_USEDEFAULT,
CW_USEDEFAULT,
WIDTH,
HEIGHT,
NULL,
NULL,
hInstance,
NULL);

/* make sure window was created */
if (!ghWnd)
return FALSE;

/* show and update main window */
ShowWindow (ghWnd, nCmdShow);

UpdateWindow (ghWnd);

/* animation loop */
while (1)
{

while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE) == TRUE)
{
if (GetMessage(&msg, NULL, 0, 0) )
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
return TRUE;
}

}


drawScene(); // Call drawScene function

}

} // Close WinMain


/* main window procedure */
LONG WINAPI MainWndProc (
HWND hWnd, /*handle for our window */
UINT uMsg, /*messages*/
WPARAM wParam,
LPARAM lParam)
{
LONG lRet = 1;
PAINTSTRUCT ps;
RECT rect;

switch (uMsg) {

/*get device context with window´s handle*/
case WM_CREATE:
ghDC = GetDC(hWnd);
if (!bSetupPixelFormat(ghDC))
PostQuitMessage (0);

/*get render context concurrent with device context*/
ghRC = wglCreateContext(ghDC);
wglMakeCurrent(ghDC, ghRC);
GetClientRect(hWnd, &rect);
initializeGL(rect.right, rect.bottom);
break;
/*Win32 Api Paint*/
case WM_PAINT:
BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
break;
/*Win32 Api window´s size*/
case WM_SIZE:
GetClientRect(hWnd, &rect);
resize(rect.right, rect.bottom);
break;

/*close window and reset render and device context to zero*/
case WM_CLOSE:
if (ghRC)
wglDeleteContext(ghRC);
if (ghDC)
ReleaseDC(hWnd, ghDC);
ghRC = 0;
ghDC = 0;

DestroyWindow (hWnd);
break;

/* destroy window and delete render and device context*/
case WM_DESTROY:
if (ghRC)
wglDeleteContext(ghRC);
if (ghDC)
ReleaseDC(hWnd, ghDC);
PostQuitMessage (0);
break;

/*User controlled Kyboard routines*/
case WM_KEYDOWN:
switch (wParam) {
case VK_LEFT:
longinc += 0.2F;
break;
case VK_RIGHT:
longinc -= 0.2F;
break;
case VK_UP:
latinc += 0.2F;
break;
case VK_DOWN:
latinc -= 0.2F;
break;
}
/* Windows procedure for messages*/
default:
lRet = DefWindowProc (hWnd, uMsg, wParam, lParam);
break;
}

return lRet;
}


BOOL bSetupPixelFormat(HDC hdc)
{
PIXELFORMATDESCRIPTOR pfd, *ppfd;
int pixelformat;

ppfd = &pfd;

ppfd->nSize = sizeof(PIXELFORMATDESCRIPTOR);
ppfd->nVersion = 1;
ppfd->dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL |
PFD_DOUBLEBUFFER;
ppfd->dwLayerMask = PFD_MAIN_PLANE;
ppfd->iPixelType = PFD_TYPE_COLORINDEX;
ppfd->cColorBits = 8;
ppfd->cDepthBits = 16;
ppfd->cAccumBits = 0;
ppfd->cStencilBits = 0;

pixelformat = ChoosePixelFormat(hdc, ppfd);

if ( (pixelformat = ChoosePixelFormat(hdc, ppfd)) == 0 )
{
MessageBox(NULL, "ChoosePixelFormat failed", "Error", MB_OK);
return FALSE;
}

if (SetPixelFormat(hdc, pixelformat, ppfd) == FALSE)
{
MessageBox(NULL, "SetPixelFormat failed", "Error", MB_OK);
return FALSE;
}

return TRUE;
}



GLvoid resize( GLsizei width, GLsizei height )
{
GLfloat aspect;

glViewport( 0, 0, width, height );

aspect = (GLfloat) width / height;

glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluPerspective( 45.0, aspect, 3.0, 7.0 );
glMatrixMode( GL_MODELVIEW );
}


/* Object Set Up */
GLvoid createObjects()
{
GLUquadricObj *quadObj;

glNewList(GLOBE, GL_COMPILE);
quadObj = gluNewQuadric ();
gluQuadricDrawStyle (quadObj, GLU_LINE);
gluSphere (quadObj, 1.5, 16, 16);
glEndList();

}

/* Function Initialize Scene objects */
GLvoid initializeGL(GLsizei width, GLsizei height)
{
GLfloat maxObjectSize, aspect;
GLdouble near_plane, far_plane;

/* Initial depth and background */
glClearIndex( (GLfloat)BLACK_INDEX);
glClearDepth( 1.0 );

/* Initial depth test */
glEnable(GL_DEPTH_TEST);

glMatrixMode( GL_PROJECTION );
aspect = (GLfloat) width / height;
gluPerspective( 45.0, aspect, 3.0, 7.0 );
glMatrixMode( GL_MODELVIEW );

near_plane = 3.0;
far_plane = 7.0;
maxObjectSize = 3.0F;
radius = near_plane + maxObjectSize/2.0;

latitude = 0.0F;
longitude = 0.0F;
latinc = 3.0F;
longinc = 0.0F;

createObjects();
}


void polarView(GLdouble radius, GLdouble twist, GLdouble latitude,
GLdouble longitude)
{
glTranslated(0.0, 0.0, -radius);
glRotated(-twist, 0.0, 0.0, 1.0);
glRotated(-latitude, 1.0, 0.0, 0.0);
glRotated(longitude, 0.0, 0.0, 1.0);

}

GLvoid drawScene(GLvoid)
{
/*Clear previous frame depth and color buffer enabling next frame*/
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

/*matrix stack save transformations*/
glPushMatrix();

/*User´s Kayboard values*/
latitude += latinc*.3;
longitude += longinc*.3;

/*Call to object´s function with current values*/
polarView( radius, 0, latitude, longitude );

/* Final Call for Objects to be rendered*/
glIndexi(GREEN_INDEX);
glCallList(GLOBE);

/*restore OpenGl´s matrix stack transformations */
glPopMatrix();

/* Make scene visible */
SwapBuffers(ghDC);
}