ホーム < ゲームつくろー! < 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; }