天行健 君子当自强而不息

D3D中网格模型的运用


点击下载源码和资源

在最底层的层次中,Direct3D并不使用网格模型,而只是使用多边形。D3DX增强了 Direct3D系统的功能性,添加了一系列负责处理网格模型的容器和进行渲染的对象。.X文件是微软公司所开发的,高度通用的三维模型存储格式。它是模板驱动并完全可扩展,这就意味着可以使用它来满足文件存储的所有需求。一个.X文件,正如它的文件扩展名所表明的,是非常通用的。它可以是基于文本的,以便更容易进行编辑;或者是基于二进制的,这样可以使文件更小,并且更容易地进行保护以便不被窥视。整个.X文件格式是基于模板的,非常类似于C语言结构。

为了读取并处理一个.X文件,可以利用COM对象的一个小集合来解析从头到尾在.X文件中所遇到的每个数据对象。将数据对象作为一个字节的数组进行处理;仅仅是将数组转换为一种可使用的结构,以便能够容易地访问到包含在对象里的数据。

根据存储在.X文件里的内容,这些对象可以改变。在这里,对象代表了网格模型以及网格模型相关的数据(例如骨骼结构,所谓的框架层次和动画数据)。程序员的工作就是去解析这些对象,加载网格模型数据,创建动画表格,并构造框架的层次。

.X文件格式的详细介绍请参阅XFile网格的应用(1),创建网格的具体API函数和方法请参阅XFile网格的应用(2)XFile网格的应用(3)。

框架层次的运用

使用框架模板(frame template)将一个或多个数据对象(通常为网格模型)进行分组,以便能够更容易地进行处理。也可以创建一个网格模型,并使用多个框架去包含网格模型引用,这样就能够对一个网格模型使用许多次。一个框架层次(frame hierarchy)定义了一个场景的结构,或者网格模型的分组,每当一个框架移动时,所有嵌入其中的框架也同样产生移动。根没有父框架,意味着他就是层次的顶端,且不属于任何别的框架。被连接到其他框架上的框架称之为子框架(child frames)(也可以称之为节点(node))。

当一个框架移动时,它的所有子框架也随之移动。例如如果移动上臂,前臂和手也跟着移动,而另一方面,如果移动人的手,只有手会改变位置,因为它没有子框架(前臂是手的父框架)。每个框架都有它自己的方位,在.X文件的术语中称之为框架变换(frame transformation)。将这种变换运用到较高层次的对象上,每个变换将被传递下去,从层次的顶端以各种方式到达每个子框架。

如下所示:骨架是骨骼框架层次的一个完美实例,层次中的每块骨骼都连接到胸部。



框架层次是使用高级网格模型和动画技术的要点,事实上,它们是某些运用所必需的,诸如蒙皮网格模型。使用框架层次,另外一个原因是隔离场景其中一部分,以这种方式,可以通过移动特定的框架来修改场景中的一小块区域,而保留场景的其余部分。

解析.X文件

为了解析一个.X文件,需要使用 ID3DXFile对象,它的工作就是打开一个.X文件并枚举文件中的数据对象,再将他们以一种易于访问的方式展现给用户。为了使用ID3DXFile组件,应该包含dxfile.h,rmfxguid.h,rmfxtmpl.h,同时还必须将dxguid.lib和d3ddxof.lib库链接到工程中。

Applications use the methods of the ID3DXFile interface to create instances of the ID3DXFileEnumObject and ID3DXFileSaveObject interfaces, and to register templates.

ID3DXFile Members

Method Description
ID3DXFile::CreateEnumObject Creates an enumerator object that will read a .x file.
ID3DXFile::CreateSaveObject Creates a save object that will be used to save data to a .x file.
ID3DXFile::RegisterEnumTemplates Registers custom templates, given an ID3DXFileEnumObject enumeration object.
ID3DXFile::RegisterTemplates Registers custom templates.

Remarks

An ID3DXFile object also contains a local template store. This local storage may be added to only with the ID3DXFile::RegisterEnumTemplates and ID3DXFile::RegisterTemplates methods.

ID3DXFileEnumObject and ID3DXFileSaveObject objects created with ID3DXFile::CreateEnumObject and ID3DXFile::CreateSaveObject also utilize the template store of the parent ID3DXFile object.

The ID3DXFile interface is obtained by calling the D3DXFileCreate function.

The globally unique identifier (GUID) for the ID3DXFile interface is IID_ID3DXFile.

The LPD3DXFILE type is defined as a pointer to the ID3DXFile interface.

typedef interface ID3DXFile *LPD3DXFILE;

解析一个.X文件并不像开始时所看到的那样困难,窍门就是搜索整个对象的层次,查找想要使用的数据对象,也就是网格模型和框架的对象。 最困难的部分是要记住那些嵌入到其他对象中的对象,因而可能会遇到对象的引用(需要解决它,以便能够访问到原始的对象数据),而不是对象。

如下图所示,.X文件允许用户将模板对象嵌入到另一个模板对象中,从而创建出一个模板层次结构。

可以通过D3DXFileCreate来创建ID3DXFile对象:

Creates an instance of an ID3DXFile object.

STDAPI D3DXFileCreate(
ID3DXFile ** lplpDirectXFile
);

Parameters

lplpDirectXFile
Address of a pointer to an ID3DXFile interface, representing the created .x file object.

Return Values

If the function succeeds, the return value is S_OK. If the function fails, the return value can be one of the following: E_POINTER, E_OUTOFMEMORY.

Remarks

After using this function, use ID3DXFile::RegisterTemplates or ID3DXFile::RegisterEnumTemplates to register templates, ID3DXFile::CreateEnumObject to create an enumerator object, or ID3DXFile::CreateSaveObject to create a save object.

创建完ID3DXFile对象后,首先需要通过ID3DXFile::RegisterTemplates来注册Mesh模板。

Registers custom templates.

HRESULT RegisterTemplates(
LPCVOID pvData,
SIZE_T cbSize
);

Parameters

pvData
[in] Pointer to a buffer consisting of a .x file in text or binary format that contains templates.
cbSize
[in] Size of the buffer pointed to by pvData, in bytes.

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

The following code fragment provides an example call to RegisterTemplates And example contents for the buffer to which pvData points.

#define XSKINEXP_TEMPLATES \
"xof 0303txt 0032\
template XSkinMeshHeader \
{ \
<3CF169CE-FF7C-44ab-93C0-F78F62D172E2> \
WORD nMaxSkinWeightsPerVertex; \
WORD nMaxSkinWeightsPerFace; \
WORD nBones; \
} \
template VertexDuplicationIndices \
{ \
<B8D65549-D7C9-4995-89CF-53A9A8B031E3> \
DWORD nIndices; \
DWORD nOriginalVertices; \
array DWORD indices[nIndices]; \
} \
template SkinWeights \
{ \
<6F0D123B-BAD2-4167-A0D0-80224F25FABB> \
STRING transformNodeName;\
DWORD nWeights; \
array DWORD vertexIndices[nWeights]; \
array float weights[nWeights]; \
Matrix4x4 matrixOffset; \
}"
.
.
.

LPD3DXFILE pD3DXFile = NULL;

if ( FAILED(hr = pD3DXFile->RegisterTemplates((LPVOID)XSKINEXP_TEMPLATES, sizeof( XSKINEXP_TEMPLATES ) - 1 ) ) )
goto End;

All templates must specify a name and a UUID.

This method calls the ID3DXFile::RegisterEnumTemplates method, obtaining an ID3DXFileEnumObject interface pointer by calling ID3DXFile::CreateEnumObject with pvData as the first parameter.

注册完Mesh模板之后,需要调用ID3DXFile::CreateEnumObject来创建枚举对象以枚举访问各个具体的XFILE数据对象。

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.

The following table specifies file load options with .x files:

#define Value Description
D3DXF_FILELOAD_FromFile 0 Load data from a file.
D3DXF_FILELOAD_FROMWFILE 1 Load data from a file.
D3DXF_FILELOAD_FROMRESOURCE 2 Load data from a resource.
D3DXF_FILELOAD_FROMMEMORY 3 Load data from memory.
 
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.

