2006年12月24日日曜日

自己写游戏引擎

目标是写一个多渲染器的游戏引擎,先来谈其中的图形渲染部分,毕竟这个是关键,如果你想了解物理,网络或者其他的内容,很遗憾,这里没有(也许 要等到很后面的了)。可以看一下我之前写的两篇blog,《游戏引擎中多渲染器的设计与实现》(1,2),里面讲到了用动态链接库实现的一种的方法,这里 就不复习了,假设你已经完成了前面最基本的实现。
引擎的核心部分是一切的前提,所以我们从这里开始。我强烈建议你在开始动手之前先看一下常用的3d模 型的格式的解析——因为引擎的底层的基本数据类型的设计是非常重要,而且在后面是相当难以改动的——看一下常用的模型格式的解析,可以帮助你设计合理的底 层数据类型(这里指的是像 纹理,材质,三角形等的表示)。先以MilkShape 3D的格式MS3D为例。在他们的网站上,你可以下到一个C++解析组件,先看头文件中的数据类型:
typedef struct
{
byte flags; // SELECTED | SELECTED2 | HIDDEN
float vertex[3]; //
char boneId; // -1 = no bone
byte referenceCount;
}
ms3d_vertex_t;

typedef
struct
{
word flags;
// SELECTED | SELECTED2 | HIDDEN
word vertexIndices[3]; //
float vertexNormals[3][3]; //
float s[3]; //
float t[3]; //
byte smoothingGroup; // 1 - 32
byte groupIndex; //
}
ms3d_triangle_t;

typedef
struct
{
word edgeIndices[
2];
}
ms3d_edge_t;

typedef
struct
{
byte flags; // SELECTED | HIDDEN
char name[32]; //
word numtriangles; //
word* triangleIndices; // the groups group the triangles
char materialIndex; // -1 = no material
}
ms3d_group_t;

typedef
struct
{
char name[32]; //
float ambient[4]; //
float diffuse[4]; //
float specular[4]; //
float emissive[4]; //
float shininess; // 0.0f - 128.0f
float transparency; // 0.0f - 1.0f
char mode; // 0, 1, 2 is unused now
char texture[128]; // texture.bmp
char alphamap[128]; // alpha.bmp
}
ms3d_material_t;
从代码中可以看出,Texture和Material都有name,都有 fileName(指的是贴图文件名),Material中有4中颜色(这和图形学里面的物理意义也是一致的),然后就是Texture可以是alpha 贴图,有属性transparency,这些都可以作为我们设计的参考。我们就停在这里,在后面我们还会看到这个经典的格式是怎样影响了我们的渲染批次的 设计的。
开始建立基础的数据类型:
typedef struct _colorStruct
{
float r;
float g;
float b;
float a;
}
UHECOLOR;

typedef
struct _materialStruct
{
UHECOLOR Diffuse;
UHECOLOR Ambient;
UHECOLOR Specular;
UHECOLOR Emissive;
float Power;
}
UHEMATERIAL;

typedef
struct _textureStruct
{
bool bAlpha;
float fAlpha;
char chName[128];
void *pData;
WORD texType;
WORD texUsage;
}
UHETEXTURE;
这里很奇怪的是,我在Texture里面用了一个void*。恩,这个是用来存储不同API中 的纹理。比如,如果是用的DX,我会一直把这个void*看成LPDIRECT3DTEXTURE9*。在引擎中,我会大量的用到指针间的静态转换(非运 行时)(hard core c++高手看到我的代码会说应该用static_cast,而不是用c语言里面的(typename)(…)。恩,我同意,只是有时候我忘了,或者懒 了)。
有很多的引擎,会将Texture单独的抽象成一个类,有相应的行为(成员函数);但是,我觉 得,Texture的实质是一堆数据,既然Texture Manager(纹理管理器,简记为TextureMng)是必要的,那么单独的Texture的类的设计似乎没有太多的必要,我们有 TextureMng的管理,这样似乎足够了,我暂时不想把事情搞得太复杂。
ps:面对对象是很好的方法,但是引擎中的很多“东西”的实质还是数据的集合,在把他们变成类之前,考虑一下于他们相关的操作,考虑一下用类实现一些很小的数据体的时候效率的影响。。。像Quake3和Ogre就是两个设计的极端,是不是有时候结合两者会好一些?)
还有很多的数据类型要写,比如很多的3D中的数学类,像Vector3,Matrix4,先把他们放一下。我比较喜欢迭代开发的感觉——用比较短的周期,作出简单的可运行的程序,然后在改进。好,我们就以dx sdk里面的例子开始,先实现一个Vertex的例子。
我们需要定义一个VertexBuffer的抽象类,来给GL和DX提供公共的接口。
//File: UHEVertexBuffer.h
#ifndef _INCLUDE_UHEVERTEXBUFFER_H
#define _INCLUDE_UHEVERTEXBUFFER_H

