Table で Key Frame を作成する

ポットのアニメーション画像

Teapot のメッシュを使って Key Frame をテーブルで定義します。
Key Frame が作成できたら、Frame 間を補完して Key Frame Animation をしてみましょう。

ゲームの最終調整は製作責任者であるディレクターが直接行う事も多く、プログラムを変更しなくても定義ファイルや テーブルをさわるだけで細かい調整ができることが望まれます。

前田稔(Maeda Minoru)の超初心者のプログラム入門

プログラムの説明

  1. g_pMesh は Teapot のメッシュです。
    viewForm, tergForm はカメラの座標と注視点です。
    g_num は現在アニメーション中のキーフレーム番号です。
        //Global Area
        HWND                    g_hWnd   = NULL;    // ウインドウ・ハンドル
        MyD3D                   *myd3d   = NULL;    // MyD3D Object Class
        bool                    g_bActive= false;   // アクティブ状態
    
        //Direct3D Area
        LPDIRECT3DDEVICE9       g_pDEV   = NULL;
        LPD3DXMESH              g_pMesh  = NULL;
        D3DXVECTOR3             viewForm;
        D3DXVECTOR3             tergForm;
        int                     g_num    = 0;
        
  2. このプログラムでは、メッシュとカメラの座標や回転角度を変化させながらアニメーションを行います。
    アニメーションを行うために、テーブルで定義するパラメータは次のとおりです。
  3. FRAME は VT[] で定義されたキーフレームの個数です。
    VT[] には上記で述べたキーフレームのパラメータが定義されています。
    右上奥方向が座標が大きくなるように設定しています。
    テーブルの値はメッシュのサイズや描画環境の設定値によっていくらでも変わります。
    下記のテーブルを使うときは、この後で説明する SetupMatrices() に従って下さい。
        #define     FRAME       8
        //View(XYZ), Terg(XYZ), 移動(XYZ), 回転(XYZ)
        float   VT[][12] = {
        {  0.00f, 0.00f, -20.00f,  0.00f, 0.00f, 0.00f,  60.00f, 36.00f, 80.00f,  -0.40f, 2.50f, 0.00f,  },
        {  0.00f, 0.00f, -20.00f,  0.00f, 0.00f, 0.00f,  20.00f, 12.00f, 30.00f,  -0.40f, 2.50f, 0.00f,  },
        {  0.00f, 0.00f, -20.00f,  0.00f, 0.00f, 0.00f,   6.00f,  4.00f,  6.00f,  -0.30f, 2.50f, 0.00f,  },
        {  0.00f, 0.00f, -20.00f,  0.00f, 0.00f, 0.00f,   2.00f,  1.20f, -2.00f,  -0.10f, 2.40f, 0.00f,  },
        {  0.00f, 0.00f, -20.00f,  0.00f, 0.00f, 0.00f,  -0.40f, -0.20f, -10.00f,  -0.10f, 2.30f, 0.00f, },
        {  0.00f, 0.00f, -20.00f,  0.00f, 0.00f, 0.00f,  -0.80f, -0.20f, -16.00f,  -0.10f, 2.30f, 0.00f, },
        {  0.00f, 0.00f, -20.00f,  0.00f, 0.00f, 0.00f,  -1.60f, -0.20f, -16.80f,  -0.10f, 2.30f, 0.00f, },
        {  0.00f, 0.00f, -20.00f,  0.00f, 0.00f, 0.00f,  -3.20f, -0.20f, -18.00f,  -0.10f, 2.30f, 0.00f, },
        };
        
  4. InitGraphics() 関数です。
    new MyD3D(g_hWnd) で Object Class をインスタンス化します。
    D3DXCreateTeapot() でティーポットのメッシュを生成します。
        HRESULT InitGraphics(void)
        {
            myd3d= new MyD3D(g_hWnd);
            myd3d->InitD3D(&g_pDEV);
            //ティーポットの生成
            D3DXCreateTeapot(g_pDEV,&g_pMesh,NULL);
            myd3d->SetMaterial(0.8f,0.7f,0.0f);
            myd3d->SetLight();
            return  S_OK;
        }
        
  5. メッセージループの中から呼び出される Draw(); 関数です。
    SetupMatrices(g_num); で VT[] で定義された描画環境を設定して描画します。
        void Draw(void)
        {
            if (!g_pDEV || !g_bActive)  return;
            g_pDEV->Clear(0,NULL,D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,D3DCOLOR_XRGB(0,0,150),1.0f,0);
            if (SUCCEEDED(g_pDEV->BeginScene()))
            {   //描画環境の設定
                SetupMatrices(g_num);
                g_pMesh->DrawSubset(0);
                g_pDEV->EndScene();
            }
            g_pDEV->Present(NULL,NULL,NULL,NULL);
        }
        
  6. 描画環境を設定する SetupMatrices() 関数です。
    myd3d->SetView() で描画環境を設定しています。
    先に掲載したテーブルの値は Teapot のメッシュを下記の描画環境で描画したときの値です。
    メッシュのサイズや描画環境が変われば、うまく描画できないこともあります。
        void  SetupMatrices(int num)
        {   RECT            rect;
            D3DXMATRIX      matView;
            D3DXMATRIX      matProj;
    
            GetClientRect(g_hWnd,&rect);
            myd3d->SetView(&viewForm,&tergForm,VT[num]);
            //View 座標の設定
            D3DXMatrixLookAtLH(&matView,&viewForm,&tergForm,&D3DXVECTOR3(0.0f,1.0f,0.0f));
            g_pDEV->SetTransform(D3DTS_VIEW,&matView);
            //透視変換の設定
            D3DXMatrixPerspectiveFovLH(&matProj,D3DXToRadian(45.0f),(float)rect.right/(float)rect.bottom,0.1f,100);
            g_pDEV->SetTransform(D3DTS_PROJECTION,&matProj);
            g_pDEV->SetRenderState(D3DRS_AMBIENT,0x000F0F0F);
        }
        
  7. CALLBACK 関数です。
    Enter キーでキーフレームを切り替えます。
        LRESULT CALLBACK WndProc(HWND hWnd,UINT msg,UINT wParam,LONG lParam)
        {
            switch(msg)
            {   case WM_ACTIVATE:
                    g_bActive= (LOWORD(wParam)!=0);
                    break;
                case WM_PAINT:
                    Draw();
                    break;
                case WM_KEYDOWN:
                    switch(wParam)
                    {   case VK_ESCAPE: // [ESCAPE]キーでウインドウを閉じる
                            PostMessage(hWnd,WM_CLOSE,0,0);
                            break;
                        case VK_RETURN:
                            g_num= (g_num+1)%FRAME;
                            break;
                    }
                    break;
                case WM_CLOSE:
                case WM_DESTROY:
                     PostQuitMessage(0);
                    return 0L;
            }
            return DefWindowProc(hWnd,msg,wParam,lParam);
        }
        
  8. その他の関数はいつもと同じです。

