代码改变世界

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

2011-06-29 13:26  独孤残云  阅读(2713)  评论(2编辑  收藏  举报

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网格对象指针
};

 

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/

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

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

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.0f0);
    
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.0f1.0f);
    D3DXCOLOR col(
1.0f1.0f1.0f1.0f);
    D3DLIGHT9 light 
= InitDirectionalLight(&dir, &col);
    g_pD3DDevice
->SetLight(0&light);
    g_pD3DDevice
->SetRenderState(D3DRS_LIGHTING,         TRUE);
    g_pD3DDevice
->LightEnable(0true);
    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网格添加了一个灯光特效~

最后是效果图:

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