e
Introduction
I've decided to open source our in-house texture atlas creation tool,
because I think this will be useful for other independent game
developers.
Features
Loads textures nearly 5 times faster than loading from PNG
Streamlines the art pipeline. Can creates multiple atlases with multiple formats, all with one click.
Java source code provided, so you can modify the tool for your own needs.
What is a texture atlas?
A texture atlas bin-packs multiple
images into a single texture. Because OpenGL textures have to be sized
as power of 2, loading the images separately would require a lot of
padding, which means wasted GPU memory. As well as that having all
images in one texture allows draw-call batching: in other words several
sprites can be drawn with one call.
Who is this for?
Although the tool was developed for the iPhone, it is useful for most platforms including Android and PC. The tool is written in java and runs on Windows, Mac and Linux.
A sample iPhone project is included, that demonstrates loading the texture atlas. The code is c++ and designed to be portable.
How to use
Download the file AtlasMaker_jar.zip and unzip. You will need java installed on your machine to use it. You may also need to install PVRTexTool, if you want to create PVR compressed textures.
The source images for your project must be in PNG format and must be
organised in directories according to file format. Select the location
of the input folder and the directory structure will be shown. Then select
the required file format for each directory
This is the default option. Choose this for best quality. Note: targa files have RGB bytes in OpenGL order, so they load faster. This means the file will look odd if you open it on your PC, but it will look fine on the device.
Choosing this option uses only 16-bits per pixel, but reduces the number of colours. Textures with subtle colour changes (i.e. gradients) will look bad.
Compressed PVR format. Uses either 2, or 4-bits per pixel. This format works better with "organic" images with no transparent borders. Note: a padding of 2 pixels is created around each image. This is to prevent pixels appearing from adjacent images (the PVR algorithm draws pixels outside of the image borders).
Select the name and location of the output file. The texture atlases will be saved to the same directory.
Advanced options
Click on the "Options..." button to open advanced options.
Transparent pixels are trimmed off all source images. The alpha threshold indicates what is considered to be transparent.
This is the file location where you installed the PVRTexTool (PVRTexTool.exe)
When the PVR file format is selected, first the texture atlas will be created as a PNG, then the PVRTexTool will be called to convert it to PVR. This is a slow operation, so by default "Convert PVRs" is deselected.
Choose 2-bit for smaller memory use, but lower quality, or 4-bit for higher quality.
Output tabs
A tab is created for each texture. The tab icon hold a preview of the texture. For convenience, clicking anywhere in the texture will display the file name of the image clicked on.
Textures created
OpenGL textures must be have height and width dimension that are power-of-two. As well as that, PVR textures must be square. The iPhone limits the maximum texture size to 1024x1024.
The application tries to fit the images in the smallest texture size possible: i.e. first it tries to fit in 32x32, then 32x64, then 64x64, then 128x64... and so on till the maximum 1024x1024. If the images still don't fit, then a second texture will be created, and so on.
Each file will be named by the directory and an index number. E.g. if images are in the directory "images" then the following textures are created: images0.tga, images1.tga, images2.tga, etc.
Output XML file
The application creates an XML file, which contains all data for each image in each texture atlas, example:
<atlas numImages="4">
<texture file="testImages0.pvr">
<image name="arch" x="0" y="0" width="89" height="71"/>
<image name="prowlerBody0_1" x="36" y="342" width="43" height="34" yOffset="5" transWidth="46" transHeight="39"/>
</texture>
<texture file="dump0.pvr">
<image name="arrowDownPressed" x="400" y="54" width="46" height="39"/>
<image name="arrowUpPressed" x="448" y="54" width="46" height="37"/>
</texture>
</atlas>
First, the "atlas" element tells you total images in atlas, so memory can be pre-allocated when loading.
Then each texture element has the file name and a list of image element.
Each image element contains the following data:
The position within the texture and size of image
- xOffset, yOffset, transWidth, transHeight
Padding data for sprite animations. If we have sprite animations, each sprite needs to be padded so that the image doesn't "jump" around during animation, but we don't want to store the blank space in the texture and waste memory.
Why PNG is unsuitable for games
PNG is a complex format and slow to decode. Here are some benchmarks, which show just how slow PNG is:
| PNG | Targa 32-bit* | Targa 16-bit |
Loading Windows * | 64 ms | 9 ms | 5 ms |
Loading iPhone 3GS* | 385 ms | 83 ms | |
| | | |
Size | 1.27 MB | 2.42 MB | 1.23 MB |
Zipped size | 1.26 MB | 1.25 MB | 339 kB |
Loading a 1024 x 1024 texture (complex image with transparency)
As you can see on the iPhone texture loading is 4.6 times slower than 32-bit Targa. Although the Targa file is twice as big it's the same size when zipped, so the package size will be the same. On top of that, if your images only need to be 16-bit quality you get double savings. PNG does not have any 16-bit option.
As you can see, using Targa is a no-brainer.
PNG decoded on Windows using libpng.PNG decoded on iPhone using native iOS API (i.e. UIImage). Targa is compressed RLE Targa.
Demo iPhone project
Included is a xcode project that opens the generated textures and displays a sprite.
The following classes are used in the project:
Main game object.
Sets up OpenGL state and initialises all resources
Reads atlas xml file and creates the AtlasImage
objects and textures and stores them in a map for retrieval.
Fast XML parser, with cut down functionality. Parses using forward-only method to avoid the overhead of creating a DOM.
Reads 32-bit and 16-bit Targa files
All texture files are loaded into a shared buffer, rather than creating and deleting each time
Loads PVR textures.
Creates a textured quad for each image.
Note: the project uses
boost/unordered_map.hpp
. You need to download boost and set the project include headers in xcode to point to where you installed boost. Alternatively, you can change the line:
#define fmap boost::unordered_map
, to
#define fmap std:map
in precompile.h
License
This application is released under the BSD license. This means both commercial and non-commercial entities can use it. Commercial and non-commercial third parties can also include it in their products for free. The only thing I ask is that the copyright notice remains in the About box and source code.