ホーム < ゲームつくろー! < DirectX技術編 < カメラの正面を向くビルボード : サンプルプログラム


その11 カメラの正面を向くビルボード:サンプルプログラム


 DirectX技術編その11「カメラの正面を向くビルボード」で説明した内容を踏まえたサンプルプログラムです。実行すると一面にプレートが出現し、動き回る立方体の方に向きます。


サンプルスクリーンショット…不気味です(^-^;


 全てのプレートの回転行列はD3DXMatrixLookAtLH関数から得られるビュー行列の逆行列を利用して回転方向を決定しています。非常に簡単な方法の割には色々なことに応用ができます。


 以下のプログラムはCube.x及びPlate.xという2つのXファイルをパスの通ったフォルダに置き、空のプロジェクトにコピペすることで完全に動きます。ソース及びオブジェクトはこちらからダウンロードできます(DXGSmp_No11_v101.lzh)。上からデバッグしていくと、何をしているか良くわかるように1つのソースで完結させています。今回のポイントになりそうなところは太文字で示されています。
(もしうまく動かないようでしたら掲示板にてご連絡下さい)

// 正面ビルボードサンプルプログラム

#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>

_TCHAR gName[100] = _T("正面ビルボードサンプルプログラム");


// ビルボード回転行列取得関数
//   BillPos    : ビルボードの制御点の位置
//   TargetPos  : ビルボードが向く注視点
//   Rpt        : 回転行列
void GetBillBoardRotation( D3DXVECTOR3 *BillPos, D3DXVECTOR3 *TargetPos, D3DXMATRIX *Rot )
{
   D3DXMatrixIdentity(Rot);
   D3DXMatrixLookAtLH(Rot, TargetPos, BillPos, &D3DXVECTOR3(0,1,0));
   D3DXMatrixInverse(Rot, NULL, Rot);
   Rot->_41 = 0.0f;   // オフセットを切る(回転行列だけにしてしまう)
   Rot->_42 = 0.0f;
   Rot->_43 = 0.0f;
}

// ウィンドウプロシージャ
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;
   }

   // 板オブジェクト生成
   ID3DXBuffer *pMaterials;
   DWORD NumMaterials;
   ID3DXMesh *pMesh;
   if(FAILED(D3DXLoadMeshFromX( _T("Plate.x"), D3DXMESH_MANAGED, g_pD3DDev, NULL, &pMaterials, NULL, &NumMaterials, &pMesh))){
      g_pD3DDev->Release(); g_pD3D->Release(); return 0;
   }

   // 立方体オブジェクト生成
   ID3DXBuffer *pCube_Materials;
   DWORD Cube_NumMaterials;
   ID3DXMesh *pCube_Mesh;
   if(FAILED(D3DXLoadMeshFromX( _T("Cube.x"), D3DXMESH_MANAGED, g_pD3DDev, NULL, &pCube_Materials, NULL, &Cube_NumMaterials, &pCube_Mesh))){
      pMesh->Release(); g_pD3DDev->Release(); g_pD3D->Release(); return 0;
   }

   g_pD3DDev->LightEnable( 0, true );

   // ウィンドウ表示
   ShowWindow( hWnd, SW_SHOW );


   ////////////////////////////
   // メッセージループ
   /////
   int Plane_Num_X = 40, x;   // 生成するプレート数(X方向の数)
   int Plane_Num_Y = 40, y;   // 生成するプレート数(Y方向の数)
   D3DXMATRIX World;          // 立方体ワールド変換行列
   D3DXMATRIX Rot;            // 板の回転行列
   D3DXMATRIX Offset;         // 板のオフセット行列
   D3DXMATRIX View;           // ビュー変換行列
   D3DXMATRIX Persp;          // 射影変換行列

   FLOAT Ang = 0.0f;   // 回転角度
   unsigned int i;
   do{
      Sleep(1);
      if( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ){ DispatchMessage(&msg);}
      g_pD3DDev->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(40,40,80), 1.0f, 0 );
      g_pD3DDev->BeginScene();

      Ang += 1;

      // ビュー変換
      D3DXMatrixLookAtLH( &View, &D3DXVECTOR3(5*cos(D3DXToRadian(Ang/2.0f)), 0, -17), &D3DXVECTOR3(0, 0, 0), &D3DXVECTOR3( 0, 1, 0) );
      g_pD3DDev->SetTransform(D3DTS_VIEW, &View);

      // 射影変換
      D3DXMatrixPerspectiveFovLH( &Persp, D3DXToRadian(45), 640.0f/480.0f, 1.0f, 1000.0f);
      g_pD3DDev->SetTransform(D3DTS_PROJECTION, &Persp);

      ///////////////////////////
      // キューブ
      ////////
      D3DXMATRIX CubeWorld;   // 立方体のワールド変換行列
      D3DXVECTOR3 CubePos(5*sin(D3DXToRadian(Ang)), 5*cos(D3DXToRadian(Ang)), 5*cos(D3DXToRadian(Ang/2.0f)));
      D3DXMATRIX CubeOffset;
      D3DXMatrixIdentity( &CubeWorld );
      D3DXMatrixRotationAxis( &CubeWorld, &D3DXVECTOR3(2*sin(D3DXToRadian(Ang)),2*cos(D3DXToRadian(Ang)),2*sin(D3DXToRadian(Ang))), Ang/100);
      D3DXMatrixTranslation( &CubeOffset, CubePos.x, CubePos.y, CubePos.z );
      D3DXMatrixMultiply( &CubeWorld, &CubeWorld, &CubeOffset );

      // 行列登録
      g_pD3DDev->SetTransform(D3DTS_WORLD, &CubeWorld);

      // キューブ描画
      for(i=0; i<NumMaterials; i++){
         D3DXMATERIAL *mtrl = (D3DXMATERIAL*)(pCube_Materials->GetBufferPointer());
         g_pD3DDev->SetMaterial( &mtrl->MatD3D );
         pCube_Mesh->DrawSubset(i); 
      };

      ///////////////////////////
      // 板
      ////////
      for(x=0; x<Plane_Num_X; x++)
      for(y=0; y<Plane_Num_Y; y++)
        {
         FLOAT PosX = (FLOAT)x-Plane_Num_X/2.0f;                                 // 板の制御点の
         FLOAT PosY = (FLOAT)y-Plane_Num_Y/2.0f;                                 // 位置(x,y,0)を決め、
         GetBillBoardRotation( &D3DXVECTOR3(PosX,PosY,0.0f), &CubePos, &Rot );   // ターゲットの方を向く回転行列を決定します。
         D3DXMatrixTranslation( &Offset, PosX, PosY, 0 );                        // オフセット行列も作っておいて、

         D3DXMatrixIdentity( &World );
         D3DXMatrixMultiply( &World, &World, &Rot );                             // まずターゲット方向に軸回転させて
         D3DXMatrixMultiply( &World, &World, &Offset );                          // 次にオフセットさせます。

         // 行列登録
         g_pD3DDev->SetTransform(D3DTS_WORLD, &World);

         // 板描画
         for(i=0; i<NumMaterials; i++){
            D3DXMATERIAL *mtrl = (D3DXMATERIAL*)(pMaterials->GetBufferPointer());
            g_pD3DDev->SetMaterial( &mtrl->MatD3D );
            pMesh->DrawSubset(i); 
         };
      }

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

   pMesh->Release();
   pCube_Mesh->Release();
   g_pD3DDev->Release();
   g_pD3D->Release();

   return 0;
}