ゲームプログラミング技術集 
ひっと

Xファイルの読み込みと表示

簡単にできますよ。Xファイルを読み込んで表示します。

プログラムとソースコード

Xファイルを読み込んで表示するプログラムです。


ダウンロード

プログラミング環境: [ VC++2008 ExpressEdition ] + [ DirectX SDK ]
モデリングツール:Blender

Xファイルの読み込み

D3DXLoadMeshFromX

Xファイルの読み込みにはD3DXLoadMeshFromXを使います。

メッシュと、属性グループのテーブルを取得できます。

属性グループというのは、メッシュの中で同じマテリアルやテクスチャを持つ頂点のグループのことです。
頂点のマテリアルやテクスチャが違うと、その分属性グループがたくさん作られます。

マテリアルとテクスチャの準備

属性グループには頂点の色、テクスチャが D3DXLoadMeshFromXでマテリアルが D3DXMATERIAL メッシュは、ポリゴンの色とか、テクスチャで分類される「マテリアル」で管理しています。

テクスチャの読み込み

テクスチャにはファイル名が入っているだけです。
テクスチャはファイル名を元に自分で読み込みます

Xファイルに法線がないときがあります

Xファイルによっては、法線が書き込まれていないものもあります。
頂点フォーマットをチェックしてD3DFVF_NORMALがない場合は、D3DXComputeNormalsでメッシュの法線を計算します。

Xファイルの読み込み ソースコード



//Xファイル読み込み
bool XFileObject::LoadXFile( LPCWSTR file )
{
	//Xファイルのディレクトリを取得(テクスチャのロードに使う)
	wchar_t dir[_MAX_DIR];
	_wsplitpath_s(file, NULL,0,dir,_MAX_DIR,NULL,0,NULL,0);

	//Xファイルロード
	LPD3DXBUFFER pD3DBufMtr;
	if ( D3D_OK != D3DXLoadMeshFromX(file, D3DXMESH_SYSTEMMEM, GetDevice(), NULL, &pD3DBufMtr, NULL, &m_dwNumMaterials, &m_pMesh ) ) {
		return false;
	}
	
	//Xファイルに法線がない場合は、法線を書き込む
	if ( !(m_pMesh->GetFVF() & D3DFVF_NORMAL) ) {
		
		ID3DXMesh* pTempMesh = NULL;
		
		m_pMesh->CloneMeshFVF(m_pMesh->GetOptions(),
				m_pMesh->GetFVF()|D3DFVF_NORMAL, GetDevice(), &pTempMesh );
	
		D3DXComputeNormals(pTempMesh, NULL);
		m_pMesh->Release();
		m_pMesh = pTempMesh;
	}
	
	//マテリアル、テクスチャの準備
	m_pMatrs = (D3DMATERIAL9*)malloc(sizeof(D3DMATERIAL9) * m_dwNumMaterials);
	m_ppTextures = (LPDIRECT3DTEXTURE9*)malloc(sizeof(LPDIRECT3DTEXTURE9) * m_dwNumMaterials);

	D3DXMATERIAL* d3dxmatrs = (D3DXMATERIAL*)pD3DBufMtr->GetBufferPointer();

	for (int i = 0; i < (int)m_dwNumMaterials; i++) {
		
		//マテリアル複写
		m_pMatrs[i] = d3dxmatrs[i].MatD3D;
		m_pMatrs[i].Ambient = m_pMatrs[i].Diffuse;

		//テクスチャをロード
		m_ppTextures[i] = NULL;
		if ( d3dxmatrs[i].pTextureFilename != NULL ) {

			//テクスチャ名をwcharに変換
			wchar_t filename[1024];
			size_t num;
			ZeroMemory( filename, sizeof(filename) );
			mbstowcs_s( &num, filename, 1024, d3dxmatrs[i].pTextureFilename, _TRUNCATE );

			//テクスチャファイルパスを作成する
			wchar_t texturefile[1024];
			ZeroMemory( texturefile, sizeof(texturefile) );
			swprintf( texturefile, 1024, TEXT("%s%s"), dir, filename );

			if ( S_OK != D3DXCreateTextureFromFile( GetDevice(), texturefile, &m_ppTextures[i] ) ) {
				m_ppTextures[i] = NULL;
			}
		}
  }

  pD3DBufMtr->Release();

  return true;

}

Xファイルの表示

DrawSubsetで描画する

読み込んだメッシュはDrawSubsetで描画します。

DrawSubsetは属性グループごとに描画します。
なので属性グループの数だけDrawSubsetを呼びます。

Xファイルの表示 ソースコード


    //画面クリア
    GetDevice()->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, g_backColor, 1.0f, 0 );

    //デバイスに描画開始を知らせる
    HRESULT hr;
    hr = g_pD3DDevice->BeginScene();

    //頂点シェーダをセット(使わない場合はNULL)
    GetDevice()->SetVertexShader( NULL );

    //頂点フォーマットをセット
    GetDevice()->SetFVF( m_pMesh->GetFVF() );

    //属性グループの数だけDrawSubsetを呼ぶ
    for ( unsigned int u = 0; u < m_dwNumMaterials; u++ ) {
        GetDevice()->SetMaterial( &m_pMatrs[u] );
        GetDevice()->SetTexture( 0, m_ppTextures[u] );
        m_pMesh->DrawSubset( u );
    }

    //デバイスに描画終了を知らせる
    g_pD3DDevice->EndScene();

    //画面をバックバッファと入れ替える。
    g_pD3DDevice->Present( NULL, NULL, NULL, NULL );

属性グループをうまく使うと
部分的に非表示にしたり
テクスチャでキャラを描き分けることもできます。