今はこれが精一杯-4

前回の続きです。

ついに数字テクスチャクラスをリファクタリングする時が来ました。
基盤は既に出来上がっているので、あとは新しい設計に合わせて実装するだけです。
数字描画の基本的な仕様は、以前と同じにします。

ただし、せっかくC++で作り直すので、初期化後はint変数みたいに使えるようにするために、演算子オーバーロードしてみます。

//
// 数字テクスチャクラス
// テクスチャ要件
// ・4×4セル内に左上から順に数字が格納されていること
//
//        0 1 2 3
//        4 5 6 7
//        8 9 _ _
//        _ _ _ _
//
// ・テクスチャ画像全体の幅、高さが等しいこと
// ・それぞれの数字の幅、高さがすべて等しいこと
// ・テクスチャ画像全体の幅、高さのサイズが4の倍数であること
//
// 制約事項
// ・現在の実装では、マイナスの値を表示できない
// ・現在の実装では、整数のみ対応しており、小数点は表示できない
//

template <typename T, typename C, typename S>
class NumberTextureObject : public TextureObject<T, C, S> {
    typedef TextureObject<T, C, S> inherited;
    
public:
    NumberTextureObject();
    virtual ~NumberTextureObject();

    operator int();
    NumberTextureObject& operator=(int);
    NumberTextureObject& operator++();
    NumberTextureObject& operator--();
    int operator++(int);
    int operator--(int);

    void SetPosition(T, T, T);
    void SetColor(C, C, C, C);
    void SetFigures(int);
    void SetFillZero(bool);
    void SetAlign(int);

    virtual void GetDrawSize(TextureVerticesHeader<S>*) final override;
    virtual void PrepareDraw(TextureVerticesPointers<T, C>*) final override;

protected:
    int CalcFigures();    // 実際の表示桁数を求める
    
protected:
    T _x, _y, _size;     // 表示座標、サイズ
    C _r, _g, _b, _a;    // 色
    int _number;         // 表示する数値
    int _figures;        // 最大表示桁数
    bool _fillZero;      // ゼロ埋めフラグ(表示する数字の桁数が_figuresに満たない場合にゼロ埋め)
    int _align;          // テキストアラインメント
};

// テキストアラインメント定数列挙
enum
{
    NUMBER_TEXTURE2D_ALIGN_CENTER = 1,   // 中央揃え(デフォルト)
    NUMBER_TEXTURE2D_ALIGN_RIGHT,        // 右揃え
    NUMBER_TEXTURE2D_ALIGN_LEFT,         // 左揃え
};

template <typename T, typename C, typename S>
NumberTextureObject<T, C, S>::NumberTextureObject()
{
    _x = _y = 0.0f;
    _size = 0.2f;
    _number = 0;
    _figures = 4;
    _fillZero = true;
    _align = NUMBER_TEXTURE2D_ALIGN_CENTER;
    _r = _g = _b = _a = 255;
}

template <typename T, typename C, typename S>
NumberTextureObject<T, C, S>::~NumberTextureObject()
{
}

template <typename T, typename C, typename S>
void NumberTextureObject<T, C, S>::SetPosition(T x, T y, T size)
{
    _x = x;
    _y = y;
    _size = size;
}

template <typename T, typename C, typename S>
void NumberTextureObject<T, C, S>::SetColor(C r, C g, C b, C a)
{
    _r = r;
    _g = g;
    _b = b;
    _a = a;
}

template <typename T, typename C, typename S>
void NumberTextureObject<T, C, S>::SetFigures(int figures)
{
    _figures = figures;
}

template <typename T, typename C, typename S>
void NumberTextureObject<T, C, S>::SetFillZero(bool onoff)
{
    _fillZero = onoff;
}

template <typename T, typename C, typename S>
void NumberTextureObject<T, C, S>::SetAlign(int align)
{
    _align = align;
}

template <typename T, typename C, typename S>
NumberTextureObject<T, C, S>::operator int()
{
    return _number;
}

template <typename T, typename C, typename S>
NumberTextureObject<T, C, S>& NumberTextureObject<T, C, S>::operator=(int n)
{
    _number = MAX(0, n);
    return *this;
}

template <typename T, typename C, typename S>
NumberTextureObject<T, C, S>& NumberTextureObject<T, C, S>::operator++()
{
    ++ _number;
    return *this;
}

template <typename T, typename C, typename S>
NumberTextureObject<T, C, S>& NumberTextureObject<T, C, S>::operator--()
{
    -- _number;
    return *this;
}

template <typename T, typename C, typename S>
int NumberTextureObject<T, C, S>::operator++(int n)
{
    int old = _number ++;
    return old;
}

template <typename T, typename C, typename S>
int NumberTextureObject<T, C, S>::operator--(int n)
{
    int old = _number --;
    return old;
}

template <typename T, typename C, typename S>
void NumberTextureObject<T, C, S>::GetDrawSize(TextureVerticesHeader<S>* header)
{
    header->vertexArraySize = 0;
    header->colorArraySize = 0;
    header->coordArraySize = 0;
    header->vertexNum = 0;
    
    // 桁数が0以下の指定の場合は何も描画しない
    if (0 >= _figures)
    {
        return;
    }
    
    // 表示サイズが負の場合は何も描画しない
    if (0.0f > _size)
    {
        return;
    }

    // 実際に描画する数字の桁数を求める
    int rf = CalcFigures();

    // メモリサイズを算出する
    header->vertexArraySize = sizeof(T) * 6 * 2 * rf;
    header->colorArraySize = sizeof(C) * 6 * 4 * rf;
    header->coordArraySize = sizeof(T) * 6 * 2 * rf;
    header->vertexNum = 6 * rf;
}

template <typename T, typename C, typename S>
void NumberTextureObject<T, C, S>::PrepareDraw(TextureVerticesPointers<T, C>* ptrs)
{
    // 桁数が0以下の指定の場合は何も描画しない
    if (0 >= _figures)
    {
        return;
    }
    
    // 表示サイズが負の場合は何も描画しない
    if (0.0f > _size)
    {
        return;
    }

    // 実際に描画する数字の桁数を求める
    int rf = CalcFigures();
    
    // 描画幅を求める
    T totalWidth = _size * (T)rf;
    
    // 描画領域の右端の座標を求める
    T rightX;
    
    if (NUMBER_TEXTURE2D_ALIGN_CENTER == _align) 
    {
        rightX = _x + totalWidth / 2;
    }
    else if (NUMBER_TEXTURE2D_ALIGN_RIGHT == _align) 
    {
        rightX = _x;
    }
    else if (NUMBER_TEXTURE2D_ALIGN_LEFT == _align) 
    {
        rightX = _x + totalWidth;
    }
    else 
    {
        return;
    }

    // 1桁目から順に数字の頂点座標を求める
    for (int i = 0; rf > i; ++ i)
    {
        // 描画する数字の中心座標を求める
        T figNX = rightX - _size / 2 - (T)i * _size;
    
        // 描画する数字を求める
        int numberToDraw = _number / (int)pow(10.0, (double)i) % 10;
    
        // 描画する数字のUV座標を求める
        T u = numberToDraw % 4 * 0.25f;
        T v = numberToDraw / 4 * 0.25f;
    
        // 座標計算
        T vLeft = -0.5f * _size + figNX;
        T vRight = 0.5f * _size + figNX;
        T vTop = 0.5f * _size + _y;
        T vBottom = -0.5f * _size + _y;
    
        // ポリゴン1
        *ptrs->vertexPointer++ = vLeft;  *ptrs->vertexPointer++ = vTop;       // 左上座標
        *ptrs->vertexPointer++ = vRight; *ptrs->vertexPointer++ = vTop;       // 右上座標
        *ptrs->vertexPointer++ = vLeft;  *ptrs->vertexPointer++ = vBottom;    // 左下座標
    
        // ポリゴン2
        *ptrs->vertexPointer++ = vRight; *ptrs->vertexPointer++ = vTop;       // 右上座標
        *ptrs->vertexPointer++ = vLeft;  *ptrs->vertexPointer++ = vBottom;    // 左下座標
        *ptrs->vertexPointer++ = vRight; *ptrs->vertexPointer++ = vBottom;    // 右下座標
    
        // 色(6頂点分)
        *ptrs->colorPointer++ = _r; *ptrs->colorPointer++ = _g; *ptrs->colorPointer++ = _b; *ptrs->colorPointer++ = _a;
        *ptrs->colorPointer++ = _r; *ptrs->colorPointer++ = _g; *ptrs->colorPointer++ = _b; *ptrs->colorPointer++ = _a;
        *ptrs->colorPointer++ = _r; *ptrs->colorPointer++ = _g; *ptrs->colorPointer++ = _b; *ptrs->colorPointer++ = _a;
        *ptrs->colorPointer++ = _r; *ptrs->colorPointer++ = _g; *ptrs->colorPointer++ = _b; *ptrs->colorPointer++ = _a;
        *ptrs->colorPointer++ = _r; *ptrs->colorPointer++ = _g; *ptrs->colorPointer++ = _b; *ptrs->colorPointer++ = _a;
        *ptrs->colorPointer++ = _r; *ptrs->colorPointer++ = _g; *ptrs->colorPointer++ = _b; *ptrs->colorPointer++ = _a;
    
        // マッピング座標
        *ptrs->coordPointer++ = u; *ptrs->coordPointer++ = v;                    // ポリゴン1 - 左上
        *ptrs->coordPointer++ = u + 0.25f; *ptrs->coordPointer++ = v;            // ポリゴン1 - 右上
        *ptrs->coordPointer++ = u; *ptrs->coordPointer++ = v + 0.25f;            // ポリゴン1 - 左下
        *ptrs->coordPointer++ = u + 0.25f; *ptrs->coordPointer++ = v;            // ポリゴン2 - 右上
        *ptrs->coordPointer++ = u; *ptrs->coordPointer++ = v + 0.25f;            // ポリゴン2 - 左下
        *ptrs->coordPointer++ = u + 0.25f; *ptrs->coordPointer++ = v + 0.25f;    // ポリゴン2 - 右下
    }
}

