13. GameProjec1_GameEngine
工程GameProject1: main.h, main.cpp这两个文件用于创建游戏Stranded。
工程GameEngine:RenderInterface.h , D3DRenderer.h, D3DRenderer.cpp, engine.h, defines.h. D3DRenderer.h和D3DRenderer.cpp用于创建Direct3D渲染系统; RenderInterface.h文件是一个基类,D3DRenderer就是从该类派生出来的;engine.h和defines.h包含了游戏引擎中要用到的Define(定义)、Function Prototype(函数原型)、Enumeration(枚举)、Structure(结构)和Include(包含)等语句。
main.h
#ifndef _UGP_MAIN_H_
#define _UGP_MAIN_H_
#include"StrandedEngine/engine.h"
#pragma comment(lib, "lib/StrandedEngine.lib")
#define WINDOW_CLASS "StrandedGame"
#define WINDOW_NAME "Stranded"
#define WIN_WIDTH 800
#define WIN_HEIGHT 600
#define FULLSCREEN 1
// Function Prototypes...
bool InitializeEngine();
void ShutdownEngine();
// Main game functions.
bool GameInitialize();
void GameLoop();
void GameShutdown();
#endif
main.cpp
#include"main.h"
// Globals...
HWND g_hwnd;
CRenderInterface *g_Render = NULL;
LRESULT WINAPI MsgProc(HWND hd, UINT msg, WPARAM wp, LPARAM lp)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
break;
case WM_KEYUP:
if(wp == VK_ESCAPE) PostQuitMessage(0);
break;
}
return DefWindowProc(hd, msg, wp, lp);
}
int WINAPI WinMain(HINSTANCE h, HINSTANCE p, LPSTR cmd, int show)
{
// Register the window class
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc,
0L, 0L, GetModuleHandle(NULL), NULL, NULL,
NULL, NULL, WINDOW_CLASS, NULL };
RegisterClassEx(&wc);
// Create the application's window
if(FULLSCREEN)
{
g_hwnd = CreateWindowEx(NULL, WINDOW_CLASS, WINDOW_NAME,
WS_POPUP | WS_SYSMENU | WS_VISIBLE, 0, 0,
WIN_WIDTH, WIN_HEIGHT,
NULL, NULL, h, NULL);
}
else
{
g_hwnd = CreateWindowEx(NULL, WINDOW_CLASS, WINDOW_NAME,
WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0,
0, WIN_WIDTH, WIN_HEIGHT,
NULL, NULL, h, NULL);
}
if(g_hwnd)
{
// Show the window
ShowWindow(g_hwnd, SW_SHOWDEFAULT);
UpdateWindow(g_hwnd);
}
// Initialize the Stranded Engine.
if(InitializeEngine())
{
// Initialize Stranded game.
if(GameInitialize())
{
// Enter the message loop
MSG msg;
ZeroMemory(&msg, sizeof(msg));
SetCursorPos(0, 0);
while(msg.message != WM_QUIT)
{
if(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
GameLoop();
}
}
}
// Release any and all resources.
GameShutdown();
ShutdownEngine();
UnregisterClass(WINDOW_CLASS, wc.hInstance);
return 0;
}
bool InitializeEngine()
{
if(!CreateD3DRenderer(&g_Render)) return false;
if(!g_Render->Initialize(WIN_WIDTH, WIN_HEIGHT,
g_hwnd, FULLSCREEN)) return false;
g_Render->SetClearCol(0, 0, 0);
return true;
}
void ShutdownEngine()
{
if(g_Render)
{
g_Render->Shutdown();
delete g_Render;
g_Render = NULL;
}
}
bool GameInitialize()
{
return true;
}
void GameLoop()
{
if(!g_Render) return;
g_Render->StartRender(1, 1, 0);
g_Render->EndRendering();
}
void GameShutdown()
{
}
InitializeEngine()函数调用CreateD3DRenderer()函数为渲染系统分配内存。该函数调用对象的Initialize()函数初始化渲染系统。它将清除色设为黑色。清除色就是在应用程序启动新帧时,清除后台缓存用的颜色。ShutdownEngine()函数只是简单地将所有系统使用的动态内存清除干净。到目前为止,只有一个基本的渲染系统,所以这就是要清除的全部内容。GameInitialize()和GameShutdown()这两个函数都是空的,因为在此还没有创建具体的游戏代码。GameLoop()函数和第1章的render()函数很类似。该函数只是完成渲染一个黑屏的基本功能。
defines.h
defines.h文件包含了游戏使用的大量的定义和枚举变量。defines.h头文件将包含游戏或引擎将用到的常用定义.
#ifndef _UGP_DEFINES_H_
#define _UGP_DEFINES_H_
#include<windows.h>
// Boolean values.
#define UGP_INVALID -1
#define UGP_OK 1
#define UGP_FAIL 0
// Window handle (need new way if porting to Mac and OpenGL).
#define WinHWND HWND
// Typedefs and enumerations.
typedef long VertexType;
enum PrimType
{
NULL_TYPE,
POINT_LIST,
TRIANGLE_LIST,
TRIANGLE_STRIP,
TRIANGLE_FAN,
LINE_LIST,
LINE_STRIP
};
// Color defines.
#define UGPCOLOR_ARGB(a,r,g,b) ((unsigned long)((((a)&0xff)<<24)|\
(((r)&0xff)<<16)|(((g)&0xff)<<8)|\
((b)&0xff)))
#endif
RenderInterface.h
RenderInterface.h头文件包含了渲染系统基类的声明。然后派生该类以创建游戏引擎使用的真实渲染系统。
渲染系统的基类所包含的成员变量有屏幕宽度和高度、一个是否渲染整个屏幕的标识符,Direct3D初始化函数所需要的窗口句柄,以及两个投影矩阵所需的近距离值和远距离值。将变量属性设为受保护的(protected),意味着它们可以是派生类的成员变量,就如同是在那些类中声明的一样。
这些渲染系统的基类开始先是构造函数和析构函数。这些构造函数只是简单地设置成员变量的默认值,而析构函数什么也不做。析构函数的属性被设置为虚拟的(virtual),因为这样可以确保所有的派生类在析构时可以调用正确的析构函数。如果不这样的话,读者可能就会遇到一些意外错误或是很难发现的内存泄漏问题。在创建基类时,要牢记一定要创建一个虚拟析构函数。
函数的其余部分直接明了。Initialize()函数用于设置Direct3D渲染系统。OneTimeInit()函数调用只需调用代码一次,这就像设置投影矩阵一样。一旦应用程序使用Shutdown()函数,它就会清除渲染系统。在Direct3D中可以释放那些在系统运行过程中用到的Direct3D对象。SetClearCol()函数用于将后台缓存颜色设置为指定的颜色。StartRender()和EndRendering()这两个函数用于启动新场景,结束场景,在渲染场景之前、之后或过程中清除场景。CalculateProjMatrix()和CalculateOrthoMatrix()这两个函数用于设置可以使用立体投影和正交投影的两个矩阵。CreateStaticBuffer()函数用于创建要绘制的静态顶点缓存,Render()函数用于将缓存内容显示在屏幕上。由于OpenGL中没有像Direct3D一样的顶点缓存,因此如果要移植到OpenGL中,就可以设置这些内容,只要用static buffer(静态缓存)类指定浮点数组即可。OpenGL版的Render()函数可以使用顶点数组或OpenGL顶点缓存对象(VBOs)渲染静态缓存内容。唯一的差异在于OpenGL并没有像Direct3D这样创建顶点缓存的结构,但这对于代码移植而言并不是问题。
#ifndef _UGP_RENDERINTERFACE_H_
#define _UGP_RENDERINTERFACE_H_
#include"defines.h"
class CRenderInterface
{
public:
CRenderInterface() : m_screenWidth(0),
m_screenHeight(0), m_near(0), m_far(0) { }
virtual ~CRenderInterface() {}
virtual bool Initialize(int w, int h,
WinHWND mainWin, bool fullScreen) = 0;
virtual void OneTimeInit() = 0;
virtual void Shutdown() = 0;
virtual void SetClearCol(float r, float g, float b) = 0;
virtual void StartRender(bool bColor, bool bDepth,
bool bStencil) = 0;
virtual void ClearBuffers(bool bColor, bool bDepth,
bool bStencil) = 0;
virtual void EndRendering() = 0;
virtual void CalculateProjMatrix(float fov, float n,
float f) = 0;
virtual void CalculateOrthoMatrix(float n, float f) = 0;
virtual int CreateStaticBuffer(VertexType, PrimType,
int totalVerts, int totalIndices,
int stride, void **data, unsigned int *indices,
int *staticId) = 0;
virtual int Render(int staticId) = 0;
protected:
int m_screenWidth;
int m_screenHeight;
bool m_fullscreen;
WinHWND m_mainHandle;
float m_near;
float m_far;
};
#endif
engine.h
engine.h文件包含了include语句,这样就可以在一个地方访问游戏引擎的不同内容。这里只包含了渲染系统。诚如所知道的一样,该文件的内容将随着游戏的开发进度而发生变动。
#ifndef _UGP_ENGINE_H_
#define _UGP_ENGINE_H_
#include"RenderInterface.h"
#include"D3DRenderer.h"
#endif
D3DRenderer.h
D3DRenderer.h头文件定义了本书一直要用到的派生渲染类。除了该类将用于实现具体的渲染功能之外,它的定义与基类没有任何区别。类还声明了几个具体的Direct3D成员变量。
首先,D3DRenderer.h头文件包含和绑定了具体的Direct3D头和库。接下来是Direct3D静态缓存的声明。该缓存用顶点缓存将静态几何图形(非动画的)绘制到屏幕上。静态缓存结构由顶点缓存、索引缓存(三角形索引)、对顶点和索引的计算、单个顶点尺寸的幅度值、Direct3D顶点FVF以及在渲染静态缓存时要用的图元类型。
D3D渲染系统为Direct3d9对象详细说明了成员变量、Direct3D设备对象、清除色、一个用于确认它是否位于当前正在渲染的场景中的标识符、保存所有静态缓存的数组链表、静态缓存数目的计数器以及正在使用的当前静态缓存变量。最后一个变量是为了避免设置已经设置过的静态缓存。当有必要在单独一帧中多次渲染相同的对象时,会多次出现这种情况。每次设置相同的静态缓存都要浪费处理时间,都会影响到程序性能。在和纹理图像打交道,尤其是和大纹理图像和对象的多个实例打交道时,更是值得注意。
文件结尾处声明的最后一个函数原型是CreateD3DRenderer()。实际上并不需要该函数,但是有了该函数,调用它就可以为渲染系统分配内存。当使用动态链接库(DLL),也就是一个用于Direct3D,一个用于OpenGL时,它们会非常有用。如果在这两个动态链接库中都有CreateRenderer()函数,它就会起作用。这样在程序运行期间,就可以为渲染系统分配要用的动态链接库。就可以让程序和渲染系统使用的对象无关。程序并不知道使用的是哪个渲染系统,它只要知道无论它用哪个渲染系统,都要指定它所用的动态链接库。
#ifndef _D3D_RENDERER_H_
#define _D3D_RENDERER_H_
#include<windows.h>
#include<d3d9.h>
#include<d3dx9.h>
#include"RenderInterface.h"
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
struct stD3DStaticBuffer
{
stD3DStaticBuffer() : vbPtr(0), ibPtr(0), numVerts(0),
numIndices(0), stride(0), fvf(0),
primType(NULL_TYPE) {}
LPDIRECT3DVERTEXBUFFER9 vbPtr;
LPDIRECT3DINDEXBUFFER9 ibPtr;
int numVerts;
int numIndices;
int stride;
unsigned long fvf;
PrimType primType;
};
class CD3DRenderer : public CRenderInterface
{
public:
CD3DRenderer();
~CD3DRenderer();
bool Initialize(int w, int h, WinHWND mainWin,
bool fullScreen);
void Shutdown();
void SetClearCol(float r, float g, float b);
void StartRender(bool bColor, bool bDepth, bool bStencil);
void ClearBuffers(bool bColor, bool bDepth, bool bStencil);
void EndRendering();
void CalculateProjMatrix(float fov, float n, float f);
void CalculateOrthoMatrix(float n, float f);
int CreateStaticBuffer(VertexType, PrimType,
int totalVerts, int totalIndices,
int stride, void **data, unsigned int *indices,
int *staticId);
int Render(int staticId);
private:
void OneTimeInit();
private:
D3DCOLOR m_Color;
LPDIRECT3D9 m_Direct3D;
LPDIRECT3DDEVICE9 m_Device;
bool m_renderingScene;
stD3DStaticBuffer *m_staticBufferList;
int m_numStaticBuffers;
int m_activeStaticBuffer;
};
bool CreateD3DRenderer(CRenderInterface **pObj);
#endif
D3DRenderer.cpp
D3DRenderer.cpp源文件开始先声明了CreateD3DRenderer()函数和CD3DRenderer类的构造函数和析构函数。CreateD3DRenderer()函数简单地实现了内存分配,以创建一个新的渲染对象。然后新对象存储在发送给函数的基类指针中。这样游戏只要和基类对象打交道就可以了,而不需要知道该对象是OpenGL还是Direct3D生成的对象。构造函数初始化类成员变量,而析构函数调用Shutdown()函数以确保在销毁对象前系统被清理干净。程序清单2.8显示了前三个函数。
#include"D3DRenderer.h"
bool CreateD3DRenderer(CRenderInterface **pObj)
{
if(!*pObj) *pObj = new CD3DRenderer;
else return false;
return true;
}
unsigned long CreateD3DFVF(int flags)
{
unsigned long fvf = 0;
return fvf;
}
CD3DRenderer::CD3DRenderer()
{
m_Direct3D = NULL;
m_Device = NULL;
m_renderingScene = false;
m_numStaticBuffers = 0;
m_activeStaticBuffer = UGP_INVALID;
m_staticBufferList = NULL;
}
CD3DRenderer::~CD3DRenderer()
{
Shutdown();
}
bool CD3DRenderer::Initialize(int w, int h, WinHWND mainWin,
bool fullScreen)
{
Shutdown();
m_mainHandle = mainWin;
if(!m_mainHandle) return false;
m_fullscreen = fullScreen;
D3DDISPLAYMODE mode;
D3DCAPS9 caps;
D3DPRESENT_PARAMETERS Params;
ZeroMemory(&Params, sizeof(Params));
m_Direct3D = Direct3DCreate9(D3D_SDK_VERSION);
if(!m_Direct3D) return false;
if(FAILED(m_Direct3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,
&mode))) return false;
if(FAILED(m_Direct3D->GetDeviceCaps(D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL, &caps))) return false;
DWORD processing = 0;
if(caps.VertexProcessingCaps != 0)
processing = D3DCREATE_HARDWARE_VERTEXPROCESSING |
D3DCREATE_PUREDEVICE;
else
processing = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
if(m_fullscreen)
{
Params.FullScreen_RefreshRateInHz = mode.RefreshRate;
Params.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
}
else
Params.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
Params.Windowed = !m_fullscreen;
Params.BackBufferWidth = w;
Params.BackBufferHeight = h;
Params.hDeviceWindow = m_mainHandle;
Params.SwapEffect = D3DSWAPEFFECT_DISCARD;
Params.BackBufferFormat = mode.Format;
Params.BackBufferCount = 1;
Params.EnableAutoDepthStencil = TRUE;
Params.AutoDepthStencilFormat = D3DFMT_D16;
m_screenWidth = w;
m_screenHeight = h;
if(FAILED(m_Direct3D->CreateDevice(D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL, m_mainHandle, processing,
&Params, &m_Device))) return false;
if(m_Device == NULL) return false;
OneTimeInit();
return true;
}
void CD3DRenderer::OneTimeInit()
{
if(!m_Device) return;
m_Device->SetRenderState(D3DRS_LIGHTING, FALSE);
m_Device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
CalculateProjMatrix(D3DX_PI / 4, 0.1f, 1000);
}
void CD3DRenderer::Shutdown()
{
for(int s = 0; s < m_numStaticBuffers; s++)
{
if(m_staticBufferList[s].vbPtr)
{
m_staticBufferList[s].vbPtr->Release();
m_staticBufferList[s].vbPtr = NULL;
}
if(m_staticBufferList[s].ibPtr)
{
m_staticBufferList[s].ibPtr->Release();
m_staticBufferList[s].ibPtr = NULL;
}
}
m_numStaticBuffers = 0;
if(m_staticBufferList) delete[] m_staticBufferList;
m_staticBufferList = NULL;
if(m_Device) m_Device->Release();
if(m_Direct3D) m_Direct3D->Release();
m_Device = NULL;
m_Direct3D = NULL;
}
void CD3DRenderer::SetClearCol(float r, float g, float b)
{
m_Color = D3DCOLOR_COLORVALUE(r, g, b, 1.0f);
}
void CD3DRenderer::StartRender(bool bColor, bool bDepth,
bool bStencil)
{
if(!m_Device) return;
unsigned int buffers = 0;
if(bColor) buffers |= D3DCLEAR_TARGET;
if(bDepth) buffers |= D3DCLEAR_ZBUFFER;
if(bStencil) buffers |= D3DCLEAR_STENCIL;
if(FAILED(m_Device->Clear(0, NULL, buffers, m_Color, 1, 0)))
return;
if(FAILED(m_Device->BeginScene())) return;
m_renderingScene = true;
}
void CD3DRenderer::ClearBuffers(bool bColor, bool bDepth,
bool bStencil)
{
if(!m_Device) return;
unsigned int buffers = 0;
if(bColor) buffers |= D3DCLEAR_TARGET;
if(bDepth) buffers |= D3DCLEAR_ZBUFFER;
if(bStencil) buffers |= D3DCLEAR_STENCIL;
if(m_renderingScene) m_Device->EndScene();
if(FAILED(m_Device->Clear(0, NULL, buffers, m_Color, 1, 0)))
return;
if(m_renderingScene)
if(FAILED(m_Device->BeginScene())) return;
}
void CD3DRenderer::EndRendering()
{
if(!m_Device) return;
m_Device->EndScene();
m_Device->Present(NULL, NULL, NULL, NULL);
m_renderingScene = false;
}
void CD3DRenderer::CalculateProjMatrix(float fov, float n, float f)
{
if(!m_Device) return;
D3DXMATRIX projection;
D3DXMatrixPerspectiveFovLH(&projection, fov,
(float)m_screenWidth/(float)m_screenHeight, n, f);
m_Device->SetTransform(D3DTS_PROJECTION, &projection);
}
void CD3DRenderer::CalculateOrthoMatrix(float n, float f)
{
if(!m_Device) return;
D3DXMATRIX ortho;
D3DXMatrixOrthoLH(&ortho, (float)m_screenWidth,
(float)m_screenHeight, n, f);
m_Device->SetTransform(D3DTS_PROJECTION, &ortho);
}
int CD3DRenderer::CreateStaticBuffer(VertexType vType,
PrimType primType, int totalVerts,
int totalIndices, int stride, void **data,
unsigned int *indices, int *staticId)
{
void *ptr;
int index = m_numStaticBuffers;
if(!m_staticBufferList)
{
m_staticBufferList = new stD3DStaticBuffer[1];
if(!m_staticBufferList) return UGP_FAIL;
}
else
{
stD3DStaticBuffer *temp;
temp = new stD3DStaticBuffer[m_numStaticBuffers + 1];
memcpy(temp, m_staticBufferList,
sizeof(stD3DStaticBuffer) * m_numStaticBuffers);
delete[] m_staticBufferList;
m_staticBufferList = temp;
}
m_staticBufferList[index].numVerts = totalVerts;
m_staticBufferList[index].numIndices = totalIndices;
m_staticBufferList[index].primType = primType;
m_staticBufferList[index].stride = stride;
m_staticBufferList[index].fvf = CreateD3DFVF(vType);
if(totalIndices > 0)
{
if(FAILED(m_Device->CreateIndexBuffer(sizeof(unsigned int) *
totalIndices, D3DUSAGE_WRITEONLY, D3DFMT_INDEX16,
D3DPOOL_DEFAULT,
&m_staticBufferList[index].ibPtr,
NULL))) return UGP_FAIL;
if(FAILED(m_staticBufferList[index].ibPtr->Lock(0, 0,
(void**)&ptr, 0))) return UGP_FAIL;
memcpy(ptr, indices, sizeof(unsigned int) * totalIndices);
m_staticBufferList[index].ibPtr->Unlock();
}
else
{
m_staticBufferList[index].ibPtr = NULL;
}
if(FAILED(m_Device->CreateVertexBuffer(totalVerts * stride,
D3DUSAGE_WRITEONLY, m_staticBufferList[index].fvf,
D3DPOOL_DEFAULT, &m_staticBufferList[index].vbPtr,
NULL))) return UGP_FAIL;
if(FAILED(m_staticBufferList[index].vbPtr->Lock(0, 0,
(void**)&ptr, 0))) return UGP_FAIL;
memcpy(ptr, data, totalVerts * stride);
m_staticBufferList[index].vbPtr->Unlock();
*staticId = m_numStaticBuffers;
m_numStaticBuffers++;
return UGP_OK;
}
int CD3DRenderer::Render(int staticId)
{
if(staticId >= m_numStaticBuffers) return UGP_FAIL;
if(m_activeStaticBuffer != staticId)
{
if(m_staticBufferList[staticId].ibPtr != NULL)
m_Device->SetIndices(m_staticBufferList[staticId].ibPtr);
m_Device->SetStreamSource(0,
m_staticBufferList[staticId].vbPtr, 0,
m_staticBufferList[staticId].stride);
m_Device->SetFVF(m_staticBufferList[staticId].fvf);
m_activeStaticBuffer = staticId;
}
if(m_staticBufferList[staticId].ibPtr != NULL)
{
switch(m_staticBufferList[staticId].primType)
{
case POINT_LIST:
if(FAILED(m_Device->DrawPrimitive(D3DPT_POINTLIST,
0, m_staticBufferList[staticId].numVerts)))
return UGP_FAIL;
break;
case TRIANGLE_LIST:
if(FAILED(m_Device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0,
0, m_staticBufferList[staticId].numVerts / 3,
0, m_staticBufferList[staticId].numIndices)))
return UGP_FAIL;
break;
case TRIANGLE_STRIP:
if(FAILED(m_Device->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0,
0, m_staticBufferList[staticId].numVerts / 2,
0, m_staticBufferList[staticId].numIndices)))
return UGP_FAIL;
break;
case TRIANGLE_FAN:
if(FAILED(m_Device->DrawIndexedPrimitive(D3DPT_TRIANGLEFAN, 0,
0, m_staticBufferList[staticId].numVerts / 2,
0, m_staticBufferList[staticId].numIndices)))
return UGP_FAIL;
break;
case LINE_LIST:
if(FAILED(m_Device->DrawIndexedPrimitive(D3DPT_LINELIST, 0,
0, m_staticBufferList[staticId].numVerts / 2,
0, m_staticBufferList[staticId].numIndices)))
return UGP_FAIL;
break;
case LINE_STRIP:
if(FAILED(m_Device->DrawIndexedPrimitive(D3DPT_LINESTRIP, 0,
0, m_staticBufferList[staticId].numVerts,
0, m_staticBufferList[staticId].numIndices)))
return UGP_FAIL;
break;
default:
return UGP_FAIL;
}
}
else
{
switch(m_staticBufferList[staticId].primType)
{
case POINT_LIST:
if(FAILED(m_Device->DrawPrimitive(D3DPT_POINTLIST,
0, m_staticBufferList[staticId].numVerts)))
return UGP_FAIL;
break;
case TRIANGLE_LIST:
if(FAILED(m_Device->DrawPrimitive(D3DPT_TRIANGLELIST, 0,
(int)(m_staticBufferList[staticId].numVerts / 3))))
return UGP_FAIL;
break;
case TRIANGLE_STRIP:
if(FAILED(m_Device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0,
(int)(m_staticBufferList[staticId].numVerts / 2))))
return UGP_FAIL;
break;
case TRIANGLE_FAN:
if(FAILED(m_Device->DrawPrimitive(D3DPT_TRIANGLEFAN, 0,
(int)(m_staticBufferList[staticId].numVerts / 2))))
return UGP_FAIL;
break;
case LINE_LIST:
if(FAILED(m_Device->DrawPrimitive(D3DPT_LINELIST, 0,
m_staticBufferList[staticId].numVerts / 2)))
return UGP_FAIL;
break;
case LINE_STRIP:
if(FAILED(m_Device->DrawPrimitive(D3DPT_LINESTRIP, 0,
m_staticBufferList[staticId].numVerts)))
return UGP_FAIL;
break;
default:
return UGP_FAIL;
}
}
return UGP_OK;
}
Initialize()和OneTimeInit()两个函数。这两个函数就像本书第1章中的演示程序一样用于初始化Direct3D。Initialize()函数像前面一样以相同的方式设置Direct3D,所以该函数没有什么新的东西。该函数的最后部分调用OneTimeInit()函数,以完成附加的初始化工作。Initialize()函数以窗口宽度(w)、高度(h)和窗口句柄(mainWin)以及一个指向窗口是否以全屏形式显示的标识(fullScreen)为参数。OneTimeInit()函数在此设置默认的投影矩阵。
Shutdown()函数主要负责清理所有渲染系统用过的内存。该函数在此循环检查,并释放所有的静态缓存数据,清理静态缓存列表,释放特定的Direct3D对象。
接下来是SetClearCol()、StartRender()、ClearBuffers()和EndRendering()函数。SetClearCol()函数以清屏的红绿蓝三色的浮点值作为参数。StartRender()和ClearBuffers()函数使用三个布尔值指明系统是否清除颜色、深度和stencil缓存。最后一个参数对渲染没有任何作用。StartRender()函数用于启动一个新的场景,而ClearBuffers()用于清理已经启动的场景。EndRendering()函数没有任何参数,用于停止场景的渲染。
下来是CalculateProjMatrix()和CalculateOrthoMatrix()函数。CalculateProjMatrix()函数用于创建平行投影矩阵,它有三个参数:视场(fv)、近距离(n)和远距离值(f)。CalculateOrthoMatrix()函数和CalculateProjMatrix()函数的功能相同,不过它创建的是一个正交投影矩阵,而且他只有两个参数:近距离值(n)和远距离值(f)。
渲染系统中的倒数第二个函数是CreateStaticBuffer()。该函数的功能是设置静态缓存对象中的一个对象内容,并将其添加到静态缓存列表中。该函数的参数包括:正在使用的顶点类型(本书将在稍后的游戏开发项目中复习该参数)、渲染该几何图形(例如:三角形列表、顶点)的指令、几何图形中的顶点总数和指向新创建的将要保存的静态缓存ID的变量指针。最后一个参数用于保存静态缓存ID,这样就在绘制几何图形时可以将该值传递给Render()函数。 CreateStaticBuffer()函数设置一个静态缓存对象内容,并将其添加到静态缓存列表中。在将该静态缓存添加到列表后,就会创建索引和顶点缓存,并保存静态缓存ID,增加静态缓存总数。使用矩阵列表可以简化对象列表的创建。
// 设置静态缓存对象中的一个对象内容,并将其添加到静态缓存列表中
int CD3DRenderer::CreateStaticBuffer(
VertexType vType, // 正在使用的顶点类型
PrimType primType, // 渲染该几何图形(例如:三角形列表、顶点)的指令
int totalVerts, // 几何图形中的顶点总数
int totalIndices, // 几何图形中的索引总数
int stride, // 单个顶点尺寸的幅度值
void **data, // 保存顶点的数组
unsigned int *indices, //保存索引的数组
int *staticId // 保存静态缓存ID的指针
);
这里,渲染系统中的最后一个函数是Render()。这个篇幅很长的函数包含了一个参数:静态缓存ID,它会将与该ID相关的几何图形渲染到屏幕上。函数最前面的部分快速检查静态缓存ID以确保其有效。接下来,该函数检查静态缓存ID是否可用。如果可用,它就会调用DrawPrimitive()函数渲染图形。如果静态缓存不可用,它就会调用SetStreamSource()和SetFvF()两个函数。可以像绘制无索引几何图形一样,Render()函数也可以绘制索引几何图形(使用索引的几何图形)。