类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);
先渲染所有不透明的子框架对象,再渲染所有半透明的子框架对象,最后恢复到先前的渲染状态。