来看看ID3DXFileEnumObject的具体定义:

Applications use the methods of the ID3DXFileEnumObject interface to cycle through the child file data objects in the file and to retrieve a child object by its globally unique identifier (GUID) or by its name.

ID3DXFileEnumObject Members

Method Description
ID3DXFileEnumObject::GetChild Retrieves a child object in this file data object.
ID3DXFileEnumObject::GetChildren Retrieves the number of child objects in this file data object.
ID3DXFileEnumObject::GetDataObjectById Retrieves the data object that has the specified GUID.
ID3DXFileEnumObject::GetDataObjectByName Retrieves the data object that has the specified name.
ID3DXFileEnumObject::GetFile Retrieves the ID3DXFile object.

Remarks

The GUID for the ID3DXFileEnumObject interface is IID_ID3DXFileEnumObject.

The LPD3DXFILEENUMOBJECT type is defined as a pointer to this interface.

typedef interface ID3DXFileEnumObject *LPD3DXFILEENUMOBJECT;
 

创建完ID3DXFileEnumObject对象后,首先需要通过ID3DXFileEnumObject::GetChildren来取得子对象数。

Retrieves the number of child objects in this file data object.

HRESULT GetChildren(
SIZE_T * puiChildren
);

Parameters

puiChildren
[in] Address of a pointer to receive the number of child objects in this file data object.

Return Values

If the method succeeds, the return value is S_OK. If the method fails, the following value will be returned: D3DXFERR_BADVALUE.

通过ID3DXFileEnumObject::GetChildren取得子对象数之后,可以遍历循环所有的子对象并通过
ID3DXFileEnumObject::GetChild来取得指向D3DXFileData的指针。

Retrieves a child object in this file data object.

HRESULT GetChild(
SIZE_T id,
ID3DXFileData ** ppObj
);

Parameters

id
[in] ID of the child object to retrieve.
ppObj
[out] Address of a pointer to receive the child object's interface pointer.

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_NOMOREOBJECTS.

示例代码如下所示:

//------------------------------------------------------------------
// A Mesh definition structure
//------------------------------------------------------------------
typedef struct MESH
{
    
char*               m_name;             // name of mesh

    ID3DXMesh*          m_mesh;             
// mesh object
    ID3DXMesh*          m_skinmesh;         // skin mesh object
    ID3DXSkinInfo*      m_skininfo;         // skin information

    DWORD               m_num_materials;    
// number of materails in mesh
    D3DMATERIAL9*       m_materials;        // array of materials
    IDirect3DTexture9** m_textures;         // array of textures    

    // clear all structure data
    MESH()
    {
        m_name  = NULL;     

        m_mesh      = NULL;
        m_skinmesh  = NULL;
        m_skininfo  = NULL;

        m_num_materials = 0;
        m_materials     = NULL;
        m_textures      = NULL;               
    }

    
// free all used resources
    ~MESH()
    {        
        delete[] m_name;
        m_name = NULL;

        Release_Com(m_mesh);
        Release_Com(m_skinmesh);
        Release_Com(m_skininfo);

        delete[] m_materials;
        m_materials = NULL;
        
        
// release all textures resource
        if(m_textures != NULL)
        {
            
for(DWORD i = 0; i < m_num_materials; i++)
                Release_Com(m_textures[i]);

            delete[] m_textures;
            m_textures = NULL;
        }
    }

} MESH;

//------------------------------------------------------------------
// Structure to contain frame information
//------------------------------------------------------------------
typedef struct FRAME
{
    
char*   m_name;     // frame's name
    MESH*   m_mesh;     // linked list of meshes    
    FRAME*  m_child;    // child frame

    FRAME()
    {
        
// clear all data
        m_name = NULL;
        m_mesh = NULL;
        m_child = NULL;
    }

    ~FRAME()
    {
        
// delete all used resources, including linked list of frames.
        delete[] m_name;    m_name    = NULL;
        delete m_mesh;      m_mesh    = NULL;
        delete m_child;     m_child   = NULL;        
    }

} FRAME;

// parent frame for .X file
FRAME* g_parent_frame = NULL;

//--------------------------------------------------------------------------------
// Parse x file, and return root frame.
//--------------------------------------------------------------------------------
FRAME* Parse_XFile(char* filename)
{
    ID3DXFile* xfile = NULL;
    ID3DXFileEnumObject* xfile_enum = NULL;
    ID3DXFileData* xfile_data = NULL;

    
// create the file object
    if(FAILED(D3DXFileCreate(&xfile)))
        
return NULL;

    
// register the templates
    if(FAILED(xfile->RegisterTemplates((LPVOID) D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES)))
    {
        xfile->Release();
        
return NULL;
    }

    
// create an enumerator object that will read a .x file
    if(FAILED(xfile->CreateEnumObject((LPVOID) filename, DXFILELOAD_FROMFILE, &xfile_enum)))
    {
        xfile->Release();
        
return NULL;
    }

    
// allocate a frame that becomes root
    FRAME* frame = new FRAME();

    SIZE_T num_child;

    
// retrieve the number of children in this file data object
    xfile_enum->GetChildren(&num_child);

    
// loop through all objects looking for the frames and meshes
    for(SIZE_T i = 0; i < num_child; i++)
    {
        
// retrieves a child object in this file data object
        if(FAILED(xfile_enum->GetChild(i, &xfile_data)))
            
return NULL;

        
// parse xfile data
        Parse_XFile_Data(xfile_data, frame);

        Release_Com(xfile_data);
    }

    
// release xfile enumerator object and xfile object
    Release_Com(xfile_enum);
    Release_Com(xfile);

    
// return root frame
    return frame;
}

 

通过Parse_XFile打开.X文件并找到每个XFile数据对象后,就可以编写一个函数Parse_XFile_Data来解析这个XFile数据对象。

来看看ID3DXFileData的定义:

Applications use the methods of the ID3DXFileData interface to build or to access the immediate hierarchy of the data object. Template restrictions determine the hierarchy.

ID3DXFileData Members

Method Description
ID3DXFileData::GetChild Retrieves a child object in this file data object.
ID3DXFileData::GetChildren Retrieves the number of children in this file data object.
ID3DXFileData::GetEnum Retrieves the enumeration object in this file data object.
ID3DXFileData::GetId Retrieves the GUID of this file data object.
ID3DXFileData::GetName Retrieves the name of this file data object.
ID3DXFileData::GetType Retrieves the template ID in this file data object.
ID3DXFileData::IsReference Indicates whether this file data object is a reference object that points to another child data object.
ID3DXFileData::Lock Accesses the .x file data.
ID3DXFileData::Unlock Ends the lifespan of the ID3DXFileData::Lock pointer.

Remarks

Data types allowed by the template are called optional members. The optional members are not required, but an object might miss important information without them. These optional members are saved as children of the data object. A child can be another data object or a reference to an earlier data object.

The GUID for the ID3DXFileData interface is IID_ID3DXFileData.

The LPD3DXFILEDATA type is defined as a pointer to this interface.

typedef interface ID3DXFileData *LPD3DXFILEDATA;

首先必须通过ID3DXFileData::GetType来获得ID3DXFileData对象的模板ID。

Retrieves the template ID in this file data object.

HRESULT GetType(
CONST GUID * pType
);

Parameters

pType
[in] Pointer to the GUID representing the template in this file data object.

Return Values

If the method succeeds, the return value is S_OK. If the method fails, the following value will be returned: D3DXFERR_BADVALUE.

接着调用ID3DXFileData::GetName来获得ID3DXFileData对象的名称。

Retrieves the name of this file data object.

HRESULT GetName(
LPSTR szName,
SIZE_T * puiSize
);

Parameters

szName
[in] Address of a pointer to receive the name of this file data object. If this parameter is NULL, then puiSize will return the size of the string. If szName points to valid memory, the name of this file data object will be copied into szName up to the number of characters given by puiSize.
puiSize
[in, out] Pointer to the size of the string that represents the name of this file data object. This parameter can be NULL if szName provides a reference to the name. This parameter will return the size of the string if szName is NULL.

