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