<@乌龟:>[原创]设计自己的3D图像引擎(4): WuguiEngine 之模型、特效、贴图
1. 前言
话说有一段时间没有更新了,这段时间好好玩了一下,也抽空写了一点程序,把八叉树场景管理写了,也优化了一点资源加载的一些内容。之前对DirectX的好些地方还是没有弄清楚,现在对这些地方至少有了更多的了解。发一下今天完成的八叉树场景管理,里面的模型用的是DirectX示例的老虎,这儿看不太清楚--;
今天主要说说我的引擎中的模型,特效,贴图的实现,至少把大概的意思说清楚。如果还有不懂的地方可以跟我联系。
2. 模型、特效、贴图的资源复用
大家也可以看到了,上面那张图上的老虎不止一个,当同一个模型的数目多到一定程度(比如说10000个),就要考虑一定的优化了,如果每次都傻傻的从文件中把模型读取出来,保存为一个ID3DXMesh,那不仅加载的时间慢的令人无法忍受,而且耗费的内存同样巨大。在WuguiEngine中,我使用了一个比较简单的方法来解决这个问题。
设计模式的中心思想就是对修改关闭,对扩展开放,中心思想就是“变化”,为了方便理解,我使用的方法,很简单,现在用起来很清晰,但是如果对之后的扩展支持不好或者有更好的架构方法还望大家指点。本系列文章的宗旨就是抛砖引玉。
我使用了一个简单的Map来完成这些内容下面给出资源管理器(ResourceManager)的定义
1: class GraphicsDevice;
2:
3: //资源的类型
4: enum ResourceType
5: {
6: RTTexture = 0,
7: RTMesh = 1,
8: RTEffect = 2,
9: };
10:
11: //一个资源的描述
12: //由ResourceType,Void*类型的指针pResource,引用次数useCount组成
13: typedef struct StructRes
14: {
15: public:
16: StructRes(void* pRes, ResourceType type)
17: {
18: pResource = pRes;
19: useCount = 1;
20: resourceType = type;
21: }
22: ResourceType resourceType;
23: void* pResource;
24: int useCount;
25: } StructRes;
26:
27: //资源管理器
28: class ResourceManager
29: {
30: public:
31: static ResourceManager* GetInstance();
32:
33: //根据文件名获取资源文件
34: void* GetD3DResource(string filename,
35: GraphicsDevice* pDevice,
36: ResourceType type);
37:
38: //根据文件名卸载资源文件
39: void DisposeD3DResource(string filename);
40: protected:
41: ResourceManager();
42:
43: map<string, StructRes*> resourceTable;
44: static ResourceManager* pInstance;
45: };
1: map<string, StructRes*> resourceTable;
第一个参数string在目前是资源的文件名,目前引擎还不具备文件系统,读取文件的时候就直接使用的文件名,如果具有一个完整的文件系统,不仅可以从硬盘中,还可以从内存中,压缩文件中,而且还可以在多个位置按先后顺序查找。StructRes*就是之前提过的结构体了。
ResourceManager最重要的方法是GetD3DResource
1: void* ResourceManager::GetD3DResource(string filename, GraphicsDevice* pDevice,
2: ResourceType type)
3: {
4: ResTable::iterator pResult = resourceTable.find(filename);
5:
6: //如果找到了对应的资源(已加载)
7: if (pResult != resourceTable.end())
8: {
9: (*pResult).second->useCount ++;
10: return (*pResult).second->pResource;
11: }
12: ..........
ResTable就是resourceTable的类型,stl的类型写起来太长了,就用typedef处理了一下。
从第4行开始就是查找资源, 首先看看这个文件名所对应的资源是否存在,如果存在,就返回,代码都是调用的基本stl的函数,如果看不太明白可以翻翻stl的帮助文件。
1: //如果没有找到对应的资源
2: else
3: {
4: //如果是需要载入贴图
5: if (type == ResourceType::RTTexture)
6: {
7: IDirect3DTexture9* pTexture;
8:
9: //从filename中创建Texture
10: LPSTR str = UtilityTools::ConvertStringLPSTR(&filename);
11: if (D3DXCreateTextureFromFileExA(pDevice->GetLPDIRECT3DDEVICE9(),
12: str,
13: D3DX_DEFAULT, //width
14: D3DX_DEFAULT, //height
15: D3DX_DEFAULT, //levels
16: 0, //usage
17: D3DFMT_UNKNOWN, //format
18: D3DPOOL_MANAGED, //pool
19: D3DX_DEFAULT,
20: D3DX_DEFAULT,
21: 0,
22: NULL,
23: NULL,
24: &pTexture) != D3D_OK)
25:
26: {
27: throw "Exception To Create Texture";
28: }
29: delete str;
30:
31: //在Table中加入这个Resource
32: StructRes* pRes = new StructRes(pTexture, type);
33: resourceTable.insert(make_pair(filename, pRes));
34:
35: return pTexture;
36: }
37: else if (type == ResourceType::RTMesh)
38: {
39: .......
40: }
41: else if (type == ResourceType::RTEffect)
42: {
43: .......
44: }
接着上面的代码,如果对应文件名的资源没有存在,则根据资源的类型进行加载,调用API函数。
这段代码应该还是比较好理解的,这里就不说了。
贴一段用的时候的代码,以Texture为例,比如需要获取一份IDirect3DTexture9*(pTexture)的资源。
1: ResourceManager* pResourceMgr = ResourceManager::GetInstance();
2:
3: pTexture = (IDirect3DTexture9*)(pResourceMgr->
4: GetD3DResource(_filename, device,ResourceType::RTTexture));
另外还有一个地方就是资源的释放,在StructRes中有一个useCount, 当被引用一次+1,被释放一次-1,最后一个出门的人就关灯锁门了。
3. 模型
之前将了一下资源复用,以贴图为例。贴图的内容相对比较简单,只需要保存一个引用IDirect3DTexture9*类型的指针就好了,但是模型就不一样,模型涉及到很多操作,比如说获取mesh的VertexBuffer,IndexBuffer等等,这些内容也同样需要在ResourceManager中加载,因为他们也是“公有”的内容。
为了更方便的使用模型,我加入了ModelResource类,保存模型中的公有数据
下面就贴上ModelResource的定义
1: class ModelResource : IDisposed
2: {
3: public:
4: ModelResource(string _filename, GraphicsDevice* device);
5: virtual void Dispose();
6:
7: ID3DXMesh* GetD3DMesh();
8: IDirect3DVertexBuffer9* GetVertexBuffer();
9: IDirect3DIndexBuffer9* GetIndexBuffer();
10: BoundingSphere* GetOriginalSphere();
11: protected:
12: void Initialize();
13: void CalculateBoundingSphere();
14:
15: string filename;
16: GraphicsDevice* pDevice;
17: ID3DXMesh* pMesh;
18: IDirect3DVertexBuffer9* pVertexBuffer;
19: IDirect3DIndexBuffer9* pIndexBuffer;
20: BoundingSphere* pOriginalSphere;
21: DWORD FVF;
22: };
我在ModelResource中保存有VertexBuffer,IndexBuffer。OriginalSphere是模型加载时候的包围球,也是属于公有的,方便做变换的时候使用。
代码具体参加ModelResource.cpp,这里就不详细的说了。
模型类中还包含有Material类(主要包含贴图),Transformation类(包含变换),这些代码都是比较好懂的,就不一一解释了。
4. 示例
本节的示例代码在Mainlogic中的Example2中可以找到
初始化一个模型,并加入贴图与Effect的代码看起来像这个样子:
1: //初始化模型
2: pModel = new Model("Content\\Tiger.x", pDevice);
3: pModel->SetDiffuseMap("Content\\Tiger.bmp");
4: pModel->SetPosition(D3DXVECTOR3(0,0,-20));
5:
6: //初始化模型的属性
7: pModel->SetEffect(new SimpleEffect(pDevice, pModel, "SimpleTexture.fx"));
8: pModel->GetEffect()->Initialize();
很简单吧。
5. 后话
还是提示一下,如果需要源代码的就用TortoiseSVN在
http://wuguiengine.googlecode.com/svn/
检出
另外这种文件系统其实是做得非常不好,不过这一块过于费代码,很多地方又是重复的活儿,所以就不太细说,具体可以参见一下”3D游戏引擎设计“这本书,里面这一块大概讲了一些