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

 

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.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网格添加了一个灯光特效~

最后是效果图:

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

 


分享给朋友:
您可能感兴趣的文章:
随机阅读: