5.3D描画周りのクラス化


0.はじめに

今回は前回作った3D描画周りをクラス化してみます。
また以前D3DXVECTOR3、D3DXMATRIXを使っていた部分を同等のメンバを持つ自作クラスVector3、Matrixで置き換えたり、
プリコンパイルヘッダーを導入したりなど幾つか微妙な修正をしました。が基本的にやっていることは変わっていません。

1.カメラ周りのクラス化

まずカメラ周りの処理のクラス化を考えて見ます。
最低限必要になデータは前回ビュー、プロジェクション行列を作成する為に使った、
カメラ位置、注視点、上方向、FOV、アスペクト比、ニアクリップ、ファークリップです。
今回はそれと共に、計算した行列の保存用に行列を2つと、
カメラを扱う処理でよく使うカメラ位置から注視点へ向かう単位ベクトルもメンバとして持たせました。
でクラス宣言はこんな感じになりました。
カメラクラス
class CameraBase
{
public:
    CameraBase();
    ~CameraBase(){}
public:
    const Vector3& GetPos(){ return pos_; }
    const Vector3& GetAt(){ return at_; }
    const Vector3& GetUp(){ return up_; }
    void SetPos(const Vector3& pos){ pos_ = pos; }
    void SetAt(const Vector3& at){ at_ = at; }
    void SetUp(const Vector3& up){ up_ = up; }
public:
    void SetProjectionParams(float fov, float aspect, float zn, float zf)
    {
        fov_ = fov;
        aspect_ = aspect;
        zn_ = zn;
        zf_ = zf;
    }
    void GetProjectionParams(float* fov, float* aspect, float* zn, float* zf)
    {
        if(fov) *fov = fov_;
        if(aspect) *aspect = aspect_;
        if(zn) *zn = zn_;
        if(zf) *zf = zf_;
    }
public:
    //!行列、ベクトルの更新関数
    void Setting()
    {
        view_ = at_ - pos_;
        view_.Normalize();
        D3DXMatrixLookAtLH(&matView_.ToD3D(), &pos_.ToD3D(), &at_.ToD3D(), &up_.ToD3D());
        D3DXMatrixPerspectiveFovLH(&matProj_.ToD3D(), fov_, aspect_, zn_, zf_);
    }
public:
    const Vector3& GetViewVector() const { return view_; }
    const Matrix& GetViewMatrix() const { return matView_; }
    const Matrix& GetProjectionMatrix() const { return matProj_; }
private:
    Vector3 pos_;       //!<位置
    Vector3 at_;        //!<注視点
    Vector3 up_;        //!<上方向
    Vector3 view_;      //!<視線ベクトル
    Matrix matView_;    //!<ビュー行列
    Matrix matProj_;    //!<プロジェクション行列
    float fov_;         //!<画角
    float aspect_;      //!<アスペクト比
    float zn_;          //!<近クリップZ
    float zf_;          //!<遠クリップZ
};
使い方としてはSetPos()、SetAt()などでデータを変更した後にSetting()を呼ぶことで、
行列、カメラ位置から注視点へ向かう単位ベクトルの更新をするようにしました。

2.頂点宣言周りのクラス化

