ホーム < ゲームつくろー! < DirectX技術編 < クォータニオンを学んで見よう : サンプルプログラム
その10 クォータニオンを学んで見よう:サンプルプログラム
DirectX技術編その10「クォータニオンを学んで見よう」で説明した内容を踏まえたサンプルプログラムです。実行するとブルーバックに座標軸と回転する球体が出現します。
サンプルスクリーンショット
球体は原点を通る回転軸を中心として回転します。回転軸を揺らすと、球体がそれに追従していることが確認できます(回転軸は示していません)。クォータニオンを計算する時に、球体の初期位置を(0,
15, 0)としています。よって、回転軸がどこへ向こうとも球体は常に(0, 15, 0)を通ることを確認しますと、クォータニオンの性質が良くわかると思います(X軸は緑、Y軸は赤、Z軸は青です)。
3D描画を使ったためソースが少しだけ長くなってしまいました。すいませんです。
以下のプログラムはObject.x及びAxis.xという2つのXファイルをパスの通ったフォルダに置き、空のプロジェクトにコピペすることで完全に動きます。2つのXファイルはこちらからダウンロードできます(DXGNo10XFile.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("クォータニオンサンプルプログラム"); // クォータニオンによる回転行列作成関数 void PosCalculate( D3DXVECTOR3 *pInit, D3DXVECTOR3 *pC, FLOAT deg, D3DXVECTOR3 *ret) { D3DXQUATERNION R, Ans; D3DXQUATERNION P(pInit->x, pInit->y, pInit->z, 0.0f); // 回転させたい点の初期位置 D3DXQUATERNION Q(0.0f, 0.0f, 0.0f, 1.0f); // 単位クォータニオンと、 FLOAT Ang = D3DXToRadian(deg); // 回転角度(ラジアン)から、 D3DXQuaternionRotationAxis( &Q, pC, Ang); // 軸pC回りの回転クォータニオンQを生成して、 D3DXQuaternionConjugate( &R, &Q); // その共役クォータニオンRを算出し、 D3DXQuaternionMultiply( &Ans, &R, &P ); // R・P・Qと掛け算すると、 D3DXQuaternionMultiply( &Ans, &Ans, &Q ); // Ansに回転後の位置が入ります。 ret->x = Ans.x; ret->y = Ans.y; ret->z = Ans.z; } // ウィンドウプロシージャ 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("Object.x"), D3DXMESH_MANAGED, g_pD3DDev, NULL, &pMaterials, NULL, &NumMaterials, &pMesh))){ g_pD3DDev->Release(); g_pD3D->Release(); return 0; } // 軸オブジェクト生成 ID3DXBuffer *pAX_Materials; DWORD AX_NumMaterials; ID3DXMesh *pAX_Mesh; if(FAILED(D3DXLoadMeshFromX( _T("Axis.x"), D3DXMESH_MANAGED, g_pD3DDev, NULL, &pAX_Materials, NULL, &AX_NumMaterials, &pAX_Mesh))){ pMesh->Release(); g_pD3DDev->Release(); g_pD3D->Release(); return 0; } // 初期位置と回転軸 D3DXVECTOR3 Pos(0, 15, 0); // 初期位置 D3DXVECTOR3 C(5, 2, 1); // 回転軸ベクトル // ライト g_pD3DDev->LightEnable( 0, true ); g_pD3DDev->SetRenderState( D3DRS_AMBIENT, 0x00808080 ); // アンビエントライト // ウィンドウ表示 ShowWindow( hWnd, SW_SHOW ); //////////////////////////// // メッセージループ ///// D3DXMATRIX World; // 球ワールド変換行列 D3DXVECTOR3 Ret; // 描画位置(ワールド座標) FLOAT Ang = 0.0f; // 球の回転角度 D3DXMATRIX AXWorld; // 軸ワールド変換行列 D3DCOLORVALUE Ambient = {0.1f, 0.1f, 0.1f, 0.0f}; // マテリアルのアンビエント D3DXMATRIX View; // ビュー変換行列 D3DXMATRIX Persp; // 射影変換行列 D3DXMatrixIdentity( &AXWorld ); // 軸は原点に位置するので単位行列 unsigned int i, j; do{ Sleep(1); if( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ){ DispatchMessage(&msg);} g_pD3DDev->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(50,130,200), 1.0f, 0 ); g_pD3DDev->BeginScene(); Ang += 1; C.z = 10*sin(D3DXToRadian(Ang/3)); for(j=0; j<360; j+=20) // 球個を描画 { /////////////////////////// // 球 //////// // ワールド変換 PosCalculate(&Pos, &C, Ang+j, &Ret); // 描画位置を計算 D3DXMatrixIdentity( &World ); // 単位行列化 D3DXMatrixTranslation( &World, Ret.x, Ret.y, Ret.z ); // 平行移動 // ビュー変換 D3DXMatrixLookAtLH( &View, &D3DXVECTOR3(40,30,-60), &D3DXVECTOR3(0,0,0), &D3DXVECTOR3(0,1,0)); // 射影変換 D3DXMatrixPerspectiveFovLH( &Persp, D3DXToRadian(45), 640.0f/480.0f, 0.1f, 10000.0f); g_pD3DDev->SetTransform(D3DTS_WORLD, &World); g_pD3DDev->SetTransform(D3DTS_VIEW, &View); g_pD3DDev->SetTransform(D3DTS_PROJECTION, &Persp); for(i=0; i<NumMaterials; i++){ // 球描画 D3DXMATERIAL *mtrl = (D3DXMATERIAL*)(pMaterials->GetBufferPointer()); mtrl->MatD3D.Ambient = Ambient; g_pD3DDev->SetMaterial( &mtrl->MatD3D ); pMesh->DrawSubset(i); }; /////////////////////////// // 軸 //////// // ワールド変換 g_pD3DDev->SetTransform(D3DTS_WORLD, &AXWorld); for(i=0; i<AX_NumMaterials; i++){ // 軸描画 D3DXMATERIAL *mtrl = ( (D3DXMATERIAL*)(pAX_Materials->GetBufferPointer()) + i); mtrl->MatD3D.Ambient = Ambient; g_pD3DDev->SetMaterial( &mtrl->MatD3D ); pAX_Mesh->DrawSubset(i); }; } g_pD3DDev->EndScene(); g_pD3DDev->Present( NULL, NULL, NULL, NULL ); }while(msg.message != WM_QUIT); pMesh->Release(); pAX_Mesh->Release(); g_pD3DDev->Release(); g_pD3D->Release(); return 0; }