Page 1 of 2
glRotatef confusion
Posted: Sun Sep 13, 2009 2:26 pm
by short
Hey guys, I'm really trying to get a better understanding of opengl, but I keep running into this problem. The red book isn't helping me to much with this, so hopefully somebody here can help. I have a rectangle class which I believe is working perfectly, so I won't post the class file (I can if needed).
Anyways, the desired effect I am looking for is the rectangle to spin according to m_orientation in two dimensions. It works fine when the rectangle is at position 0,0,0 but when I move the rectangle away from the origin (wasd keys move the rectangle) the rotation gets messed up. I think it has something to do with where my camera is, or I am not understanding where I need to move it. My draw function is this (it is called every frame):
Code: Select all
void Engine::draw(Rect * rectangle)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer
glLoadIdentity(); // Reset The Current Modelview Matrix
glTranslatef(0.0f, 0.0f, -8.0f); // Move everything 8 units into the screen
drawRectangle(rectangle, &rectangle->m_colors);
//drawSpriteOnRectangle(Vertex(100, 100, 0), Vertex(228, 100, 0), Vertex(228, 228, 0), Vertex(100, 228, 0));
}
I can't think of anything else that would be relevant, hopefully somebody can point me in the correct direction.
Code: Select all
void Engine::drawRectangle(Rect * rectangle, vector<Vertex>* color_vector)
{
glPushMatrix();
glRotatef(rectangle->m_orientation, rectangle->getPosition()->x, rectangle->getPosition()->y, 1);
//m_RotationAngle += .1f; // increase rotation angle
rectangle->m_orientation += .1f;
if(rectangle->m_orientation > 360.0) //prevent from having an angle greater than a float value can store ^^
{
rectangle->m_orientation -= 360.0;
}
glBegin(GL_QUADS);
glColor3f(rectangle->m_colors[TOP_LEFT].x, rectangle->m_colors[TOP_LEFT].y,rectangle->m_colors[TOP_LEFT].z);
glVertex3f(rectangle->m_vertices[TOP_LEFT].x, rectangle->m_vertices[TOP_LEFT].y, rectangle->m_vertices[TOP_LEFT].z);
glColor3f(rectangle->m_colors[TOP_RIGHT].x, rectangle->m_colors[TOP_RIGHT].y,rectangle->m_colors[TOP_RIGHT].z);
glVertex3f(rectangle->m_vertices[TOP_RIGHT].x, rectangle->m_vertices[TOP_RIGHT].y, rectangle->m_vertices[TOP_RIGHT].z);
glColor3f(rectangle->m_colors[BOTTOM_RIGHT].x, rectangle->m_colors[BOTTOM_RIGHT].y,rectangle->m_colors[BOTTOM_RIGHT].z);
glVertex3f(rectangle->m_vertices[BOTTOM_RIGHT].x, rectangle->m_vertices[BOTTOM_RIGHT].y, rectangle->m_vertices[BOTTOM_RIGHT].z);
glColor3f(rectangle->m_colors[BOTTOM_LEFT].x, rectangle->m_colors[BOTTOM_LEFT].y,rectangle->m_colors[BOTTOM_LEFT].z);
glVertex3f(rectangle->m_vertices[BOTTOM_LEFT].x, rectangle->m_vertices[BOTTOM_LEFT].y, rectangle->m_vertices[BOTTOM_LEFT].z);
glEnd();
glPopMatrix();
}
Please let me know if I forgot to include anything related to this.
Re: glRotatef confusion
Posted: Sun Sep 13, 2009 4:01 pm
by RyanPridgeon
glRotate rotates around the origin. So basically the center of rotation is wherever you've translated to.
So just make sure to translate to where you want the center of rotation to be, and rotation should work fine. Other than that it would be helpful to explain your problem better.
Re: glRotatef confusion
Posted: Sun Sep 13, 2009 5:00 pm
by short
I believe you are correct, thank you for explaining that to me.
Re: glRotatef confusion
Posted: Sun Sep 13, 2009 8:45 pm
by Falco Girgis
Let me guess, you wound up with a rectangle that "orbited" the origin, rather than rotating about itself? It's a simple mistake with a simple fix. Just rotate
before your translation. You will run into this kind of thing all the time when you get into OGL and matrix transformations. Usually if you play with it enough, you will start to understand it better.
Everybody has that exact same problem the first time they try to rotate a sprite.
Re: glRotatef confusion
Posted: Sun Sep 13, 2009 11:41 pm
by short
I think I understand that the translate code goes before the rotation code to produce the rotation before the translation. Correct me if I am wrong, but I did try putting the rotation before the translation in the code, and it didn't work either.
Let me do some out loud thinking.
First we push the matrix
Then we rotate the current viewing matrix (happens second)
Then we translate to the center of the rectangle (happens first because of matrices multiplication)
Then draw the rectangle
Lastly we pop the old matrix
In my head it makes sense, but I am still getting undesired effects (spinning around the 0,0,0 origin instead of ).
Code: Select all
void Engine::drawRectangle(Rect * rectangle, vector<Vertex>* color_vector)
void Engine::drawRectangle(Rect * rectangle, vector<Vertex>* color_vector)
{
glPushMatrix();
glLoadIdentity();
glTranslatef(rectangle->getPosition()->x, rectangle->getPosition()->y, rectangle->getPosition()->z);
glRotatef(rectangle->m_orientation, 0.0f,0.0f,1.0f);
if(rectangle->m_orientation > 360.0) //prevent from having an angle greater than a float value can store ^^
{
rectangle->m_orientation -= 360.0;
}
glBegin(GL_QUADS);
glColor3f(rectangle->m_colors[TOP_LEFT].x, rectangle->m_colors[TOP_LEFT].y,rectangle->m_colors[TOP_LEFT].z);
glVertex3f(rectangle->m_vertices[TOP_LEFT].x, rectangle->m_vertices[TOP_LEFT].y, rectangle->m_vertices[TOP_LEFT].z);
glColor3f(rectangle->m_colors[TOP_RIGHT].x, rectangle->m_colors[TOP_RIGHT].y,rectangle->m_colors[TOP_RIGHT].z);
glVertex3f(rectangle->m_vertices[TOP_RIGHT].x, rectangle->m_vertices[TOP_RIGHT].y, rectangle->m_vertices[TOP_RIGHT].z);
glColor3f(rectangle->m_colors[BOTTOM_RIGHT].x, rectangle->m_colors[BOTTOM_RIGHT].y,rectangle->m_colors[BOTTOM_RIGHT].z);
glVertex3f(rectangle->m_vertices[BOTTOM_RIGHT].x, rectangle->m_vertices[BOTTOM_RIGHT].y, rectangle->m_vertices[BOTTOM_RIGHT].z);
glColor3f(rectangle->m_colors[BOTTOM_LEFT].x, rectangle->m_colors[BOTTOM_LEFT].y,rectangle->m_colors[BOTTOM_LEFT].z);
glVertex3f(rectangle->m_vertices[BOTTOM_LEFT].x, rectangle->m_vertices[BOTTOM_LEFT].y, rectangle->m_vertices[BOTTOM_LEFT].z);
glEnd();
glPopMatrix();
}
edit: I added the following code to the end of the function to make sure I was getting the correct position. It works beautifully, the line always goes to the center of the rectangle, as expected.
Code: Select all
glBegin(GL_LINES);
glColor3f(1.0, 1.0, 1.0);
glVertex3f(rectangle->getPosition()->x, rectangle->getPosition()->y, rectangle->getPosition()->z);
glVertex3f(100.0f,100.0f,-6.0f);
glEnd();
Re: glRotatef confusion
Posted: Mon Sep 14, 2009 3:49 am
by RyanPridgeon
No, you have to translate BEFORE rotating, otherwise you will just be translating along the NEW rotated axis, which will result in the weird orbiting effect
First, push the matrix
Then translate to the object's center
Then glRotate to the desired orientation
Then draw the sprite
Then pop the matrix
Hopefully that will clear it up for you
Re: glRotatef confusion
Posted: Mon Sep 14, 2009 10:53 am
by MarauderIIC
Then we translate to the center of the rectangle (happens first because of matrices multiplication)
Once the command is done, it's done. It's not done when the matrix is popped, it's done when the semicolon is hit. As such, you aren't really subject to order of operations, which is what your sentence suggests your belief to be.
Re: glRotatef confusion
Posted: Mon Sep 14, 2009 11:31 am
by Falco Girgis
RyanPridgeon wrote:No, you have to translate BEFORE rotating, otherwise you will just be translating along the NEW rotated axis, which will result in the weird orbiting effect
First, push the matrix
Then translate to the object's center
Then glRotate to the desired orientation
Then draw the sprite
Then pop the matrix
Hopefully that will clear it up for you
Right. I mean, the rotation is happening
before the translation here. That's really what I said also.
Re: glRotatef confusion
Posted: Mon Sep 14, 2009 11:50 am
by qpHalcy0n
MarauderIIC wrote:Then we translate to the center of the rectangle (happens first because of matrices multiplication)
Once the command is done, it's done. It's not done when the matrix is popped, it's done when the semicolon is hit. As such, you aren't really subject to order of operations, which is what your sentence suggests your belief to be.
Unfortunately, that is not the case :[
In regards to the transformation pipeline, there is a rather lengthy order of operations that *must* happen in perfect order. In particular world transformations (which is what you're doing) follow a specific order. This is called a matrix concatenation, which is what the calls to glRotate, glScale, glTranslate, and so forth actually do. They postmultiply the topmost matrix on the current matrix mode's stack. Given that GL uses a row major ordering, this is consequently the same as PREmultiplying a column major matrix by a row major vector. Therefore the order of the multiplication of the matrices follows the OPPOSITE order than you think of the transformations in your head. In other words: "I want to translate to the origin, rotate, then translate back". The actual multiplication might look like T(d) * R * T(o). Where T(d) is the desired translation, R is the rotation, and T(o) is the translation to the origin.
Moreover if you are applying several transformations, there is a certain order. In the simplest case remember this: C = T * R * S; Where C is the final concatenation, T is the translation matrix, R is the rotation matrix, and S is a *uniform* scaling. So in that simplest case (where you have an object centered about the origin) the order of execution should be:
Code: Select all
glTranslatef(....);
glRotatef(....);
glScalef(...);
So objects that lie NOT at the origin require two additional steps, and its very simple if you think about it. A translation to the origin, and a translation back. The translation to the origin occurs at the TRAILING end of the equation, the translation back occurs at the LEADING end of the equation which then becomes: C = T(d) * R * S * T(o) where T(d) is the desired translation, and T(o) is the translation to the origin. So to get to the origin, in a 2D space, you translate by the NEGATED center of the object (said: The vertices of the object are centered ABOUT the origin). So for example, you have a box w/ a lower left of (1, 1) and upper right of (2, 2), your translation to the origin looks like (-1.5, -1.5). So then your translation back to where it was looks like (1.5, 1.5). In a 3D space, you can represent the AABB (axis aligned bounding box) of an object w/ 2 points. The center then becomes min + (vec3(min - max) * 0.5); Negate that, and that is your translation to the origin.
I wont even get into non-uniform scales here, it involves finding an inverse matrix for a scale which can be lengthy.
So the reason things APPEAR to happen out of order in most API's is due to matrix PREMULTIPLICATION. Secondly, there is a very specific order of operations. Going out of order will yield undesired results.
Re: glRotatef confusion
Posted: Mon Sep 14, 2009 2:11 pm
by short
I have my translation code immediately after pushing the matrix.
Code: Select all
glPushMatrix();
glTranslatef(rectangle->getPosition()->x, rectangle->getPosition()->y, rectangle->getPosition()->z);
glRotatef(rectangle->m_orientation, 0,0,rectangle->getPosition()->z);
The rotation works fine when the box is at 0,0,0 (it spins like it should around itself)
When I move the box around the screen, the further from the center of the screen, the more it seems to rotate about some arbitrary point.
I believe the following code may be an issue, but I do not know why..
Code: Select all
glRotatef(rectangle->m_orientation, 0,0,rectangle->getPosition()->z);
Since Immediately above the glRotatef call I translate to the center of the rectangle, I think the 2nd and 3rd parameters should be both 0 (which I have them as) since 0 would now be the center of the rectangle.?
Re: glRotatef confusion
Posted: Mon Sep 14, 2009 2:26 pm
by qpHalcy0n
Read the above post so that you understand whats going on. Then this is what you'll be looking at (if indeed your positions ARE the *center* of the object to be rotated):
Code: Select all
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glTranslatef(rectangle->getPosition()->x, rectangle->getPosition()->y, rectangle->getPosition()->z); // THIRD: translate back to original position
glRotatef(degree_measure, rot_axis_x, rot_axis_y, rot_axis_z); // SECOND: rotate
glTranslatef(-rectangle->getPosition()->x, -rectangle->getPosition()->y, -rectangle->getPosition()-z); // FIRST: translate to origin
// Draw your stuff //
glPopMatrix();
Re: glRotatef confusion
Posted: Mon Sep 14, 2009 3:54 pm
by MarauderIIC
qpHalcy0n wrote:
Unfortunately, that is not the case :[
Really? That's interesting.
Re: glRotatef confusion
Posted: Mon Sep 14, 2009 4:32 pm
by qpHalcy0n
MarauderIIC wrote:qpHalcy0n wrote:
Unfortunately, that is not the case :[
Really? That's interesting.
Correct, in general matrix multiplication is not commutative. MN != NM
Re: glRotatef confusion
Posted: Wed Sep 16, 2009 5:50 pm
by short
qpHalcy0n wrote:Read the above post so that you understand whats going on. Then this is what you'll be looking at (if indeed your positions ARE the *center* of the object to be rotated):
Code: Select all
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glTranslatef(rectangle->getPosition()->x, rectangle->getPosition()->y, rectangle->getPosition()->z);// THIRD: translate to original position
glRotatef(degree_measure, rot_axis_x, rot_axis_y, rot_axis_z); // SECOND: rotate
glTranslatef(-rectangle->getPosition()->x, -rectangle->getPosition()->y, -rectangle->getPosition()-z);// FIRST: translate to origin
// Draw your stuff //
glPopMatrix();
By adding the following line, it works perfectly
Code: Select all
glTranslatef(-rectangle->getPosition()->x, -rectangle->getPosition()->y, -rectangle->getPosition()->z);
I'm curious though as to why we have to rotate back to the origin. I thought this was what pushing and popping the matrix did. I thought it was supposed to save the state of the modelview matrix (ie when popping the matrix it was supposed to return the matrix how it was before i called the translation)..
Can anyone clarify how this works? BTW I thank you all for taking the time to answer these questions.
Re: glRotatef confusion
Posted: Wed Sep 16, 2009 6:55 pm
by qpHalcy0n
The negated translation is very simple. An object's position is simply expressed as the *VECTOR* from the local origin to that particular position. If you think of the world position in terms of a vector as opposed to a POINT (the object position is the origin (point) plus the vector to get there and a point +/- a vector yields another point), then you'll see that the NEGATED *position* is indeed the vector to get back TO the origin. The reason you need to get to the origin for an object to spin about it's own axis stems from the fact that simple rotation matrices are composed of sin/cos combinants. In a cartesian space, if all vertices lie in the same quadrant (which they do if you're not centered about the origin) then they will all rotate in unison around the unit circle centered at the origin. By translating to the origin, now all vertices are centered ABOUT the origin across all four quadrants. Then the object's center is centered about the world origin, so the object appears to rotate about it's own center. It's a matter of aligning coordinate frames.
OpenGL's matrix state is very simple. Know that ALL operations to matrices happen to "the topmost matrix on the stack". That is glTranslatef, glRotatef, glMultMatrix, glScalef, and so forth all operate on the topmost matrix. When you push a matrix, it pushes the current matrix one level down on the stack and in it's place puts a copy of that matrix. When you pop the matrix, it discards the topmost matrix where the next matrix from the top becomes the topmost. This is LIFO structure..and is also a very generic data structure. Do readup on stacks/queues if this does not make sense. The reason for the need to do this is in a state machine, you must always undo what you have done when you are finished, a stack suits this very well. I'll save the matrix as is....operate on its copy......then resurrect it when I'm done.
All vertices subsequently sent down the pipeline (via a glBegin/glEnd pair) are transformed by the topmost matrices of the matrix stacks...and this happens immediately at the conclusion of glEnd. You probably do not want ALL vertices AFTER you draw this particular object to be transformed by the SAME matrix (because they do not have the same positions, they may not rotate, they may not scale, etc...), this is why you must RESTORE the previous matrix state with a glPop when you are done drawing.