【Misson3 画面】 |
![]() |
図1 Mission3画面 |
番号 | 種類 | 説明・注意点 |
---|---|---|
1 | コンストラクタ | 他の処理を呼んでも安全な様に準備しておく。 |
2 | デストラクタ | 全ての後始末をできるようにする。 |
3 | デバイス依存メモリオブジェクト生成 | InitDeviceObjectsから呼び出される処理。 |
4 | デバイス依存デバイスオブジェクト生成 | RestoreDeviceObjectsから呼び出される処理。 |
5 | デバイス依存デバイスオブジェクト消去 | InvalidateDeviceObjectsから呼び出される処理。 |
6 | デバイス依存メモリオブジェクト消去 | DeleteDeviceObjectsから呼び出される処理。 |
7 | フレーム毎処理 | アニメーション処理を行う。 |
8 | レンダリング処理 | レンダリングを行う。 |
【ステップ3−1 クラスの挿入 Part1】 | 解説 |
![]() |
「挿入」メニューより、「クラスの新規作成」 を選択する。 |
図2 新規クラスの作成 |
【ステップ3−2 クラスの挿入 Part2】 | 解説 |
![]() |
すると、左図のようなウインドウが現れた はずである。 とりあえず、以下の設定で「OK」ボタンを クリックする。 クラスの種類:
"Generic クラス"
クラスの名前:
"CMeshTest" |
図3 新規クラスを設定 |
【ステップ3−3 新規クラスの編集 Part1】 | 解説 |
![]() |
ここで、ワークスペースを見ると、
左図のように、新しいファイル、 "MeshTest.cpp","MeshTest.h" が出てきたはずだ。 これは、クラス「CMeshTest」を記述する ファイルである。 ファイル名とクラス名の関連は 説明しなくても判りますね? ここでは、"MeshTest.h"を ダブルクリックして下さい。 |
図4 新規クラスを設定 |
【ステップ3−4 新規クラスの編集 Part2】 | 解説 |
![]() |
そうすると、左のリストのように、
クラス宣言を行っている部分が出てくるはずだ。 これを、リスト2のように書き換える。 |
リスト1 CMeshTest 宣言 |
class CMeshTest { public: CMeshTest(); // 処理1 virtual ~CMeshTest(); // 処理2 HRESULT InitDeviceObjects(LPDIRECT3DDEVICE8 lpd3ddev); // 処理3 HRESULT RestoreDeviceObjects(LPDIRECT3DDEVICE8 lpd3ddev); // 処理4 HRESULT InvalidateDeviceObjects(); // 処理5 HRESULT DeleteDeviceObjects(); // 処理6 void Update(float timeElapsed); // 処理7 void Render(LPDIRECT3DDEVICE8 lpd3ddev); // 処理8 private: HRESULT GenerateEnhancedMesh( LPDIRECT3DDEVICE8 lpd3ddev, UINT dwNewNumSegs ); // 内部処理 TCHAR m_strMeshPath[512]; // メッシュが存在するディレクトリ名を保存する。 LPD3DXMESH m_pMeshSysMem; // メモリ上のメッシュへのポインタ LPD3DXMESH m_pMeshEnhanced; // (可能ならば)ビデオメモリ上のメッシュへのポインタ D3DXMATERIAL* m_pMaterials; // マテリアル(質感)配列へのポインタ LPDIRECT3DTEXTURE8* m_ppTextures; // テクスチャー配列へのポインタ DWORD m_dwNumMaterials; // マテリアル(質感)配列の要素数 LPD3DXBUFFER m_pbufMaterials; // マテリアル(質感)配列データオブジェクト(データ本体はビデオメモリかも) D3DXVECTOR3 m_vObjectCenter; // メッシュの重心点 FLOAT m_fObjectRadius; // メッシュを囲む球体の半径 BOOL m_bUseHWNPatches; UINT m_dwNumSegs; // NPatch アルゴリズム(表面が滑らかになる)を使用する際の分割数 D3DXMATRIX m_matWorld; float m_fAngleY; };
【ステップ3−5 新規クラスの編集 Part3】 | 解説 |
![]() |
今度は、MeshTest.cpp をダブルクリック
するとまずは、リスト3の部分を書き換える。 |
図5 MeshTest.cpp を開く |
![]() |
解説 |
左の3行の中の、"stdafx.h" と "mission1.h" の間に、 #include <d3d8.h> #include <d3dx8.h> #include <GameMain.h> 以上3行を書き加え、リスト4のようにする。 |
![]() |
リスト4 ヘッダファイルの追加インクルード |
【ステップ3−6 新規クラスの編集 Part4】 | 解説 |
![]() |
次にこの部分、
これは、VisualC++ が作ってくれたプログラム・・・ といっても空の関数2つ。 次に、ここに手をつける。 |
【ステップ3−7 処理1・コンストラクタの制作】 | 解説 |
CMeshTest::CMeshTest() { GetAppPathName(m_strMeshPath); m_dwNumMaterials = 0; m_pbufMaterials = NULL; m_pMaterials = NULL; m_pMeshEnhanced = NULL; m_pMeshSysMem = NULL; m_ppTextures = NULL; m_bUseHWNPatches = FALSE; m_dwNumSegs = 2; m_fAngleY = 0.0f; } |
左のように、コンストラクタを改造する
基本的には、ステップ3−4で宣言した メンバ変数を初期化しているだけである。 ただし、 GetAppPathName(・・・); は関数呼び出しである。 この関数は、現在作成中のアプリケーションが起動した時 アプリケーションのファイルのあった位置(ファイルパス) を取得しておくものである。 この関数を、リスト7に示す。 |
リスト6 コンストラクタ |
static void GetAppPathName(TCHAR *strCmdPath) {
int l,i;
TCHAR *str = strCmdPath,*c; | // まずは変数宣言 |
lstrcpy( str, ::GetCommandLine());
| // 起動したアプリケーションの絶対パス文字列を取得。 |
l = strlen(str);
if (str[0]=='\"'){
for(i = 0; i < l; ++i){
}
str[i] = str[i+1];
}--l; | // " で始まっていたら、一文字つめる。 |
c = strrchr(str,'\\');
if (c){
*c=0;
}else{
strCmdPath[0]=0;
} | // 最後に\が出てくる所以降を削除 |
} |
【ステップ3−8 処理2・デストラクタの制作】 | 解説 |
CMeshTest::~CMeshTest() { this->InvalidateDeviceObjects(); this->DeleteDeviceObjects(); } |
ここでは、このクラス内の後述する関数。
|
リスト8 デストラクタ |
【ステップ3−9 処理3・デバイス依存メモリオブジェクト生成】 | 解説 |
HRESULT CMeshTest::InitDeviceObjects(LPDIRECT3DDEVICE8 lpd3ddev) { LPDIRECT3DVERTEXBUFFER8 pVB = NULL; BYTE* pVertices = NULL; LPD3DXMESH pTempMesh; HRESULT hr; TCHAR strMeshFilename[512]; | |
InvalidateDeviceObjects(); DeleteDeviceObjects(); | // この処理が再度呼ばれても良いように、 // 廃棄してから初期化。 |
_tcscpy(strMeshFilename,m_strMeshPath); _tcscat(strMeshFilename,"\\"); _tcscat(strMeshFilename,"tiger.x"); hr = D3DXLoadMeshFromX( strMeshFilename, D3DXMESH_SYSTEMMEM, lpd3ddev, NULL, &m_pbufMaterials, &m_dwNumMaterials, &m_pMeshSysMem ); if (FAILED(hr)){ return hr; } | // X ファイル tiger.x をロード |
// vertex buffer を読み出す為にロックする。 hr = m_pMeshSysMem->GetVertexBuffer( &pVB ); if( FAILED(hr) ) return hr; hr = pVB->Lock( 0, 0, &pVertices, 0 ); if( FAILED(hr) ) { if ( pVB != NULL){ pVB->Release(); pVB=NULL; } return hr; } hr = D3DXComputeBoundingSphere( pVertices, m_pMeshSysMem->GetNumVertices(), m_pMeshSysMem->GetFVF(), &m_vObjectCenter, &m_fObjectRadius ); pVB->Unlock(); if ( pVB != NULL){ pVB->Release(); pVB=NULL; } if( FAILED(hr) ) { return hr; } | // 中心点(m_vObjectCenter)の算出 // メッシュを囲む球形の半径の算出。 |
if( 0 == m_dwNumMaterials ) { return E_FAIL; } | // テクスチャー無しはサポートしない。 |
m_pMaterials = (D3DXMATERIAL*)m_pbufMaterials->GetBufferPointer(); m_ppTextures = new LPDIRECT3DTEXTURE8[m_dwNumMaterials]; ZeroMemory(m_ppTextures,sizeof(LPDIRECT3DTEXTURE8)*m_dwNumMaterials); for (DWORD i = 0; i < m_dwNumMaterials ; ++i) m_pMaterials[i].MatD3D.Ambient = m_pMaterials[i].MatD3D.Diffuse; | // マテリアル(質感)配列および // テクスチャ配列の取得 //※(2004/02/09 アンビエント光源対策) //マテリアルのディフューズ色を //アンビエント色にコピーしている。 |
// 頂点フォーマットの修正 if( !(m_pMeshSysMem->GetFVF() & D3DFVF_NORMAL) ) { hr = m_pMeshSysMem->CloneMeshFVF( m_pMeshSysMem->GetOptions(), m_pMeshSysMem->GetFVF() | D3DFVF_NORMAL, lpd3ddev, &pTempMesh ); if( FAILED(hr) ) return hr; D3DXComputeNormals( pTempMesh ); | // 頂点フォーマットに // 法線ベクトルを追加。 // 頂点フォーマット・法線については、 // DirectX8 ドキュメントを参照の事。 |
if ( m_pMeshSysMem != NULL) m_pMeshSysMem->Release(); m_pMeshSysMem = pTempMesh; } return S_OK; | // 変更後のメッシュに入れ替え。 |
} | |
リスト9 デバイス依存メモリオブジェクト生成 |
【ステップ3−10 処理4・デバイス依存デバイスオブジェクト生成】 | 解説 |
HRESULT CMeshTest::RestoreDeviceObjects(LPDIRECT3DDEVICE8 lpd3ddev) { HRESULT hr; D3DCAPS8 d3dCaps; InvalidateDeviceObjects(); if (m_pMeshSysMem == NULL){ return E_FAIL; } lpd3ddev->GetDeviceCaps(&d3dCaps); for( UINT i=0; i < m_dwNumMaterials; i++ ) { TCHAR tmp[512]; TCHAR strTexturePath[512] = _T(""); strcpy( tmp, m_pMaterials[i].pTextureFilename ); GetFileName(tmp); _tcscpy(strTexturePath,m_strMeshPath ); _tcscat(strTexturePath,"\\"); _tcscat(strTexturePath,tmp); if( FAILED( D3DXCreateTextureFromFile( lpd3ddev, strTexturePath, &m_ppTextures[i] ) ) ) m_ppTextures[i] = NULL; } m_bUseHWNPatches = (d3dCaps.DevCaps & D3DDEVCAPS_NPATCHES); hr = GenerateEnhancedMesh( lpd3ddev, m_dwNumSegs ); if( FAILED(hr) ) return hr; return S_OK; } |
ここでは、
マテリアル配列の要素ごとに設定されている テクスチャーの読み込みをした後、 後述の関数、GenerateEnhancedMesh を呼び出している。 GetFileName は、ファイル名からパス部分をカットする。 GenerateEnhancedMesh および GetFileName をリスト11に示す。 |
リスト10 |
HRESULT CMeshTest::GenerateEnhancedMesh( LPDIRECT3DDEVICE8 lpd3ddev, UINT dwNewNumSegs ) { LPD3DXMESH pMeshEnhancedSysMem = NULL; LPD3DXMESH pMeshTemp; HRESULT hr; if (m_bUseHWNPatches){ // ハードウェアがサポートするなら、ポリゴンを分割してビデオカードへ。 hr = m_pMeshSysMem->CloneMeshFVF( D3DXMESH_WRITEONLY | D3DXMESH_NPATCHES, m_pMeshSysMem->GetFVF(), lpd3ddev, &pMeshTemp ); if( FAILED(hr) ) return hr; }else{ // ハードウェアがサポートするなら、頂点データをビデオカードへ。 hr = m_pMeshSysMem->CloneMeshFVF( D3DXMESH_WRITEONLY, m_pMeshSysMem->GetFVF(), lpd3ddev, &pMeshTemp ); } if (m_pMeshEnhanced != NULL){ m_pMeshEnhanced->Release(); m_pMeshEnhanced = NULL; } m_pMeshEnhanced = pMeshTemp; m_dwNumSegs = dwNewNumSegs; return S_OK; } static void GetFileName(TCHAR *str) { int l,i; char *c; l = strlen(str); if ((str[0]=='\"')&&(str[l-1]=='\"')){ for(i = 0; i < (l - 1) ; ++i){ str[i] = str[i+1]; } --l; str[l] = 0; } c = strrchr(str,'\\'); if (c){ c++; i = 0; while('\0' != (str[i++] = *c++)); } } |
|
リスト11 GenerateEnhancedMesh 呼び出し |
【ステップ3−11 処理5・デバイス依存デバイスオブジェクト消去】 | 解説 |
HRESULT CMeshTest::InvalidateDeviceObjects() { if (m_pMeshEnhanced != NULL){ m_pMeshEnhanced->Release(); m_pMeshEnhanced = NULL; } if (m_ppTextures != NULL){ for( UINT i = 0; i < m_dwNumMaterials; i++ ){ if (m_ppTextures[i] != NULL){ m_ppTextures[i]->Release(); m_ppTextures[i] = NULL; } } } return S_OK; } |
ここでは読み込んだテクスチャーおよび
デバイス上の頂点データの
後始末をしている。
ポインターが NULL なら始末はしない。 後始末をすると、ポインターをNULL に している。 これにより、このルーチンは連続して 呼び出されても同じポインタを開放する事は無く 安全である。 |
リスト12 |
【ステップ3−12 処理6・デバイス依存メモリオブジェクト消去】 | 解説 |
HRESULT CMeshTest::DeleteDeviceObjects() { if (m_ppTextures != NULL){ delete [] m_ppTextures; m_ppTextures = NULL; } if (m_pMeshSysMem != NULL){ m_pMeshSysMem->Release(); m_pMeshSysMem = NULL; } if (m_pbufMaterials != NULL){ m_pbufMaterials->Release(); m_pbufMaterials = NULL; } m_dwNumMaterials = 0L; return S_OK; } |
ここでは、InitDeviceObject の処理で
準備したものをすべて処分している。 ここでは読み込んだメッシュの 後始末をしている。 ポインターが NULL なら始末はしない。 後始末をすると、ポインターをNULL に している。 これにより、このルーチンは連続して 呼び出されても安全である。 |
リスト13 |
【ステップ3−13 処理7・フレーム毎処理】 | 解説 |
void CMeshTest::Update(float timeElapsed) { D3DXMATRIX matTrans, matRotateY; m_fAngleY += timeElapsed * (D3DX_PI / 240.0f); if (m_fAngleY > D3DX_PI*2.0f) m_fAngleY -= D3DX_PI*2.0f; D3DXMatrixRotationY(&matRotateY,m_fAngleY); D3DXMatrixTranslation(&matTrans,0.0f,0.0f,1.0f); m_matWorld = matRotateY * matTrans; } |
引数:
timeElapsed: 前にこの処理が呼ばれてからの経過時間 処理内容: Y軸に沿ってキャラクタを回転させた後、 Z軸の正方向に 1.0 だけ移動させる、 というマトリックス m_matWorldを作成。 この m_matWorld は描画時に頂点データを 世界座標に移動させるのに使用される。 |
リスト14 |
【ステップ3−14 処理8・レンダリング処理】 | 解説 |
void CMeshTest::Render(LPDIRECT3DDEVICE8 lpd3ddev) { lpd3ddev->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); lpd3ddev->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); lpd3ddev->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); lpd3ddev->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2); lpd3ddev->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); lpd3ddev->SetRenderState(D3DRS_ALPHABLENDENABLE,FALSE); lpd3ddev->SetRenderState(D3DRS_LIGHTING, TRUE ); lpd3ddev->SetTransform( D3DTS_WORLD, &m_matWorld ); if (m_bUseHWNPatches){ float fNumSegs; fNumSegs = (float)m_dwNumSegs; lpd3ddev->SetRenderState(D3DRS_PATCHSEGMENTS, *((DWORD*)&fNumSegs)); } if (m_pMeshEnhanced){ for( UINT i = 0; i < m_dwNumMaterials; i++ ){ lpd3ddev->SetMaterial( &m_pMaterials[i].MatD3D ); lpd3ddev->SetTexture( 0, m_ppTextures[i] ); m_pMeshEnhanced->DrawSubset( i ); } } if (m_bUseHWNPatches){ lpd3ddev->SetRenderState(D3DRS_PATCHSEGMENTS, 0); } } |
引数に、Direct3Dデバイスへのポインタをとり、
与えられたデバイスに対して、 メッシュをレンダリングする。 メッシュの頂点データは物体座標系で示されており。 このデータについては、書き換えをしていない。 代わりにUpdate() 関数内で作成した m_matWorld (フレーム毎に計算される。) を使用して頂点座標を世界座標系へ変換してから 描画する事で、アニメーションを実現している。 といっても、具体的には、 SetTransform(D3DTS_WORLD,&m_matWorld); として変換行列を登録すれば良いだけ。 カメラの位置は、ここでは設定していない。 GameMain.cpp の中で設定されたものを そのまま使用している。 |
リスト15 |
【ステップ3−15 スタティック関数の宣言】 | 解説 |
|
今回、スタティック関数を2つ作成したので、
(GetAppPathName / GetFileName) MeshTest.cpp 冒頭部(リスト16)の直後に、 リスト17のように関数の宣言を書いておこう。 |
リスト16 |
![]() |
リスト17 |
【ステップ3−16 メッシュデータの準備】
最後にメッシュのデータを用意しよう。
幸いDirectX SDK には多くのサンプルが付いているので、とりあえずこれを活用しよう。
DirectX SDK のインストールされたフォルダの下の、
samples\Multimedia\media\の下にある、
tiger.x および tiger.bmp を今回使用する。
図6の要領でコピーされたい。
![]() |
![]() |