PDA

View Full Version : Simple transformation problem



holdeWaldfee
10-04-2004, 05:28 AM
Servus!

I just started to use the OpenGL API.
The problem I can't solve currently is about transformations.
Yes, I know that it was discussed before, but these discussions in this forum couldn't really help me.

I want to rotate around two axes.
The problem is that my program rotates with the identity matrix when I call glRotatef.
But I want to rotate using the current model view matrix.

Thanks for your help!

Aeluned
10-04-2004, 05:44 AM
Servus.

Prior to any rotations (translations, scales, whatever) all you have is an identity matrix.

rotate your object about the first axis, then rotate it about the second axis (um..and don't call glLoadIdentity()).

could you post some code showing what you're doing?

ZbuffeR
10-04-2004, 05:47 AM
Not sure if it is your case but you can reverse the order of your rotations.
It is a common problem due to matrix multiplications.
And are you sure there is no glLoadIdentity where you don't need it ?

holdeWaldfee
10-04-2004, 06:02 AM
The current modelview matrix is the transformed one before I make the glrotatef calls. I checked it with glGetFloatv.

I call

glRotatef((float)xangle , 1.0f, 0.0f, 0.0f);
glRotatef((float)yangle , 0.0f, 1.0f, 0.0f);

...
glPushMatrix()
glTranslatef(...)
drawStuff
glPopMatrix()
...
glPushMatrix()
glTranslatef(...)
drawStuff
glPopMatrix()
...

The result is that it's rotating around the (1,0,0) and (0,1,0) vector that belongs to the identity matrix, but not the current modelview matrix. But I want that the rotation transformation is using the current modelview matrix.

Aeluned
10-04-2004, 06:15 AM
Do you have any transformations going on before this ?:




glRotatef((float)xangle , 1.0f, 0.0f, 0.0f);
glRotatef((float)yangle , 0.0f, 1.0f, 0.0f);If you don't, all you have is an identity modelview matrix and everything is happening exactly as it should. If you're saying that you're getting the modelview matrix *right before* those glRotatef calls and it's not coming up as the identity but your glRotatef calls are producing results as if it where, well then I'm at a total loss as that's sort of...well...impossible.

Sorry, I'm a bit thick-headed and it's not so clear as to what you're trying to accomplish.

EDIT: remember also that the results of one glRotatef call change your coordinate system.
a rotate about the x axis by 90 degrees for example would effectively swap your y and z axes.
is this the unexpected behavior you're seeing?

holdeWaldfee
10-04-2004, 06:46 AM
This is what I am doing:


void RenderFrame(void)
{
glClear(...);
glMatrixMode(GL_MODELVIEW);

glGetFloatv(GL_MODELVIEW_MATRIX, Current_MV_Matrix);

if(rotate_x)
{
glRotatef((float)Angle_x, 1.0f, 0.0f, 0.0f);
Angle_x = 0;
rotate_x = FALSE;
}
else
{
glRotatef((float)Angle_y, 0.0f, 1.0f, 0.0f);
Angle_y = 0;
rotate_x = TRUE;
}

glPushMatrix();
glTranslatef(...);
drawStuff;
glPopMatrix();

glPushMatrix();
glTranslatef(...);
drawStuff;
glPopMatrix();
}"Current_MV_Matrix" gives me the identity matrix for the first frame and in all other frames the transformed ones.

The result is that I don't rotate around the (1,0,0) and (0,1,0) vectors of the translated modelview matrix like I wanted, but around the identity modelview matrix vectors.

Please give me a note if you need more details.

Aeluned
10-04-2004, 06:55 AM
where else are you setting Angle_x and Angle_y ?

On the first frame you haven't done any transformations yet, so it makes sense that you have the identity modelview.

after that, if you aren't setting Angle_x and/or Angle_y each frame outside of Display() you're rotating by 0 degrees each frame.

holdeWaldfee
10-04-2004, 07:12 AM
These variables are global (just for testing) and are changed dynamically (by mouse-input).

holdeWaldfee
10-04-2004, 11:50 AM
PLEASE HEEELLLP!
It's driving me cracy. :-/

10-04-2004, 12:29 PM
There is no obvious error in the code that you provided, so it must be somewhere else in your code.

You cast Angle_x/Angle_y to an float which makes me think they are probably integers. Maybe you are calculating them wrong? If you do something like:


int Angle_x = 12/11;The result will be 0 and hence there will be no rotation. Or perhaps rotate_x/rotate_y is always false...

This is where the debugger comes in handy. Set breakpoints to your glRotate() calls and check what values you are passing to OpenGL.

-NiCo-
10-04-2004, 02:14 PM
Try calling glRotatef after glTranslatef.

Greetz,

Nico

10-04-2004, 02:30 PM
Originally posted by -NiCo-:
Try calling glRotatef after glTranslatef.
Except this doesnt explain why the modelview transform still equals identity, does it?

RTFP next time.

plasmonster
10-04-2004, 04:34 PM
Some annotated code:


void RenderFrame()
{
glClear(...);

// Setup the camera FIRST.

// Setup the camera projection.
// (Just an example here)
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective( 60, viewAspectRatio, 1, 1000 );

// Now, create a matrix that positions the
// camera in world-space, then invert it.
// In other words, apply everything you
// would to position the camera in the
// world, only in reverse. This has the
// effect of transforming everything in
// world-space into camera-space.
// As an example, say you wanted an
// arbitrary rotation (Rx,Ry,Rz) at the
// origin, followed by a translation
// into the world (typical first-person view):
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef(-Camera_World_Angle_z, 0, 0, 1);
glRotatef(-Camera_World_Angle_y, 0, 1, 0);
glRotatef(-Camera_World_Angle_x, 1, 0, 0);
glTanslatef( -Camera_World_x, -Camera_World_y, -Camera_World_z );

// Now ready to transform and draw objects.

// For each object, first push the camera
// matrix.
glPushMatrix();

// The first object transform is always
// relative to the local object coordinate
// system, by definition.
// If you want a particular orientation,
// this is the place to apply it. For
// example, if you're drawing a tank, then
// you might transform the body into the
// world here.

// Now, draw the object ...

// If you want a subsequent transform
// relative to a previous one, then push
// the current modelview again, apply the
// transforms, draw, then pop.
// For the tank, you might want a turret
// here, so you could push the matrix, apply
// the turret transform, draw, then pop. Then
// you could repeat this process for the 2
// tracks, and so on, all parts relative
// to initial body transform.

// This is how skeletal animation is
// performed too, by transforming each bone
// relative to the last.
glPopMatrix();
}edit: Tried to clarify a few things.

-NiCo-
10-05-2004, 12:30 AM
Originally posted by <Somebodyelse>:

Originally posted by -NiCo-:
Try calling glRotatef after glTranslatef.
Except this doesnt explain why the modelview transform still equals identity, does it?

RTFP next time.

The current modelview matrix is the transformed one before I make the glrotatef calls. I checked it with glGetFloatv.

The result is that it's rotating around the (1,0,0) and (0,1,0) vector that belongs to the identity matrix, but not the current modelview matrix.He checked it and the modelview transform does not equal identity. He says the rotation is about the identity axes but said nothing about translation. If you postmultiply the translation calls with the rotation calls like he did, the rotation is performed around the axes of the camera coordinate frame, which correspond to the ones of the identity matrix.

I suggest you RTFP next time <Somebodyelse>.

Nico

holdeWaldfee
10-05-2004, 02:23 AM
Sorry, I still haven't found it out:

This is the OpenGL initialization that I do only once:

void InitGL(void)
{
glViewport(0, 0, 1280, 1024); // Reset The Current Viewport

glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
glLoadIdentity(); // Reset The Projection Matrix

glFrustum(-2.0f, 2.0f, -1.5f, 1.5f, 4.0f, 100000.0f); // !!! Assuming a Aspect-Ratio of 4:3 !!!

glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix

glShadeModel(GL_SMOOTH);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClearDepth(1.0f); // Depth Buffer Setup
glDepthFunc(GL_LESS); // The Type Of Depth Testing To Do
glEnable(GL_DEPTH_TEST);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
}I simplified my Frame rendering code even more:

void RenderScene(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();

glFrustum(-2.0f, 2.0f, -1.5f, 1.5f, 4.0f, 100000.0f);
glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

glRotatef((float)xmouseD , 0.0f, 1.0f, 0.0f);
glRotatef((float)ymouseD , 1.0f, 0.0f, 0.0f);



glPushMatrix();
glTranslatef(-1800.0f, 0.0f, -3000.0f);
glBegin(GL_QUADS);
glVertex3f(-250000.0f, -500.0f, -250000.0f);
glVertex3f( 250000.0f, -500.0f, -250000.0f);
glVertex3f( 250000.0f, -500.0f, 250000.0f);
glVertex3f(-250000.0f, -500.0f, 250000.0f);
glEnd();
glPopMatrix();
}I still get the same result that correct rotations are only performed, when the "camera-vector" is the same as the one when no transformations have been performed at all.

dvm
10-05-2004, 02:28 AM
Usually you load the identity matrix at the start of each frame. So your function should look:
void RencerFrame()
{
glClear(...);
glLoadIdentity();
//rest goes here
glFlush();
SwapBuffers();
}
The reason we do this is that it is less expensive computationaly to draw our object in a new position each frame, than to try to move the object from its last position which is what you're doing. So remember, at the start of each frame, there should be a glClear, glLoadIdentity call.

holdeWaldfee
10-05-2004, 02:48 AM
Thanks for the performance tip my code looks like this now:


void InitGL(void)
{
glViewport(0, 0, 1280, 1024); // Reset The Current Viewport

glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
glLoadIdentity(); // Reset The Projection Matrix

glFrustum(-2.0f, 2.0f, -1.5f, 1.5f, 4.0f, 100000.0f); // !!! Assuming a Aspect-Ratio of 4:3 !!!

glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix

glShadeModel(GL_SMOOTH);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClearDepth(1.0f); // Depth Buffer Setup
glDepthFunc(GL_LESS); // The Type Of Depth Testing To Do
glEnable(GL_DEPTH_TEST);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
}


void RenderScene(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();

glRotatef((float)xmouseD , 0.0f, 1.0f, 0.0f);
glRotatef((float)ymouseD , 1.0f, 0.0f, 0.0f);

glPushMatrix();
glTranslatef(-1800.0f, 0.0f, -3000.0f);
glBegin(GL_QUADS);
glVertex3f(-250000.0f, -500.0f, -250000.0f);
glVertex3f( 250000.0f, -500.0f, -250000.0f);
glVertex3f( 250000.0f, -500.0f, 250000.0f);
glVertex3f(-250000.0f, -500.0f, 250000.0f);
glEnd();
glPopMatrix();

glFlush();
}And the problem still exists.

Aeluned
10-05-2004, 05:16 AM
I can see nowhere else that you might be making an error except on the calculations of your rotation angles. when are they calculated and how?

Using a debugger: can you verify that your values for xmouseD and/or ymouseD are not 0?

and you're not calling glMatrixMode(GL_PROJECTION) anywhere besides your init, right?

-NiCo-
10-05-2004, 05:28 AM
Try this:


void InitGL(void)
{
glViewport(0, 0, 1280, 1024); // Reset The Current Viewport

glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
glLoadIdentity(); // Reset The Projection Matrix

glFrustum(-2.0f, 2.0f, -1.5f, 1.5f, 4.0f, 100000.0f); // !!! Assuming a Aspect-Ratio of 4:3 !!!

glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix

glShadeModel(GL_SMOOTH);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClearDepth(1.0f); // Depth Buffer Setup
glDepthFunc(GL_LESS); // The Type Of Depth Testing To Do
glEnable(GL_DEPTH_TEST);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
}


void RenderScene(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();

glTranslatef(-1800.0f, 0.0f, -3000.0f);
glRotatef((float)xmouseD , 0.0f, 1.0f, 0.0f);
glRotatef((float)ymouseD , 1.0f, 0.0f, 0.0f);
glBegin(GL_QUADS);
glVertex3f(-250000.0f, -500.0f, -250000.0f);
glVertex3f( 250000.0f, -500.0f, -250000.0f);
glVertex3f( 250000.0f, -500.0f, 250000.0f);
glVertex3f(-250000.0f, -500.0f, 250000.0f);
glEnd();

glFlush();
}

holdeWaldfee
10-05-2004, 06:01 AM
It's working finally! :-)

I tried to exchange the rotation calls before with the code that didn't load the identity matrix at start of each frame and it didn't help.

But with the new code that performs all previous rotation (and the new ones) for each frame on the identity matrix again is working correctly when the rotation calls are exchanged.

What is the reason behind this?

Big thanks for your patience to help me!

Aeluned
10-05-2004, 06:05 AM
You're probably not accumulating your rotations, and were sending the same rotation each frame after a glLoadIdentity.

what you should do is increment your globals by whatever method you use to calculate the rotation angle, i.e.:

xmouseD += <some function to calculate angle change>

ymouseD += <same thing>

I still suspect your doing this in some funky way, but...
either way...glad to hear it's finally working.

-NiCo-
10-05-2004, 06:13 AM
The reason behind this is, that rotation and translation calls are postmultiplied to to current modelview matrix. Meaning that the call you performed last will be executed first on the object you are drawing.

e.g. if a canonturret-object is defined at the origin with the Y-axis as the axes of revolution, you will want to rotate it first before you put it on top of the tank with a translation.

By calling translatef before rotatef, the rotation is performed before the translation.

If you reverse the operation, it means that you position the turret on top of the tank, and after that you rotate the turret around the axis, passing through the projection center of your camera, which was not what you intended to do.

It's hard to say why your other code with the operations exchanged didn't work without the code.

Nico

holdeWaldfee
10-05-2004, 06:17 AM
I didn't accumulate the angle when I was not loading the identity matrix at the start of each frame (and this is logical AFAIK).
Exchanging the rotation calls didn't help then.

Now I am loading the identity matrix at the start of each frame and use the accumulated angles.
It's working only when I exchange the rotation calls.

I don't understand why exchanging the rotation calls solve the problem.
And I don't understand why it doesn't work when I don't load the identity matrix and transform the old model matrix.

-NiCo-
10-05-2004, 06:25 AM
Just to be clear, by exchanging rotation calls, do you mean exchanging tarnslation and rotation calls or exchanging rotate-x and rotate-y?

Nico

holdeWaldfee
10-05-2004, 06:27 AM
Originally posted by -NiCo-:
The reason behind this is, that rotation and translation calls are postmultiplied to to current modelview matrix. Meaning that the call you performed last will be executed first on the object you are drawing.

e.g. if a canonturret-object is defined at the origin with the Y-axis as the axes of revolution, you will want to rotate it first before you put it on top of the tank with a translation.

By calling translatef before rotatef, the rotation is performed before the translation.

If you reverse the operation, it means that you position the turret on top of the tank, and after that you rotate the turret around the axis, passing through the projection center of your camera, which was not what you intended to do.

It's hard to say why your other code with the operations exchanged didn't work without the code.
NicoBut I didn't exchange the rotation with the translation, but one rotation with the other one and it worked. So how could it solve the problem that it was rotating not correctly?

-NiCo-
10-05-2004, 06:36 AM
Ok, I see now. Maybe the object you are drawing is an object of revolution about the x-axis.

In that case, by calling:

glRotatef((float)xmouseD , 0.0f, 1.0f, 0.0f);
glRotatef((float)ymouseD , 1.0f, 0.0f, 0.0f);

the glRotatef((float)ymouseD , 1.0f, 0.0f, 0.0f) call will have no effect at all.

Also, if you accumulate the angles, the total rotation becomes:

Ry_total*Rx_total

if you don't accumulate, it becomes:

Ry_frame_i*Rx_frame_i*Ry_frame_i-1*Rx_frame_i-1* . . . *Ry_frame_0*Rx_frame_0

These are not equivalent.

Nico

holdeWaldfee
10-05-2004, 06:49 AM
Originally posted by -NiCo-:
Ok, I see now. Maybe the object you are drawing is an object of revolution about the x-axis.

In that case, by calling:

glRotatef((float)xmouseD , 0.0f, 1.0f, 0.0f);
glRotatef((float)ymouseD , 1.0f, 0.0f, 0.0f);

the glRotatef((float)ymouseD , 1.0f, 0.0f, 0.0f) call will have no effect at all.What do you mean with "object of revolution"? Anyway it seems that this is not the case because a transformation occurs for both axis when the problem happens.


Also, if you accumulate the angles, the total rotation becomes:

Ry_total*Rx_total

if you don't accumulate, it becomes:

Ry_frame_i*Rx_frame_i*Ry_frame_i-1*Rx_frame_i-1* . . . *Ry_frame_0*Rx_frame_0

These are not equivalent.

NicoI just meant the addition of all previous mouse movements with acccumulation.

I was using only the DELTA angle of mousmovement for rotation when I didn't load the identity model view matrix.
Exchanging the rotation calls didn't help here.

I was using the total angle of mousmovement for rotation when I did load the identity model view matrix.
Exchanging the rotation did help here.

Is this what you considered with your explenation and I just didn't understand?

-NiCo-
10-05-2004, 07:33 AM
Objects of revolution are objects which are symmetrical about an axis.

This is what I'm trying to say:

When you don't load identity, the first frame becomes:


glRotatef((float)xmouseDelta1 , 0.0f, 1.0f, 0.0f);
glRotatef((float)ymouseDelta1 , 1.0f, 0.0f, 0.0f);For the second frame, the modelview matrix is equivalent to calling the next piece of code in the first frame:

glRotatef((float)xmouseDelta1 , 0.0f, 1.0f, 0.0f);
glRotatef((float)ymouseDelta1 , 1.0f, 0.0f, 0.0f);
glRotatef((float)xmouseDelta2 , 0.0f, 1.0f, 0.0f);
glRotatef((float)ymouseDelta2 , 1.0f, 0.0f, 0.0f);This is not equivalent to loading the identity matrix and calling:

glRotatef((float)(xmouseDelta1+xmouseDelta2) , 0.0f, 1.0f, 0.0f);
glRotatef((float)(ymouseDelta1+ymouseDelta2) , 1.0f, 0.0f, 0.0f);because the latter is equivalent to:

glRotatef((float)xmouseDelta1 , 0.0f, 1.0f, 0.0f);
glRotatef((float)xmouseDelta2 , 0.0f, 1.0f, 0.0f);
glRotatef((float)ymouseDelta1 , 1.0f, 0.0f, 0.0f);
glRotatef((float)ymouseDelta2 , 1.0f, 0.0f, 0.0f);and the second and third rotation matrices can not be interchanged.

Nico