ホーム < ゲームつくろー! < DirectX技術編 < アニメーションの根っこ:オブジェクトを読み込む


その26 アニメーションの根っこ:ワールド変換行列スタック:サンプルプログラム


 DirectX技術編その26「アニメーションの根っこ:ワールド変換行列スタック」で説明した内容を踏まえたサンプルプログラムです。実行すると、立方体を中心として球が回転します。球の回りには正四面体のオブジェクトもひそかに回っています。球は立方体と親子関係、正四面体は球と親子関係にあり、その動きはXファイル内でアニメーションキーとして記述されています。


サンプルスクリーンショット

 以下のプログラムはCubeAndSphere1.xというXファイルをパスの通ったフォルダに置き、プログラムを空のプロジェクトにコピペした後、以下の6つのファイルをプロジェクトに追加すると完全に動きます。

 ・ AllocHierarchyBase.h
 ・ AllocHierarchyBase.cpp
 ・ AllocHierWorldFrame.h
 ・ AllocHierWorldFrame.cpp
 ・ WorldTransMatStack.h
 ・ WorldTransMatStack.cpp


Xファイルを含むこれらファイルは全てこちらからダウンロードできます(DXGSmpNo26_v101.lzh)。アニメーション関係はクラス化しないととてつもない事になってしまいますので、やむを得ずクラスを用意しています。煩雑をお許し下さい。上からデバッグしていくと、何をしているか良くわかるように1つのソースで完結させています。今回のポイントになりそうなところは太文字で示されています。


○ CAllocHierWorldFrameクラスについて

 このクラスは、CAllocHierarchyBaseクラスの派生クラスで、ワールド変換行列付きのフレーム構造体であるD3DXFRAME_WORLD構造体を内部で生成するため、CreateNewFrameメンバ関数がオーバーライドされています。こういう使いかをしたいためにCAllocHierarchyBaseクラスを設計しました(^-^)


○ CWorldTransMatStackクラスについて

 このクラスはD3DXLoadMeshHierarchyFromX関数によって取得されたフレーム構造体(D3DXFRAME_WORLD)を解析し、設定されたワールド変換行列から各フレームのワールド変換行列を全て算出します。また、メッシュコンテナがあるフレームを抽出し、描画リストとして保持します。このクラスの内部がDirectX技術編その26「アニメーションの根っこ:ワールド変換行列スタック」で説明した内容の全容になります。


(2009. 5. 5追記)
 UMK様より「削除時に削除リストに登録した構造体のデストラクタが呼ばれない」というバグ報告を頂きました。それに伴いまして、v1.01からCAllocHierarchyBaseクラスにてvoidポインタを直接deleteするのではなくて「deleter」というクラスに削除してもらうように実装を変更しました。


(もしうまく動かないようでしたら掲示板にてご連絡下さい)

// 固定アニメーションテストサンプルプログラム

#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")

#include <windows.h>
#include <tchar.h>
#include <d3d9.h>
#include <d3dx9.h>
#include "AllocHierWorldFrame.h"
#include "WorldTransMatStack.h"

using namespace std;

TCHAR gName[100] = _T("固定アニメーションテストサンプルプログラム");
TCHAR Filename[128] = _T("CubeAndSphere1.x");

