DirectX游戏编程入门——第二一部分(游戏编程工具箱) ——渲染3D模型文件

DirectX游戏编程入门——第二部分(游戏编程工具箱) ——渲染3D模型文件

        本系列文章由 net_assassin 整理编写,转载请注明出处。

http://blog.csdn.net/net_assassin/article/category/1100363


作者:net_assassin    邮箱: net_assassin@hotmail.com    期待着与志同道合的朋友们相互交流



上一章我们学习了使用原始的顶点缓冲区从头创建及渲染带有纹理的3D立方体的方法。这是很好的学习体验,但手工编写代码能做出来的网格(由顶点组成的3D对象)也就只能像立方体这样的东西。

本章要前讲:

  1. 运行时使用特殊的Direct3D函数(这个函数生成诸如立方体、球体和圆柱体这样的网格形状)来创建后援3D网格的方法。
  2. 从 .X 文件中将网格装载到内存并使用纹理来渲染它的方法。

创建及渲染后援网格

在程序运行时用算法创建的,将它们称为后援网格是因为它们内建于Direct3D 中而且在任何时候创建。实际上,在制作某些类型的需要快速生成对象的游戏时后援网格极为有用。例如,卷动发射器中的子弹。

创建后援网格

Direct3D有许多可以通过一个返回ID3DXMesh对象的函数来动态创建的后援网格对象。
以下是我们可以在运行时使用Direct3D创建的一些网格:
  • 立方体
  • 球体
  • 圆柱体
  • 面包圈(圆环)
  • 茶壶
在Direct3D中有分别用于创建这些后援网格的函数。调用这些函数时,我们将传递一个指向ID3DXMesh 对象的指针。

  1. 创建立方体                                                                                                                                                                                      
    D3DXCreateBox(d3ddev,1.0f,1.0f,1.0f,&mesh,NULL);
  2. 创建球体                                                                                                                                                                                       
    D3DXCreateSphere(d3ddev,1.0f,20,20,&mesh,NULL);
  3. 创建圆柱体                                                                                                                                                                              
    D3DXCreateCylinder(d3ddev,1.0f,1.0f,2.0f,20,20,&mesh,NULL);
  4. 创建面包圈                                                                                                                                                                         
    D3DXCreateTorus(d3ddev,0.5f,1.0f,20,20,&mesh,NULL);
  5. 创建茶壶                                                                                                                                                                                          
    D3DXCreateTeapot(d3ddev,&mesh,NULL);

渲染后援网格


虽然每种后援网格都由各自的需要不同参数集的函数来创建,但所有的后援网格函数都将顶点数据填入同样的ID3DXMesh对象中。
这就意味着这些网格在穿件之后都会被以相同的方式来处理。
如果我们不关心材质或纹理,那么网格的渲染可以使用ID3DXMesh::DrawSubset 函数来实现。
mesh->DrawSubset(0);

装载并渲染模型文件


装载.X文件

