tiny_4anim.x でアニメーション

August2007 DirectX9 で tiny_4anim.x のアニメーションを描画します。

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

プロジェクトの設定

  1. 下記のフォルダーに「X-FILE(Tiny.x) でアニメーション」よりワンレベル上の Tiny のプログラムが格納されています。
    C:\Program Files\Microsoft DirectX SDK (August 2007)\Samples\C++\Direct3D\MultiAnimation\
    "tiny.x" は歩くだけですが "tiny_4anim.x" は「歩く、走る、立ち止まる、周りを見る、時計を見る」などの動作が登録されています。
    このサンプルは次のような操作が出来ます。
    1. Add Instance で Tyny が増えます。
    2. Next View(revious View) で View を Tyny の動きに合わせます。
    3. reset View で View を最初の状態に設定します。
    4. Enable Sound で音を演奏/停止します。
    5. Control this Instance で Tyny を操作できます。
      ・A/D キーで方向転換
      ・W で歩く
      ・Shift+W で走る
  2. なかなか面白いプログラムなのですが、残念ながらこのままでは複雑でどうなっているか解りません。
    そこで、このプログラムのテクニックを応用できるように機能を絞り込んで簡潔にしてみました。
    また、型の古いグラフィックボードでも動くように シェダーを外して SOFT で処理 してみました。 (^_^;)
    前回のプログラムでは、ウインドウのサイズを変更すると終了時にエラーが表示された不具合を修正しています。
    X-FILE(Tiny.x) でアニメーション をベースにプロジェクトを作成しました。
    プロジェクトの作成は、こちらを参照して下さい。
    "tiny.x" と "tiny_4anim.x" は扱い方が異なるらしく「X-FILE(Tiny.x) でアニメーション」で X-FILEの名前を変更しただけでは描画されませんでした。
    逆に "tiny_4anim.x" のプロジェクトで "tiny.x" に変更すると、動作は変ですがアニメーションされました。
  3. 元のプログラムでは STL が使われていますが、モデルの描画を一体に限定して STL を削除しました。
    stl の説明は「超初心者のプログラム入門(C/C++/CLI)/STL(Standard Template Library)」を参照して下さい。
    Sound を削除してソースを簡潔にしました。
    Object Class で分けられていたソースファイルを一つにまとめて集約しています。
    また各クラスで別々に定義されていた共通領域を Global variables で定義しました。
    その分 Object Class の独立性が無くなっていることを了承して下さい。
    これらの修正によりソースコードが半分以下になり、プログラムも解りやすくなっています。
  4. 元のフォルダーから次のファイルを "C:\\Data\\Model\\Tiny" のフォルダーにコピーしてテストしています。
    tiny_4anim.x
    Tiny_skin.dds
  5. 掲載したプログラムでは、次の操作が出来ます。
    1. 上下左右の矢印キーでモデルのX座標,Y座標が変わります。
    2. テンキーの 8,2 でモデルのZ座標が変わります。
    3. テンキーの 5 で、アニメーションセット(歩く/走る/時計や周りを見る)が変わります。
    4. スペースキーでアニメーションが停止します。
    5. マウスの左ドラッグでモデルが回転します。
    6. マウスの右ドラッグでモデルの座標が変わります。