namespace UHEngine
{
class _UHE_Export VertexBuffer
{
public:
// init and malloc a bunch of memory.
virtual void Create( UINT length, DWORD dwFVF, DWORD dwUsage, DWORD dwPoolType ) = 0;

// lock the whole buffer.
// then can write to the buffer using GetLockedData() .
virtual void Lock() = 0;

// unlock the locked data.
virtual void Unlock() = 0;

// delete .
virtual void Release() = 0;

// return the vertex buffer.
// ex. in dx, this would be trans to LPDIRECT3DVERTEXBUFFER9 .
virtual void *GetVertexBuffer() = 0;

// return the addr of buffer after method Lock() .
virtual void *GetLockedData() = 0;

// return FVF type of vertex in the buffer.
virtual DWORD GetFVF() = 0;

// return the length of buffer( actual length in memory ).
virtual UINT GetLength() = 0;

// return Usage of vertex in the buffer.
virtual DWORD GetUsage() = 0;

// reutrn pool management type.
virtual DWORD GetPoolType() = 0;
}
;
}

#endif
(ps:多渲染器引擎中,用抽象类(纯虚类)是一种实现中的设计,即使用不同的API的底层实现,对于用户来说,都是同一接口)
在DX9渲染器中我们加入文件,UHEDX9VertexBuffer.h和UHEDX9VertexBuffer.cpp。
//File: UHEDX9VertexBuffer.h
#ifndef _INCLUDE_UHEDX9VERTEXBUFFER_H
#define _INCLUDE_UHEDX9VERTEXBUFFER_H

#include
"UHEDX9RenderBase.h"
#include
"UHEVertexBuffer.h"

namespace UHEngine
{
class _UHE_Export DX9VertexBuffer : public VertexBuffer
{
public:
DX9VertexBuffer( LPDIRECT3D9
&pDX9, LPDIRECT3DDEVICE9 &pDevice ) ;
~DX9VertexBuffer() ;

void Create( UINT length, DWORD dwFVF, DWORD dwUsage, DWORD dwPoolType ) ;
void Lock() ;
void Unlock() ;
void Release() ;

void *GetVertexBuffer() { return (void*)m_pDXVertexBuffer; } ;
void *GetLockedData() { return (void*)m_pData; } ;
DWORD GetFVF()
{ return m_dwFVF; } ;
UINT GetLength()
{ return m_length; } ;
DWORD GetUsage()
{ return m_dwUsage; } ;
DWORD GetPoolType()
{ return m_dwPoolType; } ;

private:
LPDIRECT3D9 m_pDX9 ;
LPDIRECT3DDEVICE9 m_pDevice ;

LPDIRECT3DVERTEXBUFFER9 m_pDXVertexBuffer ;
void *m_pData ;
bool m_bIsLocked ;

DWORD m_dwFVF ;
UINT m_length ;
DWORD m_dwUsage ;
DWORD m_dwPoolType ;
}
;
}

#endif
在UHEDX9VertexBuffer.cpp中,完成这些实现(看一下dx sdk,没有什么值得多说的了)

0 件のコメント: