★ DirectX Class ★ * Chapter 12: メッシュの表示 *
< メッシュとは >
いよいよメッシュを扱います。
これによってポリゴン数の多い物体を描画するのが飛躍的に楽になります。
以前では考えられなかったようななめらかな物体も扱えるようになります。
例えば、人間を始めとした有機物の描画も楽になります。

『 メッシュ 』とは、DirectXで扱うことのできる3Dオブジェクトのことです。
今まではプリミティブと呼ばれる板を3Dで作成していましたが、
これだと四角形の板を作成するのに2枚のプリミティブを定義しなくてはなりません。
また、サイコロのような立方体を作成しようと思った場合、
四角形×6面で計24枚のプリミティブを定義しなくてはなりません。
このように、より立体的で、より複雑な形状になるほど、ポリゴン数は増大します。

最近のゲームを例に挙げると、『 LOST PLANET 』というゲームのキャラクター(人間)のポリゴン数は1~2万ポリゴンだそうです。

これらのプリミティブ(ポリゴン)を、一つ一つプログラムの中で定義していくのは現実的ではありません。
3Dゲームを開発したいのであって、3Dキャラを開発したいのではありません。

そこで、立体的な物体も、画像のように外部ファイルとして用意してしまおうという発想が生まれます。
3Dは3Dモデル専用の作成ソフトを利用して作成し、ゲーム中ではロードして描画するだけという発想です。

DirectXではそのような3Dの物体を『 メッシュ 』と呼びます。
拡張子が『 x 』となっているこのメッシュのファイルには、
物体の形状だけでなく、テクスチャファイル名やテクスチャUV座標、法線やマテリアル情報を含めることができます。
ですので、非常に便利です。
というか、実際に3Dゲームを開発するときにはそうでなければやってられません。
< xファイル >
『 xファイル 』はDirectX独自の形式ですが、そのファイルは市販されている様々なソフトで作成することができます。
一例を挙げますと、
metasequoia
Shade
LightWave
等です。
x形式の出力に向いていなかったり、別途プラグインが必要等の場合がありますので、買う前によく調べて買ってください。
高いソフトですし。

ちなみに、筆者も現在フリーの3Dモデリングソフトを開発中です。
去年の12月頃にほぼ完成しているのですが、いくつか酷いバグがなかなか取れないので公開に至っていません。
完成しましたら公開しますので、ご期待ください!

また、xファイルには、テキスト形式とバイナリ形式の2種類があります。
テキスト形式であればテキストエディタで開けば読むことができるので、極端な話、3D物体の形状を直接書いていくこともできます。
まぁ直接書いていくのが面倒という理由でxファイルを使いますので、直接書いては本末転倒感があります。

「じゃぁ結局そういうソフトがないと使えないの?」ということになりますが、そういうことです。
しかし、フリーでもいくつか扱えるソフトがあったはずですので、探してみてください。
それか筆者のソフトが完成するまでお待ちください。
ご要望が多ければ、バグあり御免でβリリースします。

ちなみに、DirectX SDK にもいくつかサンプルでメッシュが付いてきていますので、
練習で使いたい場合はそういったものを使うのも良いと思います。
< 必要な処理 >
今回はメッシュを扱うということなので、まずメッシュのクラスを定義します。

LPD3DXMESH g_pMesh = NULL;

当然開放も必要です。
Direct3Dデバイスの開放よりも前に開放してしまってください。
開放はおなじみの以下の形です。

g_pMesh->Release();

次にメッシュをロードします。
メッシュのロードはテクスチャのロードと同じで、関数一発でできます。
但し、メッシュにテクスチャ情報やマテリアル情報が含まれている場合には別途ロードしなくてはなりません。
今回は簡単のために純粋なメッシュのみをロードします。
メッシュからのその他の情報の取得方法はまた別の章で詳しく解説致します。
メッシュのロードは以下の関数で実現できます。

D3DXLoadMeshFromX( ".\\sample0012.x" , D3DXMESH_SYSTEMMEM , g_pd3dDevice ,
                   NULL , NULL , NULL , NULL , &g_pMesh );

第一引数はメッシュのファイル名です。
第二引数はオプションです。今はこれを指定すれば良いと思ってください。
第三引数はDirect3Dデバイスを指定します。
第四引数~第七引数は今のところNULLで結構です。
第八引数はメッシュへのポインタです。

ロードが完了したら描画できます。
描画するには以下のようなコードを使用します。

g_pMesh->DrawSubset( 0 );

第一引数は描画するメッシュのサブセットです。
サブセットとは、メッシュのマテリアルの数です。
マテリアルに関してはまた後の章で詳しく解説致します。
< サンプル >
このサンプルを実行すると、クライアント領域に蛇が描画され、回転します。
メッシュを扱う場合はZバッファ必須です。
そうでなくてもZバッファを使用する場面は多いので、デフォルトで使うくらいに思っておいてください。

テクスチャを使用する場合は第9章、第11章を参考にして、メッシュの描画前に設定してください。
今回のサンプルで使用したxファイルはテクスチャに対応していませんので、テクスチャを設定しても正常に適用されません。

サンプルプログラムをご利用の場合は、このxファイルを実行ファイルと同じディレクトリに入れて実行してください。

★蛇のxファイルはこちら★
(ブラウザによってはダウンロードにならず、直接テキストが開かれてしまう場合がありますので、
その場合は右クリックメニューからファイルとして保存してください。)

ポイントはピンク色で示してあります。

//-----------------------------------------------------------------
//
//    DirectX 3D
//
//-----------------------------------------------------------------
#define sqrtf sqrt
#define sinf  sin
#define cosf  cos
#define tanf  tan

#include <stdio.h>
#include <windows.h>
#include <d3d9.h>
#include <d3dx9.h>

#pragma comment( lib , "d3d9.dll" )
#pragma comment( lib , "d3dx9.dll" )



//-----------------------------------------------------------------
//    Grobal Variables.
//-----------------------------------------------------------------
LPDIRECT3D9             g_pD3D       = NULL;
LPDIRECT3DDEVICE9       g_pd3dDevice = NULL;
LPD3DXMESH              g_pMesh      = NULL;



//-----------------------------------------------------------------
//    Prototypes.
//-----------------------------------------------------------------
HWND    InitApp( HINSTANCE , int );
BOOL    InitDirect3D( HWND );
BOOL    CleanupDirect3D();
BOOL    SetupMatrices();
BOOL    RenderDirect3D();
LRESULT CALLBACK WndProc( HWND , UINT , WPARAM , LPARAM );



//-----------------------------------------------------------------
//    Main.
//-----------------------------------------------------------------
int WINAPI WinMain( HINSTANCE hInst , HINSTANCE hPrevinst , LPSTR nCmdLine , int nCmdShow )
{
	MSG msg;
	HWND hWnd;
	
	hWnd = InitApp( hInst , nCmdShow );
	if ( !hWnd ) return FALSE;
	
	if ( !InitDirect3D( hWnd ) ) return FALSE;
	
	while( msg.message != WM_QUIT ){
		if ( PeekMessage( &msg , NULL , 0 , 0 , PM_REMOVE ) ){
			TranslateMessage( &msg );
			DispatchMessage( &msg );
		}else{
			RenderDirect3D();
		}
		Sleep( 1 );
	}
	
	return msg.wParam;
}



//-----------------------------------------------------------------
//    Initialize Application.
//-----------------------------------------------------------------
HWND InitApp( HINSTANCE hInst , int nCmdShow )
{
	WNDCLASS wc;
	HWND hWnd;
	char szClassName[] = "Direct3D Test";
	
	wc.style         = CS_HREDRAW | CS_VREDRAW;
	wc.hInstance     = hInst;
	wc.hCursor       = LoadCursor( NULL , IDC_ARROW );
	wc.hIcon         = LoadIcon( NULL , IDI_APPLICATION );
	wc.hbrBackground = (HBRUSH)GetStockObject( WHITE_BRUSH );
	wc.lpszClassName = szClassName;
	wc.lpszMenuName  = NULL;
	wc.lpfnWndProc   = WndProc;
	wc.cbWndExtra    = 0;
	wc.cbClsExtra    = 0;
	if ( !RegisterClass( &wc ) ) return FALSE;
	
	hWnd = CreateWindow( szClassName , "Direct3D Test" , WS_OVERLAPPEDWINDOW ,
						CW_USEDEFAULT , CW_USEDEFAULT , 640 , 480,
						NULL , NULL , hInst , NULL );
	if ( !hWnd ) return FALSE;
	
	ShowWindow( hWnd , nCmdShow );
	UpdateWindow( hWnd );
	
	return hWnd;
}