プログラムの説明

  1. 基本的な説明は X-FILE(Tiny.x) でアニメーション を参照して下さい。
    g_MultiAnim はモデルのアニメーションを制御するオブジェクトです。
    g_Path[] にリソース(tiny_4anim.x など)が格納されているパスを設定します。
    Main Program では g_Tiny を通じてプログラムを制御します。
    g_Setnum にはアニメーションセットの種類を格納します。
    g_Stop はアニメーションを停止するフラグです。
    各クラスで別々に定義されていた共通領域を「Object Class で共通に使う領域」で定義しました。
    g_pFrameRoot はアニメーションするフレームリストのトップポインタです。
    g_vObjectCenter はモデルを描画する座標です。
    g_ArcBall はモデルの回転を設定する Object です。
    //☆Global variables☆
    CFirstPersonCamera      g_Camera;               // A model viewing camera
    CMultiAnim              g_MultiAnim;            // the MultiAnim class for holding Tiny's mesh and frame hierarchy
    double                  g_fLastAnimTime = 0.0;  // Time for the animations
    WCHAR                   g_Path[MAX_PATH]  = L"C:\\Data\\Model\\Tiny";
    WCHAR                   X_file[MAX_PATH]  = L"tiny_4anim.x";
    CTiny*                  g_Tiny = NULL;
    int                     g_Setnum = 0;           // Animation Set Number
    bool                    g_Stop = FALSE;         // アニメーションの停止
    
    // Object Class で共通に使う領域
    LPDIRECT3DDEVICE9       g_pDevice = NULL;
    ID3DXAnimationController* g_pAnimController = NULL;
    CAnimInstance*          g_v_pAnimInstances = NULL;
    float                   g_fBoundingRadius;      // Radius of bounding sphere of object
    MultiAnimFrame *        g_pFrameRoot = NULL;    // shared between all instances
    CMultiAnimAllocateHierarchy AH;
    D3DXMATRIXA16*          g_pBoneMatrices = NULL;
    UINT                    g_NumBoneMatricesMax = 0;
    
    // Tiny の座標と回転の領域
    D3DXVECTOR3             g_vObjectCenter = D3DXVECTOR3(0,0,0);  // Center of bounding sphere of object
    CD3DArcBall             g_ArcBall;              // Arcball for model control
    
    g_pFrameRoot; を定義している MultiAnimFrame は次のように宣言されています。
    従って、実質的に D3DXFRAME と同じものです。
    struct MultiAnimFrame : public D3DXFRAME
    {
    };
  2. OnCreateDevice() で IDirect3DDevice9 をグローバル領域にコピーします。
    アプリケーションの初期化は OnResetDevice() で行います。
    HRESULT CALLBACK OnCreateDevice( IDirect3DDevice9* pd3dDevice,
                                     const D3DSURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext )
    {
        g_pDevice = pd3dDevice;
        g_pDevice->AddRef();
    
        return S_OK;
    }
    
  3. OnResetDevice() 関数です。
    g_MultiAnim.Setup() 関数で X-FILE を入力しています。
    CTiny をインスタンス化して、セットアップします。
    描画環境を設定します。
    g_Camera とプロジェクションの設定を行ないます。
    g_ArcBall にモデルの回転(向き)の初期値を設定します。
    g_ArcBall の設定は OnBegin から OnMove でマウス操作と同様に座標を設定します。
    モデルの移動や回転は、キーやマウスで操作することが出来ます。
    HRESULT CALLBACK OnResetDevice( IDirect3DDevice9* pd3dDevice,
                                    const D3DSURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext )
    {   HRESULT hr;
        WCHAR     strCWD[MAX_PATH];
    
        GetCurrentDirectory(MAX_PATH,strCWD);
        SetCurrentDirectory(g_Path);
    
        V_RETURN( g_MultiAnim.Setup( pd3dDevice ) );
    
        // Restore steps for tiny instances
        g_Tiny = new CTiny;
        if( g_Tiny == NULL )    return E_OUTOFMEMORY;
        hr = g_Tiny->Setup( 0.f );
    
        // Setup render state
        pd3dDevice->SetRenderState( D3DRS_LIGHTING, TRUE );
        pd3dDevice->SetRenderState( D3DRS_AMBIENT, D3DCOLOR_ARGB( 255, 255, 255, 255 ) );
        pd3dDevice->LightEnable( 0, TRUE );
        pd3dDevice->SetRenderState( D3DRS_NORMALIZENORMALS, TRUE );
    
        D3DXVECTOR3 vEye( 0.0f, -100.0f, -700.f );
        D3DXVECTOR3 vAt( 0.0f,  150.0f, 0.0f );
        g_Camera.SetViewParams( &vEye, &vAt );
    
        float fAspectRatio = pBackBufferSurfaceDesc->Width / (FLOAT)pBackBufferSurfaceDesc->Height;
        g_Camera.SetProjParams( D3DX_PI/3, fAspectRatio, 1.0f, 10000.0f );
    
        // Setup the arcball parameters
        g_ArcBall.Reset();
        g_ArcBall.SetWindow( pBackBufferSurfaceDesc->Width, pBackBufferSurfaceDesc->Height, 0.85f );
        g_ArcBall.SetTranslationRadius( g_fBoundingRadius );
        //☆モデルの向きの初期値を設定☆
        g_ArcBall.OnBegin( 330, 130 );
        g_ArcBall.OnMove( 580, 260 );
        g_ArcBall.OnEnd(); 
    
        g_fLastAnimTime = DXUTGetGlobalTimer()->GetTime();;
        SetCurrentDirectory(strCWD);
    
        return S_OK;
    }
    
  4. "tiny_4anim.x" をロードする CMultiAnim::Setup() 関数です。
    D3DXLoadMeshHierarchyFromX() が X-FILE をロードする関数です。
    CMultiAnimAllocateHierarchy AH; でアニメーションデータとフレーム階層をロードします。
    pUserDataLoader(pLUD) はアプリケーションが提供するユーザーデータのロードを許可するインターフェイスで、 規定値では NULL が設定されています。
    HRESULT CMultiAnim::Setup( LPDIRECT3DDEVICE9 pDevice, LPD3DXLOADUSERDATA pLUD )
    {
        assert( pDevice != NULL );
    
        HRESULT hr = S_OK;
        WCHAR       strCWD[MAX_PATH];
        GetCurrentDirectory(MAX_PATH,strCWD);
        SetCurrentDirectory(g_Path);
    
        hr= D3DXLoadMeshHierarchyFromX( X_file, 0, pDevice, &AH, pLUD,
                                       (LPD3DXFRAME *) &g_pFrameRoot, &g_pAnimController );
        if (FAILED(hr))     goto e_Exit;
        if( !g_pAnimController )
        {   MessageBox( NULL,
                        L"The sample is attempting to load a mesh without animation or incompatible animation.  This sample requires tiny_4anim.x or a mesh with identical animation sets.  The program will now exit.",
                        L"Mesh Load Error", MB_OK );
            goto e_Exit;
        }
    
        // set up bone pointers
        hr = SetupBonePtrs( g_pFrameRoot );
        if( FAILED( hr ) )  goto e_Exit;
    
        // get bounding radius
        hr = D3DXFrameCalculateBoundingSphere( g_pFrameRoot, &g_vObjectCenter, &g_fBoundingRadius );
        if( FAILED( hr ) )  goto e_Exit;
    
    e_Exit:
        if( FAILED( hr ) )  Cleanup();
        SetCurrentDirectory(strCWD);
    
        return hr;
    }
        
    以下は DirectX9 の Help からの抜粋です。
    CMultiAnimAllocateHierarchy* pAH でアニメーションデータとフレーム階層をロードする。
    D3DXLoadMeshHierarchyFromX は、.x ファイルからアニメーション データとフレーム階層をロードする。
    .x ファイルを調べ、pAlloc で渡された、ID3DXAllocateHierarchy の派生オブジェクトに基づいてフレーム階層とアニメーション コントローラを構築する。
    データのロードは、次の手順で行う。
    1. ID3DXAllocateHierarchy から派生させて、各メソッドを実装する。
      これによって、フレームおよびメッシュの割り当てと解放をどのように行うかを制御する。
    2. ID3DXLoadUserData から派生させて、各メソッドを実装する。
      .x ファイル内に埋め込みのユーザー定義データが存在しないか、これを必要としない場合は、この部分は省いてよい。
    3. ID3DXAllocateHierarchy クラスのオブジェクトを作成し、必要に応じて LoadUserData クラスのオブジェクトを作成する。
      これらのオブジェクトのメソッドを呼び出す必要はない。
    4. D3DXLoadMeshHierarchyFromX を呼び出して ID3DXAllocateHierarchy オブジェクトおよび ID3DXLoadUserData オブジェクト (または NULL) を渡し、 フレーム階層とアニメーション コントローラを作成する。
      すべてのアニメーション セットとフレームが自動的にアニメーション コントローラに登録される。
    pUserDataLoader(pLUD) はアプリケーションが提供するユーザーデータのロードを許可するインターフェイス。
    .x ファイルに追加のユーザー データが埋め込まれている場合に、そのデータを保存するためにこのインターフェイスを実装する。
    適切なデータが見つかるたびにこのインターフェイスのインスタンスが D3DXLoadMeshHierarchyFromX に渡され、 Direct3D エクステンション (D3DX) はこのインターフェイスに対して適切なメソッドを呼び出す。
    たとえば、.x ファイル内の各フレーム オブジェクトに対して ID3DXLoadUserData::LoadFrameChildData を呼び出し、子データを渡す。
    ID3DXLoadUserData メンバ
    1. LoadFrameChildData .x ファイルからフレームの子データをロードする。
    2. LoadMeshChildData .x ファイルから最上位のデータをロードする。
    3. LoadTopLevelData .x ファイルから最上位のデータをロードする。
    CMultiAnim::Setup() 関数の詳細は「全ソースコード」を参照して下さい。
  5. OnFrameRender() 関数で描画環境を設定して g_Tiny->Draw(); で描画します。
    g_Tiny->AdvanceTime( fElapsedTime ); がモデルをアニメーションする関数です。
    void CALLBACK OnFrameRender( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext )
    {   HRESULT hr;
    
        pd3dDevice->Clear( 0L, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
                           D3DCOLOR_ARGB( 0, 0x3F, 0xAF, 0xFF ), 1.0f, 0L );
        if( SUCCEEDED( pd3dDevice->BeginScene() ) )
        {
            D3DXMATRIXA16 mx, mxView, mxProj;
            D3DXVECTOR3 vEye;
    
            mxView = *g_Camera.GetViewMatrix();
            mxProj = *g_Camera.GetProjMatrix();
            V( pd3dDevice->SetTransform( D3DTS_VIEW, & mxView ) );
            V( pd3dDevice->SetTransform( D3DTS_PROJECTION, & mxProj ) );
            vEye = *g_Camera.GetEyePt();
    
            // Setup the projection matrix
            D3DXMatrixPerspectiveFovLH( &mxProj, D3DX_PI/4, 640.0f/480.0f,
                                        g_fBoundingRadius/64.0f, g_fBoundingRadius*200.0f );
            pd3dDevice->SetTransform( D3DTS_PROJECTION, &mxProj );
    
            //☆モデルのアニメーション☆
            if (!g_Stop)    g_Tiny->AdvanceTime( fElapsedTime );
            g_Tiny->Draw();
    
            pd3dDevice->EndScene();
        }
    }
    
  6. モデルの座標と回転は CTiny::Animate() 関数で設定します。
    g_vObjectCenter と g_ArcBall を参照して g_v_pAnimInstances->SetWorldTransform() で World 座標を設定します。
    void CTiny::Animate( double dTimeDelta )
    {
        //☆g_vObjectCenter, g_ArcBall でモデルの座標を World に設定☆
        D3DXMATRIXA16 mxWorld,mxwk;
        D3DXMatrixTranslation( &mxwk, g_vObjectCenter.x, g_vObjectCenter.y, g_vObjectCenter.z );
        D3DXMatrixIdentity( &mxWorld );
        D3DXMatrixMultiply( &mxWorld, &mxWorld, g_ArcBall.GetRotationMatrix() );
        D3DXMatrixMultiply( &mxWorld, &mxWorld, g_ArcBall.GetTranslationMatrix() );
        D3DXMatrixMultiply( &mxWorld, &mxWorld, &mxwk );
    
        g_v_pAnimInstances->SetWorldTransform( & mxWorld );
    }
    
  7. 走る、歩く、跳ぶといった動作データひとつずつをアニメーションセットと言います。
    CTiny::SetMoveKey() の次のソースコードが「歩く/走る/時計や周りを見る」の切り替えです。
        //☆アニメーションセットの切り替☆
        switch(Setnum)
        {   case 0:
                g_pAnimController->GetAnimationSet( m_dwAnimIdxWalk, & pAS );
                break;
            case 1:
                g_pAnimController->GetAnimationSet( m_dwAnimIdxJog, & pAS );
                break;
            case 2:
                g_pAnimController->GetAnimationSet( m_dwAnimIdxLoiter, & pAS );
        }
    
  8. プログラムの詳細は「C:\Program Files\Microsoft DirectX SDK (August 2007)\Samples\」を参照して下さい。

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

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

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