A: 

I'm assuming you're learning OpenGL and only needs to get this to work somehow. If you need raw speed, there's shaders and vertex buffers and all sorts of both neat and complicated things.

The simplest way is to load the PNG into a texture (assuming you have the ability to load images into memory, you do need htat), then draw it with a quad setting appropriate texture coordinates (they go from 0 to 1 with floating point coordinates, so you need to divide by texture width or height accordingly).

Use glBegin(GL_QUADS), glTexcoord2f(), glVertex2f(), glEnd() for the simplest (but not fastest) way to draw this.

For making zero top left, either use gluOrtho() to set up the view matrix differently from normal GL (look up the docs for that function, set top to 0 and bottom to 1 or screen_height if you want integer coords) or just make change your drawing loop and just do glVertex2f(x/screen_width, 1-y/screen_height).

There are better and faster ways to do this, but this is probably one of the easiest if you're learning raw OpenGL from scratch.

Marcus Lindblom
+2  A: 

You have to start thinking in "texture space" where the coordinates are in the range [0, 1].

So if you have a sprite sheet:

class SpriteSheet {
    int spriteWidth, spriteHeight;
    int texWidth, texHeight;

    int tex;

public:
    SpriteSheet(int t, int tW, int tH, int sW, int sH)
    : tex(t), texWidth(tW), texHeight(tH), spriteWidth(sW), spriteHeight(sH)
    {}

    void drawSprite(float posX, float posY, int frameIndex);
};

All you have to do is submit both vertices and texture vertices to OpenGL:

    void SpriteSheet::drawSprite(float posX, float posY, int frameIndex) {
        const float verts[] = {
            posX, posY,
            posX + spriteWidth, posY,
            posX + spriteWidth, posY + spriteHeight,
            posX, posY + spriteHeight
        };
        const float tw = float(spriteWidth) / texWidth;
        const float th = float(spriteHeight) / texHeight;
        const int numPerRow = texWidth / spriteWidth;
        const float tx = (frameIndex % numPerRow) * tw;
        const float ty = (frameIndex / numPerRow + 1) * th;
        const float texVerts[] = {
            tx, ty,
            tx + tw, ty,
            tx + tw, ty + th,
            tx, ty + th
        };

        // ... Bind the texture, enable the proper arrays

        glVertexPointer(2, GL_FLOAT, verts);
        glTextureVertexPointer(2, GL_FLOAT, texVerts);
        glDrawArrays(GL_TRI_STRIP, 0, 4);
    }

};
Frank Krueger
I took this answer and replaced the arrays with glBegin, glTexCoord2f/glVertex2f and glEnd calls. Everything's upside down, including the sprite itself, which is Terra laughing, but it's very close now...
Kawa
Coordinates are now flipped. The sprite is drawn as it should be!
Kawa
One man's upside down is another man's right side up. :-) Glad you got it the way you like it.
Frank Krueger
@Kawa: Actually using glBegin/End is a very bad idea.The CPU overhead is measurable.And glBegin/End is deprecated.Having all possible "frames" in one (precalculated) array and then using the second parameter of glDrawArrays (e.g. frameIndex*4) will be a big gain, since the GFX hardware can cache the array.
Andreas
A: 

Hello. A suggestion, if I may. I use SDL to load my textures, so what I did is : 1. I loaded the texture 2. I determined how to separate the spritesheet into separate sprites. 3. I split them into separate surfaces 4. I make a texture for each one (I have a sprite class to manage them). 5. Free the surfaces. This takes more time (obviously) on loading, but pays of later. This way it's a lot easier (and faster), as you only have to calculate the index of the texture you want to display, and then display it. Then, you can scale/translate it as you like and call a display list to render it to whatever you want. Or, you could do it in immediate mode, either works :)

fingerprint211b
Using one surface/texture per "frame" is a bad idea.Switching textures is a very costly operation.Usually we try very hard to merge as many textures into one as we can.
Andreas
+2  A: 

Franks solution is already very good.

Just a (very important) sidenote, since some of the comments suggested otherwise.

Please don't ever use glBegin/glEnd. Don't ever tell someone to use it.

The only time it is OK to use glBegin/glEnd is in your very first OpenGL program.

Arrays are not much harder to handle, but...

  • ... they are faster.
  • ... they will still work with newer OpenGL versions.
  • ... they will work with GLES.
  • ... loading them from files is much easier.
Andreas