Jump to content
  • Advertisement
Sign in to follow this  
shinypixel

2D sprite not rotating at the center origin?

This topic is 1677 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Whenever I rotate my sprite object, it seems to be rotating at the top-left corner. I don't know where to begin, so I'll just spill it out.

 

This is my coordinate system, with (0,0) at the bottom-left screen. 

 

glOrtho(0.0, SCREEN_WIDTH, 0.0, SCREEN_HEIGHT, 0.0, 1.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

 

In my RenderScene() function, I have the following:

 

    static GLfloat rotate = 0.0f;
    rotate += 0.2f;
 
    // draw these in foreground
    sprPC.setRotationAngle(rotate);
     sprPC.draw();

 

Then the rotation code in Sprite::Draw()

 

    glPushMatrix();
 
 
    glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_intSpriteID);
    
// ....
 
// rotating code
glTranslatef( m_currentCell.width/2.0f, m_currentCell.height/2.0f, 0.0f );   //back to previous position
glRotatef( m_rotateAngle, 0.0f, 0.0f, -1.0f );   //rotate
glTranslatef( m_currentCell.width/2.0f, m_currentCell.height/2.0f, 0.0f );  //to the origin

        // draw Sprite
        glBegin(GL_QUADS);
            
            // bottom left
            glFogCoordf(f);
            glTexCoord2i(m_currentCell.x, m_currentCell.height);
            glVertex2f(m_x, m_y);
    
            // bottom right
            glFogCoordf(f);
            glTexCoord2i(m_currentCell.width, m_currentCell.height);
            glVertex2f(m_x + (m_currentCell.width - m_currentCell.x) * m_scale, m_y);

            // top right
            glFogCoordf(f);
            glTexCoord2i(m_currentCell.width, m_currentCell.y);
            //glVertex2f(m_x + (m_currentCell.width - m_currentCell.x) * m_scale, (m_y + m_currentCell.y) * m_scale);
            glVertex2f(m_x + (m_currentCell.width - m_currentCell.x) * m_scale, (m_y + m_currentCell.y) * m_scale);

            // top left  
            glFogCoordf(f);
            glTexCoord2i(m_currentCell.x, m_currentCell.y);
            glVertex2f(m_x, (m_y + m_currentCell.y) * m_scale);

        glEnd();
 
// .....
 
glPopMatrix();

 

Basically, the sprite is rotating clockwise. I attached a screenshot to show what I mean.

 

[attachment=21459:pic.png]

Share this post


Link to post
Share on other sites
Advertisement

At a glance, I see that you have two calls to glTranslatef(), and they both translate in the postive direction by half the size of your sprite.  One of those (the first, I believe) should be in the opposite direction.

glTranslatef( -m_currentCell.width/2.0f, -m_currentCell.height/2.0f, 0.0f );   //back to previous position
glRotatef( m_rotateAngle, 0.0f, 0.0f, -1.0f );   //rotate
glTranslatef( m_currentCell.width/2.0f, m_currentCell.height/2.0f, 0.0f );  //to the origin

Share this post


Link to post
Share on other sites

Hmm, he curves a little to the right, but then descends into hell (y position keeps going down).

 

I added a quick video to see what's happening:

Edited by shinypixel

Share this post


Link to post
Share on other sites

Hmm.  Well, it looks like I was making some incorrect assumptions about your code, being a bit too hasty to read it fully, sorry.

 

When using calls like glTranslatef() and glRotatef() to transform your objects, it helps to do all such transformations through those calls.  But when you stream out the vertices using glVertex2f(), you'll note that you're doing a translate and a scale use m_x, m_y, and m_scale.  This is limiting your ability to order your translates, rotates, and scales correctly.  What ends up happening is that you're first translating your sprite to m_x, m_y, and only then rotating around the world's (0, 0), causing all sorts of weirdness, depending on where in the world m_x and m_y are.

 

At the very least, you want the vertices you pass to glVertex2f() to be in some neutral format, not yet placed within the world, and thus ready to be transformed.  Assuming that m_x and m_y refer to the exact middle of your sprite, the following code starts with vertices nicely centered around (0, 0).  It then scales the sprite first (which expands the four corners outward in all four directions equally), rotates the scaled sprite around (0, 0), since that's still where the center of the sprite is, and only then transforms them into the position they ultimately need to be in the world.  I'm not entirely sure about your .width, .height, and m_scale values; some of your math above in the glVertex2f() calls had me confused.  But hopefully this accomplishes the correct thing given the values you've assigned for those variables.

glScalef(m_currentCell.width * m_scale, m_currentCell.height * m_scale, 1.0f); //scale to the correct sprite size
glRotatef(m_rotateAngle, 0.0f, 0.0f, -1.0f);   //rotate around the center of the sprite
glTranslatef(m_x, m_y, 0.0f );   //translate into the correct position in the world

        // draw Sprite
        glBegin(GL_QUADS);
            //fog/texture coordinates removed for brevity
            //start with a unit square centered around (0, 0)
            glVertex2f(-0.5f, -0.5f);
            glVertex2f(0.5f, -0.5f);
            glVertex2f(0.5f, 0.5f);
            glVertex2f(-0.5f, 0.5f);
        glEnd();

Share this post


Link to post
Share on other sites

Unfortunately the vertices aren't showing my sprites anymore, but I feel we're headed in the right direction. Thanks for the explanation. One thing probably assumed is that I'm using GL_TEXTURE_2D (which has 0.0 - 1.0 values), but I'm using GL_TEXTURE_RECTANGLE_ARB which uses actual (x,y,w,h) coordinates of the texture. So I'm wondering if that's conflicting with the vertices that have -0.5 to 0.5f.

Edited by shinypixel

Share this post


Link to post
Share on other sites

Shouldn't be.  In every system I've worked with, texture coordinates and vertex coordinates are completely independent from each other.

 

(TL;DR - After writing some other stuff about your original code, I might have figured out what was wrong in my last suggestion.  But the other stuff might still be helpful to you, so I kept it and added the hopeful solution at the end.)

 

Going back to your original code, I'm a little confused on what m_currentCell refers to.  Is it the sprite from a sprite sheet?  There seem to be some oddities in both your glTexCoord2i() calls and in your glVertex2f() calls.

 

For the glTexCoord2i() calls, you seem to be mixing your usage of width/height with x/y in strange ways.  If (m_currentCell.x, m_currentCell.y) refers to the top-left corner of the sprite in a sprite sheet, then your bottom-right corner would not be (m_currentCell.width, m_currentCell.height), but would rather be (m_currentCell.x + m_currentCell.width, m_currentCell.y + m_currentCell.height).

 

For glVertex2f(), your top-right corner in particular, the x parameter is funky, and it doesn't match the math of the y parameter.  First of all, I'm not sure what (m_currentCell.width - m_currentCell.x) is even attempting to calculate.  In fact, for the vertex coordinate, the location of the sprite within the sprite sheet shouldn't matter at all; that's already taken care of by the texture coordinates.  So (assuming m_currentCell is indeed a sprite in a sprite sheet) m_currentCell.x shouldn't show up at all.  I would expect to just see (m_x + m_currentCell.width * m_scale).  As for the y parameter, you included m_y inside the parentheses, which makes it get multiplied by the m_scale variable, which is inconsistent with the x parameter.  One of these is probably wrong, but it depends on if m_x and m_y are already scaled or not.

 

I suspect that in this current case, m_currentCell.x and .y are both 0, which would hide a lot of the mathematical troubles you might otherwise run into.

 

Regardless, I used the internet to refresh my memory on OpenGL transformation call ordering.  I think I got my prior suggestion backwards; I always get these things confused depending on the library/system in use.  Try the following (basically reversed the order of the first three lines):

glTranslatef(m_x, m_y, 0.0f );   //translate into the correct position in the world
glRotatef(m_rotateAngle, 0.0f, 0.0f, -1.0f);   //rotate around the center of the sprite
glScalef(m_currentCell.width * m_scale, m_currentCell.height * m_scale, 1.0f); //scale to the correct sprite size

        // draw Sprite
        glBegin(GL_QUADS);
            //fog/texture coordinates removed for brevity
            //start with a unit square centered around (0, 0)
            glVertex2f(-0.5f, -0.5f);
            glVertex2f(0.5f, -0.5f);
            glVertex2f(0.5f, 0.5f);
            glVertex2f(-0.5f, 0.5f);
        glEnd();

If that works, sorry for getting it wrong the first time.  If it doesn't, um, even bigger sorry for running around in circles?  :-)

Share this post


Link to post
Share on other sites

if you want to rotate around the object center. You need to first move it to the center of the object,perform the rotation, then translate it back.

In that case if you are using your own matrix library and assuming that it is Column Major, you would do Translate*Rotate*-Translate. But if you are using the glCommands,

you can specify in that order.

glTranslate(Translate)

glRotate(rotate)

glTranslate(-Translate)

 

-Translate:This is the object center point. So if your object is 32x32 for example then this value would -16x16

rotate: Is whaterver angle you want to rotate around the Z or any other axis for that matter.

Translate: This is the object center point. So if your object is 32x32 for example, then this value would be 16x16.

 

Now if you want to move it across the screen you can always apply an additional matrix that has your translation of where you want it to be on the screen.

 

Let me know if that helps.

Edited by BornToCode

Share this post


Link to post
Share on other sites

Shouldn't be.  In every system I've worked with, texture coordinates and vertex coordinates are completely independent from each other.

 

(TL;DR - After writing some other stuff about your original code, I might have figured out what was wrong in my last suggestion.  But the other stuff might still be helpful to you, so I kept it and added the hopeful solution at the end.)

 

Going back to your original code, I'm a little confused on what m_currentCell refers to.  Is it the sprite from a sprite sheet?  There seem to be some oddities in both your glTexCoord2i() calls and in your glVertex2f() calls.

 

For the glTexCoord2i() calls, you seem to be mixing your usage of width/height with x/y in strange ways.  If (m_currentCell.x, m_currentCell.y) refers to the top-left corner of the sprite in a sprite sheet, then your bottom-right corner would not be (m_currentCell.width, m_currentCell.height), but would rather be (m_currentCell.x + m_currentCell.width, m_currentCell.y + m_currentCell.height).

 

For glVertex2f(), your top-right corner in particular, the x parameter is funky, and it doesn't match the math of the y parameter.  First of all, I'm not sure what (m_currentCell.width - m_currentCell.x) is even attempting to calculate.  In fact, for the vertex coordinate, the location of the sprite within the sprite sheet shouldn't matter at all; that's already taken care of by the texture coordinates.  So (assuming m_currentCell is indeed a sprite in a sprite sheet) m_currentCell.x shouldn't show up at all.  I would expect to just see (m_x + m_currentCell.width * m_scale).  As for the y parameter, you included m_y inside the parentheses, which makes it get multiplied by the m_scale variable, which is inconsistent with the x parameter.  One of these is probably wrong, but it depends on if m_x and m_y are already scaled or not.

 

I suspect that in this current case, m_currentCell.x and .y are both 0, which would hide a lot of the mathematical troubles you might otherwise run into.

 

Regardless, I used the internet to refresh my memory on OpenGL transformation call ordering.  I think I got my prior suggestion backwards; I always get these things confused depending on the library/system in use.  Try the following (basically reversed the order of the first three lines):

glTranslatef(m_x, m_y, 0.0f );   //translate into the correct position in the world
glRotatef(m_rotateAngle, 0.0f, 0.0f, -1.0f);   //rotate around the center of the sprite
glScalef(m_currentCell.width * m_scale, m_currentCell.height * m_scale, 1.0f); //scale to the correct sprite size

        // draw Sprite
        glBegin(GL_QUADS);
            //fog/texture coordinates removed for brevity
            //start with a unit square centered around (0, 0)
            glVertex2f(-0.5f, -0.5f);
            glVertex2f(0.5f, -0.5f);
            glVertex2f(0.5f, 0.5f);
            glVertex2f(-0.5f, 0.5f);
        glEnd();

If that works, sorry for getting it wrong the first time.  If it doesn't, um, even bigger sorry for running around in circles?  :-)

 

That did the trick! Once I reversed the order, the sprite started spinning at the origin. No prob - I think that happens to all programmers after some time. Thanks for the help.

Share this post


Link to post
Share on other sites

Andy, if you don't mind, there's a sub question that became a side effect.

 

Since the sprite is being drawn with this...

        // rotate
        glTranslatef(m_x, m_y, 0.0f );                    //translate into the correct position in the world
        glRotatef(m_rotateAngle, 0.0f, 0.0f, -1.0f);    //rotate around the center of the sprite
        glScalef((m_currentCell.width - m_currentCell.x) * m_scale, (m_currentCell.height - m_currentCell.y) * m_scale, 0.0f); //scale to the correct sprite size
 
        // draw Sprite
        glBegin(GL_QUADS);
            
            // bottom left
            glFogCoordf(f);
            glTexCoord2i(m_currentCell.x, m_currentCell.height);
            glVertex2f(-0.5f, -0.5f);
    
            // bottom right
            glFogCoordf(f);
            glTexCoord2i(m_currentCell.width, m_currentCell.height);
            glVertex2f(0.5f, -0.5f);

            // top right
            glFogCoordf(f);
            glTexCoord2i(m_currentCell.width, m_currentCell.y);  
            glVertex2f(0.5f, 0.5f);

            // top left  
            glFogCoordf(f);
            glTexCoord2i(m_currentCell.x, m_currentCell.y);
            glVertex2f(-0.5f, 0.5f);

        glEnd();

And I start him at position (0,0), why is half of his body only showing, and the other half is at the bottom of the screen? I know it's the -0.5f/0.5f vertices, but I'm wondering what's the trick to keep the y position as the bottom position of the sprite.

Edited by shinypixel

Share this post


Link to post
Share on other sites

That's no problem.  You're in a good place to add this extra tweak.  It sounds like your m_x/m_y coordinates conceptually indicate your character's feet.  Essentially what should be the bottom middle of the sprite.  But the code as I recommended is just treating m_x/m_y as the exact center.  (Useful for rotations, but ultimately not correct for placing the character in the world.)  All you need to do now it translate your character up an extra half-height of the sprite.  You should be able to take the translate on the very first line, and just add m_currentCell.height * m_scale / 2.0f to the m_y parameter.

 

I think the multiplication by m_scale is appropriate; try it without if what I wrote doesn't work.  Also, depending on if you have your positive y axis going up or down, you might have to subtract instead of add.

 

Okay, time for me to run off to a movie.  Good luck!

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!