Direct3D轮回:基于.X文件的网格加载及渲染

DX9.0对.X文件提供了相当丰富的支持,包括高级骨骼动画的解析及渲染。

DX10之后,.X开始渐渐淡出人们的视野,取而代之的是各种自定义的网格数据文件。

虽然.X文件不再被广泛支持,但其数据定义仍具有相当的参考价值和意义~

本篇简单实现了.X网格的加载及渲染,意在服务于后续章节,感兴趣的朋友可以简单参考一下~

/* -------------------------------------

代码清单:SimpleXMesh.h
来自:
http://www.cnblogs.com/kenkao

-------------------------------------
*/

#include 
" D3DInit.h "

#pragma  once

class  CSimpleXMesh
{
public :
    CSimpleXMesh(
void );
    
~ CSimpleXMesh( void );
public :
    
bool  LoadXMesh(TCHAR *  szXFileName);         //  加载.X网格
     void  DrawXMesh( const  D3DXVECTOR3 &  pos);     //  绘制.X网格
     void  Release();                             //  释放.X网格
private :
    ID3DXBuffer
*  m_pAdjacencyBuffer;            //  邻接三角形信息缓冲区
    ID3DXBuffer *  m_pMaterialBuffer;             //  材质缓冲区
    D3DMATERIAL9  * m_pD3DMaterialArray;          //  材质数组
    IDirect3DTexture9  ** m_ppDirect3DTextureArray;   //  纹理数组
    DWORD m_dwMaterials;                        //  材质数
    ID3DXMesh *  m_pD3DXMesh;                     //  .X网格对象指针
};

 

Direct3D轮回:基于.X文件的网格加载及渲染 SimpleXMesh.cpp
/* -------------------------------------

代码清单:SimpleXMesh.cpp
来自:
http://www.cnblogs.com/kenkao

-------------------------------------
*/

#include 
" StdAfx.h "
#include 
" SimpleXMesh.h "
#include 
" D3DGame.h "

extern  IDirect3DDevice9  * g_pD3DDevice;

CSimpleXMesh::CSimpleXMesh(
void ):m_pAdjacencyBuffer(NULL),
                                 m_pMaterialBuffer(NULL),
                                 m_pD3DMaterialArray(NULL),
                                 m_ppDirect3DTextureArray(NULL),
                                 m_dwMaterials(
0 ),
                                 m_pD3DXMesh(NULL)
{

}

CSimpleXMesh::
~ CSimpleXMesh( void )
{

}

bool  CSimpleXMesh::LoadXMesh(TCHAR *  szXFileName){
    
//  加载X网格
     if (FAILED(D3DXLoadMeshFromX(
        szXFileName,                            
// .X文件名
        D3DXMESH_MANAGED,                        // 内存托管模式
        g_pD3DDevice,                            // Direct3D设备
         & m_pAdjacencyBuffer,                     // 邻接三角形信息缓冲区指针
         & m_pMaterialBuffer,                      // 材质缓冲区指针
         0 ,                                       // 特效缓冲区指针,由于没有用到特效,我们在这里置0即可
         & m_dwMaterials,                          // 材质数
         & m_pD3DXMesh                             // 得到的X网格
        ))){
            
return   false ;
    }
    
//  错误判断
     if (m_pMaterialBuffer == NULL  ||  m_dwMaterials == 0 )  
        
return   false ;
    
//  获得材质缓冲区指针
    D3DXMATERIAL *  pD3DXMaterial = (D3DXMATERIAL * )m_pMaterialBuffer -> GetBufferPointer();
    
if (pD3DXMaterial != NULL){
        
//  初始化材质数组
        m_pD3DMaterialArray = new  D3DMATERIAL9[m_dwMaterials];
        
//  初始化纹理数组
        m_ppDirect3DTextureArray = new  IDirect3DTexture9 * [m_dwMaterials];
        
//  遍历材质缓冲区,填充材质及纹理数组
         for (DWORD i = 0 ;i < m_dwMaterials;i ++ ){
            m_pD3DMaterialArray[i]
= pD3DXMaterial[i].MatD3D;
            
if (pD3DXMaterial[i].pTextureFilename != NULL)
            {
                
if (FAILED(D3DXCreateTextureFromFile(g_pD3DDevice,pD3DXMaterial[i].pTextureFilename, & m_ppDirect3DTextureArray[i]))){
                    m_ppDirect3DTextureArray[i]
= NULL;
                }
            }
            
else
            {
                m_ppDirect3DTextureArray[i]
= NULL;
            }
        }
    }
    
//  网格数据优化
    m_pD3DXMesh -> OptimizeInplace(
        D3DXMESHOPT_COMPACT  
|
        D3DXMESHOPT_ATTRSORT 
|
        D3DXMESHOPT_VERTEXCACHE,                           
// 优化模式,具体参看SDK
        (DWORD * )m_pAdjacencyBuffer -> GetBufferPointer(),     // 邻接三角形信息缓冲区指针
        NULL, NULL, NULL);                                  // 参看SDK

    
//  有效数据已经填充到材质及纹理数组,释放材质缓冲区
    m_pMaterialBuffer -> Release();
    
//  网格数据优化完毕,释放邻接三角形信息缓冲区
    m_pAdjacencyBuffer -> Release();
    
//  当然,这两个缓冲区的释放放到最后的Release函数里也没有问题 ^ ^
     return   true ;
}

void  CSimpleXMesh::DrawXMesh( const  D3DXVECTOR3 &  pos)
{
    
//  根据位置重新设定世界矩阵
    D3DXMATRIX posMatrix;
    D3DXMatrixTranslation(
& posMatrix,pos.x,pos.y,pos.z);
    g_pD3DDevice
-> SetTransform(D3DTS_WORLD, & posMatrix);
    
//  绘制X网格
     for (DWORD i = 0 ;i < m_dwMaterials;i ++ )
    {
        g_pD3DDevice
-> SetMaterial( & m_pD3DMaterialArray[i]);
        g_pD3DDevice
-> SetTexture( 0 ,m_ppDirect3DTextureArray[i]);
        m_pD3DXMesh
-> DrawSubset(i);
    }
    
//  还原世界矩阵
    D3DXMatrixTranslation( & posMatrix, 0 , 0 , 0 );
    g_pD3DDevice
-> SetTransform(D3DTS_WORLD, & posMatrix);
}

void  CSimpleXMesh::Release(){
    
//  释放纹理数组
     for (DWORD i = 0 ;i < m_dwMaterials;i ++ ){
        ReleaseCOM(m_ppDirect3DTextureArray[i]);
    }
    delete[] m_ppDirect3DTextureArray;
    
//  释放材质数组
    delete[] m_pD3DMaterialArray;
    
//  释放网格对象
    ReleaseCOM(m_pD3DXMesh);
}

注释比较详尽,完全参考了《龙书》里的例子。

简单说明一下D3DXMATERIAL结构。如下为SDK中关于D3DXMATERIAL的结构定义:

typedef struct _D3DXMATERIAL
{
    D3DMATERIAL9  MatD3D;
    LPSTR         pTextureFilename;
} D3DXMATERIAL;

该结构专门用于.X中材质信息读取,其中MatD3D是真正的材质信息,pTextureFilename则是此材质关联的纹理名称,需要调用D3DXCreateTextureFromFile来生成真正的纹理对象。相关的API说明可以参看这篇帖子:http://space.cnblogs.com/group/topic/32953/

如果大家还有什么不明白的地方,可以参看龙书,也可以在下方留言,我会尽量给予详尽的解答 ^ ^

然后是我们的主体代码部分:

Direct3D轮回:基于.X文件的网格加载及渲染 D3DGame.cpp
/* -------------------------------------

代码清单:D3DGame.cpp
来自:
http://www.cnblogs.com/kenkao

-------------------------------------
*/

#include 
" StdAfx.h "
#include 
" D3DGame.h "
#include 
" D3DCamera.h "
#include 
" CoordCross.h "
#include 
" SimpleXMesh.h "
#include 
< stdio.h >

HINSTANCE  g_hInst;
HWND       g_hWnd;
D3DXMATRIX g_matProjection;
IDirect3D9       
* g_pD3D         =  NULL;
IDirect3DDevice9 
* g_pD3DDevice   =  NULL;
CMouseInput      
* g_pMouseInput  =  NULL;
CKeyboardInput   
* g_pKeyboardInput  =  NULL;
CD3DCamera       
* g_pD3DCamera   =  NULL;
CCoordCross      
* g_pCoordCross  =  NULL;
CSimpleXMesh     
* g_pSimpleXMesh  =  NULL;

//  鼠标输入单元测试函数
void  TestMouseInput();
//  键盘输入单元测试函数
void  TestKeyboardInput();

//  Mesh特效
void  BeginEffect();
void  EndEffect();
D3DLIGHT9 InitDirectionalLight(D3DXVECTOR3
*  direction, D3DXCOLOR *  color);

void  Initialize(HINSTANCE hInst, HWND hWnd)
{
    g_hInst 
=  hInst;
    g_hWnd  
=  hWnd;
    InitD3D(
& g_pD3D,  & g_pD3DDevice, g_matProjection, hWnd);
    g_pMouseInput 
=   new  CMouseInput;
    g_pMouseInput
-> Initialize(hInst,hWnd);
    g_pKeyboardInput 
=   new  CKeyboardInput;
    g_pKeyboardInput
-> Initialize(hInst,hWnd);
    g_pD3DCamera 
=   new  CD3DCamera;
}

void  LoadContent()
{
    g_pCoordCross 
=   new  CCoordCross;
    g_pD3DCamera
-> SetCameraPos(D3DXVECTOR3( 3.0f , 2.0f , - 8.0f ));
    g_pSimpleXMesh 
=   new  CSimpleXMesh;
    g_pSimpleXMesh
-> LoadXMesh( " bigship1.x " );
}

void  Update()
{
    g_pMouseInput
-> GetState();
    g_pKeyboardInput
-> GetState();
    g_pD3DCamera
-> Update();
}

