類CDXUTMesh主要用于從一個指定的網格模型中加載數據、渲染模型以及銷毀網格模型,它將整個網格模型作為一個整體進行操作,沒有考慮網格模型內部的框架層次,對于不包含動畫信息的網格模型,使用該類是一個比較好的選擇。
這個類的定義和實現分別位于DXUTMesh.h和DXUTMesh.cpp中,其定義如下:
//-----------------------------------------------------------------------------
// Name: class CDXUTMesh
// Desc: Class for loading and rendering file-based meshes
//-----------------------------------------------------------------------------
class CDXUTMesh
{
public:
WCHAR m_strName[512];
LPD3DXMESH m_pMesh; // Managed mesh
// Cache of data in m_pMesh for easy access
IDirect3DVertexBuffer9* m_pVB;
IDirect3DIndexBuffer9* m_pIB;
IDirect3DVertexDeclaration9* m_pDecl;
DWORD m_dwNumVertices;
DWORD m_dwNumFaces;
DWORD m_dwBytesPerVertex;
DWORD m_dwNumMaterials; // Materials for the mesh
D3DMATERIAL9* m_pMaterials;
CHAR (*m_strMaterials)[MAX_PATH];
IDirect3DBaseTexture9** m_pTextures;
bool m_bUseMaterials;
public:
// Rendering
HRESULT Render( LPDIRECT3DDEVICE9 pd3dDevice,
bool bDrawOpaqueSubsets = true,
bool bDrawAlphaSubsets = true );
HRESULT Render( ID3DXEffect *pEffect,
D3DXHANDLE hTexture = NULL,
D3DXHANDLE hDiffuse = NULL,
D3DXHANDLE hAmbient = NULL,
D3DXHANDLE hSpecular = NULL,
D3DXHANDLE hEmissive = NULL,
D3DXHANDLE hPower = NULL,
bool bDrawOpaqueSubsets = true,
bool bDrawAlphaSubsets = true );
// Mesh access
LPD3DXMESH GetMesh() { return m_pMesh; }
// Rendering options
void UseMeshMaterials( bool bFlag ) { m_bUseMaterials = bFlag; }
HRESULT SetFVF( LPDIRECT3DDEVICE9 pd3dDevice, DWORD dwFVF );
HRESULT SetVertexDecl( LPDIRECT3DDEVICE9 pd3dDevice, const D3DVERTEXELEMENT9 *pDecl,
bool bAutoComputeNormals = true, bool bAutoComputeTangents = true,
bool bSplitVertexForOptimalTangents = false );
// Initializing
HRESULT RestoreDeviceObjects( LPDIRECT3DDEVICE9 pd3dDevice );
HRESULT InvalidateDeviceObjects();
// Creation/destruction
HRESULT Create( LPDIRECT3DDEVICE9 pd3dDevice, LPCWSTR strFilename );
HRESULT Create( LPDIRECT3DDEVICE9 pd3dDevice, LPD3DXFILEDATA pFileData );
HRESULT Create(LPDIRECT3DDEVICE9 pd3dDevice, ID3DXMesh* pInMesh, D3DXMATERIAL* pd3dxMaterials, DWORD dwMaterials);
HRESULT CreateMaterials(LPCWSTR strPath, IDirect3DDevice9 *pd3dDevice,
D3DXMATERIAL* d3dxMtrls, DWORD dwNumMaterials);
HRESULT Destroy();
CDXUTMesh( LPCWSTR strName = L"CDXUTMeshFile_Mesh" );
virtual ~CDXUTMesh();
};
該類中包含的成員函數按其作用可分為6類。
第一類是構造和析構函數,函數CDXUTMesh()和~CDXUTMesh()分別是該類的構造函數和析構函數,其作用分別是進行一些初始化工作以及在類CDXUTMesh的對象被銷毀時完成最后的銷毀工作。
CDXUTMesh::CDXUTMesh( LPCWSTR strName )
{
StringCchCopy( m_strName, 512, strName );
m_pMesh = NULL;
m_pMaterials = NULL;
m_pTextures = NULL;
m_bUseMaterials = TRUE;
m_pVB = NULL;
m_pIB = NULL;
m_pDecl = NULL;
m_strMaterials = NULL;
m_dwNumMaterials = 0;
m_dwNumVertices = 0;
m_dwNumFaces = 0;
m_dwBytesPerVertex = 0;
}
CDXUTMesh::~CDXUTMesh()
{
Destroy();
}
第二類是獲取網格函數,它僅包含一個函數GetMesh(),實現也非常簡單,即返回類CDXUTMesh的成員變量m_pMesh。
LPD3DXMESH GetMesh() { return m_pMesh; }
第三類是設備恢復和丟失時所采取的操作函數,這里所包含的兩個成員函數RestoreDeviceObjects()和InvalidateDeviceObjects()分別是在設備恢復和丟失時調用,用于恢復和釋放相應的資源。
HRESULT CDXUTMesh::RestoreDeviceObjects( LPDIRECT3DDEVICE9 pd3dDevice )
{
return S_OK;
}
HRESULT CDXUTMesh::InvalidateDeviceObjects()
{
SAFE_RELEASE( m_pIB );
SAFE_RELEASE( m_pVB );
SAFE_RELEASE( m_pDecl );
return S_OK;
}
第四類是創建和銷毀函數,這里首先重載了3個創建網格模型函數Create(),它們依次用于從指定的.x文件創建網格模型,從接口ID3DXFileData創建網格模型,從輸入的網格模型中創建新的網格模型。函數CreateMaterials()用于創建網格模型中所需的材質和紋理。函數Destroy()用來在程序退出時銷毀指定的資源。
來看第一個Create()函數的實現:
HRESULT CDXUTMesh::Create( LPDIRECT3DDEVICE9 pd3dDevice, LPCWSTR strFilename )
{
WCHAR strPath[MAX_PATH];
LPD3DXBUFFER pAdjacencyBuffer = NULL;
LPD3DXBUFFER pMtrlBuffer = NULL;
HRESULT hr;
// Cleanup previous mesh if any
Destroy();
// Find the path for the file, and convert it to ANSI (for the D3DX API)
DXUTFindDXSDKMediaFileCch( strPath, sizeof(strPath) / sizeof(WCHAR), strFilename );
// Load the mesh
if(FAILED(hr = D3DXLoadMeshFromXW(strPath, D3DXMESH_MANAGED, pd3dDevice, &pAdjacencyBuffer, &pMtrlBuffer, NULL,
&m_dwNumMaterials, &m_pMesh)))
{
return hr;
}
// Optimize the mesh for performance
if( FAILED( hr = m_pMesh->OptimizeInplace(
D3DXMESHOPT_COMPACT | D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_VERTEXCACHE,
(DWORD*) pAdjacencyBuffer->GetBufferPointer(), NULL, NULL, NULL)))
{
SAFE_RELEASE( pAdjacencyBuffer );
SAFE_RELEASE( pMtrlBuffer );
return hr;
}
// Set strPath to the path of the mesh file
WCHAR* pLastBSlash = wcsrchr( strPath, L'\\' );
if( pLastBSlash )
*(pLastBSlash + 1) = L'\0';
else
*strPath = L'\0';
D3DXMATERIAL* d3dxMtrls = (D3DXMATERIAL*) pMtrlBuffer->GetBufferPointer();
hr = CreateMaterials( strPath, pd3dDevice, d3dxMtrls, m_dwNumMaterials );
SAFE_RELEASE( pAdjacencyBuffer );
SAFE_RELEASE( pMtrlBuffer );
// Extract data from m_pMesh for easy access
D3DVERTEXELEMENT9 decl[MAX_FVF_DECL_SIZE];
m_dwNumVertices = m_pMesh->GetNumVertices();
m_dwNumFaces = m_pMesh->GetNumFaces();
m_dwBytesPerVertex = m_pMesh->GetNumBytesPerVertex();
m_pMesh->GetIndexBuffer( &m_pIB );
m_pMesh->GetVertexBuffer( &m_pVB );
m_pMesh->GetDeclaration( decl );
pd3dDevice->CreateVertexDeclaration( decl, &m_pDecl );
return hr;
}
函數首先銷毀舊的資源,并調用DXUTFindDXSDKMediaFileCch()通過文件名查找文件所在的路徑,接著調用D3DXLoadMeshFromXW()從文件中加載網格模型。
DXUTFindDXSDKMediaFileCch()的實現分析請參閱DXUT源碼分析
---- 媒體文件查找函數。
WCHAR strPath[MAX_PATH];
LPD3DXBUFFER pAdjacencyBuffer = NULL;
LPD3DXBUFFER pMtrlBuffer = NULL;
HRESULT hr;
// Cleanup previous mesh if any
Destroy();
// Find the path for the file, and convert it to ANSI (for the D3DX API)
DXUTFindDXSDKMediaFileCch( strPath, sizeof(strPath) / sizeof(WCHAR), strFilename );
// Load the mesh
if(FAILED(hr = D3DXLoadMeshFromXW(strPath, D3DXMESH_MANAGED, pd3dDevice, &pAdjacencyBuffer, &pMtrlBuffer, NULL,
&m_dwNumMaterials, &m_pMesh)))
{
return hr;
}
接著調用OptimizeInplace()對網格模型進行優化,該函數調用時第一個參數的含義如下:
D3DXMESHOPT_COMPACT — 從mesh中移除沒有用的頂點和索引項。
D3DXMESHOPT_ATTRSORT — 根據屬性給三角形排序并調整屬性表,這將使DrawSubset執行更有效。
D3DXMESHOPT_VERTEXCACHE — 增加頂點緩存的命中率。
// Optimize the mesh for performance
if( FAILED( hr = m_pMesh->OptimizeInplace(
D3DXMESHOPT_COMPACT | D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_VERTEXCACHE,
(DWORD*) pAdjacencyBuffer->GetBufferPointer(), NULL, NULL, NULL)))
{
SAFE_RELEASE( pAdjacencyBuffer );
SAFE_RELEASE( pMtrlBuffer );
return hr;
}
接下來,函數將模型文件所在的路徑存儲在strPath,如果沒有路徑,則strPath置為NULL。
// Set strPath to the path of the mesh file
WCHAR* pLastBSlash = wcsrchr( strPath, L'\\' );
if( pLastBSlash )
*(pLastBSlash + 1) = L'\0';
else
*strPath = L'\0';
接下來,函數調用CreateMaterials()創建存儲材質和紋理的內存,并釋放鄰接信息緩存和材質緩存。
D3DXMATERIAL* d3dxMtrls = (D3DXMATERIAL*) pMtrlBuffer->GetBufferPointer();
hr = CreateMaterials( strPath, pd3dDevice, d3dxMtrls, m_dwNumMaterials );
SAFE_RELEASE( pAdjacencyBuffer );
SAFE_RELEASE( pMtrlBuffer );
最后,函數獲取模型的頂點數,面數,每個頂點所占的字節大小,頂點索引緩存,頂點緩存,頂點聲明,以方便以后訪問。
// Extract data from m_pMesh for easy access
m_dwNumVertices = m_pMesh->GetNumVertices();
m_dwNumFaces = m_pMesh->GetNumFaces();
m_dwBytesPerVertex = m_pMesh->GetNumBytesPerVertex();
m_pMesh->GetIndexBuffer( &m_pIB );
m_pMesh->GetVertexBuffer( &m_pVB );
m_pMesh->GetDeclaration( decl );
pd3dDevice->CreateVertexDeclaration( decl, &m_pDecl );
return hr;