ゲームフレームワーク的なものを作る。(7)〜Textureインターフェイスとスプライト描画メソッド〜

前回「ゲームフレームワーク的なものを作る。(6)〜スプライト描画〜 - while( c++ );」のサンプルでは、Direct3DのIDirect3DTexture9インターフェイスとID3DXSpriteインターフェイスを直接使って描画を行っていましたが、フレームワーク利用者に対してDirectXなどの具体的な描画エンジンは隠蔽したいですし、今後はOpenGLもサポートしていく予定なので、まずはテクスチャインターフェイスを導入します。

Textureインターフェイス

とりあえず仮想デストラクタとテクスチャサイズを返すメソッドを持たせます。

/**
 *  テクスチャインターフェイス
 */
class ITexture
    : public IStringizable      //ストリームへ出力可能
{
public:
    /**
     *  デストラクタ
     */
    virtual ~ITexture(){}

public:
    /**
     *  サイズを取得
     */
    virtual Size GetSize()const = 0;
};
Direct3DによるTextureクラスの実装

Graphicsクラス側で生成したテクスチャをコンストラクタで渡して初期化しています。

/**
 *  テクスチャクラス(Direct3Dによる実装)
 */
class Texture
    : public ITexture
{
    //  -------------------------------------------------------------------------------
    //  生成と破棄
    //  -------------------------------------------------------------------------------
public:
    /**
     *  コンストラクタ
     */
    Texture( IDirect3DTexture9* instance, const std::string& filename );
    /**
     *  デストラクタ
     */
    ~Texture();


    //  -------------------------------------------------------------------------------
    //  ITexture実装
    //  -------------------------------------------------------------------------------
public:
    Size GetSize()const{ return size; }


    //  -------------------------------------------------------------------------------
    //  基本機能
    //  -------------------------------------------------------------------------------
public:
    /**
     *  IDirect3DTexture9の実体を取得
     */
    IDirect3DTexture9* GetInstance()const{ return instance; }
    /**
     *  ファイル名を取得
     */
    std::string GetFileName()const{ return filename; }

private:
    IDirect3DTexture9* instance;        ///<    IDirect3DTexture9の実体
    std::string filename;               ///<    ファイル名
    Size size;                          ///<    テクスチャサイズ


    //  -------------------------------------------------------------------------------
    //  IStringizable実装
    //  -------------------------------------------------------------------------------
private:
    /**
     *  文字列への変換
     */
    std::string to_string()const;


    //  -------------------------------------------------------------------------------
    //  コピー禁止
    //  -------------------------------------------------------------------------------
private:
    Texture( const Texture& );
    Texture& operator=( const Texture& );
};
/**
 *  コンストラクタ
 *
 *  @param  instance    生成済みのテクスチャ
 *  @param  filename    ファイル名
 */
Texture::Texture( IDirect3DTexture9* instance, const std::string& filename )
    : instance( instance )
    , filename( filename )
{
    //テクスチャサイズ取得
    D3DSURFACE_DESC desc;
    instance->GetLevelDesc( 0, &desc );
    size.width = desc.Width;
    size.height = desc.Height;
}

/**
 *  デストラクタ
 */
Texture::~Texture()
{
    instance->Release();
}

/**
 *  文字列への変換。とりあえずファイル名とサイズを出力
 */
std::string Texture::to_string()const
{
    std::stringstream ss;
    ss << "[ " << filename << " ]" << size;
    return ss.str();
}
Graphicsクラスにスプライト描画メソッドを追加する

画像ファイルからstd::shared_ptrを返すメソッドとID3DXSpriteを使った描画機能をGraphicsクラスに追加します。

  • CreateTexture(ファイル名)
    • std::shared_ptrを返す
  • BeginSprite
    • スプライト描画開始。以降、AddSprite()で内部のバッファにスプライトを追加しEndSprite()で一気にまとめて描画する。
  • AddSprite
    • 内部バッファにスプライトを追加する。
    • Textureインターフェイス(std::shared_ptr)、Rect、Color、MatrixStackを指定。
  • EndSprite
    • バッファ内のスプライトをまとめて描画。
typedef std::shared_ptr< ITexture > TexturePtr;
/**
 *  Graphicsクラス
 */
class Graphics
{
/*略*/

private:
    /**
     *  スプライト描画オブジェクトの生成
     */
    ID3DXSprite* CreateSprite();

public:
    /**
     *  画像ファイルからテクスチャを生成
     */
    TexturePtr CreateTexture( const std::string& filename );

    //  -------------------------------------------------------------------------------
    //  スプライト描画機能
    //  -------------------------------------------------------------------------------
public:
    /**
     *  スプライト描画開始。以降、AddSpriteにより内部バッファにスプライトを追加。
     */
    void BeginSprite();
    /**
     *  スプライト描画終了。内部のバッファに溜めてあるスプライトリストをバックバッファへ出力。
     */
    void EndSprite();
    /**
     *  バッファにスプライトを追加。
     */
    void AddSprite( const TexturePtr& texture, const Rect& rect, const Color& color, const MatrixStackPtr& matrix_stack );

private:
    std::shared_ptr< ID3DXSprite > sprite;              ///<    スプライト描画担当
};

AddSprite()はTextureインターフェイスのshared_ptrでテクスチャを受け取るので、内部の描画時にstd::static_pointer_castで実装クラスへキャストしてIDirect3DTexture9を取得します。std::dynamic_pointer_castを使う必要はないでしょう。

/**
 *  画像ファイルからテクスチャを生成
 *
 *  @param  filename    ファイル名
 *  @return 生成したテクスチャを指すスマートポインタ
 */
TexturePtr Graphics::CreateTexture( const std::string& filename )
{
    IDirect3DTexture9* texture = nullptr;
    HRESULT hr = D3DXCreateTextureFromFile( device.get(), filename.c_str(), &texture );
    if( FAILED( hr ) )throw std::runtime_error( "D3DXCreateTextureFromFile[" + filename + "]" );

    return TexturePtr( new Texture( texture, filename ) );
}

/**
 *  スプライト描画オブジェクトの生成
 *
 *  @return スプライト描画オブジェクトを指すスマートポインタ
 */
ID3DXSprite* Graphics::CreateSprite()
{
    ID3DXSprite* sprite = nullptr;
    HRESULT hr = D3DXCreateSprite( device.get(), &sprite );
    if( FAILED( hr ) )throw std::runtime_error( "D3DXCreateSprite" );

    return sprite;
}

/**
 *  スプライト描画開始。以降、AddSpriteにより内部バッファにスプライトを追加。
 */
void Graphics::BeginSprite()
{
    sprite->Begin( D3DXSPRITE_ALPHABLEND );
}

/**
 *  スプライト描画終了。内部のバッファに溜めてあるスプライトリストをバックバッファへ出力。
 */
void Graphics::EndSprite()
{
    sprite->End();
}

/**
 *  バッファにスプライトを追加。
 *
 *  @param  texture         テクスチャ
 *  @param  rect            描画元矩形
 *  @param  color           合成する色
 *  @param  matrix_stack    行列スタック
 */
void Graphics::AddSprite(
    const TexturePtr& texture,
    const Rect& rect, const Color& color,
    const MatrixStackPtr& matrix_stack )
{
    RECT r = { rect.left, rect.top, rect.right, rect.bottom };
    //デフォルトで矩形の中心を回転の中心とする
    D3DXVECTOR3 center( rect.Width() / 2.0f, rect.Height() / 2.0f, 0.0f );

    sprite->SetTransform( matrix_stack->GetTop() );
    sprite->Draw(
        std::static_pointer_cast< Texture >( texture )->GetInstance(),
        &r, &center, nullptr, color.to_A8R8G8B8()
        );
}
使い方
    graphics.Clear();
    if( graphics.BeginScene() )
    {
        graphics.BeginSprite();

        //背景の描画
        matrix_stack->Push();
        {
            matrix_stack->TranslateLocal( 360.0f, 240.0f, 0.0f );
            graphics.AddSprite(
                texture, Rect( 0, 0, 720, 480 ), Color( 1.0f, 1.0f, 1.0f, 1.0f ),
                matrix_stack
                );
        }
        matrix_stack->Pop();

        //ミクの描画
        //  マウス位置に描画
        matrix_stack->Push();
        {
            matrix_stack->TranslateLocal(
                static_cast< float >( mouse_pos.x ), static_cast< float >( mouse_pos.y ), 0.0f
                );
            matrix_stack->RotateYawPitchRollLocal( 0.0f, 0.0f, rotate );
            matrix_stack->ScaleLocal( 1.0f, 1.0f, 1.0f );

            //ネギの描画
            //  ミクを中心に自転しながら公転する
            matrix_stack->Push();
            {
                matrix_stack->RotateYawPitchRollLocal( 0.0f, 0.0f, rotate );
                matrix_stack->TranslateLocal( 100.0f, 0.0f, 0.0f );
                matrix_stack->RotateYawPitchRollLocal( 0.0f, 0.0f, rotate );
                matrix_stack->ScaleLocal( 1.0f, 1.0f, 1.0f );
                
                graphics.AddSprite(
                    texture2, Rect( 128, 0, 256, 128 ), Color( 1.0f, 1.0f, 1.0f, 1.0f ),
                    matrix_stack
                    );
            }
            matrix_stack->Pop();

            graphics.AddSprite(
                texture2, Rect( 0, 0, 128, 128 ), Color( 1.0f, 1.0f, 1.0f, 1.0f ),
                matrix_stack
                );
        }
        matrix_stack->Pop();
        
        graphics.EndSprite();

        //debug
        std::stringstream ss;
        ss  << "texture:" << texture << std::endl
            << "texture2:" << texture2 << std::endl
            ;
        graphics.DrawString( Point( 0, 0 ), ss.str(), Color( 1.0f, 1.0f, 1.0f, 1.0f ) );

        graphics.EndScene();
    }
    graphics.Update();
ダウンロード
Graphicsクラスに行列スタック制御メソッドを追加する

行列スタック用インターフェイスを作るか、Graphicsの機能として行列スタックを制御するか迷ったのですが、とりあえずGraphicsクラスにメソッドを追加しておきます。

  • Push()
    • topをコピーしてpush
  • Pop()
    • pop
  • Scale( x, y, z )
    • topに左からスケーリング行列を掛ける
  • Rotate( x, y, z )
    • topに左から回転行列を掛ける
  • Translate( x, y, z )
    • topに左から平行移動行列を掛ける

/**
 *  Graphicsクラス
 */
class Graphics
{
/*略*/

    //  -------------------------------------------------------------------------------
    //  行列スタック制御機能
    //  -------------------------------------------------------------------------------
public:
    /**
     *  topをコピーしてpush
     */
    void Push();
    /**
     *  pop
     */
    void Pop();
    /**
     *  Ms * top
     */
    void Scale( float x, float y, float z );
    /**
     *  Mr * top
     */
    void Rotate( float x, float y, float z );
    /**
     *  Mt * top
     */
    void Translate( float x, float y, float z );


private:
    std::shared_ptr< ID3DXMatrixStack > matrix_stack;   ///<    行列スタック制御
};
/**
 *  スタックトップをコピーしてpush
 */
void Graphics::Push()
{
    matrix_stack->Push();
}

/**
 *  スタックトップをpop
 */
void Graphics::Pop()
{
    matrix_stack->Pop();
}

/**
 *  Ms * top
 */
void Graphics::Scale( float x, float y, float z )
{
    matrix_stack->ScaleLocal( x, y, z );
}

/**
 *  Mr * top
 */
void Graphics::Rotate( float x, float y, float z )
{
    matrix_stack->RotateYawPitchRollLocal( x, y, z );
}

/**
 *  Mt * top
 */
void Graphics::Translate( float x, float y, float z )
{
    matrix_stack->TranslateLocal( x, y, z );
}
使い方

ちょっとGraphicsクラスに機能が集中しているような気もしますが。

    graphics.Clear();
    if( graphics.BeginScene() )
    {
        //シーンの描画
        ;

        graphics.BeginSprite();

        //背景の描画
        graphics.Push();
        {
            graphics.Translate( 360.0f, 240.0f, 0.0f );
            graphics.AddSprite(
                texture, Rect( 0, 0, 720, 480 ), Color( 1.0f, 1.0f, 1.0f, 1.0f )
                );
        }
        graphics.Pop();

        //ミクの描画
        //  マウス位置に描画
        graphics.Push();
        {
            graphics.Translate(
                static_cast< float >( mouse_pos.x ), static_cast< float >( mouse_pos.y ), 0.0f
                );
            graphics.Rotate( 0.0f, 0.0f, rotate );
            graphics.Scale( 1.0f, 1.0f, 1.0f );

            //ネギの描画
            //  ミクを中心に自転しながら公転する
            graphics.Push();
            {
                graphics.Rotate( 0.0f, 0.0f, rotate );
                graphics.Translate( 100.0f, 0.0f, 0.0f );
                graphics.Rotate( 0.0f, 0.0f, rotate );
                graphics.Scale( 1.0f, 1.0f, 1.0f );
                
                graphics.AddSprite(
                    texture2, Rect( 128, 0, 256, 128 ), Color( 1.0f, 1.0f, 1.0f, 1.0f )
                    );
            }
            graphics.Pop();

            graphics.AddSprite(
                texture2, Rect( 0, 0, 128, 128 ), Color( 1.0f, 1.0f, 1.0f, 1.0f )
                );
        }
        graphics.Pop();
        
        graphics.EndSprite();

        //debug
        std::stringstream ss;
        ss  << "texture:" << texture << std::endl
            << "texture2:" << texture2 << std::endl
            ;
        graphics.DrawString( Point( 0, 0 ), ss.str(), Color( 1.0f, 1.0f, 1.0f, 1.0f ) );

        graphics.EndScene();
    }
    graphics.Update();
ダウンロード
今後の予定