Direct3D提供了一个从已装载的.X文件中创建网格的函数。于是,要将一个模型文件读入我们的游戏中,将非常简单。我们慢慢来,详细探究每一个步骤,最后给出一组可重用的函数。
  1. 定义新的MODEL 结构                                                                                                                                                                                                                                                                 首先,我们需要一个新的结构来处理要装载的模型文件:                                                                                                                                                                                                       
    struct MODEL
    {
         LPD3DXMESH                  mesh;
         D3DMATERIAL9*             materials;
         LPDIRECT3DTEXTURE9*  textures;
         DWORD                           material_count;
    }

  2. 装载网格                                                                                                                                                                                                                                                                                        装载模型文件的关键在于D3DXLoadMeshFromX函数:                                                                                                                                                                                                      
    HRESULT WINAPI D3DXLoadMeshFromX(
        LPCTSTR pFilename,
        DWORD   Options,
        LPDIRECT3DDEVICE9  pDevice,
        LPD3DXBUFFER  * ppAdjacency,
        LPD3DXBUFFER  * ppMaterials,
        LPD3DXBUFFER  * ppEffectInstances,
        DWORD  *pNumMaterials,
        LPD3DXMESH * ppMesh
    )

               材质缓冲区用于装载材质:

              LPD3DXBUFFER matbuffer;

               MODEL *model = (MODEL*)malloc ( sizeof ( MODEL ));

               result = D3DXLoadMeshFromX(

                         filename,

                         D3DXMESH_SYSTEMMEM,

                         d3ddev,

                         NULL,

                         &matbuffer,

                         NULL,

                         &model->material_count,

                         &model->mesh

                );

        3.  装载材质和纹理

               材质存储于材质缓冲区中,不过,在渲染模型之前需要将他们转换成Direct3D材质和纹理。

               以下是从材质缓冲区中将材质和纹理复制到各个材质和纹理数组中的方法:

              首先,创建数组:

                    D3DXMATERIAL * d3dxMaterials = (LPD3DXMATERIAL) matbuffer->GetBufferPointer();

                    model->materials = new D3DMATERIAL9[model->material_count];

                    model->textures    = new LPDIRECT3DTEXTURE9[model->material_count];

              下一步,迭代材质并将它们从材质缓冲区中取出:

                    

//create the materials and textures
    for(DWORD i=0; i<model->material_count; i++)
    {
        //grab the material
        model->materials[i] = d3dxMaterials[i].MatD3D;

        //set ambient color for material 
        model->materials[i].Ambient = model->materials[i].Diffuse;

        model->textures[i] = NULL;
        if (d3dxMaterials[i].pTextureFilename != NULL) 
        {
            string filename = d3dxMaterials[i].pTextureFilename;
            if( FindFile(&filename) )
            {
                result = D3DXCreateTextureFromFile(d3ddev, filename.c_str(), &model->textures[i]);
                if (result != D3D_OK) 
                {
                    MessageBox(NULL, "Could not find texture file", "Error", MB_OK);
                    return false;
                }
            }
        }
    }

在网格中遇到一个文件名时,为了定位纹理文件,需要三个函数一起工作:

 FindFile 

 DoesFileExist

 SplitPath


渲染完整的模型

在装在了模型之后,绘制它就是小菜一碟了。
首先设置材质和纹理,然后调用DrawPrimitive函数来显示多边形(面)。
//any materials in this mesh?
    if (model->material_count == 0) 
    {
        model->mesh->DrawSubset(0);
    }
    else {
        //draw each mesh subset
        for( DWORD i=0; i < model->material_count; i++ )
        {
            // Set the material and texture for this subset
            d3ddev->SetMaterial( &model->materials[i] );

            if (model->textures[i]) 
            {
                if (model->textures[i]->GetType() == D3DRTYPE_TEXTURE) 
                {
                    D3DSURFACE_DESC desc;
                    model->textures[i]->GetLevelDesc(0, &desc);
                    if (desc.Width > 0) 
                    {
                        d3ddev->SetTexture( 0, model->textures[i] );
                    }
                }
            }
            
            // Draw the mesh subset
            model->mesh->DrawSubset( i );
        }
    }

从内存中删除一个模型


在使用完一个MODEL对象之后,需要将其资源释放,否则会给程序带来内存泄露。游戏中所用的资源通常在游戏结束时释放。
//remove materials from memory
    if( model->materials != NULL ) 
        delete[] model->materials;

    //remove textures from memory
    if (model->textures != NULL)
    {
        for( DWORD i = 0; i < model->material_count; i++)
        {
            if (model->textures[i] != NULL)
                model->textures[i]->Release();
        }
        delete[] model->textures;
    }
    
    //remove mesh from memory
    if (model->mesh != NULL)
        model->mesh->Release();

    //remove model struct from memory
    if (model != NULL)
        free(model);

Render_Mesh程序:
DirectX游戏编程入门——第二一部分(游戏编程工具箱) ——渲染3D模型文件

源代码地址:
http://download.csdn.net/detail/net_assassin/4477512