類CDXUTMeshFile位于DXUTMesh.h和DXUTMesh.cpp中,繼承自類CDXUTMeshFrame,其實類CDXUTMeshFrame本身只完成一些基礎操作,不是最終使用的一個類,CDXUTMeshFile在CDXUTMeshFrame的基礎上將各種操作進一步封裝。
在.x網格模型中使用框架的主要目的是實現模型自身包含的動畫,而CDXUTMeshFile和CDXUTMeshFrame雖然考慮了網格模型的層次框架,可是并沒有實現對網格模型動畫的播放,所以通常不直接使用這兩個類,因為對于不包含動畫信息的靜態網格模型CDXUTMesh類就已經足夠了。當然也完全可以像使用CDXUTMesh類一樣使用CDXUTMeshFile類來操作不包含動畫信息的網格模型。
來看看CDXUTMeshFile的定義:
//-----------------------------------------------------------------------------
// Name: class CDXUTMeshFile
// Desc: Class for loading and rendering file-based meshes
//-----------------------------------------------------------------------------
class CDXUTMeshFile : public CDXUTMeshFrame
{
HRESULT LoadMesh( LPDIRECT3DDEVICE9 pd3dDevice, LPD3DXFILEDATA pFileData, CDXUTMeshFrame* pParentFrame );
HRESULT LoadFrame( LPDIRECT3DDEVICE9 pd3dDevice, LPD3DXFILEDATA pFileData, CDXUTMeshFrame* pParentFrame );
public:
HRESULT Create( LPDIRECT3DDEVICE9 pd3dDevice, LPCWSTR strFilename );
HRESULT CreateFromResource( LPDIRECT3DDEVICE9 pd3dDevice, LPCWSTR strResource, LPCWSTR strType );
// For pure devices, specify the world transform.
// If the world transform is not specified on pure devices, this function will fail.
HRESULT Render( LPDIRECT3DDEVICE9 pd3dDevice, D3DXMATRIX* pmatWorldMatrix = NULL );
CDXUTMeshFile() : CDXUTMeshFrame( L"CDXUTMeshFile_Root" ) {}
};
LoadMesh()負責從ID3DXFileData中加載一個網格,該函數是內部調用的,其實質是調用CDXUTMesh::Create()函數來加載。
HRESULT CDXUTMeshFile::LoadMesh(LPDIRECT3DDEVICE9 pd3dDevice, LPD3DXFILEDATA pFileData, CDXUTMeshFrame* pParentFrame)
{
// Currently only allowing one mesh per frame
if( pParentFrame->m_pMesh )
return E_FAIL;
// Get the mesh name
CHAR strAnsiName[512] = {0};
WCHAR strName[512];
SIZE_T dwNameLength = 512;
HRESULT hr;
if( FAILED( hr = pFileData->GetName(strAnsiName, &dwNameLength) ) )
return hr;
MultiByteToWideChar(CP_ACP, 0, strAnsiName, -1, strName, 512);
strName[511] = 0;
// Create the mesh
pParentFrame->m_pMesh = new CDXUTMesh(strName);
if( pParentFrame->m_pMesh == NULL )
return E_OUTOFMEMORY;
pParentFrame->m_pMesh->Create( pd3dDevice, pFileData );
return S_OK;
}
LoadFrame()用于從ID3DXFileData中加載一個框架:
HRESULT CDXUTMeshFile::LoadFrame(LPDIRECT3DDEVICE9 pd3dDevice, LPD3DXFILEDATA pFileData, CDXUTMeshFrame* pParentFrame)
{
LPD3DXFILEDATA pChildData = NULL;
GUID Guid;
SIZE_T cbSize;
CDXUTMeshFrame* pCurrentFrame;
HRESULT hr;
// Get the type of the object
if( FAILED( hr = pFileData->GetType( &Guid ) ) )
return hr;
if(Guid == TID_D3DRMMesh)
{
hr = LoadMesh(pd3dDevice, pFileData, pParentFrame);
if( FAILED(hr) )
return hr;
}
if(Guid == TID_D3DRMFrameTransformMatrix)
{
D3DXMATRIX* pmatMatrix;
hr = pFileData->Lock(&cbSize, (LPCVOID*) &pmatMatrix);
if( FAILED(hr) )
return hr;
// Update the parent's matrix with the new one
pParentFrame->SetMatrix(pmatMatrix);
}
if(Guid == TID_D3DRMFrame)
{
// Get the frame name
CHAR strAnsiName[512] = "";
WCHAR strName[512];
SIZE_T dwNameLength = 512;
SIZE_T cChildren;
if( FAILED( hr = pFileData->GetName(strAnsiName, &dwNameLength) ) )
return hr;
MultiByteToWideChar(CP_ACP, 0, strAnsiName, -1, strName, 512);
strName[511] = 0;
// Create the frame
pCurrentFrame = new CDXUTMeshFrame(strName);
if(pCurrentFrame == NULL)
return E_OUTOFMEMORY;
pCurrentFrame->m_pNext = pParentFrame->m_pChild;
pParentFrame->m_pChild = pCurrentFrame;
// Enumerate child objects
pFileData->GetChildren(&cChildren);
for (UINT iChild = 0; iChild < cChildren; iChild++)
{
// Query the child for its FileData
hr = pFileData->GetChild(iChild, &pChildData);
if( SUCCEEDED(hr) )
{
hr = LoadFrame(pd3dDevice, pChildData, pCurrentFrame);
SAFE_RELEASE( pChildData );
}
if( FAILED(hr) )
return hr;
}
}
return S_OK;
}
首先,該函數調用GetType()獲取對象的GUID:
// Get the type of the object
if( FAILED( hr = pFileData->GetType( &Guid ) ) )
return hr;
接下來根據GUID分別進行相應的操作,若是網格則調用LoadMesh()加載網格,若是框架變換矩陣則設置變換矩陣,若是框架,相應的操作有些復雜,首先獲取框架的名稱并將其轉化為widechar類型:
if( FAILED( hr = pFileData->GetName(strAnsiName, &dwNameLength) ) )
return hr;
MultiByteToWideChar(CP_ACP, 0, strAnsiName, -1, strName, 512);
strName[511] = 0;
接著新建一個CDXUTMeshFrame對象并將其添加進鏈表:
// Create the frame
pCurrentFrame = new CDXUTMeshFrame(strName);
if(pCurrentFrame == NULL)
return E_OUTOFMEMORY;
pCurrentFrame->m_pNext = pParentFrame->m_pChild;
pParentFrame->m_pChild = pCurrentFrame;
最后遍歷子框架并遞歸調用LoadFrame()加載框架:
// Enumerate child objects
pFileData->GetChildren(&cChildren);
for (UINT iChild = 0; iChild < cChildren; iChild++)
{
// Query the child for its FileData
hr = pFileData->GetChild(iChild, &pChildData);
if( SUCCEEDED(hr) )
{
hr = LoadFrame(pd3dDevice, pChildData, pCurrentFrame);
SAFE_RELEASE( pChildData );
}
if( FAILED(hr) )
return hr;
}
第一個Create()函數負責從模型文件加載網格模型:
HRESULT CDXUTMeshFile::Create(LPDIRECT3DDEVICE9 pd3dDevice, LPCWSTR strFilename)
{
LPD3DXFILE pDXFile = NULL;
LPD3DXFILEENUMOBJECT pEnumObj = NULL;
LPD3DXFILEDATA pFileData = NULL;
HRESULT hr;
SIZE_T cChildren;
// Create a x file object
if( FAILED( hr = D3DXFileCreate(&pDXFile) ) )
return E_FAIL;
// Register templates for d3drm and patch extensions.
if( FAILED( hr = pDXFile->RegisterTemplates((void*) D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES) ) )
{
SAFE_RELEASE( pDXFile );
return E_FAIL;
}
// Find the path to the file, and convert it to ANSI (for the D3DXOF API)
WCHAR strPath[MAX_PATH];
CHAR strPathANSI[MAX_PATH];
DXUTFindDXSDKMediaFileCch(strPath, sizeof(strPath) / sizeof(WCHAR), strFilename);
WideCharToMultiByte(CP_ACP, 0, strPath, -1, strPathANSI, MAX_PATH, NULL, NULL);
strPathANSI[MAX_PATH - 1] = 0;
// Create enum object
hr = pDXFile->CreateEnumObject((void*) strPathANSI, D3DXF_FILELOAD_FROMFILE, &pEnumObj);
if( FAILED(hr) )
{
SAFE_RELEASE( pDXFile );
return hr;
}
// Enumerate top level objects (which are always frames)
pEnumObj->GetChildren(&cChildren);
for (UINT iChild = 0; iChild < cChildren; iChild++)
{
hr = pEnumObj->GetChild(iChild, &pFileData);
if (FAILED(hr))
return hr;
hr = LoadFrame(pd3dDevice, pFileData, this);
SAFE_RELEASE(pFileData);
if( FAILED(hr) )
{
SAFE_RELEASE(pEnumObj);
SAFE_RELEASE(pDXFile);
return E_FAIL;
}
}
SAFE_RELEASE(pFileData);
SAFE_RELEASE(pEnumObj);
SAFE_RELEASE(pDXFile);
return S_OK;
}
首先函數創建了一個ID3DXFileData對象并注冊了XFILE標準模板:
// Create a x file object
if( FAILED( hr = D3DXFileCreate(&pDXFile) ) )
return E_FAIL;
// Register templates for d3drm and patch extensions.
if( FAILED( hr = pDXFile->RegisterTemplates((void*) D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES) ) )
{
SAFE_RELEASE( pDXFile );
return E_FAIL;
}
接下來查找網格模型文件將路徑存儲在strPath中,并將該路徑轉化為ANSI類型存儲在strPathANSI中:
// Find the path to the file, and convert it to ANSI (for the D3DXOF API)
WCHAR strPath[MAX_PATH];
CHAR strPathANSI[MAX_PATH];
DXUTFindDXSDKMediaFileCch(strPath, sizeof(strPath) / sizeof(WCHAR), strFilename);
WideCharToMultiByte(CP_ACP, 0, strPath, -1, strPathANSI, MAX_PATH, NULL, NULL);
strPathANSI[MAX_PATH - 1] = 0;
再接下來創建一個ID3DXFileEnumObject枚舉對象負責從.x文件中讀取數據:
// Create enum object
hr = pDXFile->CreateEnumObject((void*) strPathANSI, D3DXF_FILELOAD_FROMFILE, &pEnumObj);
if( FAILED(hr) )
{
SAFE_RELEASE( pDXFile );
return hr;
}
函數CreateEnumObject()聲明如下:
Creates an enumerator object that will read a .x file.
HRESULT CreateEnumObject(
LPCVOID pvSource,
D3DXF_FILELOADOPTIONS loadflags,
ID3DXFileEnumObject ** ppEnumObj
);
Parameters
- pvSource
- [out] The data source. Either:
- A file name
- A D3DXF_FILELOADMEMORY structure
- A D3DXF_FILELOADRESOURCE structure
Depending on the value of loadflags. - loadflags
- [in] Value that specifies the source of the data.
This value can be one of the D3DXF_FILELOADOPTIONS flags.
- ppEnumObj
- [out] Address of a pointer to an
ID3DXFileEnumObject interface, representing the created enumerator object.
Return Values
If the method succeeds, the return value is S_OK. If
the method fails, the return value can be one of the following:
D3DXFERR_BADVALUE, D3DXFERR_PARSEERROR.
Remarks
After using this method, use one of the
ID3DXFileEnumObject methods to retrieve a data object.
再接下來通過ID3DXFileEnumObjec枚舉對象讀取數據并調用LoadFrame()加載框架:
// Enumerate top level objects (which are always frames)
pEnumObj->GetChildren(&cChildren);
for (UINT iChild = 0; iChild < cChildren; iChild++)
{
hr = pEnumObj->GetChild(iChild, &pFileData);
if (FAILED(hr))
return hr;
hr = LoadFrame(pd3dDevice, pFileData, this);
SAFE_RELEASE(pFileData);
if( FAILED(hr) )
{
SAFE_RELEASE(pEnumObj);
SAFE_RELEASE(pDXFile);
return E_FAIL;
}
}
最后釋放不再使用的COM對象:
SAFE_RELEASE(pFileData);
SAFE_RELEASE(pEnumObj);
SAFE_RELEASE(pDXFile);
第二個Create()函數與第一個非常類似,只是該函數負責從資源加載網格模型:
HRESULT CDXUTMeshFile::CreateFromResource(LPDIRECT3DDEVICE9 pd3dDevice, LPCWSTR strResource, LPCWSTR strType)
{
LPD3DXFILE pDXFile = NULL;
LPD3DXFILEENUMOBJECT pEnumObj = NULL;
LPD3DXFILEDATA pFileData = NULL;
HRESULT hr;
SIZE_T cChildren;
// Create a x file object
if( FAILED( hr = D3DXFileCreate(&pDXFile) ) )
return E_FAIL;
// Register templates for d3drm and patch extensions.
if( FAILED( hr = pDXFile->RegisterTemplates((void*) D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES) ) )
{
SAFE_RELEASE( pDXFile );
return E_FAIL;
}
CHAR strTypeAnsi[MAX_PATH];
CHAR strResourceAnsi[MAX_PATH];
WideCharToMultiByte(CP_ACP, 0, strType, -1, strTypeAnsi, MAX_PATH, NULL, NULL);
strTypeAnsi[MAX_PATH - 1] = 0;
WideCharToMultiByte(CP_ACP, 0, strResource, -1, strResourceAnsi, MAX_PATH, NULL, NULL);
strResourceAnsi[MAX_PATH - 1] = 0;
D3DXF_FILELOADRESOURCE dxlr;
dxlr.hModule = NULL;
dxlr.lpName = strResourceAnsi;
dxlr.lpType = strTypeAnsi;
// Create enum object
hr = pDXFile->CreateEnumObject((void*) &dxlr, D3DXF_FILELOAD_FROMRESOURCE, &pEnumObj);
if( FAILED(hr) )
{
SAFE_RELEASE( pDXFile );
return hr;
}
// Enumerate top level objects (which are always frames)
pEnumObj->GetChildren(&cChildren);
for (UINT iChild = 0; iChild < cChildren; iChild++)
{
hr = pEnumObj->GetChild(iChild, &pFileData);
if (FAILED(hr))
return hr;
hr = LoadFrame(pd3dDevice, pFileData, this);
SAFE_RELEASE( pFileData );
if( FAILED(hr) )
{
SAFE_RELEASE(pEnumObj);
SAFE_RELEASE(pDXFile);
return E_FAIL;
}
}
SAFE_RELEASE(pFileData);
SAFE_RELEASE(pEnumObj);
SAFE_RELEASE(pDXFile);
return S_OK;
}
唯一需要解釋的代碼是:
D3DXF_FILELOADRESOURCE dxlr;
dxlr.hModule = NULL;
dxlr.lpName = strResourceAnsi;
dxlr.lpType = strTypeAnsi;
// Create enum object
hr = pDXFile->CreateEnumObject((void*) &dxlr, D3DXF_FILELOAD_FROMRESOURCE, &pEnumObj);
該代碼片段負責從指定的資源中加載網格模型,D3DXF_FILELOADRESOURCE的聲明如下:
Identifies resource data.
typedef struct D3DXF_FILELOADRESOURCE {
HMODULE hModule;
LPCSTR lpName;
LPCSTR lpType;
} D3DXF_FILELOADRESOURCE, *LPD3DXF_FILELOADRESOURCE;
Members
- hModule
- Handle of the module containing the resource to be
loaded. If this member is NULL, the resource must be attached to the
executable file that will use it.
- lpName
- Pointer to a string specifying the name of the
resource to be loaded. For example, if the resource is a mesh, this member
should specify the name of the mesh file.
- lpType
- Pointer to a string specifying the user-defined
type identifying the resource.
Remarks
This structure identifies a resource to be loaded when
an application uses the ID3DXFile::CreateEnumObject method and specifies the
D3DXF_FILELOAD_FROMRESOURCE flag.
Render()函數負責網格模型的渲染,它允許用戶在調用時設置一個世界坐標矩陣:
HRESULT CDXUTMeshFile::Render(LPDIRECT3DDEVICE9 pd3dDevice, D3DXMATRIX* pmatWorldMatrix)
{
// For pure devices, specify the world transform.
// If the world transform is not specified on pure devices, this function will fail.
// Set up the world transformation
D3DXMATRIX matSavedWorld, matWorld;
if (NULL == pmatWorldMatrix)
pd3dDevice->GetTransform(D3DTS_WORLD, &matSavedWorld);
else
matSavedWorld = *pmatWorldMatrix;
D3DXMatrixMultiply(&matWorld, &matSavedWorld, &m_mat);
pd3dDevice->SetTransform(D3DTS_WORLD, &matWorld);
// Render opaque subsets in the meshes
if(m_pChild)
m_pChild->Render(pd3dDevice, TRUE, FALSE, &matWorld);
// Enable alpha blending
pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
// Render alpha subsets in the meshes
if(m_pChild)
m_pChild->Render(pd3dDevice, FALSE, TRUE, &matWorld);
// Restore state
pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
pd3dDevice->SetTransform(D3DTS_WORLD, &matSavedWorld);
return S_OK;
}
因為CDXUTMeshFile繼承自CDXUTMeshFrame,并且該類的默認構造函數定義如下:
CDXUTMeshFile() : CDXUTMeshFrame( L"CDXUTMeshFile_Root" ) {}
也就是說在創建該類對象時默認創建了一個CDXUTMeshFrame對象,這也意味著在加載網格模型時所有后續的CDXUTMeshFrame對象都是該對象的子對象,所以下面的代碼是合法的:
// Render opaque subsets in the meshes
if(m_pChild)
m_pChild->Render(pd3dDevice, TRUE, FALSE, &matWorld);
// Enable alpha blending
pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
// Render alpha subsets in the meshes
if(m_pChild)
m_pChild->Render(pd3dDevice, FALSE, TRUE, &matWorld);
// Restore state
pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
pd3dDevice->SetTransform(D3DTS_WORLD, &matSavedWorld);
先渲染所有不透明的子框架對象,再渲染所有半透明的子框架對象,最后恢復到先前的渲染狀態。