Direct3D轮回:基于.X文件的网格加载及渲染
2011-06-29 13:26 独孤残云 阅读(2723) 评论(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.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);
}
代码清单: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;
}
代码清单: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网格添加了一个灯光特效~
最后是效果图:
这架新型战机相信熟悉龙书的朋友一定不会陌生~ 呵呵~