Return Values

If the method succeeds, the return value is S_OK. If the method fails, the following value will be returned: D3DXFERR_BADVALUE.

Remarks

For this method to succeed, either szName or puiSize must be non-NULL.

代码示例如下:

    ID3DXFileData*  sub_xfile_data = NULL;
    ID3DXBuffer*    adjacency = NULL;

    GUID  type;
    
char* name = NULL;
    DWORD size;

    MESH* mesh = NULL;
    ID3DXBuffer* material_buffer = NULL;
    D3DXMATERIAL* materials = NULL;

    
// get the template type
    // retrieves the globally unique identifier (GUID) of the object's template
    if(FAILED(xfile_data->GetType(&type)))
        
return;

    
// get the template name (if any)
    // retrieves a pointer to a microsoft directX file object's name
    if(FAILED(xfile_data->GetName(NULL, &size)))
        
return;

    
if(size != 0)
    {
        
if((name = new char[size]) != NULL)
            xfile_data->GetName(name, &size);
    }

    
// give template a default name if none found
    if(name == NULL)
    {
        
if((name = new char[9]) == NULL)
            
return;

        strcpy(name, "Template");
    }

取得ID3DXFileData对象的类型和名称后,就可以根据ID3DXFileData对象的类型分别进行不同的处理。


如果类型是框架引用(TID_D3DRMFrame),则新建一个框架对象,并将新框架对象添加到父框架对象的子对象列表中 ,并将当前框架对象设置为新框架对象。
如果类型是Mesh对象(TID_D3DRMMesh),则加载Mesh网格并将该网格添加到父框架的Mesh成员中。
如果是其他类型,比如(TID_D3DRMAnimationSet,TID_D3DRMAnimation,TID_D3DRMAnimationKey),则不做任何的处理。

如下所示:

    // set sub frame
    FRAME* sub_frame = parent_frame;

    
// process the templates
    FRAME* frame = NULL;

    
if(type == TID_D3DRMFrame)  // it is a frame
    {
        
// create a new frame structure
        frame = new FRAME();

        
// store the name
        frame->m_name = name;
        name = NULL;

        
// add to parent frame
        parent_frame->m_child = frame;

        
// set sub frame parent
        sub_frame = frame;
    }
    
else if(type == TID_D3DRMMesh)  // it is a mesh
    {
        
// create a new mesh structure
        mesh = new MESH();

        
// store the name
        mesh->m_name = name;
        name = NULL;

        
// load mesh data (as a skinned mesh)
        // loads a skin mesh from microsoft directX .x file data object
        if(FAILED(D3DXLoadSkinMeshFromXof(xfile_data, 0, g_d3d_device, &adjacency, &material_buffer, NULL,
            &mesh->m_num_materials, &mesh->m_skininfo, &mesh->m_mesh)))
        {
            delete[] name;
            delete mesh;
            
return;
        }

        Release_Com(adjacency);

        
// clone skin mesh if bones exist
        if(mesh->m_skininfo != NULL && mesh->m_skininfo->GetNumBones() != 0)
        {
            
// clones a mesh using a flexible vertex format (FVF) code
            if(FAILED(mesh->m_mesh->CloneMeshFVF(0, mesh->m_mesh->GetFVF(), g_d3d_device, &mesh->m_skinmesh)))
            {
                mesh->m_skininfo->Release();
                mesh->m_skininfo = NULL;
            }
        }

        
// load materials or create a default one if none
        if(mesh->m_num_materials == 0)
        {
            
// create a default one
            mesh->m_materials = new D3DMATERIAL9[1];
            mesh->m_textures  = 
new LPDIRECT3DTEXTURE9[1];

            ZeroMemory(mesh->m_materials, 
sizeof(D3DMATERIAL9));

            mesh->m_materials[0].Diffuse.r = 1.0;
            mesh->m_materials[0].Diffuse.g = 1.0;
            mesh->m_materials[0].Diffuse.b = 1.0;
            mesh->m_materials[0].Diffuse.a = 1.0;
            mesh->m_materials[0].Ambient   = mesh->m_materials[0].Diffuse;
            mesh->m_materials[0].Specular  = mesh->m_materials[0].Diffuse;

            mesh->m_textures[0] = NULL;

            mesh->m_num_materials = 1;
        }
        
else
        {
            
// load the materials
            materials = (D3DXMATERIAL*) material_buffer->GetBufferPointer();

            mesh->m_materials = 
new D3DMATERIAL9[mesh->m_num_materials];
            mesh->m_textures  = 
new LPDIRECT3DTEXTURE9[mesh->m_num_materials];

            
// set materials and textures for mesh
            for(DWORD i = 0; i < mesh->m_num_materials; i++)
            {
                mesh->m_materials[i] = materials[i].MatD3D;
                mesh->m_materials[i].Ambient = mesh->m_materials[i].Diffuse;

                
// build a texture path and load it
                if(FAILED(D3DXCreateTextureFromFile(g_d3d_device, materials[i].pTextureFilename, &mesh->m_textures[i])))
                    mesh->m_textures[i] = NULL;
            }
        }

接着必须遍历处理所有的ID3DXFileData子对象,首先需要调用ID3DXFileData::GetChildren来获得ID3DXFileData子对象数。

Retrieves the number of children in this file data object.

HRESULT GetChildren(
SIZE_T * puiChildren
);

Parameters

puiChildren
[in] Address of a pointer to receive the number of children in this file data object.

Return Values

If the method succeeds, the return value is S_OK. If the method fails, the following value will be returned: D3DXFERR_BADVALUE.

接着调用ID3DXFileData::GetChild获得ID3DXFileData子对象。

Retrieves a child object in this file data object.

HRESULT GetChild(
SIZE_T uiChild,
ID3DXFileData ** ppChild
);

Parameters

uiChild
[in] ID of the child object to retrieve.
ppChild
[in] Address of a pointer to receive the child object's interface pointer.

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 values: D3DXFERR_BADVALUE, D3DXFERR_NOMOREOBJECTS.

然后递归调用Parse_XFile_Data来处理每个ID3DXFileData子对象,如下所示:

    SIZE_T num_child;
    xfile_data->GetChildren(&num_child);

    
// scan for embedded templates
    for(SIZE_T i = 0; i < num_child; i++)
    {
        xfile_data->GetChild(i, &sub_xfile_data);

        
// process embedded xfile data, recursive call.
        Parse_XFile_Data(sub_xfile_data, sub_frame);

        Release_Com(sub_xfile_data);
    }


Parse_XFile_Data完整代码示例如下:

 

//--------------------------------------------------------------------------------
// Parse specified xfiel data, recursive function.
//--------------------------------------------------------------------------------
void Parse_XFile_Data(ID3DXFileData* xfile_data, FRAME* parent_frame)
{
    ID3DXFileData*  sub_xfile_data = NULL;
    ID3DXBuffer*    adjacency = NULL;

    GUID  type;
    
char* name = NULL;
    DWORD size;

    MESH* mesh = NULL;
    ID3DXBuffer* material_buffer = NULL;
    D3DXMATERIAL* materials = NULL;

    
// get the template type
    // retrieves the globally unique identifier (GUID) of the object's template
    if(FAILED(xfile_data->GetType(&type)))
        
return;

    
// get the template name (if any)
    // retrieves a pointer to a microsoft directX file object's name
    if(FAILED(xfile_data->GetName(NULL, &size)))
        
return;

    
if(size != 0)
    {
        
if((name = new char[size]) != NULL)
            xfile_data->GetName(name, &size);
    }

    
// give template a default name if none found
    if(name == NULL)
    {
        
if((name = new char[9]) == NULL)
            
return;

        strcpy(name, "Template");
    }

    
// set sub frame
    FRAME* sub_frame = parent_frame;

    
// process the templates
    FRAME* frame = NULL;

    
if(type == TID_D3DRMFrame)  // it is a frame
    {
        
// create a new frame structure
        frame = new FRAME();

        
// store the name
        frame->m_name = name;
        name = NULL;

        
// add to parent frame
        parent_frame->m_child = frame;

        
// set sub frame parent
        sub_frame = frame;
    }
    
else if(type == TID_D3DRMMesh)  // it is a mesh
    {
        
// create a new mesh structure
        mesh = new MESH();

        
// store the name
        mesh->m_name = name;
        name = NULL;

        
// load mesh data (as a skinned mesh)
        // loads a skin mesh from microsoft directX .x file data object
        if(FAILED(D3DXLoadSkinMeshFromXof(xfile_data, 0, g_d3d_device, &adjacency, &material_buffer, NULL,
            &mesh->m_num_materials, &mesh->m_skininfo, &mesh->m_mesh)))
        {
            delete[] name;
            delete mesh;
            
return;
        }

        Release_Com(adjacency);

        
// clone skin mesh if bones exist
        if(mesh->m_skininfo != NULL && mesh->m_skininfo->GetNumBones() != 0)
        {
            
// clones a mesh using a flexible vertex format (FVF) code
            if(FAILED(mesh->m_mesh->CloneMeshFVF(0, mesh->m_mesh->GetFVF(), g_d3d_device, &mesh->m_skinmesh)))
            {
                mesh->m_skininfo->Release();
                mesh->m_skininfo = NULL;
            }
        }

        
// load materials or create a default one if none
        if(mesh->m_num_materials == 0)
        {
            
// create a default one
            mesh->m_materials = new D3DMATERIAL9[1];
            mesh->m_textures  = 
new LPDIRECT3DTEXTURE9[1];

            ZeroMemory(mesh->m_materials, 
sizeof(D3DMATERIAL9));

            mesh->m_materials[0].Diffuse.r = 1.0;
            mesh->m_materials[0].Diffuse.g = 1.0;
            mesh->m_materials[0].Diffuse.b = 1.0;
            mesh->m_materials[0].Diffuse.a = 1.0;
            mesh->m_materials[0].Ambient   = mesh->m_materials[0].Diffuse;
            mesh->m_materials[0].Specular  = mesh->m_materials[0].Diffuse;

            mesh->m_textures[0] = NULL;

            mesh->m_num_materials = 1;
        }
        
else
        {
            
// load the materials
            materials = (D3DXMATERIAL*) material_buffer->GetBufferPointer();

            mesh->m_materials = 
new D3DMATERIAL9[mesh->m_num_materials];
            mesh->m_textures  = 
new LPDIRECT3DTEXTURE9[mesh->m_num_materials];

            
// set materials and textures for mesh
            for(DWORD i = 0; i < mesh->m_num_materials; i++)
            {
                mesh->m_materials[i] = materials[i].MatD3D;
                mesh->m_materials[i].Ambient = mesh->m_materials[i].Diffuse;

                
// build a texture path and load it
                if(FAILED(D3DXCreateTextureFromFile(g_d3d_device, materials[i].pTextureFilename, &mesh->m_textures[i])))
                    mesh->m_textures[i] = NULL;
            }
        }

        Release_Com(material_buffer);

        
// set mesh to parent frame
        parent_frame->m_mesh = mesh;
    }   
// end if(type == TID_D3DRMMesh)
    else if(type == TID_D3DRMAnimationSet || type == TID_D3DRMAnimation || type == TID_D3DRMAnimationKey)
    {
        
// skip animation sets and animations
        delete[] name;
        
return;
    }

    
// release name buffer
    delete[] name;

    SIZE_T num_child;
    xfile_data->GetChildren(&num_child);

    
// scan for embedded templates
    for(SIZE_T i = 0; i < num_child; i++)
    {
        xfile_data->GetChild(i, &sub_xfile_data);

        
// process embedded xfile data, recursive call.
        Parse_XFile_Data(sub_xfile_data, sub_frame);

        Release_Com(sub_xfile_data);
    }
}

Parse_XFile和Parse_XFile_Data协同工作以解析.X文件里的每个数据对象,Parse_XFile功能函数打开.X文件并枚举它,以查找层次结构中最顶层的对象,每个被找到的对象都将被传递给Parse_XFile_Data功能函数。 Parse_XFile_Data功能函数负责处理数据对象,它从获取对象的类型以及对象的实例名(如果有的话)开始进行处理。从那里,可以处理对象数据,然后递归地调用功能函数以便枚举所有的子对象,这个处理过程持续不断,直到所有的数据对象都被处理。

网格模型和D3DX

基本上,在Direct3D中使用到两种类型的网格模型,标准网格模型(standard mesh)和蒙皮网格模型(skinned mesh)。所谓的标准网格模型,除了可以使用纹理映射以增强外观外,它没有什么花哨的功能。蒙皮网格模型的独特之处在于它们是可变形的 (deformable),也就是说,网格模型可以在运行当中动态地改变它的形状。为网格模型的变形做准备,必须在三维建模程序中将网格模型的顶点附属到一个虚构的骨骼集中,在任何骨骼移动的时候,附属其上的顶点也将跟着移动。

ID3DXBuffer对象

ID3DXBuffer 对象用于存储并检索数据的缓冲区,D3DX使用ID3DXBuffer对象去存储网格模型所涉及的信息,例如材质和纹理映射的列表。 ID3DXBuffer仅有两个功能函数。第1个功能函数是ID3DXBuffer::GetBufferPointer,可以使用它去获取一个只想包含在对象缓冲区里的数据的指针。 GetBufferPointer功能函数的调用将返回一个可以强制转换成任何数据类型的指针:

Retrieves a pointer to the data in the buffer.

LPVOID GetBufferPointer(VOID);

第2个功能函数是ID3DXBuffer:: GetBufferSize,它返回存储数据所需的字节数。

Retrieves the total size of the data in the buffer.

DWORD GetBufferSize(VOID);

标准网格模型

一个标准的网格模型十分简单,它仅包含一个单一网格模型的定义,使用D3DX来处理标准网格模型尤其容易,因为D3DX仅要求使用一个简短的代码序列去装载并显示标准网格模型,标准网格模型有一个 ID3DXMESH对象表示,它的任务就是存储并绘制一个单一的网格模型。

在实例化一个ID3DXMESH对象后,使用下面的功能函数从.X文件中加载网格模型的对象。

Loads a mesh from an ID3DXFileData object.

HRESULT D3DXLoadMeshFromXof(
LPD3DXFILEDATA pxofMesh,
DWORD Options,
LPDIRECT3DDEVICE9 pDevice,
LPD3DXBUFFER * ppAdjacency,
LPD3DXBUFFER * ppMaterials,
LPD3DXBUFFER * ppEffectInstances,
DWORD * pNumMaterials,
LPD3DXMESH * ppMesh
);

Parameters

pxofMesh
[in] Pointer to an ID3DXFileData interface, representing the file data object to load.
Options
[out] Combination of one or more flags from the D3DXMESH enumeration, specifying creation options for the mesh.
pDevice
[in] Pointer to an IDirect3DDevice9 interface, the device object associated with the mesh.
ppAdjacency
[out] Pointer to a buffer that contains adjacency data. The adjacency data contains an array of three DWORDs per face that specify the three neighbors for each face in the mesh. For more information about accessing the buffer, see ID3DXBuffer.
ppMaterials
[in, out] Address of a pointer to an ID3DXBuffer interface. When the method returns, this parameter is filled with an array of D3DXMATERIAL structures.
ppEffectInstances
[out] Pointer to a buffer containing an array of effect instances, one per attribute group in the returned mesh. An effect instance is a particular instance of state information used to initialize an effect. See D3DXEFFECTINSTANCE. For more information about accessing the buffer, see ID3DXBuffer.
pNumMaterials
[in, out] Pointer to the number of D3DXMATERIAL structures in the ppMaterials array, when the method returns.
ppMesh
[out, retval] Address of a pointer to an ID3DXMesh interface, representing the loaded mesh.

Return Values

If the function succeeds, the return value is D3D_OK. If the function fails, the return value can be one of the following: D3DERR_INVALIDCALL.

D3DXERR_INVALIDDATA E_OUTOFMEMORY

Remarks

For mesh files that do not contain effect instance information, default effect instances will be generated from the material information in the .x file. A default effect instance will have default values that correspond to the members of the D3DMATERIAL9 structure.

The default texture name is also filled in, but is handled differently. The name will be Texture0@Name, which corresponds to an effect variable by the name of "Texture0" with an annotation called "Name." This will contain the string file name for the texture.


D3DXLoadMeshFromXof函数里的大多数参数是在执行期间由D3DX来填入的,用户只需提供所加载的.X文件名,一个预初始化的ID3DXBuffer和ID3DXMesh对象,以及一个DWORD变量去存储在网格模型中所使用的材质的数量。

渲染网格模型

ID3DXMesh 对象的核心是一个称之为 DrawSubset的渲染功能函数,它的工作是渲染网格模型的一个子集。一个子集(subset)就是一个网格模型的一个部分,它根据渲染的不同情况被分隔开来,例如不同材质或纹理的子集。可以将一个网格模型分割成许多子集,程序员的工作就是去领会每个子集所代表的是什么,并渲染它。

如下所示:使用子集分隔网格模型的不同部分。



在加载好一个.X文件后,便获得了一个网格模型对象以及它的材质。网格模型的子集与那些材质相关联,所以如果在一个网格模型里有5种材质,那么网格模型就包含了5个要绘制的子集。子集的布置允许用户能够容易地渲染一个网格模型,只需要简单地搜索每种材质,设置它,然后渲染子集,重复这些步骤直到绘制完整个网格实例。为了定位世界中的网格模型,需要在进行绘制之前设置世界变换矩阵。

示例代码如下:

       // render the mesh
        for(DWORD i = 0; i < mesh->m_num_materials; i++)
        {
            
// set the materials properties for the device
            g_d3d_device->SetMaterial(&mesh->m_materials[i]);

            
// assigns a texture to a stage for a device
            g_d3d_device->SetTexture(0, mesh->m_textures[i]);

            
// draw a subset of a mesh
            mesh_to_draw->DrawSubset(i);
        }
 

蒙皮网格模型

Direct3D中一个最令人兴奋的就是蒙皮网格模型,一个蒙皮网格模型可以动态地改变形状。通过连接那些构造“骨骼”底层结构中网格模型的各自顶点,或框架层次来实现它。一个蒙皮网格模型使用骨骼去定义它的轮廓,当骨骼移动时,网格模型改变形状以便与之相匹配。骨骼在一个.X文件里被表示为一个框架层次,当构造网格模型时,以一种父与子的形式来连接框架。当一个父框架被重新定向时,所有附属其上的子框架继承父框架的变换,并兼有它们自身的变换,这使得动画的实现更加容易,只需移动一个框架,其所有附属框架也将相应地移动。

要加载并使用一个蒙皮网格模型,可以直接处理.X文件的数据对象。枚举包含在一个.X文件里的网格模型的数据对象时,需要调用各种不同的D3DX网格模型加载功能函数去处理那些对象数据。当加载蒙皮网格模型时,要用到的一个功能函数就是 D3DXLoadSkinMeshFromXof。这个功能函数的作用就是从.X文件里读取网格模型的数据对象,创建一个包含网格模型的 ID3DXMesh对象,同时创建一个ID3DXSkinInfo对象,该对象描述了网格模型变形所需要的骨骼到顶点的连接数据。

来看看ID3DXSkinInfo的具体定义:

Applications use the methods of the ID3DXSkinInfo interface to manipulate bone matrices, which are used to skin vertex data for animation. This interface is no longer strictly tied to ID3DXMesh and can be used to skin any set of vertex data.

ID3DXSkinInfo Members

Method Description
ID3DXSkinInfo::Clone Clones a skin info object.
ID3DXSkinInfo::ConvertToBlendedMesh Takes a mesh and returns a new mesh with per-vertex blend weights and a bone combination table. The table describes which bones affect which subsets of the mesh.
ID3DXSkinInfo::ConvertToIndexedBlendedMesh Takes a mesh and returns a new mesh with per-vertex blend weights, indices, and a bone combination table. The table describes which bone palettes affect which subsets of the mesh.
ID3DXSkinInfo::FindBoneVertexInfluenceIndex Retrieves the index of the bone influence affecting a single vertex.
ID3DXSkinInfo::GetBoneInfluence Gets the vertices and weights that a bone influences.
ID3DXSkinInfo::GetBoneName Gets the bone name, from the bone index.
ID3DXSkinInfo::GetBoneOffsetMatrix Gets the bone offset matrix.
ID3DXSkinInfo::GetBoneVertexInfluence Retrieves the blend factor and vertex affected by a specified bone influence.
ID3DXSkinInfo::GetDeclaration Gets the vertex declaration.
ID3DXSkinInfo::GetFVF Gets the fixed function vertex value.
ID3DXSkinInfo::GetMaxFaceInfluences Gets the maximum face influences in a triangle mesh with the specified index buffer.
ID3DXSkinInfo::GetMaxVertexInfluences Gets the maximum number of influences for any vertex in the mesh.
ID3DXSkinInfo::GetMinBoneInfluence Gets the minimum bone influence. Influence values smaller than this are ignored.
ID3DXSkinInfo::GetNumBoneInfluences Gets the number of influences for a bone.
ID3DXSkinInfo::GetNumBones Gets the number of bones.
ID3DXSkinInfo::Remap Updates bone influence information to match vertices after they are reordered. This method should be called if the target vertex buffer has been reordered externally.
ID3DXSkinInfo::SetBoneInfluence Sets the influence value for a bone.
ID3DXSkinInfo::SetBoneName Sets the bone name.
ID3DXSkinInfo::SetBoneOffsetMatrix Sets the bone offset matrix.
ID3DXSkinInfo::SetBoneVertexInfluence Sets an influence value of a bone on a single vertex.
ID3DXSkinInfo::SetDeclaration Sets the vertex declaration.
ID3DXSkinInfo::SetFVF Sets the flexible vertex format (FVF) type.
ID3DXSkinInfo::SetMinBoneInfluence Sets the minimum bone influence. Influence values smaller than this are ignored.
ID3DXSkinInfo::UpdateSkinnedMesh Applies software skinning to the target vertices based on the current matrices.

Remarks

Create a ID3DXSkinInfo interface with D3DXCreateSkinInfo, D3DXCreateSkinInfoFromBlendedMesh, or D3DXCreateSkinInfoFVF.

The LPD3DXSKININFO type is defined as a pointer to the ID3DXSkinInfo interface.

typedef struct ID3DXSkinInfo *LPD3DXSKININFO;


要使用蒙皮网格模型,需要创建两个网格模型容器。第一个网格模型负责从.X文件进行加载的工作,它确实是一个标准的网格模型,使用了一个ID3DXMesh对象去包含网格模型的信息。在这里要做的就是去克隆(复制)该网格模型,以便渲染变形后的蒙皮网格模型。

为了创建这个克隆的网格模型,调用网格模型的CloneMeshFVF功能函数:

Clones a mesh using a flexible vertex format (FVF) code.

HRESULT CloneMeshFVF(
DWORD Options,
DWORD FVF,
LPDIRECT3DDEVICE9 pDevice,
LPD3DXMESH * ppCloneMesh
);

Parameters

Options
[in] A combination of one or more D3DXMESH flags specifying creation options for the mesh.
FVF
[in] Combination of FVF codes, which specifies the vertex format for the vertices in the output mesh. For the values of the codes, see D3DFVF.
pDevice
[in] Pointer to an IDirect3DDevice9 interface representing the device object associated with the mesh.
ppCloneMesh
[out, retval] Address of a pointer to an ID3DXMesh interface, representing the cloned mesh.

Return Values

If the method succeeds, the return value is D3D_OK. If the method fails, the return value can be one of the following: D3DERR_INVALIDCALL, E_OUTOFMEMORY.

Remarks

ID3DXBaseMesh::CloneMeshFVF is used to reformat and change the vertex data layout. This is done by creating a new mesh object. For example, use it to to add space for normals, texture coordinates, colors, weights, etc. that were not present before.

ID3DXBaseMesh::UpdateSemantics updates the vertex declaration with different semantic information without changing the layout of the vertex buffer. This method does not modify the contents of the vertex buffer. For example, use it to relabel a 3D texture coordinate as a binormal or tangent or vice versa.


可以这么使用这个函数:

        // clone skin mesh if bones exist
        if(mesh->m_skininfo != NULL && mesh->m_skininfo->GetNumBones() != 0)
        {
            
// clones a mesh using a flexible vertex format (FVF) code
            if(FAILED(mesh->m_mesh->CloneMeshFVF(0, mesh->m_mesh->GetFVF(), g_d3d_device, &mesh->m_skinmesh)))
            {
                mesh->m_skininfo->Release();
                mesh->m_skininfo = NULL;
            }
        }

要修改骨骼的变换,必须构造一个D3DXMATRIX对象的数组(每个骨骼都有一个矩阵)。因此,如果蒙皮网格模型使用了10块骨骼,那么就需要 10个D3DXMATRIX对象去包含每块骨骼的变换。现在开始存储每块骨骼的各种各样的变换,需要记住的重要事情就是,每块骨骼都继承了它的父框架的变换,因此,改变一个骨骼的定位时,存在某种程度的连锁效应。

一旦使用了变换,就能更新蒙皮网格模型的顶点并渲染网格模型,为了更新蒙皮网格模型,需要锁定初始网格模型和蒙皮网格模型的(所克隆的辅助网格模型)顶点缓冲区:
 
void* source = NULL;
void* dest = NULL;

// locks a vertex buffer and obtains a pointer to the vertex buffer memory
mesh->m_mesh->LockVertexBuffer(0, &source);
mesh->m_skinmesh->LockVertexBuffer(0, &dest);

一旦锁定,调用ID3DXSkinInfo::UpdateSkinnedMesh功能函数去变换蒙皮网格模型里的所有顶点,以便匹配在D3DXMATRIX数组里所设置的骨骼的方向。

来看看ID3DXSkinInfo::UpdateSkinnedMesh的具体使用信息:

Applies software skinning to the target vertices based on the current matrices.

HRESULT UpdateSkinnedMesh(
CONST D3DXMATRIX * pBoneTransforms,
CONST D3DXMATRIX * pBoneInvTransposeTransforms,
LPCVOID pVerticesSrc,
PVOID pVerticesDst
);

Parameters

pBoneTransforms
[in] Bone transform matrix.
pBoneInvTransposeTransforms
[in] Inverse transpose of the bone transform matrix.
pVerticesSrc
[in] Pointer to the buffer containing the source vertices.
pVerticesDst
[in] Pointer to the buffer containing the destination vertices.

Return Values

If the method succeeds, the return value is D3D_OK. If the method fails, the return value can be D3DERR_INVALIDCALL.

Remarks

When used to skin vertices with two position elements, this method skins the second position element with the inverse of the bone instead of the bone itself.


 可以这么使用 ID3DXSkinInfo::UpdateSkinnedMesh。

// update skinned mesh, applies software skinning to the target vertices based on the current matrices.
 mesh->m_skininfo->UpdateSkinnedMesh(matrices, NULL, source, dest);

最后,解锁顶点缓冲区:
 
// unlock buffers
mesh->m_skinmesh->UnlockVertexBuffer();
mesh->m_mesh->UnlockVertexBuffer();
 

以下代码给出了如何绘制蒙皮网格:

//--------------------------------------------------------------------------------
// Draw current frame, recursive function.
//--------------------------------------------------------------------------------
void Draw_Frame(FRAME* frame)
{
    MESH* mesh;
    D3DXMATRIX* matrices = NULL;
    ID3DXMesh* mesh_to_draw;

    
// return if no frame
    if(frame == NULL)
        
return;

    
// draw meshes if any in frame
    if((mesh = frame->m_mesh) != NULL)
    {
        
// setup pointer to mesh to draw
        mesh_to_draw = mesh->m_mesh;

        
// generate mesh from skinned mesh to draw with
        if(mesh->m_skinmesh != NULL && mesh->m_skininfo != NULL)
        {
            DWORD num_bones = mesh->m_skininfo->GetNumBones();

            
// allocate an array of matrices to orient bones
            matrices = new D3DXMATRIX[num_bones];

            
// set all bones orientation to identity
            for(DWORD i = 0; i < num_bones; i++)
                D3DXMatrixIdentity(&matrices[i]);

            
// lock source and destination vertex buffers

            
void* source = NULL;
            
void* dest = NULL;

            
// locks a vertex buffer and obtains a pointer to the vertex buffer memory
            mesh->m_mesh->LockVertexBuffer(0, &source);
            mesh->m_skinmesh->LockVertexBuffer(0, &dest);

            
// update skinned mesh, applies software skinning to the target vertices based on the current matrices.
            mesh->m_skininfo->UpdateSkinnedMesh(matrices, NULL, source, dest);

            
// unlock buffers
            mesh->m_skinmesh->UnlockVertexBuffer();
            mesh->m_mesh->UnlockVertexBuffer();

            
// point to skin mesh to draw
            mesh_to_draw = mesh->m_skinmesh;
        }

        
// render the mesh
        for(DWORD i = 0; i < mesh->m_num_materials; i++)
        {
            
// set the materials properties for the device
            g_d3d_device->SetMaterial(&mesh->m_materials[i]);

            
// assigns a texture to a stage for a device
            g_d3d_device->SetTexture(0, mesh->m_textures[i]);

            
// draw a subset of a mesh
            mesh_to_draw->DrawSubset(i);
        }

        
// free array of matrices
        delete[] matrices;
        matrices = NULL;
    }

    
// draw child frames, recursively call.
    Draw_Frame(frame->m_child);
}


现在给出完整的示例代码:

 
/***************************************************************************************
PURPOSE:
    XFile/Skinned Mesh Demo
 ***************************************************************************************/


#include <windows.h>
#include <stdio.h>
#include "d3d9.h"
#include "d3dx9.h"
#include "DxFile.h"
#include "RmxfGuid.h"
#include "RmxfTmpl.h"

#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "winmm.lib")
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
#pragma comment(lib, "d3dxof.lib")

#pragma warning(disable : 4305 4996)

#define WINDOW_WIDTH    400
#define WINDOW_HEIGHT   400

#define Safe_Release(p) if(p) (p)->Release();
#define Release_Com(p)  if(p)  { (p)->Release(); (p) = NULL; }

// window handles, class and caption text.
HWND g_hwnd;
HINSTANCE g_inst;
static char g_class_name[] = "XFileClass";
static char g_caption[]    = "XFile Demo";

// the Direct3D and device object
IDirect3D9* g_d3d = NULL;
IDirect3DDevice9* g_d3d_device = NULL;

//------------------------------------------------------------------
// A Mesh definition structure
//------------------------------------------------------------------
typedef struct MESH
{
    
char*               m_name;             // name of mesh

    ID3DXMesh*          m_mesh;             
// mesh object
    ID3DXMesh*          m_skinmesh;         // skin mesh object
    ID3DXSkinInfo*      m_skininfo;         // skin information

    DWORD               m_num_materials;    
// number of materails in mesh
    D3DMATERIAL9*       m_materials;        // array of materials
    IDirect3DTexture9** m_textures;         // array of textures    

    // clear all structure data
    MESH()
    {
        m_name  = NULL;     

        m_mesh      = NULL;
        m_skinmesh  = NULL;
        m_skininfo  = NULL;

        m_num_materials = 0;
        m_materials     = NULL;
        m_textures      = NULL;               
    }

    
// free all used resources
    ~MESH()
    {        
        delete[] m_name;
        m_name = NULL;

        Release_Com(m_mesh);
        Release_Com(m_skinmesh);
        Release_Com(m_skininfo);

        delete[] m_materials;
        m_materials = NULL;
        
        
// release all textures resource
        if(m_textures != NULL)
        {
            
for(DWORD i = 0; i < m_num_materials; i++)
                Release_Com(m_textures[i]);

            delete[] m_textures;
            m_textures = NULL;
        }
    }

} MESH;

//------------------------------------------------------------------
// Structure to contain frame information
//------------------------------------------------------------------
typedef struct FRAME
{
    
char*   m_name;     // frame's name
    MESH*   m_mesh;     // linked list of meshes    
    FRAME*  m_child;    // child frame

    FRAME()
    {
        
// clear all data
        m_name = NULL;
        m_mesh = NULL;
        m_child = NULL;
    }

    ~FRAME()
    {
        
// delete all used resources, including linked list of frames.
        delete[] m_name;    m_name    = NULL;
        delete m_mesh;      m_mesh    = NULL;
        delete m_child;     m_child   = NULL;        
    }

} FRAME;

// parent frame for .X file
FRAME* g_parent_frame = NULL;

//--------------------------------------------------------------------------------
// Window procedure.
//--------------------------------------------------------------------------------
long WINAPI Window_Proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    
switch(msg)
    {
    
case WM_DESTROY:
        PostQuitMessage(0);
        
return 0;
    }

    
return (long) DefWindowProc(hwnd, msg, wParam, lParam);
}

//--------------------------------------------------------------------------------
// Parse specified xfiel data, recursive function.
//--------------------------------------------------------------------------------
void Parse_XFile_Data(ID3DXFileData* xfile_data, FRAME* parent_frame)
{
    ID3DXFileData*  sub_xfile_data = NULL;
    ID3DXBuffer*    adjacency = NULL;

    GUID  type;
    
char* name = NULL;
    DWORD size;

    MESH* mesh = NULL;
    ID3DXBuffer* material_buffer = NULL;
    D3DXMATERIAL* materials = NULL;

    
// get the template type
    // retrieves the globally unique identifier (GUID) of the object's template
    if(FAILED(xfile_data->GetType(&type)))
        
return;

    
// get the template name (if any)
    // retrieves a pointer to a microsoft directX file object's name
    if(FAILED(xfile_data->GetName(NULL, &size)))
        
return;

    
if(size != 0)
    {
        
if((name = new char[size]) != NULL)
            xfile_data->GetName(name, &size);
    }

    
// give template a default name if none found
    if(name == NULL)
    {
        
if((name = new char[9]) == NULL)
            
return;

        strcpy(name, "Template");
    }

    
// set sub frame
    FRAME* sub_frame = parent_frame;

    
// process the templates
    FRAME* frame = NULL;

    
if(type == TID_D3DRMFrame)  // it is a frame
    {
        
// create a new frame structure
        frame = new FRAME();

        
// store the name
        frame->m_name = name;
        name = NULL;

        
// add to parent frame
        parent_frame->m_child = frame;

        
// set sub frame parent
        sub_frame = frame;
    }
    
else if(type == TID_D3DRMMesh)  // it is a mesh
    {
        
// create a new mesh structure
        mesh = new MESH();

        
// store the name
        mesh->m_name = name;
        name = NULL;

        
// load mesh data (as a skinned mesh)
        // loads a skin mesh from microsoft directX .x file data object
        if(FAILED(D3DXLoadSkinMeshFromXof(xfile_data, 0, g_d3d_device, &adjacency, &material_buffer, NULL,
            &mesh->m_num_materials, &mesh->m_skininfo, &mesh->m_mesh)))
        {
            delete[] name;
            delete mesh;
            
return;
        }

        Release_Com(adjacency);

        
// clone skin mesh if bones exist
        if(mesh->m_skininfo != NULL && mesh->m_skininfo->GetNumBones() != 0)
        {
            
// clones a mesh using a flexible vertex format (FVF) code
            if(FAILED(mesh->m_mesh->CloneMeshFVF(0, mesh->m_mesh->GetFVF(), g_d3d_device, &mesh->m_skinmesh)))
            {
                mesh->m_skininfo->Release();
                mesh->m_skininfo = NULL;
            }
        }

        
// load materials or create a default one if none
        if(mesh->m_num_materials == 0)
        {
            
// create a default one
            mesh->m_materials = new D3DMATERIAL9[1];
            mesh->m_textures  = 
new LPDIRECT3DTEXTURE9[1];

            ZeroMemory(mesh->m_materials, 
sizeof(D3DMATERIAL9));

            mesh->m_materials[0].Diffuse.r = 1.0;
            mesh->m_materials[0].Diffuse.g = 1.0;
            mesh->m_materials[0].Diffuse.b = 1.0;
            mesh->m_materials[0].Diffuse.a = 1.0;
            mesh->m_materials[0].Ambient   = mesh->m_materials[0].Diffuse;
            mesh->m_materials[0].Specular  = mesh->m_materials[0].Diffuse;

            mesh->m_textures[0] = NULL;

            mesh->m_num_materials = 1;
        }
        
else
        {
            
// load the materials
            materials = (D3DXMATERIAL*) material_buffer->GetBufferPointer();

            mesh->m_materials = 
new D3DMATERIAL9[mesh->m_num_materials];
            mesh->m_textures  = 
new LPDIRECT3DTEXTURE9[mesh->m_num_materials];

            
// set materials and textures for mesh
            for(DWORD i = 0; i < mesh->m_num_materials; i++)
            {
                mesh->m_materials[i] = materials[i].MatD3D;
                mesh->m_materials[i].Ambient = mesh->m_materials[i].Diffuse;

                
// build a texture path and load it
                if(FAILED(D3DXCreateTextureFromFile(g_d3d_device, materials[i].pTextureFilename, &mesh->m_textures[i])))
                    mesh->m_textures[i] = NULL;
            }
        }

        Release_Com(material_buffer);

        
// set mesh to parent frame
        parent_frame->m_mesh = mesh;
    }   
// end if(type == TID_D3DRMMesh)
    else if(type == TID_D3DRMAnimationSet || type == TID_D3DRMAnimation || type == TID_D3DRMAnimationKey)
    {
        
// skip animation sets and animations
        delete[] name;
        
return;
    }

    
// release name buffer
    delete[] name;

    SIZE_T num_child;
    xfile_data->GetChildren(&num_child);

    
// scan for embedded templates
    for(SIZE_T i = 0; i < num_child; i++)
    {
        xfile_data->GetChild(i, &sub_xfile_data);

        
// process embedded xfile data, recursive call.
        Parse_XFile_Data(sub_xfile_data, sub_frame);

        Release_Com(sub_xfile_data);
    }
}

//--------------------------------------------------------------------------------
// Parse x file, and return root frame.
//--------------------------------------------------------------------------------
FRAME* Parse_XFile(char* filename)
{
    ID3DXFile* xfile = NULL;
    ID3DXFileEnumObject* xfile_enum = NULL;
    ID3DXFileData* xfile_data = NULL;

    
// create the file object
    if(FAILED(D3DXFileCreate(&xfile)))
        
return NULL;

    
// register the templates
    if(FAILED(xfile->RegisterTemplates((LPVOID) D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES)))
    {
        xfile->Release();
        
return NULL;
    }

    
// create an enumerator object that will read a .x file
    if(FAILED(xfile->CreateEnumObject((LPVOID) filename, DXFILELOAD_FROMFILE, &xfile_enum)))
    {
        xfile->Release();
        
return NULL;
    }

    
// allocate a frame that becomes root
    FRAME* frame = new FRAME();

    SIZE_T num_child;

    
// retrieve the number of children in this file data object
    xfile_enum->GetChildren(&num_child);

    
// loop through all objects looking for the frames and meshes
    for(SIZE_T i = 0; i < num_child; i++)
    {
        
// retrieves a child object in this file data object
        if(FAILED(xfile_enum->GetChild(i, &xfile_data)))
            
return NULL;

        
// parse xfile data
        Parse_XFile_Data(xfile_data, frame);

        Release_Com(xfile_data);
    }

    
// release xfile enumerator object and xfile object
    Release_Com(xfile_enum);
    Release_Com(xfile);

    
// return root frame
    return frame;
}

//--------------------------------------------------------------------------------
// Initialize d3d, d3d device, vertex buffer; set render state for d3d;
// set perspective matrix and view matrix, load xfile.
//--------------------------------------------------------------------------------
BOOL Do_Init()
{
    D3DPRESENT_PARAMETERS present_param;
    D3DDISPLAYMODE  display_mode;
    D3DXMATRIX mat_proj, mat_view;

    
// do a windowed mode initialization of Direct3D
    if((g_d3d = Direct3DCreate9(D3D_SDK_VERSION)) == NULL)
        
return FALSE;

    
// retrieves the current display mode of the adapter
    if(FAILED(g_d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &display_mode)))
        
return FALSE;

    ZeroMemory(&present_param, 
sizeof(present_param));

    
// initialize d3d presentation parameter
    present_param.Windowed               = TRUE;
    present_param.SwapEffect             = D3DSWAPEFFECT_DISCARD;
    present_param.BackBufferFormat       = display_mode.Format;
    present_param.EnableAutoDepthStencil = TRUE;
    present_param.AutoDepthStencilFormat = D3DFMT_D16;

    
// creates a device to represent the display adapter
    if(FAILED(g_d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hwnd,
                                  D3DCREATE_SOFTWARE_VERTEXPROCESSING, &present_param, &g_d3d_device)))
        
return FALSE;     

    
// set render state

    // disable d3d lighting
    g_d3d_device->SetRenderState(D3DRS_LIGHTING, FALSE);
    
// enable z-buffer
    g_d3d_device->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);

    
// create and set the projection matrix

    // builds a left-handed perspective projection matrix based on a field of view
    D3DXMatrixPerspectiveFovLH(&mat_proj, D3DX_PI/4.0, 1.33333, 1.0, 1000.0);

    
// sets a single device transformation-related state
    g_d3d_device->SetTransform(D3DTS_PROJECTION, &mat_proj);

    
// create and set the view matrix
    D3DXMatrixLookAtLH(&mat_view, 
                       &D3DXVECTOR3(0.0, 50.0, -150.0),
                       &D3DXVECTOR3(0.0, 50.0,  0.0), 
                       &D3DXVECTOR3(0.0, 1.0,   0.0));

    g_d3d_device->SetTransform(D3DTS_VIEW, &mat_view);

    
// load a skinned mesh from an .X file
    g_parent_frame = Parse_XFile("warrior.x");

    
return TRUE;
}

//--------------------------------------------------------------------------------
// Release all d3d resource.
//--------------------------------------------------------------------------------
BOOL Do_Shutdown()
{
    delete g_parent_frame;

    Safe_Release(g_d3d_device);
    Safe_Release(g_d3d);

    
return TRUE;
}

//--------------------------------------------------------------------------------
// Draw current frame, recursive function.
//--------------------------------------------------------------------------------
void Draw_Frame(FRAME* frame)
{
    MESH* mesh;
    D3DXMATRIX* matrices = NULL;
    ID3DXMesh* mesh_to_draw;

    
// return if no frame
    if(frame == NULL)
        
return;

    
// draw meshes if any in frame
    if((mesh = frame->m_mesh) != NULL)
    {
        
// setup pointer to mesh to draw
        mesh_to_draw = mesh->m_mesh;

        
// generate mesh from skinned mesh to draw with
        if(mesh->m_skinmesh != NULL && mesh->m_skininfo != NULL)
        {
            DWORD num_bones = mesh->m_skininfo->GetNumBones();

            
// allocate an array of matrices to orient bones
            matrices = new D3DXMATRIX[num_bones];

            
// set all bones orientation to identity
            for(DWORD i = 0; i < num_bones; i++)
                D3DXMatrixIdentity(&matrices[i]);

            
// lock source and destination vertex buffers

            
void* source = NULL;
            
void* dest = NULL;

            
// locks a vertex buffer and obtains a pointer to the vertex buffer memory
            mesh->m_mesh->LockVertexBuffer(0, &source);
            mesh->m_skinmesh->LockVertexBuffer(0, &dest);

            
// update skinned mesh, applies software skinning to the target vertices based on the current matrices.
            mesh->m_skininfo->UpdateSkinnedMesh(matrices, NULL, source, dest);

            
// unlock buffers
            mesh->m_skinmesh->UnlockVertexBuffer();
            mesh->m_mesh->UnlockVertexBuffer();

            
// point to skin mesh to draw
            mesh_to_draw = mesh->m_skinmesh;
        }

        
// render the mesh
        for(DWORD i = 0; i < mesh->m_num_materials; i++)
        {
            
// set the materials properties for the device
            g_d3d_device->SetMaterial(&mesh->m_materials[i]);

            
// assigns a texture to a stage for a device
            g_d3d_device->SetTexture(0, mesh->m_textures[i]);

            
// draw a subset of a mesh
            mesh_to_draw->DrawSubset(i);
        }

        
// free array of matrices
        delete[] matrices;
        matrices = NULL;
    }

    
// draw child frames, recursively call.
    Draw_Frame(frame->m_child);
}

//--------------------------------------------------------------------------------
// Render a frame.
//--------------------------------------------------------------------------------
BOOL Do_Frame()
{
    D3DXMATRIX mat_world;

    
// clear device back buffer
    g_d3d_device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_RGBA(0, 64, 128, 255), 1.0f, 0);

    
// Begin scene
    if(SUCCEEDED(g_d3d_device->BeginScene()))
    {
        
// create and set the world transformation matrix
        // rotate object along y-axis
        D3DXMatrixRotationY(&mat_world, (float) (timeGetTime() / 1000.0));
        
        g_d3d_device->SetTransform(D3DTS_WORLD, &mat_world);              

        
// draw frames
        Draw_Frame(g_parent_frame);

        
// end the scene
        g_d3d_device->EndScene();
    }

    
// present the contents of the next buffer in the sequence of back buffers owned by the device
    g_d3d_device->Present(NULL, NULL, NULL, NULL);

    
// release texture
    g_d3d_device->SetTexture(0, NULL);

    
return TRUE;
}

//--------------------------------------------------------------------------------
// Main function, routine entry.
//--------------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE inst, HINSTANCE, LPSTR cmd_line, int cmd_show)
{
    WNDCLASSEX  win_class;
    MSG         msg;

    g_inst = inst;

    
// create window class and register it
    win_class.cbSize        = sizeof(win_class);
    win_class.style         = CS_CLASSDC;
    win_class.lpfnWndProc   = Window_Proc;
    win_class.cbClsExtra    = 0;
    win_class.cbWndExtra    = 0;
    win_class.hInstance     = inst;
    win_class.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    win_class.hCursor       = LoadCursor(NULL, IDC_ARROW);
    win_class.hbrBackground = NULL;
    win_class.lpszMenuName  = NULL;
    win_class.lpszClassName = g_class_name;
    win_class.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

    
if(! RegisterClassEx(&win_class))
        
return FALSE;

    
// create the main window
    g_hwnd = CreateWindow(g_class_name, g_caption, WS_CAPTION | WS_SYSMENU, 0, 0,
                          WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL, inst, NULL);

    
if(g_hwnd == NULL)
        
return FALSE;

    ShowWindow(g_hwnd, SW_NORMAL);
    UpdateWindow(g_hwnd);

    
// initialize game
    if(Do_Init() == FALSE)
        
return FALSE;

    
// start message pump, waiting for signal to quit.
    ZeroMemory(&msg, sizeof(MSG));

    
while(msg.message != WM_QUIT)
    {
        
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        
        
// draw a frame
        if(Do_Frame() == FALSE)
            
break;
    }

    
// run shutdown function
    Do_Shutdown();

    UnregisterClass(g_class_name, inst);
    
    
return (int) msg.wParam;
}


运行效果:


 

posted on 2007-07-22 23:27 lovedday 阅读(4962) 评论(2)  编辑 收藏 引用 所属分类: ■ DirectX 9 Program

评论

# re: D3D中网格模型的运用 2007-09-25 20:15 niyongfu

请问你用的directx sdk是那一个版本啊~~
我的和这个程序不兼容啊  回复  更多评论   

# re: D3D中网格模型的运用 2007-09-25 22:28 lovedday

Microsoft DirectX SDK (April 2007)

到这个网页给出的链接下载:

http://www.cppblog.com/lovedday/archive/2007/05/15/24156.html  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


公告

导航

统计

常用链接

随笔分类(178)

3D游戏编程相关链接

搜索

最新评论