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. 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. On some platforms 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_2.2.1.zip and unzip. You will need Java installed on your machine to use it. You may also need to install the PVRTexTool from Imagination Technologies 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. The file is 32-bit, or 24 bit if there is no alpha channel.
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.
This is the maximum texture size (both width and height) for the device you are targetting.
- Power-of-two / Non power-of-two
Power of two restricts the size to power of two dimensions (ie. 32x32, 64x32, etc.). Non power-of-two means there is no restriction at all.
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 command line PVRTexTool.
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.
Choose either the default XML format,
or Cocos2D plist format.
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
The application tries to fit the images in the smallest texture size
possible. In the case of power-of-two textures first it tries to fit in 32x32, then 32x64, then 64x64,
then 128x64... and so on till the maximum. In the case of Non power-of-two it'll choose the minimum arbitary size automatically. If the images don't in the maximum size texture, additional textures are created until everything fits.
If you choose the PVR format the textures will be square and power-of-two.
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 may be 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" trans="true">
<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" trans="true">
<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, transparency and a list of image elements.
Each image element contains the following data:
- x, y, width, height
The position within the texture and size of the 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 (libpng) | 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 iPhone using native iOS API (i.e. UIImage). Targa is compressed RLE Targa.
Loading Targa
See the ImageTarga
and TargaReader
classes for details how the targa files are loaded.
Targa 16 bit is stored as GL_UNSIGNED_SHORT_4_4_4_4
. However, if the texture atlas is completely opaque the alpha channel is not saved and the pixels are stored as GL_UNSIGNED_SHORT_5_6_5
.
If "Targa True Color" is selected then it's saved as either 32-bit or 24-bit, depending on if it has alpha transparency or not.
Demo iPhone project
Included is a xcode project that opens the generated textures and displays a sprite. Most of the code is c++ for portablity.
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
History
Version 2.0
- Added Non power-of-two functionality
- Allowed user to specify Maximum texture size
- Improved atlas creation algorithm
- Made code more robust
Version 2.1
- Doesn't save alpha channel if there is no transparency in the texture
- Fixed bug: Mac version wasn't reading PVRTexTool executable properly
Version 2.2.1
- Added support for Cocos2D (with help from Starfair :) )
- Small improvements in algorithm