So I've set up my framework in a neat little system to wrap SDL, openGL and box2D all together for a 2D game.
Now how it works is that I create an object of "GameObject" class, specify a "source PNG", and then it automatically creates an openGL texture and a box2d body of the same dimensions.
Now I am worried about if I start needing to render many different textures on screen.
Is it possible to load in all my sprite sheets at run time, and then group them all together into one texture? If so, how? And what would be a good way to implement it (so that I wouldn't have to manually be specifying any parameters or anything).
The reason I want to do it at run time and not pre-done is so that I can easily load together all (or most) of the tiles, enemies etc.. of a certain level into this one texture, because every level won't have the same enemies. It'd also make the whole creating art process easier.
There are two main reasons to use a texture atlas. First, it can increase rendering speed by allowing you to batch more objects into a single draw call. For example, if all the plants in the game use just one texture atlas, then you can draw them all at the same time.
With a common maximum texture size of 4,096 x 4,096 pixels, the build system will ideally fit all of your source textures into one destination texture.
One solution is to use a format that have generated mipmaps, then you could create thees mipmaps your self and correct the error. Another solution is to force a specific mipmap level in your fragmentshader when you are sampling the texture. Another solution is to fill the mipmaps with the first mipmap.
Minecraft uses both procedurally-generated and predefined texture atlases for different purposes.
There are likely some libraries that already exist for creating texture atlases (optimal packing is a nontrivial problem) and converting old texture coordinates to the new ones.
However, if you want to do it yourself, you probably would do something like this:
Obviously you also want to check to make sure you are not doing something silly like loading the same texture into the atlas twice, but that's a concern that's outside this procedure.
To "blit" (copy) from the source image to the target image you'd do something like this (assuming you're copying a 128x128 texture into a 512x512 atlas texture, starting at (128, 0) on the target):
unsigned char* source = new unsigned char[ 128 * 128 * 4 ]; // in reality, comes from your texture loader
unsigned char* target = new unsigned char[ 512 * 512 * 4 ];
int targetX = 128;
int targetY = 0;
for(int sourceY = 0; sourceY < 128; ++sourceY) {
for(int sourceX = 0; sourceX < 128; ++sourceX) {
int from = (sourceY * 128 * 4) + (sourceX * 4); // 4 bytes per pixel (assuming RGBA)
int to = ((targetY + sourceY) * 512 * 4) + ((targetX + sourceX) * 4); // same format as source
for(int channel = 0; channel < 4; ++channel) {
target[to + channel] = source[from + channel];
}
}
}
This is a very simple brute force implementation: there are much faster, more succinct and more clever ways to copy an array, but the idea is that you are basically copying the contents of the source texture into the target texture at a given X and Y offset. In the end, you will have created a new texture which contains the old textures in it.
If the indexing math doesn't make sense to you, think about how a 2D array is actually indexed inside a 1D space (such as computer memory).
Please forgive any bugs. This isn't production code but instead something I wrote without checking if it compiles or runs.
Since you're using SDL, I should mention that it has a nice function that might be able to help you: SDL_BlitSurface
. You can create an SDL_Surface
entirely within SDL and simply use SDL_BlitSurface
to copy your source surfaces into it, then convert the atlas surface into a GL texture.
It will take care of all the math, and can also do a format conversion for you on the fly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With