LRESULT CALLBACK WndProc(HWND hWnd, UINT mes, WPARAM wParam, LPARAM lParam){
   if(mes == WM_DESTROY) {PostQuitMessage(0); return 0;}
   return DefWindowProc(hWnd, mes, wParam, lParam);
}

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
   // アプリケーションの初期化
   MSG msg; HWND hWnd;
   WNDCLASSEX wcex ={sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, hInstance, NULL, NULL,
                                    (HBRUSH)(COLOR_WINDOW+1), NULL, (TCHAR*)gName, NULL};
   if(!RegisterClassEx(&wcex))
      return 0;

   if(!(hWnd = CreateWindow(gName, gName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
                                    NULL, NULL, hInstance, NULL)))
      return 0;

   // Direct3Dの初期化
   LPDIRECT3D9 g_pD3D;
   LPDIRECT3DDEVICE9 g_pD3DDev;
   if( !(g_pD3D = Direct3DCreate9( D3D_SDK_VERSION )) ) return 0;

   D3DPRESENT_PARAMETERS d3dpp = {0,0,D3DFMT_UNKNOWN,0,D3DMULTISAMPLE_NONE,0,
                                  D3DSWAPEFFECT_DISCARD,NULL,TRUE,TRUE,D3DFMT_D24S8,0,D3DPRESENT_RATE_DEFAULT,D3DPRESENT_INTERVAL_DEFAULT}; 

   if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &g_pD3DDev ) ) )
   if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_pD3DDev ) ) )
   {
      g_pD3D->Release();
      return 0;
   }

   // メッシュオブジェクトの読み込み
   CAllocHierWorldFrame AH;         // CAllocHierarchyBaseの派生クラス
   D3DXFRAME_WORLD* pFR;            // ワールド変換行列付きフレーム構造体
   ID3DXAnimationController* pAC;   // アニメーションコントローラ
   if(FAILED(D3DXLoadMeshHierarchyFromX( Filename, D3DXMESH_MANAGED, g_pD3DDev, &AH, NULL, (D3DXFRAME**)(&pFR), &pAC))){
      g_pD3DDev->Release(); g_pD3D->Release();
      return 0;
   }

   // ビュー変換
   D3DXMATRIX View;
   D3DXMatrixLookAtLH(
      &View,
      &D3DXVECTOR3(4.0f, 3.0f, 2.0f),
      &D3DXVECTOR3(0.0f, 0.0f, 0.0f),
      &D3DXVECTOR3( 0.0f, 1.0f, 0.0f)
     );

   // 射影変換
   D3DXMATRIX Persp;
   D3DXMatrixPerspectiveFovLH( &Persp, D3DXToRadian(45), 640.0f/480.0f, 1.0f, 10000.0f);

   // 行列登録
   g_pD3DDev->SetTransform(D3DTS_VIEW, &View);
   g_pD3DDev->SetTransform(D3DTS_PROJECTION, &Persp);

   // ライト
   D3DLIGHT9 light;
   ZeroMemory(&light, sizeof(D3DLIGHT9) );
   light.Direction = D3DXVECTOR3(-1, -1, -1);
   light.Type = D3DLIGHT_DIRECTIONAL; 
   light.Diffuse.r = 0.8f;
   light.Diffuse.g = 0.8f;
   light.Diffuse.b = 0.8f;
   g_pD3DDev->SetLight( 0, &light );
   g_pD3DDev->LightEnable( 0, true );
   g_pD3DDev->SetRenderState( D3DRS_LIGHTING, TRUE );

   ShowWindow(hWnd, nCmdShow);

   // メッセージループ
   CWorldTransMatStack WTMStack;
   list< D3DXFRAME_WORLD* > *pDrawList;
   D3DXMATRIX WorldMat, Rot;

   pAC->SetTrackSpeed(0, 1000.0f);   // アニメーションスピードを調整
   FLOAT Ang = 0.0f;

   do{
      Sleep(1);
      if( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ){ DispatchMessage(&msg);}
        
        // アニメーションをフレーム分進める
        pAC->AdvanceTime( 6.0f, NULL );

         // ワールド変換行列スタックによるワールド変換行列の計算
         Ang += 0.004f;
         D3DXMatrixIdentity( &WorldMat );
         D3DXMatrixRotationYawPitchRoll( &Rot, Ang, Ang/2.37f, 0);
         D3DXMatrixMultiply( &WorldMat, &WorldMat, &Rot );
         WTMStack.SetWorldMatrix( &WorldMat );

         // フレームのワールド変換行列を計算
         WTMStack.UpdateFrame( pFR );
         
         // 描画プロセス
         g_pD3DDev->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(40,40,80), 1.0f, 0 );
         g_pD3DDev->BeginScene();

            pDrawList = WTMStack.GetDrawList();              // 描画リストを取得

            list< D3DXFRAME_WORLD* >::iterator it = pDrawList->begin();
            int materialnum;
            int i;
            for( ; it!=pDrawList->end(); it++)
            {
               g_pD3DDev->SetTransform( D3DTS_WORLD, &(*it)->WorldTransMatrix );   // ワールド変換行列を設定
               materialnum = (*it)->pMeshContainer->NumMaterials;
               for(i=0; i<materialnum; i++)
               {
                  g_pD3DDev->SetMaterial( &(*it)->pMeshContainer->pMaterials[i].MatD3D );
                  (*it)->pMeshContainer->MeshData.pMesh->DrawSubset(i);
               }
            }

         g_pD3DDev->EndScene();
         g_pD3DDev->Present( NULL, NULL, NULL, NULL );
   }while(msg.message != WM_QUIT);

   pAC->Release();
   g_pD3DDev->Release();
   g_pD3D->Release();

   return 0;
}