ホーム < ゲームつくろー! < DirectX技術編


その51 スクリーンにある2Dビルボードを行列で操作する:サンプルプログラム


 DirectX技術編その51「スクリーンにある2Dビルボードを行列で操作する」で説明した内容を踏まえたサンプルプログラムです。実行すると正方形のテクスチャ付きビルボードがマウスカーソルの座標を中心に回転しながら表示されます。


サンプルスクリーンショット。キャプチャできていませんが、ビルボードの真ん中にカーソルがあります。


 このサンプルでは3Dで使うワールド変換行列を用いてテクスチャの位置を決めています。よってスケール変換や回転なども全く同じように扱えます。


 以下のプログラムは新規プロジェクトにコピペしてPict.jpgという絵を用意すると完全に動きます。ソース及びオブジェクトはこちらからダウンロードできます(DXGSmp_No51.lzh)。上からデバッグしていくと、何をしているか良くわかるように1つのソースで完結させています。今回のポイントになりそうなところは太文字で示されています。
(もしうまく動かないようでしたら掲示板にてご連絡下さい)

// 2Dビルボード空間テストプログラム

#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("2Dビルボード空間テストプログラム");

#define SAFERELEASE(x) if(x){x->Release();x=0;}

// カスタム頂点
struct Vertex {
    D3DXVECTOR3 pos;
    float u, v;
};

#define VERTEX_FVF    D3DFVF_XYZ | D3DFVF_TEX1

struct LineVertex {
    float x, y, z, w;
    DWORD color;
};

// ビルボード頂点
Vertex billboard_g[ 4 ] = {
    { D3DXVECTOR3( -0.5f, -0.5f, 0.1f ), 0.0f, 1.0f },
    { D3DXVECTOR3( -0.5f,  0.5f, 0.1f ), 0.0f, 0.0f },
    { D3DXVECTOR3(  0.5f, -0.5f, 0.1f ), 1.0f, 1.0f },
    { D3DXVECTOR3(  0.5f,  0.5f, 0.1f ), 1.0f, 0.0f }
};

// グリッド描画
void drawGrid( IDirect3DDevice9* g_pD3DDev ) {
   g_pD3DDev->SetFVF( D3DFVF_XYZRHW | D3DFVF_DIFFUSE );
   LineVertex line[ 2 ] = {
       { 0, 0, 0, 1.0f, 0xff888888 },
       { 0, 0, 0, 1.0f, 0xff888888 },
   };
   for( int x = 0; x <= 640; x+= 20 ) {
        line[ 0 ].x = (float)x; line[ 0 ].y =   0.0f; 
        line[ 1 ].x = (float)x; line[ 1 ].y = 480.0f;
       g_pD3DDev->DrawPrimitiveUP( D3DPT_LINELIST, 1, line, sizeof( LineVertex ) );    
   }

   for ( int y = 0; y <= 480; y+= 20 ) {
        line[ 0 ].x =   0.0f; line[ 0 ].y = (float)y;
        line[ 1 ].x = 640.0f; line[ 1 ].y = (float)y;
       g_pD3DDev->DrawPrimitiveUP( D3DPT_LINELIST, 1, line, sizeof( LineVertex ) );    
   }

   line[ 0 ].x =   0; line[ 0 ].y = 239.5f; line[ 0 ].color = 0xffff0000;
   line[ 1 ].x = 640; line[ 1 ].y = 239.5f; line[ 1 ].color = 0xffff0000;
   g_pD3DDev->DrawPrimitiveUP( D3DPT_LINELIST, 1, line, sizeof( LineVertex ) );    

   line[ 0 ].x = 320; line[ 0 ].y =   0; line[ 0 ].color = 0xffff0000;
   line[ 1 ].x = 320; line[ 1 ].y = 480; line[ 1 ].color = 0xffff0000;
   g_pD3DDev->DrawPrimitiveUP( D3DPT_LINELIST, 1, line, sizeof( LineVertex ) );    
}

// ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT mes, WPARAM wParam, LPARAM lParam){
   if(mes == WM_DESTROY || mes == WM_CLOSE ) {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;

   RECT R;
   SetRect( &R, 0, 0, 639, 479 );
   AdjustWindowRect( &R, WS_OVERLAPPEDWINDOW, false );
   if(!(hWnd = CreateWindow(gName, gName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, R.right - R.left, R.bottom - R.top,
                                    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 = { 640, 480, 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;
   }

   // 板ポリゴン作成
   IDirect3DVertexBuffer9 *buffer = 0;
   if ( FAILED( g_pD3DDev->CreateVertexBuffer( sizeof( Vertex ) * 4, 0, VERTEX_FVF, D3DPOOL_MANAGED, &buffer, 0 ) ) ) {
      SAFERELEASE( g_pD3DDev );  SAFERELEASE( g_pD3D );  return 0;
   }
   Vertex *p;
   buffer->Lock( 0, 0, (void**)&p, 0 );
   {
       memcpy( p, billboard_g, sizeof( Vertex ) * 4 );
       buffer->Unlock();
   }

   // 擬似スクリーンに板ポリを置くワールド行列
   // 大きさ(200, 200)の板ポリを(0, 0, 0)の位置に置くと仮定します。
   // 大きさはスケールで、位置はオフセットで表現します。
   D3DXMATRIX worldMat;
   D3DXMatrixIdentity( &worldMat );
   worldMat._11 = 200.0f;
   worldMat._22 = 200.0f;
   worldMat._41 = 0.0f;
   worldMat._42 = 0.0f;

   // 正則射影行列
   // 今回は記事に沿った正則射影行列を作成します。
   // スクリーンの大きさは640×480を想定します。
   D3DXMATRIX orthoMat;
   D3DXMatrixIdentity( &orthoMat );
   orthoMat._11 = 2.0f/ 640.0f;
   orthoMat._22 = 2.0f/ 480.0f;

   // テクスチャ作成
   IDirect3DTexture9* tex = 0;
   D3DXCreateTextureFromFile( g_pD3DDev, _T("Pict.jpg"), &tex );
   if ( tex == 0 ) {
       return 0;
   }

   // ライトオフ
   g_pD3DDev->SetRenderState( D3DRS_LIGHTING, false );

   ShowWindow(hWnd, nCmdShow);

   // メッセージ ループ
   float angle = 0.0f;
   do{
      Sleep(1);
      if( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ){ DispatchMessage(&msg);}

         // 行列設定
         POINT cursor;
         GetCursorPos( &cursor );
         ScreenToClient( hWnd, &cursor );
         D3DXMATRIX rot, mat;
         D3DXMatrixRotationZ( &rot, angle += 0.01f );
         mat = worldMat * rot;
         mat._41 = cursor.x - 320.0f;  // <- 擬似スクリーン座標はど真ん中が(0, 0)
         mat._42 = 240.0f - cursor.y;
         g_pD3DDev->SetTransform( D3DTS_WORLD, &mat );
         g_pD3DDev->SetTransform( D3DTS_PROJECTION, &orthoMat );

         g_pD3DDev->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(40,40,80), 1.0f, 0 );
         g_pD3DDev->BeginScene();

         // 板ポリを描画
         g_pD3DDev->SetTexture( 0, tex );
         g_pD3DDev->SetStreamSource( 0, buffer, 0, sizeof( Vertex) );
         g_pD3DDev->SetFVF( VERTEX_FVF );
         g_pD3DDev->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2 );

         // グリッド描画
         drawGrid( g_pD3DDev );
                         
         g_pD3DDev->EndScene();
         g_pD3DDev->Present( NULL, NULL, NULL, NULL );
   }while(msg.message != WM_QUIT);

   SAFERELEASE( tex );
   SAFERELEASE( buffer );
   SAFERELEASE( g_pD3DDev );
   SAFERELEASE( g_pD3D );
   return 0;
}