实 验 二 报 告
(2018-2019 学年第二学期)
3D 游戏引擎架构设计基础
(Foundations of 3D Game Engine Architecture Design)
学生姓名
成绩:
实验题目:
学号:
年级 16 级,班级:5 班
任课教师签名:
日期:
目录:
1. 实验内容描述: 引擎名称和版本,将分析的引擎模块名称
2. 将分析的引擎模块的主要功能描述
3. 将分析的引擎模块的主要类和类关系描述
4. 类的数据成员和成员函数描述
5. 总结
实验报告:
版本:1.11.5
1.
引擎名称:OGRE
模块名称:资源模块
2.
游戏资源管理主要做什么?
·资源组织
根据各种资源的特性和使用的方式等,设计资源数据的内存组织方式
·资源管理
统一的资源处理方法,如:内存分配、资源状态管理、加载和卸载操作、资源调度算法、以
及多线程管理等
游戏资源管理目的:通过有效地组织和管理各类资源,能在合适的时候、快速地提供给游戏
程序。
游戏资源管理的种类
文件管理:游戏的资源是以文件的形式存储的
·包括文件的加载和卸载,文件解析,格式转换,打包和传输资源文件等。
内存管理:使用资源时需要加载到内存
·内存分配
·访问保证
·内存释放
状态管理:资源当前的状态,如已定义、已加载、已移除等
资源调度算法:在有限的内存中,为使用资源的程序提供最好的方案
·优先队列
·LRU
多线程处理:资源管理的并行处理
3.
类和类关系描述
3.1 主要类的关系
简单来说,他们之间的关系
Resource、ResourceManager、ResourceGroupManager 这三个类是 OGRE 引擎进行资源
管理的三个核心类,Ogre 把资源分为“Font”、“GpuProgram”、“Material”、“Mesh”、
“Skeleton”和“Texture”等类型,它们分别用 Font、GpuProgram、Material、Mesh、Skeleton、
Texture 等同名的类对象来描述,这些类都直接从 Resource 基类派生。Ogre 的 Resource 对象
都由 ResourceManager 来管理。不同类型资源的管理,分别由不同的资源管理器来实现,比
如以上各种类型资源都对应着各自的资源管理器,FontManager、GpuProgramManager、
MaterialManager 、 MeshManager 、 SkeletonManager 、 TextureManager 等 , 它 们 都 以
ResourceManager 作为自已的基类。各种类型资源类对象的创建、Load/Unload、销毁等操作,
都由相应的 ResourceManager 来完成。但 Ogre 的对资源的管理还不仅限于此。为了更方便
资源的使用,提高资源的使用的效率。Ogre 中有一个被称为 ResourceGroupManager 的类,
其 中 内 嵌 了 一 个 ResourceGroup 的 结 构 定 义 , 很 明 显 定 义 ResourceGroup 只 是 为 了
ResourceGroupManager 内部使用。在需要进行 3D 场景展示的一般应用中,经常会遇到需要
进行场景切换的时候,比如游戏中的关卡切换时,虚拟现实中角色由一个地点转换到另一个
地点时等等。而在渲染每个场景时所需的资源往往涉及了所有的资源类型,一旦场景发生切
换,当前所使用的大量资源都需要被逐一卸载,而新的场景所需的各类资源要逐一被加载。
在游戏编程时,可以在自已编写的关卡管理器中处理类似工作,这明显会产生额外的工作量,
更麻烦的是这部分代码逻辑可能需要在每个应用中被重复编写,而如果借助 Ogre 提供的
ResourceGroup 就可以直接方便地实现类似功能了。
Ogre 处理一个资源的简单流程:1、Ogre 启动时对外部资源并不了解,通过添加资源名称、
组名、文件路径等信息,录入资源信息(通常 Ogre 通过外面脚本定义资源路径)。2、将一
个资源归入一个资源组,并通过定义的信息,在内存中实例化相应的资源(具体的创建过程
是由 ResourceManager 调用 Resource 完成的)。
4.
Undefined:这是程序开始时,所有资源的缺省状态。除非它们被声明,ogre 对程序用到的资
源一无所知,手工调用代码 ResourceGroupManager::declareResource()或在脚本中被解析之
后,资源的状态变为 Declared
Declared:声明就是告诉 ogre 想要加载某些资源。ResourceGroupManager::declareResource()
总是有效的,包括在渲染系统初始之前,这与 ResourceManager::create()不同,因为后者依
赖于渲染系统。
Unloaded:通过调用
ResourceGroupManager::initialiseAllResourceGroup(),ResourceGroupManager::initialiseResource
Group(),或 Root::initialise()(它会初始化在此调用之前所有声明的资源),资源状态进入到
Unloaded,资源会占用一点内存来保存它的定义的实例,但是资源本身还没有加载到内存。
从另一角度看(从 Loaded 到 Unloaded),引起状态变化的调用有:
ResourceManager::unload(),ResourceManager::unloadAll(),
ResourceManager::unloadAllUnreferencedResources(),ResourceGroupManager::unloadResource
Group(), or Resource::unload().所有这些调用仍会保存资源实例,但真正的资源数据会从内存
中卸载。
Loaded: 这 种 状 态 下 , 所 有 数 据 都 变 得 有 效 。 与 此 状 态 有 关 的 调 用 有 Resource::load(),
ResourceManager::reload(),
Resource::reload(),
ResourceManager::reloadAll(),
and
ResourceGroupManager:: loadResourceGroup().
ResourceManager::reloadAllUnreferencedResources(),
ResourceManager::load(),
Resource
该类定义了所有 Ogre 中可导入资源的统一接口,保存了资源的信息。该类为抽象类
类的定义如下
/// Creator
ResourceManager* mCreator;
/// Unique name of the resource
String mName;
/// The name of the resource group
String mGroup;
/// Numeric handle for more efficient look up than name
ResourceHandle mHandle;
/// The size of the resource in bytes
size_t mSize;
/// Optional manual loader; if provided, data is loaded from here instead of a file
ManualResourceLoader* mLoader;
mCreator:每个资源类都是由 ResourceManager 创建,该变量保留创建者的指针。
mName/mGroup/mHandle/mSize:资源类的名称,所属的组名,句柄以及占用内存大小。
mLoader:一般资源可以有 Ogre 自动导入,当需要手动进行导入时,需要设置该变量来设
定手动导入操作的类,实现不同的导入操作。
mLoadingState:资源当前状态。AtomicScalar是一个封装原子操作的模板类,这个为多线
程资源管理提供了可能。
资源状态 LoadingState 枚举如下
enum LoadingState
{
/// Not loaded
LOADSTATE_UNLOADED,
/// Loading is in progress
LOADSTATE_LOADING,
/// Fully loaded
LOADSTATE_LOADED,
/// Currently unloading
LOADSTATE_UNLOADING,
/// Fully prepared
LOADSTATE_PREPARED,
/// Preparing is in progress
LOADSTATE_PREPARING
};
从上述枚举中可以看出,一个资源有两个非常重要的状态:load 和 prepare。Resource 类
中的函数基本也就是围绕着这两个状态改变而进行的。
virtual void preLoadImpl(void) {}
virtual void postLoadImpl(void) {}
virtual void preUnloadImpl(void) {}
virtual void postUnloadImpl(void) {}
virtual void prepareImpl(void) {}
virtual void unprepareImpl(void) {}
virtual void loadImpl(void) = 0;
virtual void unloadImpl(void) = 0;
可以看出这些函数的作用就是定义 load 和 prepare 前、中。后需要进行的操作。 这里用到了设计
模式的 Template 模板,定义接口,将所有实现都延迟到了子类中。 同时 Resource 也提供了更细颗
粒度的方法来控制 load 和 prepare 过程——监听。Resource 定义了公共内嵌类 Listener,可以通过
继承该监听类回调函数,灵活地设置资源状态改变时的操作。
引用 Resource 时,使用的是智能指针。
typedef SharedPtr ResourcePtr;
在 ResourceManager 中都是以这样的形式出现的。
ResourceManager
Resource 类是负责具体资源的 load 和 prepare 细节,而 ResourceManager 类是负责创建、删除和调
用 Resource 的,并管理 ResourcePool 方便同一资源的多次使用。
该类的主要数据结构定义如下:
typedef HashMap< String, ResourcePtr > ResourceMap;
typedef HashMap< String, ResourceMap > ResourceWithGroupMap;
typedef map::type ResourceHandleMap;
ResourceMap 将资源名称与资源一一对应起来。
ResourceWithGroupMap 则是将资源进行一定分组管理。
ResourceHandleMap 则是将资源句柄与资源对应。
ResourceGroupManager
如果说 ResourceManger 是对 Resource 的管理,那 ResourceGroupManager 就是对 ResourceManger 的
集中管理。刚才提到过 ResourceManager 中将资源进行了分组管理,ResourceGroupManager 就针对
这每个分组进行了管理。
分组的信息记录在结构体 ResourceGroup 中
/// Group name
String name;
/// Group status
Status groupStatus;
/// List of possible locations to search
LocationList locationList;
/// Created resources which are ready to be loaded / unloaded
// Group by loading order of the type (defined by ResourceManager)
typedef map::type LoadResourceOrderMap;
LoadResourceOrderMap loadResourceOrderMap;
上述是 ResourceGroup 中的部分代码。
该结构体中定义了一个 Group 的名字、状态、文件所在的位置,以及导入资源的顺序。关于导入顺序,
Ogre 代码中给出了一个例子,在导入 mesh 信息前需要导入骨骼和材质,所以后者的优先级就高于前
者。 拥有了这样的 Group 描述信息,ResourceGroupManager 就可以进行管理了。
/// Map from resource group names to groups
typedef map::type ResourceGroupMap;
ResourceGroupMap mResourceGroupMap;
默认在 ResourceGroupManager 构造函数中,创建三个分组 General、Internal、Autodetect。
还有另一个重要的结构体 ResourceDeclaration。
String resourceName;
String resourceType;
ManualResourceLoader* loader;
NameValuePairList parameters;
loader 是手动导入资源的类指针;parameters 用于 StringInterface 类设置类成员变量。
该结构定义了一个资源的信息,但注意,Ogre 中定义了一个资源并没有真正地将它导入进来,也就
是并没有生成资源句柄,只是录入了一些资源信息而已。
这些数据结构定义就是用来保存 ResourceDeclaration 信息的,ResourceDeclaration 同样也保存在
ResourceGroup 中。
/// List of resource declarations
typedef list::type ResourceDeclarationList;
typedef map::type ResourceManagerMap;
Ogre 处理一个资源的简单流程了。
1、Ogre 启动时对外部资源并不知晓,通过添加资源名称、组名、文件路径等信息,录入资源信
息(通常 Ogre 通过外面脚本定义资源路径);
2、将一个资源归入一个资源组,并通过定义的信息,在内存中实例化相应的资源(具体的创建
过程是由 ResourceManager 调用 Resource 完成的);
5.下面是游戏资源的加载和创建过程:
实验总结:
本次实验我研究了 OGRE 的资源管理模块。了解了其中的流程,感觉收获很大。