类CDXUTMeshFrame封装了CDXUTMesh,与CDXUTMesh不同的是,类CDXUTMeshFrame可以包含框架层次结构,适用于更复杂的网格模型,框架层次正是骨骼动画所必须的。
首先来看看它的定义:
//-----------------------------------------------------------------------------
// Name: class CDXUTMeshFrame
// Desc: Class for loading and rendering file-based meshes
//-----------------------------------------------------------------------------
class CDXUTMeshFrame
{
public:
WCHAR m_strName[512]; // 框架名称
D3DXMATRIX m_mat; // 框架变换矩阵(相对于网格模型的原点)
CDXUTMesh* m_pMesh; // 指向CDXUTMesh对象
CDXUTMeshFrame* m_pNext; // 指向下一个框架对象
CDXUTMeshFrame* m_pChild; // 指向子框架对象
public:
// Matrix access
void SetMatrix( D3DXMATRIX* pmat ) { m_mat = *pmat; }
D3DXMATRIX* GetMatrix() { return &m_mat; }
CDXUTMesh* FindMesh( LPCWSTR strMeshName );
CDXUTMeshFrame* FindFrame( LPCWSTR strFrameName );
bool EnumMeshes( bool (*EnumMeshCB)(CDXUTMesh*, void*), void* pContext );
HRESULT Destroy();
HRESULT RestoreDeviceObjects(LPDIRECT3DDEVICE9 pd3dDevice);
HRESULT InvalidateDeviceObjects();
HRESULT Render( LPDIRECT3DDEVICE9 pd3dDevice,
bool bDrawOpaqueSubsets = true,
bool bDrawAlphaSubsets = true,
D3DXMATRIX* pmatWorldMatrix = NULL);
CDXUTMeshFrame( LPCWSTR strName = L"CDXUTMeshFile_Frame" );
virtual ~CDXUTMeshFrame();
};
构造函数和析构函数只是负责初始化数据和释放分配的资源:
CDXUTMeshFrame::CDXUTMeshFrame( LPCWSTR strName )
{
StringCchCopy(m_strName, 512, strName);
D3DXMatrixIdentity(&m_mat);
m_pMesh = NULL;
m_pChild = NULL;
m_pNext = NULL;
}
CDXUTMeshFrame::~CDXUTMeshFrame()
{
SAFE_DELETE( m_pChild );
SAFE_DELETE( m_pNext );
}
FindMesh()和FindFrame()是两个递归查找函数,根据输入的网格名称和框架名称查找对应的网格和框架:
CDXUTMesh* CDXUTMeshFrame::FindMesh( LPCWSTR strMeshName )
{
CDXUTMesh* pMesh;
if( m_pMesh )
if( !lstrcmpi(m_pMesh->m_strName, strMeshName) )
return m_pMesh;
if( m_pChild )
if( NULL != ( pMesh = m_pChild->FindMesh(strMeshName) ) )
return pMesh;
if( m_pNext )
if( NULL != ( pMesh = m_pNext->FindMesh(strMeshName) ) )
return pMesh;
return NULL;
}
CDXUTMeshFrame* CDXUTMeshFrame::FindFrame( LPCWSTR strFrameName )
{
CDXUTMeshFrame* pFrame;
if( !lstrcmpi(m_strName, strFrameName) )
return this;
if( m_pChild )
if( NULL != ( pFrame = m_pChild->FindFrame(strFrameName) ) )
return pFrame;
if( m_pNext )
if( NULL != ( pFrame = m_pNext->FindFrame(strFrameName) ) )
return pFrame;
return NULL;
}
lstrcmpi()在比较两个字符串时忽略大小写:
The lstrcmpi function compares two character
strings. The comparison is not case sensitive.
To perform a comparison that is case sensitive, use
the lstrcmp function.
Syntax
int lstrcmpi(
LPCTSTR lpString1,
LPCTSTR lpString2
);
Parameters
- lpString1
- [in] Pointer to the first null-terminated
string to be compared.
- lpString2
- [in] Pointer to the second null-terminated
string to be compared.
Return Value
If the string pointed to by lpString1 is
less than the string pointed to by lpString2, the return value is
negative. If the string pointed to by lpString1 is greater than
the string pointed to by lpString2, the return value is positive.
If the strings are equal, the return value is zero.
Remarks
The lstrcmpi function compares two
strings by checking the first characters against each other, the second
characters against each other, and so on until it finds an inequality or
reaches the ends of the strings.
Note that the lpString1 and lpString2
parameters must be null terminated, otherwise the string comparison can
be incorrect.
The function returns the difference of the
values of the first unequal characters it encounters. For example,
lstrcmpi determines that "abcz" is greater than "abcdefg" and
returns the difference of z and d.
The language (user locale) selected by the user
at setup time, or through Control Panel, determines which string is
greater (or whether the strings are the same). If no language (user
locale) is selected, the system performs the comparison by using default
values.
For some locales, the lstrcmpi function
may be insufficient. If this occurs, use CompareString to ensure proper
comparison. For example, in Japan call with the
NORM_IGNORECASE, NORM_IGNOREKANATYPE,
and NORM_IGNOREWIDTH values to achieve the
most appropriate non-exact string comparison. The
NORM_IGNOREKANATYPE and NORM_IGNOREWIDTH
values are ignored in non-Asian locales, so you can set these values for
all locales and be guaranteed to have a culturally correct "insensitive"
sorting regardless of the locale. Note that specifying these values
slows performance, so use them only when necessary.
With a double-byte character set (DBCS) version
of the system, this function can compare two DBCS strings.
The lstrcmpi function uses a word sort,
rather than a string sort. A word sort treats hyphens and apostrophes
differently than it treats other symbols that are not alphanumeric, in
order to ensure that words such as "coop" and "co-op" stay together
within a sorted list. For a detailed discussion of word sorts and string
sorts, see the Remarks section for the CompareString function.
EnumMeshes()是一个递归枚举函数,对所有框架的所有网格递归调用传递进来的函数:
bool CDXUTMeshFrame::EnumMeshes( bool (*EnumMeshCB)(CDXUTMesh*, void*), void* pContext )
{
if( m_pMesh )
EnumMeshCB( m_pMesh, pContext );
if( m_pChild )
m_pChild->EnumMeshes( EnumMeshCB, pContext );
if( m_pNext )
m_pNext->EnumMeshes( EnumMeshCB, pContext );
return TRUE;
}
Destroy()、RestoreDeviceObjects()、InvalidateDeviceObjects()分别当摧毁网格框架、设备恢复、设备丢失时调用,需要注意的是该类的析构函数并没有释放分配的资源,只是删除了链表指针,必须显式调用Destroy()来释放资源:
HRESULT CDXUTMeshFrame::Destroy()
{
if( m_pMesh ) m_pMesh->Destroy();
if( m_pChild ) m_pChild->Destroy();
if( m_pNext ) m_pNext->Destroy();
SAFE_DELETE( m_pMesh );
SAFE_DELETE( m_pNext );
SAFE_DELETE( m_pChild );
return S_OK;
}
HRESULT CDXUTMeshFrame::RestoreDeviceObjects( LPDIRECT3DDEVICE9 pd3dDevice )
{
if( m_pMesh ) m_pMesh->RestoreDeviceObjects( pd3dDevice );
if( m_pChild ) m_pChild->RestoreDeviceObjects( pd3dDevice );
if( m_pNext ) m_pNext->RestoreDeviceObjects( pd3dDevice );
return S_OK;
}
HRESULT CDXUTMeshFrame::InvalidateDeviceObjects()
{
if( m_pMesh ) m_pMesh->InvalidateDeviceObjects();
if( m_pChild ) m_pChild->InvalidateDeviceObjects();
if( m_pNext ) m_pNext->InvalidateDeviceObjects();
return S_OK;
}
Render()负责网格框架的绘制,它只是调用CDXUTMesh::Render()来负责网格的绘制,由于框架的层次包含关系,所以该函数也是一个递归函数。需要注意的是该函数包含的最后一个参数,它允许用户指定世界坐标变换矩阵,如果该参数为NULL,则从设备获取已设置好的世界坐标矩阵,注意如果是虚拟设备,必须设置该矩阵,不能为NULL。
HRESULT CDXUTMeshFrame::Render( LPDIRECT3DDEVICE9 pd3dDevice,
bool bDrawOpaqueSubsets, bool bDrawAlphaSubsets,
D3DXMATRIX* pmatWorldMatrix )
{
// For pure devices, specify the world transform.
// If the world transform is not specified on pure devices, this function will fail.
D3DXMATRIX matSavedWorld, matWorld;
if (NULL == pmatWorldMatrix)
pd3dDevice->GetTransform(D3DTS_WORLD, &matSavedWorld);
else
matSavedWorld = *pmatWorldMatrix;
D3DXMatrixMultiply(&matWorld, &m_mat, &matSavedWorld);
pd3dDevice->SetTransform(D3DTS_WORLD, &matWorld);
if( m_pMesh )
m_pMesh->Render(pd3dDevice, bDrawOpaqueSubsets, bDrawAlphaSubsets);
if( m_pChild )
m_pChild->Render(pd3dDevice, bDrawOpaqueSubsets, bDrawAlphaSubsets, &matWorld);
pd3dDevice->SetTransform(D3DTS_WORLD, &matSavedWorld);
if( m_pNext )
m_pNext->Render( pd3dDevice, bDrawOpaqueSubsets, bDrawAlphaSubsets, &matSavedWorld );
return S_OK;
}
代码中需要注意的一点是,调用Render()绘制兄弟框架时传递的世界坐标矩阵是matSavedWorld,而绘制子框架时传递的世界坐标矩阵是matWorld。