在DirectX9.0中使用Mesh(2)

    技术2022-05-11  56

    本章介绍D3DX库提供的与Mesh有关的接口、结构、函数。通过本章的学习,将能够加载复杂的3D模型,能够控制Mesh对象的精细程度。本章要达到的目标:

    l          学习加载.x文件

    l          理解使用渐进MeshProgressive Mesh)的好处和学习如何使用渐进Mesh接口ID3DXPMesh。将原文中的Progressive Mesh翻译为渐进网格,不知是否恰当

    l          学习边界范围(Bounding Volume),以及如何使用D3DX函数创建边界范围

    11.1.    关于ID3DXBuffer

    这个接口贯穿整个D3DX库,需要对该接口有大体上的认识。ID3DXBufferD3DX用来管理连续内存块的结构,他只有两个方法:

    l          LPVOID GetBufferPointer(); --返回数据块的首地址

    l          DWORD GetBufferSize(); --返回缓冲区的大小,以字节为单位

    例如,D3DXLoadMeshFromX函数就使用ID3DXBuffer返回Mesh对象的邻接信息。因邻接信息是DWORD数组,所以需要进行类型转换。如:

    DWORD* info =(DWORD*)adjacencyInfo->GetBufferPointer();

    D3DXMATERIAL* mtrls = (D3DXMATERIAL*)mtrlBuffer->GetBufferPointer();

    又因为ID3DXBuffer是一个COM对象,所以,用完后,需要进行释放。

    adjacencyInfo->Release();

    mtrlBuffer->Release();

    也可以使用如下函数创建一个空的ID3DXBuffer对象:

    HRESULT WINAPI D3DXCreateBuffer(

        DWORD NumBytes,

        LPD3DXBUFFER *ppBuffer

    );

    其中参数的含义显而易见。例如,创建一个包含四个整型数的缓冲区:

    ID3DXBuffer* buffer = 0;

    D3DXCreateBuffer( 4 * sizeof(int), &buffer );

    11.2.    X文件

    使用D3DXCreate*函数,可以创建一些简单的几何体,如球、圆柱、立方体等。如果想通过手动设定顶点的方式创建较复杂的3D对象,你会发现这太麻烦了,简直无法做到!现在,可以使用很多种3D建模工具软件来完成这项枯燥工作,如3DS MAXLightWave 3DMaya等。使用这样的建模工具,可以在可视化的、交互的环境中设计复杂、逼真的模型,而且还有丰富的工具可用,使整个建模过程相当简单。这里的简单是相对于在“程序中手动设定顶点的方式建模”,实际上,这些建模工具还是相当复杂的,想得心应手的使用,可不是一朝一夕之功。

    这些建模工具可以将所建立的模型的数据(几何信息,材质,动画等)保存到文件。我们需要从文件中分析提取需要的数据,然后应用到自己的3D程序中。有一种常用的文件格式,XFile,其扩展名为.x,较为简单,是Direct3D定义的文件格式,D3DX库提供了完整的支持,可满足一般的需要。

    11.2.1. 加载一个.x文件

    使用下面的函数加载存储在.x文件中的Mesh数据。它创建一个ID3DXMesh对象,然后从.x文件中读取Mesh的几何信息。

    HRESULT WINAPI D3DXLoadMeshFromX(

        LPCTSTR pFilename,

        DWORD Options,

        LPDIRECT3DDEVICE9 pD3DDevice,

        LPD3DXBUFFER *ppAdjacency,

        LPD3DXBUFFER *ppMaterials,

        LPD3DXBUFFER *ppEffectInstances,

        DWORD *pNumMaterials,

        LPD3DXMESH *ppMesh

    );

    l          pFileName –.x文件的文件名

    l          Options –创建Mesh的标志。详情可参考SDK文档中的D3DXMESH枚举类型。常用的几个标志如下:

    n          D3DXMESH_32BIT –使用32位的顶点索引,默认为16

    n          D3DXMESH_MANAGED –使用受控的内存缓冲池

    n          D3DXMESH_WRITEONLY –缓冲区只可执行写操作

    n          D3DXMESH_DYNAMIC –使用动态内存缓冲池

    l          pD3DDevice –D3D设备指针

    l          ppAdjacency –使用ID3DXBuffer返回Mesh的邻接信息,这是一个DWORD数组

    l          ppMaterials –使用ID3DXBuffer返回Mesh的材质数据,这是一个D3DXMATERIAL类型数组

    l          ppEffectInstances –使用ID3DXBuffer返回一个D3DXEFFECTINSTANCE结构数组

    l          pNumMaterials –返回Mesh对象的材质数量,也就是通过ppMaterials返回的D3DXMATERIAL数组的元素数

    l          ppMesh –返回ID3DXMesh对象

    11.2.2. XFile材质

    函数D3DXLoadMeshFromX的第七个参数返回Mesh对象的材质数量,第五个参数是D3DXMATERIAL的数组,包含Mesh的材质数据。D3DXMATERIAL结构的定义如下:

    typedef struct D3DXMATERIAL {

        D3DMATERIAL9 MatD3D;

        LPSTR pTextureFilename;

    } D3DXMATERIAL;

    这个结构很简单,包含一个D3DMATERIAL9结构和一个以0字符结束的字符串的指针,表示相关联的纹理文件。. x文件并不包含纹理数据,只包含纹理文件的文件名。使用该函数加载.x文件后,还需要根据纹理文件的文件名手动加载纹理。

    函数D3DXLoadMeshFromX返回的D3DXMATERIAL数组正好与Mesh对象的子集相对应。也就是说,第I个子集的材质纹理信息就存储在ppMaterials[I]中。

    11.2.3. X文件的应用实例

    这个例子相当的简单,它加载bigship1.x文件,这是DirectX SDK中的一个文件。这里只列出代码的主要框架。

    ID3DXMesh* Mesh = 0;

    vector<D3DMATERIAL9> Mtrls(0);

    vector<IDirect3DTexture9*> Textures(0);

     

    bool Setup()

    {

        HRESULT hr = 0;

        //

        // Load the XFile data.

        //

        ID3DXBuffer* adjBuffer = 0;

        ID3DXBuffer* mtrlBuffer = 0;

        DWORD numMtrls = 0;

        hr = D3DXLoadMeshFromX(

            "bigship1.x",

            D3DXMESH_MANAGED,

            Device,

            &adjBuffer,

            &mtrlBuffer,

            0,

            &numMtrls,

            &Mesh);

        if(FAILED(hr))

        {

            ::MessageBox(0, "D3DXLoadMeshFromX() - FAILED", 0, 0);

            return false;

        }

        //

        // Extract the materials, load textures.

        //

        if( mtrlBuffer != 0 && numMtrls != 0 )

        {

            D3DXMATERIAL* mtrls=(D3DXMATERIAL*)mtrlBuffer->GetBufferPointer();

            for(int i = 0; i < numMtrls; i++)

            {

                // the MatD3D property doesn't have an ambient value

                // set when it’s loaded, so set it now:

                mtrls[i].MatD3D.Ambient = mtrls[i].MatD3D.Diffuse;

                // save the ith material

                Mtrls.push_back( mtrls[i].MatD3D );

                // check if the ith material has an associative

                // texture

                if( mtrls[i].pTextureFilename != 0 )

                {

                    // yes, load the texture for the ith subset

                    IDirect3DTexture9* tex = 0;

                    D3DXCreateTextureFromFile(

                        Device,

                        mtrls[i].pTextureFilename,

                        &tex);

                    // save the loaded texture

                    Textures.push_back( tex );

                }

                else

                {

                    // no texture for the ith subset

                    Textures.push_back( 0 );

                }

            }

        }

        Release<ID3DXBuffer*>(mtrlBuffer); // done w/ buffer

        .

        . // Snipped irrelevant code to this chapter (e.g., setting up lights,

        . // view and projection matrices, etc.)

        .

        return true;

    }

    最后,渲染Mesh对象:

    Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,0xffffffff, 1.0f, 0);

    Device->BeginScene();

    for(int i = 0; i < Mtrls.size(); i++)

    {

        Device->SetMaterial( &Mtrls[i] );

        Device->SetTexture(0, Textures[i]);

        Mesh->DrawSubset(i);

    }

    Device->EndScene();

    Device->Present(0, 0, 0, 0);

    11.2.4. 创建顶点的法向量

    有时.x文件不包含顶点的法向量,这时,如果使用光照,则需要手动计算顶点的法向量。对于接口ID3DXMesh和其父接口ID3DXBaseMesh,可以使用如下函数计算顶点的法向量:

    HRESULT WINAPI D3DXComputeNormals(

        LPD3DXBASEMESH pMesh,

        const DWORD *pAdjacency

    );

    该函数将使用法向量的平均值作为顶点的法向量。如果提供了Mesh对象的邻接信息,则重复的顶点会被忽略;如果没有邻接信息,重复的顶点也会被重复计算。另外一点更加重要,需要计算法向量的Mesh对象的顶点格式必须包含D3DFVF_NORMAL标志。

    如果.x文件中没有法向量数据,通过D3DXLoadMeshFromX函数创建的ID3DXMesh对象的顶点格式就不包含D3DFVF_NORMAL标志。因此,在计算法向量之前,必须使用D3DFVF_NORMAL标志复制Mesh对象。

    // does the mesh have a D3DFVF_NORMAL in its vertex format?

    if ( !(pMesh->GetFVF() & D3DFVF_NORMAL) )

    {

        // no, so clone a new mesh and add D3DFVF_NORMAL to its format:

        ID3DXMesh* pTempMesh = 0;

        pMesh->CloneMeshFVF(

            D3DXMESH_MANAGED,

            pMesh->GetFVF() | D3DFVF_NORMAL, // add it here

            Device,

            &pTempMesh );

        // compute the normals:

        D3DXComputeNormals( pTempMesh, 0 );

        pMesh->Release(); // get rid of the old mesh

        pMesh = pTempMesh; // save the new mesh with normals

    }

    11.3.    渐进模型(Progressive Mesh

    渐进MeshID3DXPMesh接口的对象,可以简化边缩减转换(Edge Collapse Transformations (ECT))。每次ECT都回减少一个顶点和一两个面。由于ECT过程是可逆的(他的逆过程叫顶点分裂),所以,可以通过逆过程将Mesh恢复到原始状态。当然,我们也无法得到比原始状态更精细的Mesh对象,最多只能将其恢复到原始状态。

    Progressive Mesh和纹理中的mipmap十分相似。在较小的和远距离的对象上使用高分辨率的纹理纯粹是浪费,因为纹理的细节根本就表现不出来。对于Mesh对象也是一样,较小的距离较远的Mesh不需要太多的三角形,多了纯粹是浪费。所以,在渲染时,实在没有必要在这些根本表现不出来的地方浪费时间。

    一种方法是,根据Mesh对象距离视点的距离调整其精细水准(LODLevel Of Detail)。当距离增加时,可降低LOD;反之,则增加LOD

    这里只讨论ID3DXPMesh接口的用法,不讨论其实现细节。如果你感兴趣,可参考其它资料。

    11.3.1. 生成一个渐进Mesh

    使用下面的函数创建ID3DXPMesh对象:

    HRESULT WINAPI D3DXGeneratePMesh(

        LPD3DXMESH pMesh,

        const DWORD *pAdjacency,

        const D3DXATTRIBUTEWEIGHTS *pVertexAttributeWeights,

        const FLOAT *pVertexWeights,

        DWORD MinValue,

        DWORD Options,

        LPD3DXPMESH *ppPMesh

    );

    l          pMesh –输入的普通的Mesh对象

    l          pAdjacency –Mesh对象的邻接信息,这是一个DWORD数组

    l          pVertexAttributeWeights –结构D3DXATTRIBUTEWEIGHTS的数组,元素个数为pMesh->GetNumVertices(),表示顶点的属性的权。在简化Mesh对象时,权值决定一个顶点被删除的可能性大小。该参数可以设为NULL,这时顶点使用默认的权值。

    l          pVertexWeights –顶点的权,是float数组,元素个数是pMesh->GetNumVertices(),用于决定顶点在简化时被删除的可能性的大小。该参数也可设为NULL,这时,顶点默认的权值为1.0f

    l          MinValue –在简化Mesh时,顶点或者三角形数的最小个数。该参数是必要的,而且与顶点权值和顶点属性权值有关系,最终也许达不到该数值。

    l          Options –只能取D3DXMESHSIMP枚举类型中的一个值:

    n          D3DXMESHSIMP_VERTEX –上一个参数MinValue指顶点数

    n          D3DXMESHSIMP_FACE –上一个参数MinValue指三角形数

    l          ppPMesh –返回生成的渐进Mesh

    11.3.2. 顶点的属性权

    typedef struct _D3DXATTRIBUTEWEIGHTS {

        FLOAT Position;

        FLOAT Boundary;

        FLOAT Normal;

        FLOAT Diffuse;

        FLOAT Specular;

        FLOAT Texcoord[8];

        FLOAT Tangent;

        FLOAT Binormal;

    } D3DXATTRIBUTEWEIGHTS, *LPD3DXATTRIBUTEWEIGHTS;

    通过这个结构,可以为顶点的每个属性指定一个权值,0.0表示属性没有权。权值越高,在简化时,越不易被删除。默认的权值如下:

    D3DXATTRIBUTEWEIGHTS AttributeWeights;

    AttributeWeights.Position = 1.0;

    AttributeWeights.Boundary = 1.0;

    AttributeWeights.Normal = 1.0;

    AttributeWeights.Diffuse = 0.0;

    AttributeWeights.Specular = 0.0;

    AttributeWeights.Tex[8] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};

    一般情况下,推荐使用默认的权值,除非你认为非常有必要使用不同的权值。

    11.3.3. ID3DXPMesh的方法

    接口ID3DXPMesh继承自ID3DXBaseMesh,下面介绍一些常用的方法。

    l          DWORD GetMaxFaces(VOID); --返回Mesh的最大三角形数

    l          DWORD GetMaxVertices(VOID); --返回Mesh的最大顶点数

    l          DWORD GetMinFaces(VOID); --返回Mesh的最少三角形数

    l          DWORD GetMinVertices(VOID); --返回Mesh的最少顶点数

    l          HRESULT SetNumFaces(DWORD Faces); --设置Mesh的三角形数。例如,假定Mesh现在有50个三角形,而想将其简化为30个三角形,则调用pmesh->SetNumFaces(30)。调整后的三角形数可能并不是我们设定的个数,因为PMesh的三角形数还有最大和最少的限制。

    l          HRESULT SetNumVertices(DWORD Vertices); --设置PMesh的顶点个数。例如,假设现在PMesh20个顶点,而为了增加其精细程度将顶点数增为40个,则只需调用pmesh->SetNumVertices(40)。与三角形数一样,最终的结果可能不是我们指定的数值,同样有最大最少个数的限制。

    l          HRESULT TrimByFaces(

        DWORD NewFacesMin,

        DWORD NewFacesMax,

        DWORD *rgiFaceRemap,

        DWORD *rgiVertRemap

    ); --该方法设定PMesh三角形数的最大最小值。新的最大最小值必须在当前的最大最小值之间,即必须在[GetMinFaces()GetMaxFaces()]内。同时,该方法还将返回三角形和顶点的重影射信息。

    l          HRESULT TrimByVertices(

        DWORD NewVerticesMin,

        DWORD NewVerticessMax,

        DWORD *rgiFaceRemap,

        DWORD *rgiVertRemap

    ); --该方法与上面的方法相似。

    11.3.4. 应用举例:Progressive Mesh

    这个例子与前面的XFile例子相似,只是其中使用ID3DXPMesh接口。

    与前例相似,我们使用如下的全局变量:

    ID3DXMesh* SourceMesh = 0;

    ID3DXPMesh* PMesh = 0; // progressive mesh

    vector<D3DMATERIAL9> Mtrls(0);

    vector<IDirect3DTexture9*> Textures(0);

    在创建Progressive Mesh之前,需要使用ID3DXMesh接口加载.x文件:

    HRESULT hr = 0;

    // ...Load XFile data into SourceMesh snipped.

    //

    // ...Extracting materials and textures snipped.

     

    //

    // Generate the progressive mesh.

    //

    hr = D3DXGeneratePMesh(

        SourceMesh,

        (DWORD*)adjBuffer->GetBufferPointer(), // adjacency

        0, // default vertex attribute weights

        0, // default vertex weights

        1, // simplify as low as possible

        D3DXMESHSIMP_FACE, // simplify by face count

        &PMesh);

    Release<ID3DXMesh*>(SourceMesh); // done w/ source mesh

    Release<ID3DXBuffer*>(adjBuffer); // done w/ buffer

    if(FAILED(hr))

    {

        ::MessageBox(0, "D3DXGeneratePMesh() - FAILED", 0, 0);

        return false;

    }

    通常,因为顶点和顶点属性权值的缘故,很难将Mesh简化到只有一个三角形的程度,但是,如果指定将Mesh简化到一个三角形的程度,则可以将Mesh简化到解析度最低的程度。

    现在,渐进Mesh已经生成了,但是,如果直接渲染,则Mesh的解析度此时最低。如果想渲染全解析度的PMesh,首先需要设置其三角形数:

    // set to original (full) detail

    DWORD maxFaces = PMesh->GetMaxFaces();

    PMesh->SetNumFaces(maxFaces);

    在渲染PMesh时,我们使用键盘输入控制其解析度:A键将增加解析度,S键减小解析度。

    // Get the current number of faces the pmesh has.

    int numFaces = PMesh->GetNumFaces();

    // Add a face, note the SetNumFaces() will automatically

    // clamp the specified value if it goes out of bounds.

    if( ::GetAsyncKeyState('A') & 0x8000f )

    {

        // Sometimes we must add more than one face to invert

        // an edge collapse transformation because of the internal

        // implementation details of the ID3DXPMesh interface. In

        // other words, adding one face may possibly result in a

        // mesh with the same number of faces as before. Thus to

        // increase the face count we may sometimes have to add

        // two faces at once.

        PMesh->SetNumFaces(numFaces + 1);

        if(PMesh->GetNumFaces() == numFaces)

            PMesh->SetNumFaces(numFaces + 2);

    }

    // Remove a face, note the SetNumFaces() will automatically

    // clamp the specified value if it goes out of bounds.

    if(::GetAsyncKeyState('S') & 0x8000f)

        PMesh->SetNumFaces(numFaces - 1);

    上面的方法直截了当,只是增加三角形数时,有时需要增加两个来满足ECT的需要。

    最后,使用和渲染ID3DXMesh同样的方法渲染ID3DXPMesh。另外,为了更加直观的观察PMesh的三角形数的变化情况,使用黄色材质在线框模式(Wireframe Mode)下渲染Mesh的三角形。

    Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,0xffffffff, 1.0f, 0);

    Device->BeginScene();

    for(int i = 0; i < Mtrls.size(); i++)

    {

        Device->SetMaterial( &Mtrls[i] );

        Device->SetTexture(0, Textures[i]);

        PMesh->DrawSubset(i);

        // draw wireframe outline

        Device->SetMaterial(&d3d::YELLOW_MTRL);

        Device->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);

        PMesh->DrawSubset(i);

        Device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);

    }

    Device->EndScene();

    Device->Present(0, 0, 0, 0);

    11.4.    对象的边界范围

    有时,需要计算Mesh对象的边界范围,常用的有两种类型:立方体和球。也有使用其它方法的,如圆柱体、椭球体、菱形体、太空舱形等。这里,我们只讨论立方体和球体两种边界形式。

    边界盒或边界球常用来加速多物体间的可视范围测试、碰撞检测等。如果一个Mesh的边界盒/球不可见,就可认为Mesh也不可见。检测边界盒/球是否可见比检测Mesh中所有的三角形是否可见要方便得多。在碰撞检测中,如果一枚导弹点火起飞,我们需要检测他是否撞到了同一个场景中的目标。由于这些对象全是大量的三角形构成,我们可以依次检测每个对象的每个三角形,检测导弹(可以使用数学模型中的射线)是否撞到了这些三角形。这个方法需要进行多次的射线/三角形交点的运算。较好的方法是使用边界盒或边界球,计算射线与场景中的每个对象的边界盒/边界球的交点。如果射线与对象的边界范围相交,可以认为该对象被击中了。这是一个公平的近似方法,如果需要更高的精度,可以用边界范围法先去除那些明显不会相撞的对象,然后用更精确地方法检测很可能相撞的对象。如果边界范围检测发现相撞,则该对象就很有可能相撞。

    D3DX库提供了计算Mesh对象边界盒/球的函数。这些函数使用顶点数组作为输入计算边界盒/球,可以使用各种顶点格式:

    HRESULT WINAPI D3DXComputeBoundingSphere(

        const D3DXVECTOR3 *pFirstPosition,

        DWORD NumVertices,

        DWORD dwStride,

        D3DXVECTOR3 *pCenter,

        FLOAT *pRadius

    );

    l          pFirstPosition –顶点数组的地址,顶点的第一个向量需要是顶点的位置坐标

    l          NumVertices –顶点的数目

    l          dwStride –顶点大小,以字节为单位。因顶点中有很多附加数据,如法向量、纹理坐标等,计算边界范围不需要这些数据,所以,需要知道跳过多少数据才能找到下一个顶点的坐标。

    l          pCenter –返回边界范围的中心

    l          pRadius –返回边界球的半径

    HRESULT WINAPI D3DXComputeBoundingBox(

        const D3DXVECTOR3 *pFirstPosition,

        DWORD NumVertices,

        DWORD dwStride,

        D3DXVECTOR3 *pMin,

        D3DXVECTOR3 *pMax

    );

    前三个参数与计算边界球的函数相同;后两个参数返回边界盒的最小和最大点。

    11.4.1. 边界检测类型

    为了使边界检测易于使用,我们实现几个辅助的数据结构:

    struct BoundingBox

    {

        BoundingBox();

        bool isPointInside(D3DXVECTOR3& p);

        D3DXVECTOR3 _min;

        D3DXVECTOR3 _max;

    };

    struct BoundingSphere

    {

        BoundingSphere();

        D3DXVECTOR3 _center;

        float _radius;

    };

     

    BoundingBox::BoundingBox()

    {

        // infinite small bounding box

        _min.x = FLT_MAX;

        _min.y = FLT_MAX;

        _min.z = FLT_MAX;

        _max.x = -FLT_MAX;

        _max.y = -FLT_MAX;

        _max.z = -FLT_MAX;

    }

    bool BoundingBox::isPointInside(D3DXVECTOR3& p)

    {

        // is the point inside the bounding box?

        if (p.x >= _min.x && p.y >= _min.y && p.z >= _min.z &&

            p.x <= _max.x && p.y <= _max.y && p.z <= _max.z)

        {

            return true;

        }

        else

        {

            return false;

        }

    }

    BoundingSphere::BoundingSphere()

    {

        _radius = 0.0f;

    }

    11.4.2. 边界范围应用举例

    该例子演示D3DXComputeBoundingSphereD3DXComputeBoundingBox函数的用法。程序首先加载一个.x文件,然后计算Mesh的边界盒/球。代码中创建两个ID3DXMesh对象,分别使用边界盒和边界球。最后,分别渲染他们。

    这个例子很简单,这里只给出有关边界范围的代码:

    bool ComputeBoundingSphere(

        ID3DXMesh* mesh, // mesh to compute bounding sphere for

        BoundingSphere* sphere) // return bounding sphere

    {

        HRESULT hr = 0;

        BYTE* v = 0;

        mesh->LockVertexBuffer(0, (void**)&v);

        hr = D3DXComputeBoundingSphere(

            (D3DXVECTOR3*)v,

            mesh->GetNumVertices(),

            D3DXGetFVFVertexSize(mesh->GetFVF()),

            &sphere->_center,

            &sphere->_radius);

        mesh->UnlockVertexBuffer();

        if( FAILED(hr) )

            return false;

        return true;

    }

    bool ComputeBoundingBox(

        ID3DXMesh* mesh, // mesh to compute bounding box for

        BoundingBox* box) // return bounding box

    {

        HRESULT hr = 0;

        BYTE* v = 0;

        mesh->LockVertexBuffer(0, (void**)&v);

        hr = D3DXComputeBoundingBox(

            (D3DXVECTOR3*)v,

            mesh->GetNumVertices(),

            D3DXGetFVFVertexSize(mesh->GetFVF()),

            &box->_min,

            &box->_max);

        mesh->UnlockVertexBuffer();

        if( FAILED(hr) )

            return false;

        return true;

    }

    类型转换(D3DXVECTOR3*)v假定顶点坐标在顶点结构的开头位置,一般都是如此。

    11.5.    总结

    l          现在,我们可以用3D建模软件导出的.x文件构建复杂的Mesh对象。使用D3DXLoadMeshFromX函数取得ID3DXMesh对象,就可以在自己的应用程序中自由使用了。

    l          使用ID3DXPMesh接口表示的渐进Mesh,可以控制其精细程度。可以根据对象在场景中的突出程度调整PMesh的精细程度。

    l          我们可以使用D3DXComputeBoundingSphereD3DXComputeBoundingBox函数计算Mesh对象的边界。边界范围很有用,其接近对象真实的边界,可加速碰撞检测等的计算。


    最新回复(0)