void  Draw()
{
    g_pD3DDevice
-> SetTransform(D3DTS_VIEW, & g_pD3DCamera -> GetViewMatrix());
    g_pD3DDevice
-> Clear( 0 , NULL, D3DCLEAR_TARGET  |  D3DCLEAR_ZBUFFER, D3DCOLOR_RGBA( 100 , 149 , 237 , 255 ),  1.0f 0 );
    
if (SUCCEEDED(g_pD3DDevice -> BeginScene())) 
    {
        g_pCoordCross
-> Draw();

        BeginEffect();
        g_pSimpleXMesh
-> DrawXMesh(D3DXVECTOR3( 0.0f , 0.0f , 0.0f ));
        EndEffect();

        g_pD3DDevice
-> EndScene();
    }
    g_pD3DDevice
-> Present(NULL, NULL, NULL, NULL);
}

void  UnloadContent()
{
    ReleaseCOM(g_pSimpleXMesh);
    ReleaseCOM(g_pCoordCross);
}

void  Dispose()
{
    ReleaseCOM(g_pD3DCamera);
    ReleaseCOM(g_pKeyboardInput);
    ReleaseCOM(g_pMouseInput);
    ReleaseCOM(g_pD3DDevice);
    ReleaseCOM(g_pD3D);
}

void  TestMouseInput()
{
    POINT point;
    g_pMouseInput
-> GetState();
    g_pMouseInput
-> GetPosition(point);
    TCHAR tmpText[
50 ];
    
if (g_pMouseInput -> LeftButton() == BUTTONSTATE_PRESSED)
    {
        sprintf(tmpText,
" 鼠标左键已按下,X-Y坐标为(%d,%d) " ,point.x,point.y);
        MessageBox(NULL,tmpText,
" 提示 " ,MB_OK | MB_ICONINFORMATION);
    }
    
else   if (g_pMouseInput -> MiddleButton() == BUTTONSTATE_PRESSED)
    {
        sprintf(tmpText,
" 鼠标滚轮已按下,X-Y坐标为(%d,%d) " ,point.x,point.y);
        MessageBox(NULL,tmpText,
" 提示 " ,MB_OK | MB_ICONINFORMATION);
    }
    
else   if (g_pMouseInput -> RightButton() == BUTTONSTATE_PRESSED)
    {
        sprintf(tmpText,
" 鼠标右键已按下,X-Y坐标为(%d,%d) " ,point.x,point.y);
        MessageBox(NULL,tmpText,
" 提示 " ,MB_OK | MB_ICONINFORMATION);
    }
}

void  TestKeyboardInput()
{
    TCHAR tmpText[
50 ];
    
//  获得键盘输入设备状态
    g_pKeyboardInput -> GetState();
    
//  单键检测
     if (g_pKeyboardInput -> IsKeyDown(DIK_D))
    {
        sprintf(tmpText,
" D键被按下 " );
        MessageBox(NULL,tmpText,
" 提示 " ,MB_OK | MB_ICONINFORMATION);
    }
    
//  组合键检测
     else   if (g_pKeyboardInput -> IsKeyDown(DIK_A) & g_pKeyboardInput -> IsKeyDown(DIK_S))
    {
        sprintf(tmpText,
" A&S组合键被按下 " );
        MessageBox(NULL,tmpText,
" 提示 " ,MB_OK | MB_ICONINFORMATION);
    }
}

void  BeginEffect()
{
    D3DXVECTOR3 dir(
- 1.0f - 1.0f 1.0f );
    D3DXCOLOR col(
1.0f 1.0f 1.0f 1.0f );
    D3DLIGHT9 light 
=  InitDirectionalLight( & dir,  & col);
    g_pD3DDevice
-> SetLight( 0 & light);
    g_pD3DDevice
-> SetRenderState(D3DRS_LIGHTING,         TRUE);
    g_pD3DDevice
-> LightEnable( 0 true );
    g_pD3DDevice
-> SetRenderState(D3DRS_NORMALIZENORMALS, TRUE);
    g_pD3DDevice
-> SetRenderState(D3DRS_SPECULARENABLE,   TRUE);
}

void  EndEffect()
{
    g_pD3DDevice
-> SetRenderState(D3DRS_LIGHTING,         FALSE);
    g_pD3DDevice
-> SetRenderState(D3DRS_NORMALIZENORMALS, FALSE);
    g_pD3DDevice
-> SetRenderState(D3DRS_SPECULARENABLE,   FALSE);
}

D3DLIGHT9 InitDirectionalLight(D3DXVECTOR3
*  direction, D3DXCOLOR *  color)
{
    D3DLIGHT9 light;
    ::ZeroMemory(
& light,  sizeof (light));
    light.Type      
=  D3DLIGHT_DIRECTIONAL;
    light.Ambient   
=   * color  *   0.4f ;
    light.Diffuse   
=   * color;
    light.Specular  
=   * color  *   0.6f ;
    light.Direction 
=   * direction;
    
return  light;
}

BeginEffect和EndEffect只是为我们的X网格添加了一个灯光特效~

最后是效果图:

Direct3D轮回:基于.X文件的网格加载及渲染

这架新型战机相信熟悉龙书的朋友一定不会陌生~ 呵呵~

 

你可能感兴趣的:(文件)