テクスチャアトラスジェネレータ#

概要#

テクスチャアトラスジェネレータは複数の画像を結合して一括で管理するためのツールです。テクスチャ切り替えによる描画パフォーマンスへの影響を改善すると同時に、アプリケーション側からの扱いも簡略化されます。

本ツールが出力するリソースは、専用のエクステンションを用いることでMGLから容易にアクセス可能になります。本項ではこのエクステンションについても併せて解説します。

ツールの導入#

アウトラインフォントをラスタライズするためにはmgl-texatlasというツールを用います。このツールはコマンドラインツールであり、コンソール(Windowsのコマンドプロンプト、macOSのターミナル等)から利用します。

ツールの導入方法は次の通りです。

Windows#

MGLのダウンロードページにアクセスし、「ツール」からmgl-texatlasのWindows向けの実行可能バイナリをダウンロードしてください。ダウンロードしたアーカイブ内にある「mgl-texatlas.exe」がツール本体になります。このファイルを必要に応じてパスの通ったディレクトリにコピーしてください。

macOS#

macOSではHomebrewを用いて導入します。Homebrewについては次のリンク先を参照してください。

MGL関連のツールは公式パッケージとして登録されていないため、最初にtapコマンドを用いてパッケージの追加を行う必要があります。

$ brew tap AcerolaSoftware/tap

このコマンドにより、MGLの配布元が提供しているツール類をHomebrewから導入できるようになります。

tapコマンド実行後、installコマンドによりmgl-texatlasをインストールします。

$ brew install mgl-texatlas

実行後、mgl-texatlas -vと入力してバージョン情報が表示されていれば成功です。

その他の環境#

一般的なPOSIX準拠のシステムであれば、ソースコードからビルドすることで利用可能です。

mgl-texatlasはビルドに次のソフトウェアを利用します。

  • C++17に対応したコンパイラ(clang推奨)

  • SCons (バージョン4.0.0以降)

  • pkg-config

依存ライブラリは次の通りです。これらはpkg-configで参照できる必要があります。

  • libpng

  • JsonCpp

ビルド方法は次の通りです。

$ scons

正常にビルドが完了した場合、そのディレクトリにmgl-texatlasが生成されます。

ツールの使用方法#

ここでは例として、次のサンプル画像を用いて解説します。

サンプル画像
../_images/sample_a.png ../_images/sample_b.png ../_images/sample_c.png

画像のファイル名は左から順にsample_a.pngsample_b.pngsample_c.pngで、それぞれのサイズは128x128です。

注釈

現時点ではmgl-texatlasは入力・出力共にPNG形式の画像にのみ対応しています。

基本的な使い方#

mgl-texatlasへの入力ファイルは引数にファイル名で指定します。

$ mgl-texatlas sample_a.png sample_b.png sample_c.png

コマンド実行後、texture0.png , texture_loc.bintexture_loc.jsonの3つのファイルが生成されます。

texture0.pngが結合後の画像です。texture_loc.bintexture_loc.jsonはロケーション情報と呼ばれ、入力画像がどこに配置されたかの情報を保持したファイルになります。ロケーション情報はプログラム側から利用する際に必要になる情報で、詳細については後述します。

このコマンドで出力された画像texture0.pngの内容は次の通りです。

出力結果(1024x1024)
../_images/output_1024.png

注釈

ここでは背景を黒で表示していますが、実際には透過しています。

入力ファイルの指定には、ワイルドカードを用いることも可能です。

$ mgl-texatlas *.png

ただし、このコマンドを2回実行すると、出力結果であるtexture0.pngも入力に含まれてしまうため注意が必要です。これを避けるには、生成されるファイルの出力先を--outputオプションにて変更してください。例として、出力先をdestディレクトリに指定する方法を次に示します。

$ mgl-texatlas *.png --output dest

このコマンドの実行後のファイル構成は次のようになります。

./
├── sample_a.png
├── sample_b.png
├── sample_c.png
└── dest
    ├── texture0.png
    ├── texture_loc.bin
    └── texture_loc.json

入力に使用したファイルパスはプログラム側から指定する名前として扱われるため、これらを考慮して扱いやすいディレクトリ構成にしてください。

サイズの変更#

