以文本方式查看主题

-  计算机科学论坛  (http://bbs.xml.org.cn/index.asp)
--  『 C/C++编程思想 』  (http://bbs.xml.org.cn/list.asp?boardid=61)
----  DXUT源码分析 ---- 类CDXUTMeshFile  (http://bbs.xml.org.cn/dispbbs.asp?boardid=61&rootid=&id=70889)


--  作者:卷积内核
--  发布时间:12/26/2008 10:52:00 AM

--  DXUT源码分析 ---- 类CDXUTMeshFile
类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);


--  作者:卷积内核
--  发布时间:12/26/2008 10:53:00 AM

--  
该代码片段负责从指定的资源中加载网格模型,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);
先渲染所有不透明的子框架对象,再渲染所有半透明的子框架对象,最后恢复到先前的渲染状态。


--  作者:秋十三
--  发布时间:1/2/2009 8:32:00 PM

--  

我顶啊!!!
W 3 C h i n a ( since 2003 ) 旗 下 站 点
苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
93.750ms