For 2D work like sprites, there are a number of considerations that are important, aside from cross platform considerations (nicely handled for us in the main by GLFW) a really important concern is not just scaling but also aspect ratio. Aside from platforms where you have resizable windows, some devices could have who knows what aspect ratio. It’s very nice to have everything scaled so that your game logic just has to deal with a nominal resolution, however it’s still as well to have defines for this nominal resolution rather than magic values littered through your code.
I’ve chosen to target specifically OpenGL version 3.3, this is the version where a lot of kruft was deprecated and although GL4.0 was released at the same time, 3.3 might just be that bit more compatible in regards to audience share.
Actually setting up and rendering a textured rectangle isn’t terribly arduous but I decided to separate out the 2d rendering and isolate it as much as possible, its quite possible at a later date you might want to render sprites from different compilation units and at different times (main menu and the actual game for example), so not having a bunch of global floating around is likely to make life easier.
I’ve included the code at the end and its worth taking a quick whistle stop tour of some of the main features of the example. Starting with sprite.c there are a bunch of variables for things like buffers and even the data for the rectangle shape. The shader is rather straight forward it allows sprites to have different sizes as a parameter but other than that it couldn’t really be much simpler.
So you don’t have to pass the perspective matrix every time you want to draw a sprite, there is a function setSpriteProjection which you need to call whenever the screen / window is resized. The drawSprite routine just requires position, size, rotation and texture handle, nice and simple but still quite flexible.
In main.c the first point of note is the windowSizeCallback function, which is called by GLFW any time that our physical screen size changes, by sticking to an aspect ratio (in this case 16:9) we can minimise the area on the screen where we don’t want to draw (thus preserving our aspect ratio), and oh the horror there are globals… the rest of the scaling magic happens at the start of the main loop, if a resize has happened the perspective matrix is recalculated and the whole physical screen is cleared black, the GL scissor function is used to preserve the new screen area at our required aspect ratio, because of the new perspective matrix the nominal resolution is scaled into this scissored area.
Once you have the lower level basics like this down, you can concentrate on higher level stuff like game logic and in fact you might even want to abstract out the scaling code (somehow!) just so that you can concentrate on the more interesting stuff without the lower level minutia getting in the way.
You can get the code here