【ヒント】

VT[] で定義された描画環境を設定する myd3d->SetView() の説明です。
    D3DXVECTOR3             viewForm;
    D3DXVECTOR3             tergForm;

    myd3d->SetView(&viewForm,&tergForm,VT[num]);
&viewForm VT[num] で定義されたカメラ座標が設定されます
&tergForm VT[num] で定義された注視点が設定されます
VT[num] カメラの座標, 注視点, 座標, 回転 が定義された配列です
  1. viewForm と tergForm は、VT[] の値を格納するだけです。
        viewForm->x= viewt[0];
            :
        tergForm->x= viewt[3];
            :
        
  2. 座標の平行移動は D3DXMatrixTranslation() で計算します。
        D3DXMATRIX      matTrans;
        D3DXMatrixTranslation(&matTrans,viewt[6],viewt[7],viewt[8]);
        
  3. 座標の回転は D3DXMatrixRotationX,Y,Z() で個別に計算して掛け合わせます。
    このとき乗算の順序が変わると演算結果(モデルの姿勢)が異なります。
        D3DXMATRIX      matRotX,matRotY,matRotZ;
        D3DXMATRIX      matRot;
    
        D3DXMatrixRotationX(&matRotX,viewt[9]);
        D3DXMatrixRotationY(&matRotY,viewt[10]);
        D3DXMatrixRotationZ(&matRotZ,viewt[11]);
        matRot= matRotZ * matRotY * matRotX;
        
  4. 平行移動と回転を掛け合わせて座標変換の完成です。
        D3DXMATRIX      matWorld;
        matWorld= matRot * matTrans;
        pDEV->SetTransform(D3DTS_WORLD,&matWorld);
        

【課題1】