mgl-texatlasが出力する画像のデフォルトサイズは1024x1024です。このサイズを変更するには--sizeオプションを使用します。

出力サイズを512x512に変更する例を次に示します。

$ mgl-texatlas *.png --size 512x512
出力結果(512x512)
../_images/output_512.png

技術的な理由から、GPUは2の累乗サイズの正方テクスチャを好む傾向にあります。また、扱えるテクスチャの最大サイズはGPUによって異なります。これらの理由から、指定するサイズは256x256、512x512、1024x1024、2048x2048などを推奨します。

入力画像が指定のサイズに収まりきらなかった場合はtexture1.pngtexture2.png ……と連番で分割されて出力します。分割数に制限はありませんが、分割数に比例して描画パフォーマンスに影響を及ぼす可能性があるため、1枚の画像に収まっている状態が理想的です。

注釈

既知の問題として、mgl-texatlasの配置アルゴリズムはあまり作り込まれていないため、1枚に収まりきらなかった場合に効率の悪い配置を行う場合があります。この症状は、指定サイズのおおよそ40%以上の画像を複数入力した場合に特に顕著です。

なお、出力サイズよりも大きな画像を入力した場合はエラーとなり処理を中断します。

ロケーション情報#

結合した画像と共に出力されるtexture_loc.bintexture_loc.jsonには、入力画像が何枚目のどの位置に配置されたかが記録されています。mgl-texatlasはこれらのファイルをロケーション情報と呼んでいます。

texture_loc.binはバイナリフォーマットで格納されたロケーション情報で、後述のMGL向けエクステンションはこちらを使用します。バイナリフォーマットの詳細についてはロケーション情報のバイナリフォーマットの節にて解説します。

texture_loc.jsonはロケーション情報をJSON形式のテキストで格納したファイルです。主に出力結果の確認や、他のファイルとの連携等に使用することを想定して出力しています。

例として、先述の3つの画像を結合した際に出力されるtexture_loc.jsonは次の通りです。

texture_loc.json
{
    "hash" :
    {
        "algorithm" : "FNV1a32",
        "prime" : 16777619,
        "seed" : 2750857790
    }, "locations" :
    [
        {
            "hash" : 2086089304,
            "height" : 128,
            "index" : 0,
            "name" : "sample_a",
            "width" : 128,
            "x" : 1,
            "y" : 1
        },
        {
            "hash" : 2119644542,
            "height" : 128,
            "index" : 0,
            "name" : "sample_c",
            "width" : 128,
            "x" : 259,
            "y" : 1
        },
        {
            "hash" : 2136422161,
            "height" : 128,
            "index" : 0,
            "name" : "sample_b",
            "width" : 128,
            "x" : 130,
            "y" : 1
        }
    ]
}

入力画像の分割#

引数に指定する画像をさらに分割して扱いたい場合、同名のCSVファイルを用意することで、その領域をロケーション情報へと追加できます。

例として、次の画像ghost.pngを分割することを想定します。この画像には1枚の画像に16x16ドットのキャラクターのアニメーションパターンが描かれています。

ghost.png(88x16)
../_images/ghost.png

この画像に対応したCSVファイルghost.csvは、入力画像と同じディレクトリに配置する必要があります。

CSVファイルはRFC 4180に準拠した形式である必要があります。すなわち、区切り文字に「,」を使用し、改行コードはCR+LFで保存してください。推奨はしませんが、UTF-8エンコーディングを用いることで非ASCII文字を扱うことも可能です。

CSVファイルのフィールドには順に名前、X座標、Y座標、幅、高さを指定します。ghost.pngを分割したghost.csvの記述例を次に示します。

テキストでの記述例
pattern0,0,0,16,16
pattern1,24,0,16,16
pattern2,48,0,16,16
pattern3,72,0,16,16
CSV+による記述例
../_images/ghost_csv+.png

この場合、ロケーション情報にはghost/pattern0からghost/pattern3までの4つの名前で追加登録されます。目的の領域を表示したい場合、これらの名前をプログラム側から指定することになります。

先述の3つの画像にこれらを加えた際の実行結果は次のようになります。

実行コマンド
$ mgl-texatlas --size 512x512 sample_a.png sample_b.png sample_c.png ghost.png
出力画像(512x512)
../_images/output_split.png
ロケーション情報
{
    "hash" :
    {
        "algorithm" : "FNV1a32",
        "prime" : 16777619,
        "seed" : 2750857790
    },
    "locations" :
    [
        {
            "hash" : 2086089304,
            "height" : 128,
            "index" : 0,
            "name" : "sample_a",
            "width" : 128,
            "x" : 1,
            "y" : 1
        },
        {
            "hash" : 2119644542,
            "height" : 128,
            "index" : 0,
            "name" : "sample_c",
            "width" : 128,
            "x" : 259,
            "y" : 1
        },
        {
            "hash" : 2136422161,
            "height" : 128,
            "index" : 0,
            "name" : "sample_b",
            "width" : 128,
            "x" : 130,
            "y" : 1
        },
        {
            "hash" : 2863951557,
            "height" : 16,
            "index" : 0,
            "name" : "ghost",
            "width" : 88,
            "x" : 388,
            "y" : 1
        },
        {
            "hash" : 4004035316,
            "height" : 16,
            "index" : 0,
            "name" : "ghost/pattern0",
            "width" : 16,
            "x" : 388,
            "y" : 1
        },
        {
            "hash" : 4020812935,
            "height" : 16,
            "index" : 0,
            "name" : "ghost/pattern1",
            "width" : 16,
            "x" : 412,
            "y" : 1
        },
        {
            "hash" : 4037590554,
            "height" : 16,
            "index" : 0,
            "name" : "ghost/pattern2",
            "width" : 16,
            "x" : 436,
            "y" : 1
        },
        {
            "hash" : 4054368173,
            "height" : 16,
            "index" : 0,
            "name" : "ghost/pattern3",
            "width" : 16,
            "x" : 460,
            "y" : 1
        }
    ]
}

追加した画像ghostとは別に、ghost/pattern0からghost/pattern3までの領域がロケーション情報に追加されています。

オプション一覧#

--hash-seed

ロケーション名に使用するハッシュ生成関数のシード値を指定します。指定しなかった場合は0xA3F6C23E ( MGL::Hash::kFNV1aDefaultValue32 )が使用されます。

ハッシュ値が衝突を起こす場合はこのオプションを使用してシード値を変更してください。

--help

簡易ヘルプを表示して終了します。

--margin, -m

結合する画像の間隔をドット単位で指定します。0から32の間の値で指定し、省略時には1が使用されます。

--name, -n

出力するファイル名に付ける名前を指定します。省略時には"texture"が使用されます。

--output, -o

出力ディレクトリを指定します。指定しなかった場合はカレントディレクトリに出力します。

--size, -s

出力する画像のサイズを指定します。幅と高さを「x」で区切って指定してください。省略時には1024x1024が使用されます。

--verbose

より多くのメッセージを表示します。

--version, -v

バージョン情報を表示して終了します。

MGLでの利用方法#

ライセンス#

ここで紹介するエクステンションはMGL本体と同じzlibライセンスに基づいて配布されています。もしMGLと併用する場合、このエクステンションはMGLの一部として扱って問題ありません。zlibライセンスの詳細はMGLのライセンスを参照してください。

導入#

MGLのダウンロードページではmgl-texatlasの出力結果をMGLで扱うためのエクステンションも提供しています。

エクステンションはソースコードとして配布されています。まず、ダウンロードページの「エクステンション」からmglext-texture-atlas-holderをダウンロードしてください。アーカイブ内のmglextディレクトリ内にソースコードが含まれていますので、これをアプリケーション側のプロジェクトへと追加してください。

ここでは、導入方法各プラットフォーム共通で示したディレクトリ構成に追加し、次のような構成になることを想定します。

src
├── app_main.cc
├── app_main.h
└── mglext
    ├── texture_atlas_holder.cc
    ├── texture_atlas_holder.h
    └── texture_piece.h

また、mgl-texatlasの出力結果をリソースとして扱う必要があります。workdirに全てのPNGファイルとtexture_loc.binをコピーします。Xcodeの場合はプロジェクトへの追加も忘れないでください。

結果、次のような構成になることを想定します。

workdir
├── texture0.png
└── texture_loc.bin

注釈

ディレクトリ構成は一例です。作成するゲームに合わせ適宜変更してください。