template <typename T, typename C, typename S>
int NumberTextureObject<T, C, S>::CalcFigures()
{
    int rf;
    
    if (_fillZero)
    {
        rf = _figures;
    }
    else
    {
        int f = 1;
        
        for (int i = 10; 0 != _number / i; i *= 10)
        {
            ++ f;
        }
        
        rf = MIN(_figures, f);
    }

    return rf;
}

設計の都合上、GetDrawSize〜PrepareDrawまでの呼び出しはアトミックでなければなりません。
その間にoperator=などによって数字の桁数が変えられてしまうと、正しく動作しない場合があるということです。
その点は、TextureDrawerクラスが隠蔽してくれる機構なので、とりあえず良しとしておきます。
(要するに、マルチスレッドにすると壊れてしまうが、通常、描画を行うスレッドは1つになるように設計するのでOKということです)


【使い方】

// 事前に適切な型でtypedefしておく
typedef NumberTextureObject<GLfloat, GLubyte, size_t> CpNumberTextureObject;

// 必要な分だけ数字テクスチャオブジェクトを作成しておく
CpNumberTextureObject _lapCount;
CpNumberTextureObject _lapTimeSec;
CpNumberTextureObject _lapTimeMilli;
...

// 画面レイアウトを設定しておく
_lapCount.SetPosition(-1.0f, 0.75f, 0.2f);
_lapCount.SetFigures(2);
_lapCount.SetFillZero(true);
_lapCount = 1;    // _lapCount.operator=(1);
...

// 後は通常のint変数のように扱い、表示する数値を保持させるようにする
++ _lapCount;    // _lapCount.operator++();
...

// 描画するときはTextureDrawerを使う
CpTextureObjectsDrawer drawer;
drawer.Add(&_lapCount);
drawer.Add(&_lapTimeSec);
drawer.Add(&_lapTimeMilli);
drawer.Draw(texture);


やふう


数字テクスチャ3枚使った実行例です。
前の設計だと、画像3枚読み込んだ上に、glDrawArraysの呼び出しも3回必要でしたが、今回は画像1枚ですし、glDrawArraysも1回です。
やったね!



続く