//-----------------------------------------------------------------
//    Initialize Direct3D.
//-----------------------------------------------------------------
BOOL InitDirect3D( HWND hWnd )
{
	D3DPRESENT_PARAMETERS d3dpp;
	HRESULT hr;
	
	if ( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) ){
		MessageBox( hWnd , "Can't create Direct3D." , "Error" , MB_OK );
		return FALSE;
	}
	ZeroMemory( &d3dpp , sizeof( d3dpp ) );
	d3dpp.Windowed               = TRUE;
	d3dpp.SwapEffect             = D3DSWAPEFFECT_DISCARD;
	d3dpp.BackBufferFormat       = D3DFMT_UNKNOWN;
	d3dpp.EnableAutoDepthStencil = TRUE;
	d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
	
	hr = g_pD3D->CreateDevice( D3DADAPTER_DEFAULT , D3DDEVTYPE_HAL , hWnd ,
	                           D3DCREATE_SOFTWARE_VERTEXPROCESSING ,
	                           &d3dpp , &g_pd3dDevice );
	if ( FAILED( hr ) ){
		MessageBox( hWnd , "Can't create device." , "Error" , MB_OK );
		return FALSE;
	}
	
	g_pd3dDevice->SetRenderState( D3DRS_CULLMODE         , D3DCULL_NONE );
	g_pd3dDevice->SetRenderState( D3DRS_LIGHTING         , FALSE );
	
	g_pd3dDevice->SetRenderState( D3DRS_ZENABLE , TRUE );
	
	D3DXLoadMeshFromX( ".\\sample0012.x" , D3DXMESH_SYSTEMMEM , g_pd3dDevice ,
	                   NULL , NULL , NULL , NULL , &g_pMesh );
	
	return TRUE;
}



//-----------------------------------------------------------------
//    Cleanup Direct3D.
//-----------------------------------------------------------------
BOOL CleanupDirect3D()
{
	if ( g_pMesh != NULL )
		g_pMesh->Release();
	
	if ( g_pd3dDevice != NULL )
		g_pd3dDevice->Release();
	
	if ( g_pD3D != NULL )
		g_pD3D->Release();
	
	return TRUE;
}



//-----------------------------------------------------------------
//    Window Proc.
//-----------------------------------------------------------------
LRESULT CALLBACK WndProc( HWND hWnd , UINT msg , WPARAM wp , LPARAM lp )
{
	switch( msg ){
		case WM_DESTROY:
			CleanupDirect3D();
			PostQuitMessage( 0 );
			break;
		default:
			return DefWindowProc( hWnd , msg , wp , lp );
	}
	
	return 0L;
}



//-----------------------------------------------------------------
//    Setup Matrices.
//-----------------------------------------------------------------
BOOL SetupMatrices()
{
	D3DXMATRIXA16 matWorld , matView , matProj;
	D3DXVECTOR3 vEyePt , vLookatPt , vUpVec;
	
	// World Matrix.
	D3DXMatrixRotationY( &matWorld , timeGetTime()/1000.0f );
	g_pd3dDevice->SetTransform( D3DTS_WORLD , &matWorld );
	
	// Camera.
	vEyePt.x    = 0.0f;
	vEyePt.y    = 3.0f;
	vEyePt.z    = 0.0f-5.0f;
	vLookatPt.x = 0.0f;
	vLookatPt.y = 0.0f;
	vLookatPt.z = 0.0f;
	vUpVec.x    = 0.0f;
	vUpVec.y    = 1.0f;
	vUpVec.z    = 0.0f;
	D3DXMatrixLookAtLH( &matView , &vEyePt , &vLookatPt , &vUpVec );
	g_pd3dDevice->SetTransform( D3DTS_VIEW , &matView );
	
	// Projection Matrix.
	D3DXMatrixPerspectiveFovLH( &matProj , 3.0f / 4.0f , 1.0f , 1.0f , 100.0f );
	g_pd3dDevice->SetTransform( D3DTS_PROJECTION , &matProj );
	
	return TRUE;
}



//-----------------------------------------------------------------
//    Render Direct3D.
//-----------------------------------------------------------------
BOOL RenderDirect3D()
{
	RECT rc;
	D3DXVECTOR3 center , position;
	
	g_pd3dDevice->Clear( 0 , NULL , D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER ,
	                     0x00000000 , 1.0f , 0 );
	
	g_pd3dDevice->BeginScene();
	
	SetupMatrices();
	
	g_pMesh->DrawSubset( 0 );
	
	g_pd3dDevice->EndScene();
	
	g_pd3dDevice->Present( NULL , NULL , NULL , NULL );
	
	return TRUE;
}

実行結果

クリックすると実物大で表示されます。
執筆: 2009/07/17 (THU)