Creating the texture atlases

Before we start creating a texture atlas, let's first find out what this technique is actually good for. A texture atlas (also known as a sprite sheet) is just an ordinary image file that can be rendered to the screen like any other image. So what makes it so special? It is used as a container image that holds several smaller subimages arranged in such a way that they do not overlap each other and it still fits into the size of the texture atlas. This way, we can greatly reduce the amount of textures that are sent to the graphics processor, which will significantly improve the overall render performance. The texture atlases are especially useful for games where a lot of small and different images are rendered at once. The reason for this is that switching between different textures is a very costly process. Each time you change textures while rendering, new data needs to be sent to the video memory. If you use the same texture for everything, this can be avoided.

The texture atlases will not only increase the frame rate of the game significantly, but will also allow us to use subimages as Non-Power-Of-Two (NPOT) textures. The reason why our subimages can be of arbitrary size is that the power-of-two rule only applies to textures that are loaded into the video memory. Therefore, when we actually render a subimage, we are still using the texture atlas, which is a power-of-two texture as our pixel source; however, we will only use a certain part of it as our final texture to draw something.

Note

Due to the default support of OpenGL ES 2.0, LibGDX will support NPOT textures or images; however, it will take more time to render than a POT texture, depending on the underlying hardware. Nevertheless, it is more efficient to store the subimages in a texture atlas, which is treated as a single unit by the graphics hardware. Also, it can be faster to bind one large texture once than to bind many smaller images.

Take a look at the following screenshot that shows all the images of our game objects in separate image files:

Creating the texture atlases

You might wonder why the cloud and mountain images in the preceding screenshot are filled with plain white color. This is because the images contain only white and transparent pixels, so it is indeed hard or rather impossible to make out the actual image information on a white background, as it usually appears in print media. Therefore, all the images of our game objects that follow will be shown with an added gray background to rectify this small display issue. However, the actual image files still remain unchanged, as shown in the preceding screenshot.

LibGDX has a built-in TexturePacker that we will use to automate the process of creating and refreshing the texture atlas for Canyon Bunny. We will put all the game's object images shown in the preceding screenshot into the atlas to get the following result:

Creating the texture atlases

The images have been nicely arranged on the atlas without any overlap. The purple border around each image is a debugging feature of LibGDX's TexturePacker that can be toggled on and off. It can be used to visualize the true size of your subimages, which otherwise can be difficult to see whether the subimages use transparency. Good examples of these are the cloud and mountain images in our texture atlas. Also, when padding is enabled, which is the default by using two pixels for each direction, you would barely see the difference without the enabled debugging lines.

Note

Padding your images inside a texture atlas helps you avoid an issue called texture bleeding (also known as pixel bleeding) while texture filtering and/or mipmapping are enabled.

The texture filter mode can be set to smooth pixels of a texture. This is basically done by looking for the pixel information that is next to the current pixel that is to be smoothened. The problem here is that if there is a pixel of a neighboring subimage, its pixels can also be taken into account, which results in an unwanted effect of pixels bleeding from one subimage to another.

For the TexturePacker, the following preparations need to be done beforehand as it is a so-called extension to LibGDX that is not a part of the core functionality:

  1. Go to C:libgdx and extract extensions/gdx-tools.jar from the libgdx-1.2.0.zip file you downloaded earlier in Chapter 1, Introduction to LibGDX and Project Setup.
  2. Put the gdx-tools.jar file in the CanyonBunny-desktop/libs subfolder. Next, the extension has to be added to Build Path in Eclipse.
  3. In Eclipse, right-click on the CanyonBunny-desktop project and navigate to Build Path | Configure Build Path | Libraries.
  4. Then, click on the Add JARs button, which will open a new window titled JAR selection that shows a list of projects.
  5. In this list, search for the CanyonBunny-desktop project and expand it until you reach the libs subfolder.
  6. Finally, select the newly added gdx-tools.jar extension and confirm each opened window by clicking on their OK buttons.

    Note

    For Gradle users, adding gdx-tools is easy; we just need to add the following highlighted line to the build.gradle file in C:/libgdx:

    project(":desktop") {
    …
    compile "com.badlogic.gdx:gdx-tools:$gdxVersion"

    Make sure that you are editing under the section project(":desktop"). After editing, we need to refresh our dependencies. To do this, right-click on the CanyonBunny-desktop project and go to the Refresh All option in the Gradle menu. Make sure that you are connected to the Internet because Eclipse will download the relevant dependencies.

We will now add the code to automate the generation process of the texture atlas. Perform the following steps:

  1. Create a new folder called assets-raw under CanyonBunny-desktop. Also, add a subfolder named assets-raw/images. This is where we put our image files to be included in the texture atlas.
  2. Next, open the starter class for CanyonBunny-desktop and add the following two lines of code to import the TexturePacker and its Settings class:
    import com.badlogic.gdx.tools.texturepacker.TexturePacker;
    import com.badlogic.gdx.tools.texturepacker.TexturePacker.Settings;
  3. Then, apply the following changes to Main.java in the CanyonBunny-desktop project:
     public class Main {
         private static boolean rebuildAtlas = true;
         private static boolean drawDebugOutline = true;
    
       public static void main(String[] args) {
         if (rebuildAtlas) {
              Settings settings = new Settings();
              settings.maxWidth = 1024;
              settings.maxHeight = 1024;
              settings.duplicatePadding = false;
              settings.debug = drawDebugOutline;
              TexturePacker.process(settings, "assets-raw/images", "../CanyonBunny-android/assets/images", "canyonbunny.pack");
         }
    
         LwjglApplicationConfiguration cfg = new LwjglApplicationConfiguration();
         cfg.title = "CanyonBunny";
         cfg.width = 800;
         cfg.height = 480;
    
         new LwjglApplication(new CanyonBunnyMain(), cfg);
       }
    }

The added code provides a convenient way to rebuild the texture atlas every time the game is run on the desktop. The rebuildAtlas variable controls whether the atlas is rebuilt on startup or not by setting it to true or false. Using the TexturePacker class to create the texture atlas is pretty straightforward. It contains a static method called process() that takes an optional settings object to configure the way the texture atlas will be generated as well as the three parameters that are mandatory. The first mandatory parameter is the source folder that contains our image files. The second one is the destination folder where the generated texture atlas should be created. Finally, the third parameter is the name of the description file that is needed to load and use the texture atlas.

The source folder (in our example, assets-raw/images) is specified relative to the desktop project as the TexturePacker code is executed from here. The destination folder (in our example, ../CanyonBunny-android/assets/images) is also specified relative to the desktop project. However, the resulting texture atlas has to be put into the assets folder of the Android project so that it becomes available to all platform-specific projects. The description file (in our example, canyonbunny.pack) will be created by TexturePacker and will contain all the information about all the subimages, such as their location in the texture atlas, their size, and offsets.

However, for projects generated from Gradle, the project folders will have different names, refer to the gdx-setup versus gdx-setup-ui section in Chapter 1, Introduction to LibGDX and Project Setup. Hence, for targeting the assets folder inside the Android project folder in a Gradle-based project, the destination path is ../android/assets/images.

The maxWidth and maxHeight variables of the Settings instance define the maximum dimensions (in pixels) for the texture atlas. Always make sure that a single subimage does not exceed the maximum size of the atlas either in the width or height or both dimensions. Padding the subimages in the atlas will reduce the available size a little bit more, so make sure to take this factor into account too. The debug variable controls whether the debug lines should be added to the atlas or not. We use the drawDebugOutline variable to set the value to debug. The static variables rebuildAtlas and drawDebugOutline are there just for our convenience to make these two behavior controls stand out a bit more because we usually change these variables every now and then while debugging our game.

Note

If the TexturePacker cannot fit all the subimages into a single texture, it will automatically split them up into several texture atlases. However, there is a chance that the subimages are distributed in an unfavorable way between these atlases if it creates two textures that will be switched between frequently, which in turn could have an impact on render performance.

LibGDX's TexturePacker has a very smart feature to tackle this type of problem. All you need to do is group the subimages in their own subfolder in assets-raw. This way TexturePacker will create one image file per subfolder that belongs to the texture atlas. You have to use the full path to the subimage if you want to use this functionality; for example, a subimage assets-raw/items/gold_coin.png would be referenced as items/gold_coin.

Now you know how to create texture atlases in code. This approach mostly works very well, but it is not very user friendly in terms of seeing the outcome directly when a setting or an image is changed in the atlas. Fortunately, there is already a nice tool called TexturePacker-GUI that has been developed by Aurélien Ribon. This tool is directly designed for LibGDX to work with its TexturePacker.

Check out the official project website at https://code.google.com/p/libgdx-texturepacker-gui/. You can also find more about the offline TexturePacker at https://github.com/libgdx/libgdx/wiki/Texture-packer.

The following screenshot is taken from the project's website that shows the tool in action:

Creating the texture atlases

There is also a popular commercial tool called TexturePacker to create texture atlases. This tool has been developed by Andreas Löw and is available for all three major platforms. For more information, check out the official website at http://www.codeandweb.com/texturepacker.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset