Atlas Creator Tool

Texture packing or atlasing is a way to group multiple textures into a single larger texture, usually referred to as an atlas. This is usually done in order to reduce the number of draw calls to the GPU. Prysm supports texture atlasing internally to provide increased performance for its users.

Atlas Creator tool

Introduction

The SDK comes with an easy-to-use and easy-to-automate command-line tool for packing. The Atlas Creator takes in a list of textures to pack and produces an atlas, or atlases containing the textures. It also produces a JSON-formatted file, containing the mapping between each texture and the location in its respective atlas.

Usage

To use the tool simply navigate to the Tools/AtlasCreator folder in your Prysm package, where you will find the AtlasCreatorTool.exe file. The tool currently only has a Windows version and operates in the following way:

  1. You should specify:
  • The textures to pack via a directory, a list of files, or a combination of the two.
  • A root path so that all generated texture mappings will be relative to it.
  • An output directory, atlas name and atlas dimensions. (optional)
  1. The Atlas Creator will output:
    • An atlas or multiple atlases(depending on the setup) in a PNG or TGA format, containing your textures.
    • A mappings file, containing information about the coordinates of each texture in the atlas(es), including their respective atlas and coordinates inside the atlas.

Example usage:

AtlasCreator.exe -w 1024 -h 1024 -c configFile.txt -d E:\\textures\\ -s --coui-root E:\\ -o E:\\atlasOutput\\

Mapping file format

For every atlas created there will be an object in the JSON file containing the atlas name, width, height and an array of belonging textures. The array of textures will contain one object per texture packed in the atlas, containing the x, y, width, height and name, where x and y are the coordinates of the top-left corner of the packed texture inside the respective atlas. The final output should look like this:

[
    {
        "name": "UIAtlas0.png",
        "width": 2044,
        "height": 2019,
        "textures":
        [
            {
                "URL": "Path/To/Your/Image.png",
                "top": 1392,
                "left": 1984,
                "width": 57,
                "height":61
            },
            {
                "URL": "Path/To/Your/Image2.png",
                "top": 203,
                "left": 632,
                "width": 530,
                "height":182
            },
        ...
        ]
    },
    {
        "name": "UIAtlas1.png",
        "width": 2024,
        "height": 1091,
        "textures":
        [
        ...
        ]
    }
    ...
]

Additional info:

  • The supported input image types are JPG, PNG, BMP, TGA.
  • The Atlas Creator can take parameters from a configuration file in order to make texture batching automation easier. The configuration file should contain the command line parameters you wish to pass to the tool. Any parameters passed to the command line will override parameters in the configuration file.
  • Any generated atlases will be clamped to their minimal possible size unless the –no-clamp parameter is specified.
  • If your textures do not fit into a single atlas multiple atlases will be generated with the –single-atlas parameter is specified.
  • The atlas creator tool automatically adds padding to your textures in order to ensure that they work on all platforms. The padding is as follows: the height of each texture is padded by one pixel, the width of each texture is padded to a multiple of four and if it is already a multiple of four a padding of four pixels is added. This does not affect the final width and height values of a texture in the mappings file, it just modifies the texture coordinates inside the atlas.
  • The atlas creator tool takes in non-pre-multiplied textures and produces pre-multiplied atlases with regard to the alpha channel of the images.

Command line parameters table:

ArgumentShorthandDescriptionExample
--height “value”-hSpecifies the max height of the generated texture atlas. Defaults to 2048.-h 1024
--width “value”-wSpecifies the max width of the generated texture atlas. Defaults to 2048.-w 1024
--config “value”-cThis parameter should provide a path to a config text file. The config file should contain parameter-value pairs, separated by an interval in the format PARAMETER VALUE, separated by whitespace. Any parameters passed to the command line will override parameters in the config file.-c E:\files\config.txt
--input-dir “value”-dSpecifies a folder path, containing textures that need to be packed. They can have the following types: JPG, PNG, BMP, TGA.-d E:\textures\
--input-file-list “value”-iSpecifies a path to a file, which includes a list of textures to pack into the atlas. The file should contain file names separated by newlines.-i E:\files\inputFiles.txt
--regex “value”-rSpecifies a regular expression in the c++ "regex" format and includes all the files matching the expression inside the –input-dir as textures to pack.-r \D*\d.png this would match img1.png, myimg3.png, etc.
--coui-root “value”NoneSpecifies the root path. All mappings are generated relative to the root path. If you want to use the atlas in Cohtml this should point to the same directory as “coui://”. Defaults to the current directory.--coui-root E:\textures\
--atlas-name “value”-nSpecifies the output atlas name. The output file type is PNG. Defaults to UIAtlas.-n myAtlas
--output-dir “value”-oSpecifies the folder where atlas(es) and mappings file should be generated. Defaults to the current directory.-o E:\output\
--no-clampNoneSpecifies that the atlas will always be of the specified size even if there is leftover space after packing. If not specified the atlas will always be clamped to the minimum possible size.--no-clamp
--single-atlas “value”-sSpecified for the tool to create only a single atlas. The tool will exit with an error if the given textures exceed the size of the atlas.-s
--input-texture-max-height “value”NoneSpecifies the maximum allowed height for the input textures. Any textures not meeting that requirement will not be processed.--input-texture-max-height 100
--input-texture-min-height “value”NoneSpecifies the minimum allowed height for the input textures. Any textures not meeting that requirement will not be processed.--input-texture-min-height 100
--input-texture-max-width “value”NoneSpecifies the maximum allowed width for the input textures. Any textures not meeting that requirement will not be processed.--input-texture-max-width 100
--input-texture-min-width “value”NoneSpecifies the minimum allowed width for the input textures. Any textures not meeting that requirement will not be processed.--input-texture-min-width 100
--output-format-fSpecifies the atlas output format. Supported formats are: PNG, TGA. Defaults to PNG.-f PNG
--helpNonePrints out the possible parameters with their corresponding descriptions.--help

Using atlases in Cohtml

The code snippets below have been shortened in order to make this tutorial easier. You can find the full implementation of the code used here in the TextureAtlasingSample.

Generally speaking, using an atlas in Cohtml consists of three steps:

  1. Reading the JSON mapping file produced by the Atlas Creator and storing all atlased textures
  2. Preloading the atlas textures using your rendering backend
  3. When a resource request from Cohtml comes in and the requested texture is in an atlas -> passing the atlased texture and the corresponding coordinates instead of the original image. All of those steps are demonstrated below.

Reading the mapping file

The ResourceLoader class provided in the package already has a LoadAtlasMapping(const std::string& mappingFilePath) method in it:

// Parse the JSON file using a parser of your choice. In our case that's the nlohmannJSON parser.
auto mapping = json::parse(jsonContent);
for (auto atlas : mapping)
{
    std::string name = atlas["name"];
    // For each texture in each atlas
    for (const auto& texture : atlas["textures"])
    {
        // Store the relevant information in the mapping file
        // about the position of the texture in the atlas
        AtlasedTextureInfo currentTextureInfo;
        auto textureURL = texture["URL"].get<std::string>();
        currentTextureInfo.AtlasName = name;
        currentTextureInfo.X = texture["left"];
        currentTextureInfo.Y = texture["top"];
        currentTextureInfo.Width = texture["width"];
        currentTextureInfo.Height = texture["height"];
        // Put the texture->atlas mapping information inside an unordered_map so we can look it up later
        m_AtlasMapping.emplace(textureURL, currentTextureInfo);
    }
    atlases.push_back(name);
}
// Return a vector of all atlas names found for convenience
return atlases;

Loading the atlas textures

Cohtml samples come with an ImageLoader class that can be used to load images on the GPU. Here is some of the code inside it’s PreloadImage function

resources::UserImageData* PreloadImage(const char* src)
{
    // Get the raw pixels from the image
    std::vector<unsigned char> image;
    // In our case we're decoding with the lodepng library, but you can use any library you want
    unsigned error = lodepng::decode(image, props.Width, props.Height, src);
    // Create the texture on the backend
    ITexture* result = m_Renderer->CreateTexture(props, image.data());
    std::unique_ptr<resources::PreloadedImageData> userImageData(new resources::PreloadedImageData);
    userImageData->Texture.reset(result);
    std::string imageName;
    // Put the data on the GPU
    FillUserImageData(m_RendererType, src, *userImageData.get(), imageName);
    // Return a plain pointer to the generated userImageData, which contains the texture
    return userImageData.release();
}

Modifying the resource loader

In order to actually use the loaded atlases you need to pass them instead of the requested textures. Below is a snippet from the sample ResourceLoader’s OnResourceRequest method.

// Check the mapping to see if the texture is in an atlas
auto atlasedImageIt = m_AtlasMapping.find(path);
if (atlasedImageIt != m_AtlasMapping.end())
{
    auto& atlasedTextureInfo = atlasedImageIt->second;
    auto& atlasImage = m_PreloadedAtlases[atlasedTextureInfo.AtlasName];
    // Generate the default data for the atlas containing the texture as if we were going to draw the entire atlas.
    auto data = atlasImage->GenerateUserImageData();
    // Replace the coordinates, width and height of the texture with those provided by the mapping file
    data.ContentRectX = atlasedTextureInfo.X;
    data.ContentRectY = atlasedTextureInfo.Y;
    data.ContentRectWidth = atlasedTextureInfo.Width;
    data.ContentRectHeight = atlasedTextureInfo.Height;
    // Add a user-defined identifier that will be used to identify textures belonging to the same atlas.
    // This should be the same for all textures belonging to the same atlas so a pointer to the atlas texture is sufficient
    data.TextureBatchingHint = atlasImage->GetTexture();
    // Pass the modified data to Cohtml
    response->ReceiveUserImage(data);
    // Signall success
    response->Finish(cohtml::IAsyncResourceResponse::Status::Success);
    return;
}

Making it all work together

Here is a snippet from the AtlasingSample’s Initialize method. It uses the functionality described above and demonstrates the workflow of adding an atlas to your program.

    // This can be the output folder from the AtlasCreator tool or any other location you move your atlases to.
    std::string atlasesLocation = "uiresources/Kits/FPS-Kit/FPS-HUD/atlases/";
    // We load the mapping into the resource handler by providing a path to the JSON file.
    // Again this could be the output folder from the Atlas Creator tool or any other location you choose.
    auto atlases = m_AppResourceHandler->LoadAtlasMapping("cohtml/Samples/uiresources/Kits/FPS-Kit/FPS-HUD/atlases/UIAtlas.json");
    for (auto atlasName : atlases)
    {
        // For every atlas we generate the full name
        std::string atlasPath = atlasesLocation + atlasName;
        // We preload the atlas images via the ImageLoader class and create an atlas->atlasTexture mapping for the ResourceLoader to use
        m_AppResourceHandler->AddAtlas(atlasName, resources::UserImageDataPtr(m_ImageLoader.PreloadImage(atlasPath.c_str())));
    }

Should you set up your application in the way shown above, all atlased textures will be read from their respective atlas and everything else will be loaded from disk.