次に頂点宣言周りのクラス化をしてみます。
今回は頂点バッファの初期化時などに使う、
FVFと一頂点に必要なバイト数も頂点宣言の初期化時に求めておくようにしました。
クラス宣言はこんな感じになりました。
頂点宣言まとめクラス
enum
{
    VERTEX_TYPE_3DBASE,
    VERTEX_TYPE_2DBASE,
    VERTEX_TYPE_MAX
};
class VertexDecManager
{
private:
    VertexDecManager();
    ~VertexDecManager();
public:
    #define GetVertexDecManager() VertexDecManager::GetInstance()
    static VertexDecManager& GetInstance()
    {
        static VertexDecManager inst_;
        return inst_;
    }
public:
    bool CreateDeclareVertexTypes(LPDIRECT3DDEVICE9 pDev);
    bool ReleaseDeclareVertexTypes();
public:
    LPDIRECT3DVERTEXDECLARATION9 GetDeclare(int type) const
    {
        MMM_CHECK_INDEX(type, 0, VERTEX_TYPE_MAX);
        return decs_[type].pDec_;
    }
    UINT GetStride(int type) const
    {
        MMM_CHECK_INDEX(type, 0, VERTEX_TYPE_MAX-1);
        return decs_[type].stride_;
    }
    UINT GetFVF(int type) const
    {
        MMM_CHECK_INDEX(type, 0, VERTEX_TYPE_MAX-1);
        return decs_[type].fvf_;
    }
    bool Is2D(int type) const
    {
        MMM_CHECK_INDEX(type, 0, VERTEX_TYPE_MAX-1);
        return (decs_[type].fvf_ & D3DFVF_XYZRHW) > 0;
    }
    bool IsUseColor(int type) const
    {
        MMM_CHECK_INDEX(type, 0, VERTEX_TYPE_MAX-1);
        return (decs_[type].fvf_ & D3DFVF_DIFFUSE) > 0;
    }
private:
    struct DecData
    {
        LPDIRECT3DVERTEXDECLARATION9 pDec_;
        UINT fvf_;
        UINT stride_;
    };
private:
    DecData decs_[VERTEX_TYPE_MAX];
};
CreateDeclareVertexTypes()で生成し、GetFVF()やGetStride()でデータを取得し、使い終わったら、
ReleaseDeclareVertexTypes()で解放します。
インスタンスは一つでいいので、シングルトンにしてみました。

3.頂点バッファ周りのクラス化

今回は一つのバッファの中に位置、法線データと色データなど各種データを順番に格納する形にしました。
ストリームの設定部分などが汚くなってしまいましたが、
メモリ的に多少軽くなりそうだったのでこの形にしてみました。
頂点バッファだけでなく、インデックスバッファも管理するようにした為、
GeometryBufferという大げさな名前になってしまいました。
でクラス宣言はこんな感じになりました。
頂点、インデックスバッファまとめクラス
class GeometryBuffer
{
public:
    GeometryBuffer() : vertexType_(0), vertexCount_(0), pVertex_(0), pIndex_(0){}
    ~GeometryBuffer(){ MMM_CHECK(pVertex_ == 0); MMM_CHECK(pIndex_ == 0); }
public:
    bool CreateVertex(LPDIRECT3DDEVICE9 pDev, UINT count, int type, void* pMem = 0);
    bool CreateIndecies(LPDIRECT3DDEVICE9 pDev, UINT count, void* pMem = 0);
    bool Release();
private:
    void _SetVertexBuffer(void* pMem, UINT offset, UINT size);
    void _SetIndexBuffer(void* pMem, UINT offset, UINT size);
public:
    bool SetVertex3D(void* pMem);
    bool SetVertexColor(void* pMem);
    bool SetIndecies(void* pMem);
private:
    bool IsUse3D() const { return GetVertexDecManager().Is2D(vertexType_) == false; }
    bool IsUseColor() const { return GetVertexDecManager().IsUseColor(vertexType_); }
    UINT GetVertex3DOffset() const { return 0; }
    UINT GetVertex2DOffset() const { return 0; }
    UINT GetVertexColorOffset() const { return ((IsUse3D())? sizeof(Vertex3D) : sizeof(Vertex2D)) * vertexCount_; }
    UINT GetVertexTexOffset() const {return GetVertexColorOffset() + IsUseColor() * sizeof(VertexColor) * vertexCount_;}
public:
    void SettingRenderSource();
private:
    int vertexType_;                    //!<頂点の種類
    UINT vertexCount_;                  //!<頂点の数
    IDirect3DVertexBuffer9* pVertex_;   //!<頂点バッファ
    UINT indexCount_;                   //!<インデックスの数
    IDirect3DIndexBuffer9* pIndex_;     //!<インデックスバッファ
};
CreateVertex()で必要なサイズのバッファを作り、SetVertex3D()、SetVertexColor()などの関数を使ってデータを設定し、
描画時にSettingRenderSource()でストリームに各データを設定し、使い終わったらRelease()で解放するというような使い方です。
とここまでクラス宣言のみで見てきましたが、
メンバ関数内でやっていることは以前WinMain.cppの中でやっていたことと大差ないので、
宣言をみただけでおおよそどんなつくりなのかは分かると思います。
以上で前回作った3D描画部分を何とかクラス化できました。
次回は入力周りを作ってみようかと思います。
今回のソース。
戻る。