VT[] のパラメータを調整して Key Frame が作成できたら、Frame 間を補完して Key Frame Animation をしてみましょう。
  1. SetupMatrices() 関数から二個のキーフレームをブレンドして描画環境を設定する myd3d->BlendFrame() を呼び出します。
    g_Weight がブレンドの割合を設定する変数で「1.0 から 0.0 まで 0.02 きざみ」で変化します。
    VT[num] の割合が g_Weight で、VT[num+1] の割合が (1-g_Weight) です。
        void  SetupMatrices(int num)
        {   RECT            rect;
            D3DXMATRIX      matView;
            D3DXMATRIX      matProj;
    
            GetClientRect(g_hWnd,&rect);
            myd3d->BlendFrame(&viewForm,&tergForm,VT[num],VT[num+1],g_Weight);
            if (g_Weight>0.02)  g_Weight-= 0.02f;
            //View 座標の設定
            D3DXMatrixLookAtLH(&matView,&viewForm,&tergForm,&D3DXVECTOR3(0.0f,1.0f,0.0f));
            g_pDEV->SetTransform(D3DTS_VIEW,&matView);
            //透視変換の設定
            D3DXMatrixPerspectiveFovLH(&matProj,D3DXToRadian(45.0f),(float)rect.right/(float)rect.bottom,0.1f,100);
            g_pDEV->SetTransform(D3DTS_PROJECTION,&matProj);
            g_pDEV->SetRenderState(D3DRS_AMBIENT,0x000F0F0F);
        }
        
  2. Enter キーが押されると g_num に次のキーフレーム番号を設定して、g_Weight に 1.0f を格納してアニメーションを開始します。
    SetupMatrices() で g_Weight がゼロになるまで 0.02 ずつ減らされながらアニメーションが実行されます。
            case VK_RETURN:
                g_num= (g_num+1)%(FRAME-1);
                g_Weight= 1.0f;
                break;
        

【課題1のヒント】

myd3d->BlendFrame(&viewForm,&tergForm,VT[num],VT[num+1],g_Weight); では num 番目と num+1 番目を g_Weight の 率で掛け合わせて姿勢(dst[])を計算します。
    float       dst[12];
    float       finvWeight= 1.0f - fWeight;

    for(i=0; i<12; i++)
        dst[i]= fWeight*src1[i] + finvWeight*src2[i];
    SetView(viewForm,tergForm,dst);

【課題2】

VT[] で定義しているパラメータを TEXT 形式でファイルにタイプして、これを VT[] に読み込めば「毎回コンパイルする必要が無くなり」より使い勝手が増します。
  1. 入力したフレーム数を格納する領域と VT[] の定義です。
        int                     g_KN;                // フレーム数
    
        //View(XYZ), Terg(XYZ), 移動(XYZ), 回転(XYZ)
        float   VT[51][12];  //キーフレームテーブル
        
  2. WinMain() で InitGraphics() の後で GetData() を呼び出して VT[] に格納します。
    g_num と g_Weight に値を格納して、最初のアニメーションを設定します。
            if (FAILED(InitGraphics()))    return FALSE;
    
            g_KN= GetData();        //KeyFrame.txt から入力して VT[i][0] に格納
            g_num= 0;
            g_Weight= 1.0f;
        
  3. データを KeyFrame.txt から入力して VT[][] に格納する GetData() 関数です。
        int  GetData()
        {   int     i;
                    :
            // "KeyFrame.txt" からデータを入力して VT[51][12] に格納する。
            // return i; でキーフレームの個数をリターンする。
        }
        
  4. KeyFrame.txt にタイプするデータの形式です。
    プログラムで処理しやすいように、全てのデータはカンマで区切ります。
        0.00,    0.00,  -20.00,     0.00,    0.00,    0.00,    60.00,   36.00,   80.00,    -0.40,    2.50,    0.00,
        0.00,    0.00,  -20.00,     0.00,    0.00,    0.00,    20.00,   12.00,   30.00,    -0.40,    2.50,    0.00,
        0.00,    0.00,  -20.00,     0.00,    0.00,    0.00,     6.00,    4.00,    6.00,    -0.30,    2.50,    0.00,
        0.00,    0.00,  -20.00,     0.00,    0.00,    0.00,     2.00,    1.20,   -2.00,    -0.10,    2.40,    0.00,
        0.00,    0.00,  -20.00,     0.00,    0.00,    0.00,    -0.40,   -0.20,  -10.00,    -0.10,    2.30,    0.00,
        0.00,    0.00,  -20.00,     0.00,    0.00,    0.00,    -0.80,   -0.20,  -16.00,    -0.10,    2.30,    0.00,
        0.00,    0.00,  -20.00,     0.00,    0.00,    0.00,    -1.60,   -0.20,  -16.80,    -0.10,    2.30,    0.00,
        0.00,    0.00,  -20.00,     0.00,    0.00,    0.00,    -3.20,   -0.20,  -18.00,    -0.10,    2.30,    0.00,
        

【課題2のヒント】

整数データの変換には atoi() を使いましたが、小数点のあるデータ変換には atof() を使います。
    char    *p;
    float   ans;

    ans= (float)atof(p);
float 型のデータを文字に編集するときは %f を使います。
    char    w[80];
    float   dat;

    sprintf(w,"%8.2f, ",dat);

超初心者の方のために全ソースコードを掲載します。 (^_^;)
全ソースコード

前田稔(Maeda Minoru)の超初心者のプログラム入門

超初心者のプログラム入門(DirectX9 game program)

超初心者のプログラム入門(DirectX10 game program)