今はこれが精一杯-2

前回の続きです。

1枚のテクスチャを使って表示されるオブジェクトが1つだけの場合

例えば、背景とか、レースゲームでユーザーが操作する車とか、そういう単純なパターンも割と多いです。
こういう場合は、前回のTextureDrawerとほぼ同じことをするだけで十分と思われます。
一応、C++で書きます。

template <typename T, typename C>
class SimpleTextureObject
{
public:
    SimpleTextureObject();
    virtual ~SimpleTextureObject();
    virtual void Draw(GLuint);
	
public:
    T x, y, width, height, u, v, tw, th;
    C r, g, b, a;
	
protected:
    void Clear();
};

template <typename T, typename C>
SimpleTextureObject<T, C>::SimpleTextureObject()
{
    Clear();
}

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

template <typename T, typename C>
void SimpleTextureObject<T, C>::Draw(GLuint texture)
{
    GLfloat vertices[] =
    {
        x - width / 2, y - height / 2,
        x + width / 2, y - height / 2,
        x - width / 2, y + height / 2,
        x + width / 2, y + height / 2,
    };
	
    GLfloat texCoords[] =
    {
        u,       v + th,
        u + tw,  v + th,
        u,       v,
        u + tw,  v,
    };
	
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, texture);
    glVertexPointer(2, GL_FLOAT, 0, vertices);
    glEnableClientState(GL_VERTEX_ARRAY);
    glColor4ub(r, g, b, a);
    glDisableClientState(GL_COLOR_ARRAY);
    glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
	
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisable(GL_TEXTURE_2D);
}

template <typename T, typename C>
void SimpleTextureObject<T, C>::Clear()
{
    x = y = u = v = 0.0f;
    width = height = tw = th = 1.0f;
    r = g = b = a = 255;
}


テンプレートを使っているのは、座標の型と色の型を汎用的に指定できるようにするためです。
結構面倒臭いソースになってしまいますが、使う側からすると、それほど面倒でもありません。

例えば、アプリの共通ヘッダで

typedef SimpleTextureObject<GLfloat, GLubyte> CpSimpleTextureObject;

こんな風にしてしまうことを想定しています。


使い方ですが、背景のように単純で動かないものに関しては、SimpleTextureObjectをそのまま使うようにします。

@interface ES1Renderer : NSObject<ESRenderer>
{
    // ...

    Texture* _bgTexture;
    CpSimpleTextureObject _bg;
}
@end

@implementation ES1Renderer
-(id)init {
    // ...
		
    // 背景を初期化する
    _bgTexture = [[[Texture alloc] initWithImageName:@"hoge.png"] load];
    _bg.width = _bg.height = 2.0f;

    // ...
}

-(void)renderMain {
    // 背景を描画する
    _bg.Draw(_bgTexture.raw);
	
    // ...	
}

// ...

@end


レースゲームの車のように、独自の座標管理などが必要なクラスは、SimpleTextureObjectから派生させることにします。

class CpCar : public CpSimpleTextureObject
{
    typedef CpSimpleTextureObject inherited;
	
public:
    CpCar();
    void SetControlLeft(bool);
    void SetControlRight(bool);
    void SetControlAccel(bool);
    float GetPosX();
    float GetPosY();
    float GetAngle();
    float GetSize();
    void Move();
	
    void Draw(GLuint texture) override;
	
private:
    float _posX, _posY;  // 座標
    float _angle;        // 向き
    float _size;         // サイズ
    float _vx, _vy;      // 慣性力
    bool _controlLeft;	 // 左ハンドルON/OFF
    bool _controlRight;  // 右ハンドルON/OFF
    bool _controlAccel;  // アクセルON/OFF
};

CpCar::CpCar() : inherited()
{
    _posX = -0.5f;
    _posY = 0.0f;
    _angle = 0.0f;
    _size = 0.05f;
    _vx = _vy = 0.0f;
    _controlLeft = _controlRight = _controlAccel = false;
}

void CpCar::SetControlLeft(bool onoff)
{
    _controlLeft = onoff;
}

void CpCar::SetControlRight(bool onoff)
{
    _controlRight = onoff;
}

void CpCar::SetControlAccel(bool onoff)
{
    _controlAccel = onoff;
}

float CpCar::GetPosX()
{
    return _posX;
}

float CpCar::GetPosY()
{
    return _posY;
}

float CpCar::GetAngle()
{
    return _angle;
}

float CpCar::GetSize()
{
    return _size;
}

void CpCar::Move()
{
    // 左旋回
    if (_controlLeft) 
    {
        _angle += 3.0f;
    }
	
    // 右旋回
    if (_controlRight) 
    {
        _angle -= 3.0f;
    }
	
    // アクセル制御
    if (_controlAccel) 
    {
        float theta = _angle / 180 * M_PI;
        _vx += cos(theta) * 0.000125f;
        _vy += sin(theta) * 0.000125f;
    }
	
    // 慣性抵抗
    _vx *= 0.98f;
    _vy *= 0.98f;
	
    // 移動
    _posX += _vx;
    _posY += _vy;
}

void CpCar::Draw(GLuint texture)
{
    inherited::Clear();
    inherited::Draw(texture);
}
@implementation ES1Renderer
-(id)init {
    // ...
		
    // 車を初期化する
    _carTexture = [[[Texture alloc] initWithImageName:@"hoge.png"] load];
    _car = new CpCar();

    // ...
}

-(void)renderMain {
    // ...

    glPushMatrix();
    glTranslatef(_car->GetPosX(), _car->GetPosY(), 0.0f);
    glRotatef(_car->GetAngle(), 0.0f, 0.0f, 1.0f);
    glScalef(_car->GetSize(), _car->GetSize(), 1.0f);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    car->Draw([_carTexture raw]);

    glDisable(GL_BLEND);
    glPopMatrix();

    // ...	
}

// ...

@end


さて、ここまでは、ただ実装がC++になっただけで、以前の設計から内容的には進歩していません。
問題は、同一テクスチャで多数のオブジェクトを描画する場合です。


続く