网格(三)加载小汽车X文件的一个demo
前面我们已经学习了关于网格的一些知识,由于篇幅较大,所以,这里我们将示例单独拿出来作为一篇内容。
1. 首先,当然是通用的头文件d3dUtility.h文件。我们直接来欣赏源代码。
//**********************************************************************************//
// Utility.h
//**********************************************************************************//
#ifndef __d3dUtilityH__
#define __d3dUtilityH__
#include<d3dx9.h>
#include<string>
namespace d3d
{
//**********************初始化函数******************************************//
bool InitiateD3D(HINSTANCE hInstance, int iWidth, int iHeight, bool windowed,
D3DDEVTYPE deviceType, IDirect3DDevice9** device);
//**********************消息循环函数****************************************//
int EnterMessageLoop(bool (*ptr_display)(float timeDelta));
//***********************消息处理函数**************************************//
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
//**********************模版函数,用于释放资源删除对象等*******************//
template<class T> void Release(T t)
{
if(t)
{
t->Release(); // 释放资源
t = 0;
}
}
template<class T> void Delete(T t)
{
if(t)
{
delete t; // 删除对象
}
}
//**********************常用颜色定义****************************************//
const D3DXCOLOR WHITE(D3DCOLOR_XRGB(255, 255, 255) );
const D3DXCOLOR BLACK( D3DCOLOR_XRGB( 0, 0, 0) );
const D3DXCOLOR RED( D3DCOLOR_XRGB(255, 0, 0) );
const D3DXCOLOR GREEN( D3DCOLOR_XRGB( 0, 255, 0) );
const D3DXCOLOR BLUE( D3DCOLOR_XRGB( 0, 0, 255) );
const D3DXCOLOR YELLOW( D3DCOLOR_XRGB(255, 255, 0) );
const D3DXCOLOR CYAN( D3DCOLOR_XRGB( 0, 255, 255) );
const D3DXCOLOR MAGENTA( D3DCOLOR_XRGB(255, 0, 255) );
//**********************光照初始化******************************************//
D3DLIGHT9 IniateDirectionalLight(D3DXVECTOR3* direction, D3DXCOLOR* color);
D3DLIGHT9 IniatePointLight(D3DXVECTOR3* position, D3DXCOLOR* color);
D3DLIGHT9 IniateSpotLight(D3DXVECTOR3* direction, D3DXVECTOR3* position, D3DXCOLOR* color);
//**********************材质************************************************//
D3DMATERIAL9 IniateMaterial(D3DXCOLOR a, D3DXCOLOR d, D3DXCOLOR s, D3DXCOLOR e, float p);
const D3DMATERIAL9 WHITE_MTRL = IniateMaterial(WHITE, WHITE, WHITE, BLACK, 2.0f);
const D3DMATERIAL9 RED_MTRL = IniateMaterial(RED, RED, RED, BLACK, 2.0f);
const D3DMATERIAL9 GREEN_MTRL = IniateMaterial(GREEN, GREEN, GREEN, BLACK, 2.0f);
const D3DMATERIAL9 BLUE_MTRL = IniateMaterial(BLUE, BLUE, BLUE, BLACK, 2.0f);
const D3DMATERIAL9 YELLOW_MTRL = IniateMaterial(YELLOW, YELLOW, YELLOW, BLACK, 2.0f);
}
#endif
这个头文件是很通用的,这里也就不解释了。
2. 接下来就是对d3dUtility.h文件中声明的变量和函数进行定义的d3dUtility.cpp了。我们直接欣赏源代码。
//*************************************************************************************//
// Utility.cpp
// 对在Utility.hpp中声明的函数定义
//*************************************************************************************//
#include"d3dUtility.h"
#define WINDOWCLASSNAME TEXT("Direct3D9App")
#define REGISTERFAILED TEXT("RegisterClass()-FAILED")
#define CREATEWINDOWFAILED TEXT("CreateWindow()-FAILED")
#define DIRECT3DCREATE9FAILED TEXT("Direct3DCreate9()-FAILED")
#define CREATEDEVICEFALIED TEXT("CreateDevice()-FAILED")
#define WINDOWNAME TEXT("WindowByJosh")
//***************************初始化函数*******************************************//
bool d3d::InitiateD3D(HINSTANCE hInstance, int iWidth, int iHeight, bool windowed,
D3DDEVTYPE deviceType, IDirect3DDevice9** device)
{
//**************************创建主窗口******************************//
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC)d3d::WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = 0;
wc.lpszClassName = WINDOWCLASSNAME;
if(!RegisterClass(&wc))
{
::MessageBox(0, REGISTERFAILED, 0, 0);
return false;
}
HWND hWnd = 0;
hWnd = ::CreateWindow(WINDOWCLASSNAME, WINDOWNAME, WS_EX_TOPMOST,
0, 0, iWidth, iHeight, 0, 0, hInstance, 0);
if(!hWnd)
{
::MessageBox(0, CREATEWINDOWFAILED, 0, 0);
return false;
}
::ShowWindow(hWnd, SW_SHOW);
::UpdateWindow(hWnd);
//************************初始化D3D**********************************//
HRESULT hr = 0;
// 1.创建Direct3D设备接口对象,即IDirect3D9对象
IDirect3D9* d3d9;
d3d9 = Direct3DCreate9(D3D_SDK_VERSION);
if(!d3d9)
{
::MessageBox(0, DIRECT3DCREATE9FAILED, 0, 0);
return false;
}
// 2.检验硬件顶点运算
D3DCAPS9 caps;
d3d9->GetDeviceCaps(D3DADAPTER_DEFAULT, deviceType, &caps);
int vp = 0;
if(caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT )
vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;
else
vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
// 3.填充D3DPRESENT_PARAMETERS结构
D3DPRESENT_PARAMETERS d3dpp;
d3dpp.BackBufferWidth = iWidth;
d3dpp.BackBufferHeight = iHeight;
d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
d3dpp.BackBufferCount = 1;
d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
d3dpp.MultiSampleQuality = 0;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = hWnd;
d3dpp.Windowed = windowed;
d3dpp.EnableAutoDepthStencil = true;
d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
d3dpp.Flags = 0;
d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
// 4.创建Direct3D设备对象
hr = d3d9->CreateDevice(D3DADAPTER_DEFAULT, deviceType, hWnd, vp, &d3dpp, device);
if(FAILED(hr))
{
//尝试使用16bits深度缓存
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
hr = d3d9->CreateDevice(
D3DADAPTER_DEFAULT,
deviceType,
hWnd,
vp,
&d3dpp,
device);
if(FAILED(hr))
{
::MessageBox(hWnd, CREATEDEVICEFALIED, 0, 0);
return false;
}
}
d3d9->Release(); //释放接口对象
return true;
}
//***************************消息循环函数*****************************************//
int d3d::EnterMessageLoop( bool (*ptr_display)(float timeDelta) )
{
MSG msg;
::ZeroMemory(&msg, sizeof(MSG));
static float lastTime = (float)timeGetTime();
while(msg.message != WM_QUIT)
{
if(::PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
else
{
float currTime = (float)timeGetTime();
float timeDelta = (currTime - lastTime)*0.001f;
ptr_display(timeDelta);
lastTime = currTime;
}
}
return msg.wParam;
}
//***************************光照初始化函数************************************************************//
D3DLIGHT9 d3d::IniateDirectionalLight(D3DXVECTOR3* direction, D3DXCOLOR* color)
{
D3DLIGHT9 light;
::ZeroMemory(&light, sizeof(D3DLIGHT9));
light.Type = D3DLIGHT_DIRECTIONAL;
light.Ambient = *color * 0.4f;
light.Diffuse = *color;
light.Specular = *color * 0.6f;
light.Direction = *direction;
return light;
}
D3DLIGHT9 d3d::IniatePointLight(D3DXVECTOR3* position, D3DXCOLOR* color)
{
D3DLIGHT9 light;
::ZeroMemory(&light, sizeof(D3DLIGHT9));
light.Type = D3DLIGHT_POINT;
light.Ambient = *color * 0.4f;
light.Diffuse = *color;
light.Specular = *color * 0.6f;
light.Position = *position;
light.Range = 1000.0f;
light.Falloff = 1.0f;
light.Attenuation0 = 1.0f;
light.Attenuation1 = 0.0f;
light.Attenuation2 = 0.0f;
return light;
}
D3DLIGHT9 d3d::IniateSpotLight(D3DXVECTOR3* position, D3DXVECTOR3* direction, D3DXCOLOR* color)
{
D3DLIGHT9 light;
::ZeroMemory(&light, sizeof(D3DLIGHT9));
light.Type = D3DLIGHT_SPOT;
light.Ambient = *color * 0.4f;
light.Diffuse = *color;
light.Specular = *color * 0.6f;
light.Position = *position;
light.Direction = *direction;
light.Range = 1000.0f;
light.Falloff = 1.0f;
light.Attenuation0 = 1.0f;
light.Attenuation1 = 0.0f;
light.Attenuation2 = 0.0f;
light.Theta = 0.5f;
light.Phi = 0.7f;
return light;
}
//***************************材质初始化函数*********************************************//
D3DMATERIAL9 d3d::IniateMaterial(D3DXCOLOR a, D3DXCOLOR d, D3DXCOLOR s, D3DXCOLOR e, float p)
{
D3DMATERIAL9 material;
material.Ambient = a;
material.Diffuse = d;
material.Specular = s;
material.Emissive = e;
material.Power = p;
return material;
}
3. 接下来就是我们的核心代码了。这里,我把它命名为MyGame.cpp。这里直接欣赏源代码。
//***********************************************************************************************//
// MyGame.cpp
//***********************************************************************************************//
#include "d3dUtility.h"
#include <vector>
#include <tchar.h>
//****************************全局变量**************************************//
const int Width = 800;
const int Height = 600;
IDirect3DDevice9* Device = 0; // 设备接口对象
ID3DXMesh* Mesh = 0; //网格接口对象
std::vector<D3DMATERIAL9> Materials(0); //
std::vector<IDirect3DTexture9*> Textures(0); //
//******************************Setup函数**********************************//
bool Setup()
{
HRESULT hr = 0;
// 加载XFile
ID3DXBuffer* adjBuffer = 0;
ID3DXBuffer* mtrlBuffer = 0;
DWORD numMtrls = 0;
hr = D3DXLoadMeshFromX( TEXT("car.x"), D3DXMESH_MANAGED, Device, &adjBuffer,
&mtrlBuffer, 0, &numMtrls, &Mesh );
if(FAILED(hr))
{
::MessageBox(0, TEXT("D3DXLoadMeshFromX()-FAILED"), 0, 0);
return false;
}
// 从XFile文件中取出材质,并加载纹理文件(XFile文件中只提供纹理文件名)
if( (mtrlBuffer != 0) && (numMtrls != 0))
{
D3DXMATERIAL* materials = (D3DXMATERIAL*)mtrlBuffer->GetBufferPointer();
for(int i=0; i<numMtrls; i++)
{
materials[i].MatD3D.Ambient = materials[i].MatD3D.Diffuse;
Materials.push_back( materials[i].MatD3D );
if(materials[i].pTextureFilename != 0)
{
IDirect3DTexture9* texture = 0;
D3DXCreateTextureFromFile(Device, _T("mtrls[i].pTextureFilename"), &texture);
Textures.push_back( texture );
}
else
{
Textures.push_back(0);
}
}
}
d3d::Release<ID3DXBuffer*>(mtrlBuffer);
// 网格优化
hr = Mesh->OptimizeInplace(D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_COMPACT | D3DXMESHOPT_VERTEXCACHE,
(DWORD*) adjBuffer->GetBufferPointer(), 0, 0, 0);
d3d::Release<ID3DXBuffer*>(adjBuffer);
if(FAILED(hr))
{
::MessageBox(0, TEXT("OptimizeInplace()-FAILED"), 0, 0);
return false;
}
// 设置纹理过滤器
Device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_POINT);
// 设置光照
D3DXVECTOR3 dir(1.0f, -1.0f, 1.0f);
D3DXCOLOR col(1.0f, 1.0f, 1.0f, 1.0f);
D3DLIGHT9 light = d3d::IniateDirectionalLight(&dir, &col);
Device->SetLight(0, &light);
Device->LightEnable(0, true);
Device->SetRenderState(D3DRS_NORMALIZENORMALS, true);
Device->SetRenderState(D3DRS_SPECULARENABLE, true);
// 设置摄像机
D3DXVECTOR3 pos(4.0f, 4.0f, -13.0f);
D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
D3DXMATRIX V;
D3DXMatrixLookAtLH(&V, &pos, &target, &up);
Device->SetTransform(D3DTS_VIEW, &V);
// 设置投影矩阵
D3DXMATRIX proj;
D3DXMatrixPerspectiveFovLH(&proj, D3DX_PI * 0.5f,
(float)Width / (float)Height, 1.0f, 1000.0f);
Device->SetTransform(D3DTS_PROJECTION, &proj);
return true;
}
//*******************************CleanUp函数*******************************//
void CleanUp()
{
d3d::Release<ID3DXMesh*>(Mesh);
for(int i = 0; i < Textures.size(); i++)
d3d::Release<IDirect3DTexture9*>( Textures[i] );
}
//*******************************Display函数*******************************//
bool Display( float timeDelta)
{
if(Device)
{
static float y = 0.0f;
D3DXMATRIX yRot;
D3DXMatrixRotationY(&yRot, y);
y += timeDelta;
if( y>= 6.28f )
y = 0.0f;
D3DMATRIX World = yRot;
Device->SetTransform(D3DTS_WORLD, &World);
Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0);
Device->BeginScene();
for(int i = 0; i < Materials.size(); i++)
{
Device->SetMaterial( &Materials[i] );
Device->SetTexture(0, Textures[i]);
Mesh->DrawSubset(i);
}
Device->EndScene();
Device->Present(0, 0, 0, 0);
}
return true;
}
//*******************************WndProc***********************************//
LRESULT CALLBACK d3d::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch( msg )
{
case WM_DESTROY:
::PostQuitMessage(0);
break;
case WM_KEYDOWN:
if( wParam == VK_ESCAPE )
::DestroyWindow(hwnd);
break;
}
return ::DefWindowProc(hwnd, msg, wParam, lParam);
}
//*******************************WinMain***********************************//
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE prevInstance, PSTR cmdLine, int showCmd)
{
if(!d3d::InitiateD3D(hinstance,
Width, Height, true, D3DDEVTYPE_HAL, &Device))
{
::MessageBox(0, TEXT("InitD3D() - FAILED"), 0, 0);
return 0;
}
if(!Setup())
{
::MessageBox(0, TEXT("Setup() - FAILED"), 0, 0);
return 0;
}
d3d::EnterMessageLoop( Display );
CleanUp();
Device->Release();
return 0;
}
4. 把这三个文件输入,并且将car.x文件放在工程的根目录下,就可以编译运行了。下面,给出运行的效果图,如下。
好了,最后,我们就完成了一个三维的小汽车旋转的效果了。
另外需要说明的是,X文件一般可以由Maya,3DS MAX等软件建模转换而来。还有,在DirectX SDK中,微软也提供的一些实例当中也有一些模型的X文件,我这里的目录是E:\Microsoft DirectX SDK\Samples\Media。
初学者看这些代码多少会有些云里雾里。这个时候不要急功近利,把一个demo看懂看透,我相信就可以以一反三了。
学习游戏编程是必须要学习3D建模的,这里给出Maya的下载链接和本示例完整工程的下载。
Maya:http://www.cngr.cn/dir/211/280/2012042182573.html
Maya视频教学(零基础):http://pcedu.pconline.com.cn/videoedu/3dsmax/0910/1861304.html
本示例完整工程下载:http://download.csdn.net/download/chenwu_843402773/4938339
———— by Josh
2012年12月28日