リソースの読み込み#

エクステンションに含まれているTextureAtlasHolderは、結合したテクスチャとロケーション情報を保持するためのクラスです。まずはこのクラスを初期化してリソースを読み込む必要があります。

TextureAtlasHolderの初期化と読み込み処理の例を次に示します。

// TextureAtlasHolderを使用するためのヘッダをインクルード
#include "mglext/texture_atlas_holder.h"

...

/* ------------------------------------------------------------------------- */
/*!
 *  \brief      初期化処理
 *  \retval     true    成功
 *  \retval     false   失敗
 */
/* ------------------------------------------------------------------------- */
bool Application::OnInitialize() noexcept
{
    // TextureAtlasHolderのインスタンスの生成
    auto &textureAtlasHolder = MGLExt::TextureAtlasHolder::CreateInstance();

    // TextureAtlasHolderにリソースを読み込み
    if (!textureAtlasHolder.Load("$resource/", "texture"))
    {
        return false;
    }

    return true;
}

TextureAtlasHolder::Load()の第1引数にはmgl-texatlasの出力結果が格納されたディレクトリを、第2引数には出力名のプリフィックスを指定します。プリフィックスはデフォルトではtextureです。--nameオプションを使用して出力名を変更している場合は、その名前を指定してください。

正常に読み込みが行われた場合は戻り値にtrueが返ります。

テクスチャの利用#

読み込まれたテクスチャを利用するには、TextureAtlasHolderを直接呼び出さずにTexturePieceクラスを利用します。このクラスはハッシュのシード値を外から指定するテンプレートクラスとなっているため、次のようなエイリアスを定義することを推奨します。

#include "mglext/texture_piece.h"

namespace YourApp   // YourAppの名前は作るゲームに合わせて変更する
{
    using TexturePiece = MGLExt::TexturePiece<>;

    // ハッシュのシード値を変更した場合は次のように引数にその値を指定する
    // using TexturePiece = MGLExt::TexturePiece<0x12345678>;
}

テンプレート引数にはハッシュのシード値を指定しますが、デフォルトのまま変更していない場合は省略可能です。もしハッシュの衝突などの理由で--hash-seedオプションを使用してシード値を変更した場合は、そのシード値をテンプレート引数に記述してください。

このエイリアスの定義を記述したヘッダを作成し、以降はそれをインクルードしてYourApp::TexturePieceを使用するようにしてください。

これで一通りの準備は整いました。後はYourApp::TexturePieceにロケーション名を渡せば、その領域を表すテクスチャを取得できるようになります。

ロケーション名はmgl-texatlasの引数に指定したファイルパスから拡張子を除いた名前が使用されます。もしサブディレクトリのファイルを入力した場合は「/」で区切った名前で指定してください。例えば、subdir/image.pngを入力した場合の名前はsubdir/imageです。出力結果にどのような名前で登録されているかはtexture_loc.jsonを参照することでも確認できます。

注釈

Windowsのファイルシステムは区切り記号に「\」が使用されますが、ロケーション名は常に「/」を使用します。

YourApp::TexturePieceを利用したスプライトの描画の例を次に示します。

/* ------------------------------------------------------------------------- */
/*!
 *  \brief      フレーム更新処理
 */
/* ------------------------------------------------------------------------- */
void Application::OnFrameUpdate() noexcept
{
    MGL::Render::Renderer2D renderer;

    // 黒色で画面をクリア
    renderer.Clear(MGL::kColorBlue);

    // テクスチャを取得して描画
    TexturePiece piece("sample_a");
    if (piece)
    {
        renderer.DrawSprite(MGL::Vector2(100.0f, 100.0f), piece);
    }
}
実行結果(640x480)
../_images/output_sample.png

MGLExt::TexturePieceは暗黙的なMGL::Render::TextureWithBoundsへの変換が可能となっているため、MGL::Render::Renderer2D::DrawSpriteの第2引数にそのまま渡せます。もし引数に指定された名前が見つからなかった場合、pieceは無効なオブジェクトとなります。

ヒント

TextureAtlasHolderはロケーション名をハッシュ化した値で管理するため、引数に指定した文字列はハッシュ値へと変換されます。この変換はconstexprを用いた定数式によって実装されており、通常はコンパイル時に定数へと置き換わりますが、コンパイラの実装や最適化レベルによって実行時に計算される場合もあります。

ハッシュ値の計算コストとロケーション情報の検索コストを抑えるために、TexturePieceはメンバ変数に保持し、コンストラクタでロケーション名を指定することを推奨します。

ロケーション情報のバイナリフォーマット#

ロケーション情報のバイナリファイルには、大きく分けて次の順でデータが格納されています。

  • ヘッダ

  • ロケーション情報

  • フッタ

全ての値のバイトオーダーはリトルエンディアンです。

ヘッダ#

ヘッダは合計24バイトで、次のパラメータが順に格納されています。全ての値は符号なしの32-bit整数値です。

名前

サイズ

内容

hashSeed

4バイト

ハッシュのシード値

identify

4バイト

識別子

revision

4バイト

リビジョン番号

flags

4バイト

フラグ情報

imageCount

4バイト

画像ファイルの数

locationCount

4バイト

ロケーション情報の数

+0 hashSeed (4Byte)

このファイルが使用するハッシュ計算のシード値です。--hash-seedオプションで指定した値がこの領域に格納されます。

+4 identify (4Byte)

このファイルがロケーション情報である事を確認するための識別子で、hashSeedを用いて文字列"TextureAtlasLocations"をハッシュ化した値が格納されています。

この領域はこのファイルがロケーション情報であることを確認すると同時に、ハッシュ生成アルゴリズムが意図した値を算出するかのチェックも兼ねています。ハッシュアルゴリズムの詳細についてはハッシュアルゴリズムの節を参照してください。

+8 revision (4Byte)

このバイナリフォーマットのリビジョン情報です。現在は常に0であり、将来フォーマットの変更が行われた場合に加算される予定です。

+12 flags (4Byte)

この領域は将来のために予約されており、現在は使用されていません。

+16 imageCount (4Byte)

このロケーション情報が扱う画像の数を表す値です。出力された画像の数がこの領域に格納されます。

+20 locationCount (4Byte)

このファイルに格納されているロケーション情報の数を表す値です。

ロケーション情報#

ロケーション情報は1つあたり28バイトで構成され、ヘッダのlocationCountの数だけ格納されています。全ての値は符号なしの32-bit整数値です。これらはhashの値を昇順にソートした状態で格納されています。

ロケーション情報の内容は次の通りです。

名前

サイズ

内容

hash

4バイト

ロケーション名のハッシュ値

imageIndex

4バイト

画像のインデックス

x

4バイト

X座標

y

4バイト

Y座標

width

4バイト

height

4バイト

高さ

flags

4バイト

フラグ情報

+0 hash (4byte)

ロケーション名をハッシュ化した値です。バイナリファイルではロケーションの判別にハッシュ値を使用し、その値は入力ファイルのパスを元に算出します。

ハッシュの生成アルゴリズムについてはハッシュアルゴリズムの節を参照してください。

+4 imageIndex (4byte)

出力した画像の番号を表す値です。この値は連番で出力した画像の何番目に目的の領域があるかを表しています。

+8 x (4byte)

目的の領域のX座標を表す値です。

+12 y (4byte)

目的の領域のY座標を表す値です。

+16 width (4byte)

目的の領域の幅を表す値です。

+20 height (4byte)

目的の領域の高さ表す値です。

+24 flags (4byte)

この領域は将来のために予約されており、現在は使用されていません。

フッタ#

フッタは4バイトで、文字列"EndOfRecord"をハッシュ化した32-bit値が格納されています。全ての要素を読み込んだ後にこの値が読み取れない場合、ロケーション情報のパースに失敗している可能性があります。

ハッシュアルゴリズム#

mgl-texatlasが出力するバイナリファイルを扱うには、MGL::Hash::FNV1aと等価のハッシュアルゴリズムが必要です。このアルゴリズムのC++での実装例を次に示します。

constexpr uint32_t kFNV1aPrime32 = 0x1000193;

constexpr uint32_t FNV1a(const char *str, const uint32_t seed) noexcept
{
    if (str[0] == '\0')
    {
        return seed;
    }

    return FNV1a(&str[1], (seed ^ uint32_t(str[0])) * kFNV1aPrime32);
}