Of course it’s easy to just do
glVertex2f(x + localX, y + localY); //localX = the relative coordinate (0 or width in your example code)
but what if you have a 3D model with 20 000 vertices? Each vertex has to be positioned relative to where the actual object is supposed to be rendered. Doing
glVertex3f(x + localX, y + localY, z + localZ); //Assuming 3D
for 20 000 vertices is 60 0000 floating point additions done on the CPU, plus that you can’t store the data on the GPU since it has to be updated on the CPU each frame, meaning that we’ll also transfer 20 000 x 3 floats x 4 bytes = 234,375kb per instance. Assuming something like 16 instances (for example 16 player models in a first person shooter game), and we have 320 000 vertices (easily handled by our GPU), 960 000 floating point additions per frame done on the CPU (NOT easily handled by our CPU) and also 219,7265625 MBs/sec of data transfered to our GPU at 60 frames per second.
By storing the vertex data on the GPU and then using glTranslatef() instead to position the whole set to where you want it, you can save so much work from the CPU for something that can be done free with the modelview matrix instead.
For example, if you want to “move the camera”, the proper way of doing this is to apply an offset to every tile in the game.
- NO: Add this offset manually to everything you draw with glVertex2f().
- YES: Add a single glTranslatef() to the beginning of the game loop and just draw everything with their world coordinates.
Matrices have the ability to move a coordinate from one coordinate system to another. The matrices in OpenGL are meant to transform coordinates from WORLD SPACE (= the positions the vertices have in the game world, not on the screen) all the way to “normalized device coordinates”. Normalized device coordinates are just coordinates which go from -1 to 1 for both X and Y, with (-1, -1) being the bottom left corner of the screen, an (1, 1) being the top right corner of the screen, regardless of the resolution game window. Since we have two matrices in OpenGL, we also get an intermediate coordinate system. For now I’ll stick to 2D to keep things simple.
Basically, you send your coordinates in world space. Send a tile’s position in the world. Never manually calculate where this tile would end up on the screen manually. OpenGL then transforms this by the modelview matrix. This matrix is supposed to take the coordinates from world space to view space. In the case of 2D, view space is most often simply the pixels of the screen, with the top left corner being (0, 0) and the bottom right corner being the (width, height) of the window in pixels. In other words, the modelview matrix takes your coordinates from where they are in the world to where they should be on the screen.
Now, by now we should be done, right? We have the coordinates on the screen of each vertex! But wait, OpenGL expects our coordinates in normalized device coordinates! This is a side-effect of OpenGL being made for 3D games and not primarily for 2D. Luckily there’s a function for this, and you most likely know it by heart: glOrtho(). This is what we put in the projection matrix.
Obviously, you can get the same visual result regardless of if you use glTranslatef() or not, but you get a lot better performance and you’re using OpenGL as you’re supposed to. For your code though, using glTranslatef() to position individual tiles make no sense whatsoever. There is no point in using glTranslatef() for anything that is static, since if it doesn’t move we can just reuse the same data over and over again.
Summary:
- Use glTranslatef() when positioning large number of vertices dynamically each frame. In your case, use it for the movement of the camera.
- Do not use glTranslatef() when position static data. Just precompute the data and store it on the GPU with a display list or VBO.
Your current code has a problem though: Each tile has its own texture. It is much better to have all tiles in a single texture, since the overhead of binding a new texture for each tile is pretty high, but more importantly it prevents you from being able to batch up your tiles. Preferably you’d want to draw as much as possible with the least number of OpenGL calls and state changes, and you’re doing over 2 calls per vertex at the moment. If your tiles were all in the same texture you could easily batch it into a display list (easy as pie, Google), and draw the whole map with only 2 OpenGL calls regardless of how many tiles you have, and with no state changes per tile. Note however that putting everything in one texture can make it possible to get bleeding between tiles if you don’t handle the texture correctly.
In the end, this might be completely overkill for you. If you’re drawing only drawing hundreds or even a few thousands of tiles per frame your current code will work perfectly fine as it is. If you realize that you need that CPU time for something heavy like physics or simply lots of objects, etc you could optimize it. I’m the guy who tries to make everyone make awesome graphics in their games with (correct) OpenGL, so I guess I’m the one to ask if you have any questions.
絶望した!言葉の壁で絶望した!
[spoiler]I’m in despair! The wall of words has left me in despair![/spoiler]