Direct3D轮回:快速构建基于win32工程的Direct3D游戏框架
2011-06-21 22:11 独孤残云 阅读(3083) 评论(15) 编辑 收藏 举报前段时间一直混迹于C++与C#语言,徘徊于DirectX与Xna之间,一直没什么大的收获。
重新拾回C++&DirectX,有一种返璞归真的感慨~ 多少有一些心得,简单总结一点~ 请园子的前辈们多多指教,多多拍砖^ ^
用过Xna的朋友都知道,客户在Xna中从来不用自己去实现3D设备的初始化,游戏的主循环,甚至是退出时的设备资源释放等等相关事宜。
不过很不幸,DirectX没有赐予大家这种特权,相关工作我们需要自己实现:
1.新建一个Win32工程,并在此基础上新增CD3DInit类(这里其实没有必要真正形成类,个人感觉全局函数用起来反倒方便);
代码清单:D3DInit.h
来自:http://kenkao.cnblogs.com/
----------------------------------*/
#pragma once
#include <windows.h>
#include "d3d9.h"
#include "d3dx9.h"
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
#define ReleaseCOM(x){if(x!=NULL) x->Release();x=NULL;}
HRESULT InitD3D(IDirect3D9 **ppD3D,
IDirect3DDevice9 **ppD3DDevice,
HWND hWnd, BOOL ForceWindowed = FALSE,
BOOL MultiThreaded = FALSE);
代码清单:D3DInit.cpp
来自:http://kenkao.cnblogs.com/
----------------------------------*/
#include "D3DInit.h"
/*---------------------------------------------------
参考了《Advanced Animation with DirectX》书中的实现方法
参数一:IDirect3D9指针引用(指针的指针)
参数二:IDirect3DDevice9设备指针引用
参数三:有效的窗口句柄
参数四:是否全屏
参数五:是否多线程
----------------------------------------------------*/
HRESULT InitD3D(IDirect3D9 **ppD3D,
IDirect3DDevice9 **ppD3DDevice,
HWND hWnd,
BOOL ForceWindowed,
BOOL MultiThreaded)
{
IDirect3D9 *pD3D = NULL;
IDirect3DDevice9 *pD3DDevice = NULL;
HRESULT hr;
// 错误检查
if(!ppD3D || !ppD3DDevice || !hWnd)
return E_FAIL;
// 初始化 Direct3D
if((pD3D = Direct3DCreate9(D3D_SDK_VERSION)) == NULL)
return E_FAIL;
*ppD3D = pD3D;
// 是否使用全屏状态
int Mode;
if(ForceWindowed == TRUE)
Mode = IDNO;
else
Mode = MessageBox(hWnd, "Use fullscreen mode? (640x480x16)", "Initialize D3D", MB_YESNO | MB_ICONQUESTION);
// 全屏与非全屏状态下的参数设置
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
if(Mode == IDYES) {
DWORD Width = 640;
DWORD Height = 480;
D3DFORMAT Format = D3DFMT_R5G6B5;
d3dpp.BackBufferWidth = Width;
d3dpp.BackBufferHeight = Height;
d3dpp.BackBufferFormat = Format;
d3dpp.SwapEffect = D3DSWAPEFFECT_FLIP;
d3dpp.Windowed = FALSE;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
} else {
RECT ClientRect, WndRect;
GetClientRect(hWnd, &ClientRect);
GetWindowRect(hWnd, &WndRect);
DWORD DesiredWidth = 640;
DWORD DesiredHeight = 480;
DWORD Width = (WndRect.right - WndRect.left) + (DesiredWidth - ClientRect.right);
DWORD Height = (WndRect.bottom - WndRect.top) + (DesiredHeight - ClientRect.bottom);
MoveWindow(hWnd, WndRect.left, WndRect.top, Width, Height, TRUE);
D3DDISPLAYMODE d3ddm;
pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);
d3dpp.BackBufferWidth = DesiredWidth;
d3dpp.BackBufferHeight = DesiredHeight;
d3dpp.BackBufferFormat = d3ddm.Format;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.Windowed = TRUE;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
}
// 创建3D设备
DWORD Flags= D3DCREATE_MIXED_VERTEXPROCESSING;
if(MultiThreaded == TRUE)
Flags |= D3DCREATE_MULTITHREADED;
if(FAILED(hr = pD3D->CreateDevice(
D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL, hWnd, Flags,
&d3dpp, &pD3DDevice)))
return hr;
*ppD3DDevice = pD3DDevice;
// 设置投影矩阵
float Aspect = (float)d3dpp.BackBufferWidth / (float)d3dpp.BackBufferHeight;
D3DXMATRIX matProjection;
D3DXMatrixPerspectiveFovLH(&matProjection, D3DX_PI/4.0f, Aspect, 1.0f, 10000.0f);
pD3DDevice->SetTransform(D3DTS_PROJECTION, &matProjection);
// 设置默认渲染状态
pD3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
pD3DDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
pD3DDevice->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
// 设置默认纹理融合态
pD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
pD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
// 设置默认纹理采样器
pD3DDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
pD3DDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
return S_OK;
}
D3DInit.h相当于一个总的头文件,我们可以把用于初始化D3D所需要引入的头文件、库文件统统放到这个下面,供其他文件引用。
D3DInit.cpp是其实现部分,目前只有一个用于快速初始化D3D设备的InitD3D函数,代码中给出了参考书名,不得不佩服与作者的匠心独运。函数内部给出了中文注释,其奥妙留待读者自己体会~
至于ReleaseCOM宏,可以作为通用的COM安全释放函数,熟练运用D3D的朋友大都会留出这样一个宏,建议大家养成这样一个习惯~
2.新增CD3DGame类
代码清单:D3DGame.h
来自:http://kenkao.cnblogs.com/
----------------------------------*/
#pragma once
#include "D3DInit.h"
// 初始化
void Initialize(HWND hWnd);
// 内容加载
void LoadContent();
// 逻辑更新
void Update();
// 绘图更新
void Draw();
// 卸载内容
void UnloadContent();
// 释放并退出
void Dispose();
/*---------------------------------------------------------
1.用如下这一段替代_tWinMain消息循环的那一段
LoadContent();
ZeroMemory(&msg, sizeof(MSG));
while(msg.message != WM_QUIT) {
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Update();
Draw();
}
UnloadContent();
Dispose();
2.InitInstance的ShowWindow(hWnd, nCmdShow)之前加入
Initialize(hWnd);
----------------------------------------------------------*/
代码清单:D3DGame.cpp
来自:http://kenkao.cnblogs.com/
----------------------------------*/
#include "StdAfx.h"
#include "D3DGame.h"
IDirect3D9 *g_pD3D = NULL;
IDirect3DDevice9 *g_pD3DDevice = NULL;
void Initialize(HWND hWnd)
{
InitD3D(&g_pD3D, &g_pD3DDevice, hWnd);
}
void LoadContent()
{
}
void Update()
{
}
void Draw()
{
g_pD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_RGBA(100,149,237,255), 1.0f, 0);
if(SUCCEEDED(g_pD3DDevice->BeginScene()))
{
g_pD3DDevice->EndScene();
}
g_pD3DDevice->Present(NULL, NULL, NULL, NULL);
}
void UnloadContent()
{
}
void Dispose()
{
ReleaseCOM(g_pD3DDevice);
ReleaseCOM(g_pD3D);
}
安插这两个文件的目的,主要是为了构建我们自己的D3D游戏框架,各个函数的用途均已说明,完全仿Xna结构而来~(用习惯了^ ^)
我们可以按照D3DGame.h最下方的注释,将这个框架跟Win32默认代码框架关联起来。
以后所有的编码工作均在我们自己的这部分代码中实现,基本不需要再关心令人眼花缭乱的Win32工程代码。
3.改动后的Win32代码为:
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: 在此放置代码。
MSG msg;
HACCEL hAccelTable;
// 初始化全局字符串
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_KEN3DGAME, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// 执行应用程序初始化:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_KEN3DGAME));
LoadContent();
ZeroMemory(&msg, sizeof(MSG));
while(msg.message != WM_QUIT) {
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Update();
Draw();
}
UnloadContent();
Dispose();
return (int) msg.wParam;
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
hInst = hInstance; // 将实例句柄存储在全局变量中
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
Initialize(hWnd);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
这样,一个可以无限复用的D3D基本框架就完成了~
如下执行效果图: