SceneManager分析
最近以为写引擎,需要研究ogre,以下是研究场景管理的结果。
Ogre使用插件机制来实现场景管理功能。ScenceManager只是接口,他可以有很多具体的实现。Ogre允许在同一时刻同一场景中使用多个Scene Manager,这样在不同的场景类型切换时带来好处。(v 1.4 版本后加了一个SceneManagerEnumerator)。
场景管理主要工作包括以下几点:
1.可移动、不可移动和可渲染物体的创建删除。
2.场景查询。
3.渲染队列。
4.动态阴影。
一. 场景对象创建
场景中的所有对象,包括可移动与不可移动的:Camera、Light、SceneNode、Entity、ManualObject、BillboardChain、RibbonTrail、ParticleSystem、BillboardSet、Animation、AnimationState、StaticGeometry、MovableObject等13种物体的createXXX、getXXX、hasXXX、destroyXXX.都由场景管理器来创建。场景中的任何东西都由场景管理器来管理。任何通过场景管理器得到的东西,都必须由场景管理器来销毁。用户不能delete通过由场景管理器得到的指针。
SkyDome, SkyPlane, SkyBox
主要的相似点是它们与相机保持一个常量的距离。它们可以在场景中其它对象之前或是之后渲染。它们使用普通的ogre material,因此纹理动画与其他纹理没有什么不同。它们可以被场景管理器打开或关闭,与相机的距离也可以设置。
skyplane 是一个平面。用距离和法线定义它与相机的位置关系。可以弯曲,可以分多个段,可对纹理进行多次平铺。
skydome由五个平面组成,底部空。使用改变纹理坐标的方式来达到外观上的曲率变化。有一个值用来调节,值越低,曲率越柔和,值越高,越陡峭。
skybox 像skydome,但他不能“弯曲”材质坐标。它可以使用立方材质。可使用硬件加速功能达到很好渲染效率。
SkyDome, SkyPlane, SkyBox等3种天空形式的setSkyXXX、isSkyXXX、getSkyXXX、
getSkyXXXGenParameters.以及setFog和getFog等参数的操作。
以下是这些操作一览:
1.Create函数
2.Destroy函数
3.Set函数
4.Get函数
二. 场景查询
Ogre的场景查询目前分为3种,一种是相交查询,一种是射线查询,一种是区域查询。查询的结果分为两种,一种是可运动对象(movable objects),一种是关卡的一部分(world geometry)。对于不同的场景管理器,同种查询得到的结果是不一样的。
其实查询可以直接做到场景管理器中,不过Ogre另外开辟了一个类,专门负责处理查询,就是SceneQuery。这个类的查询操作,其实就是包装场景管理器的查询接口。
相交查询,主要是查询两两相交的可运动对象,以及可运动对象和关卡子部分。
射线查询也是这种。
区域查询,是设定一个区域,如AABB区域,或者球形区域,或者由多面体组成的区域,查询在该区域中的可运动对象。
所有这些查询都是 maskble的,这表明可以在查询时过滤掉不关心的对象类型。如球查询时,只想看看它包含了多少lights,其他的对象即使包含在球里也不必返回,实际上根本不用计算。以下是UML图
1. WorldFragmentType和WorldFragment
前面说过查询的结果分两种, movable objects和world geometry。而world geometry根据不同的查询方式得到的结果又分几种。WorldFragment就是表示world geometry查询结果的结构。WorldFragmentType是指用那种查询方式得到的结果。
enum WorldFragmentType {
WFT_NONE,
WFT_PLANE_BOUNDED_REGION, // Region Scene Query结果
WFT_SINGLE_INTERSECTION, // Ray Scene Query结果
WFT_CUSTOM_GEOMETRY, // 自定义结果
WFT_RENDER_OPERATION // ?没发现有用
};
struct WorldFragment {
WorldFragmentType fragmentType;
Vector3 singleIntersection; // WFT_SINGLE_INTERSECTION
std::list<Plane>* planes; // WFT_PLANE_BOUNDED_REGION
void* geometry; // WFT_CUSTOM_GEOMETRY
RenderOperation* renderOp; // ?没发现有用
};
2. SceneQuery和SceneQueryListener
SceneQuery场景查询的基类,负责保存与之相关的SceneManager、查询掩码和查询类掩码(SceneQuery类型掩码需要自己定义,也自己解释)和场景查询支持的World Fragment Type。
SceneManager* mParentSceneMgr;
uint32 mQueryMask;
uint32 mQueryTypeMask;
std::set<WorldFragmentType> mSupportedWorldFragments;
WorldFragmentType mWorldFragmentType;
在SceneManager中定义了几种类型掩码,如下:
static uint32 WORLD_GEOMETRY_TYPE_MASK; // for world geometry
static uint32 ENTITY_TYPE_MASK; // for entities
static uint32 FX_TYPE_MASK; // for effects like billboardsets / particle systems
static uint32 STATICGEOMETRY_TYPE_MASK; // for StaticGeometry
static uint32 LIGHT_TYPE_MASK; // for lights
static uint32 USER_TYPE_MASK_LIMIT; // max limit
SceneQueryListener是负责执行场景查询的回调抽象类,在3种查询的执行中把
SceneQueryListener的指针传递给SceneQuery的子类。
virtual void execute(SceneQueryListener* listener);
以下是SceneQueryListener的定义
class _OgreExport SceneQueryListener
{
public:
virtual ~SceneQueryListener() { }
virtual bool queryResult(MovableObject* object) = 0;
virtual bool queryResult(SceneQuery::WorldFragment* fragment) = 0;
};
3. RegionSceneQuery RaySceneQuery IntersectionSceneQuery
上面3个查询的功能很简单,主要负责执行查询和保存查询结果。RegionSceneQuery又分别派生AxisAlignedBoxSceneQuery、SphereSceneQuery和PlaneBoundedVolumeListSceneQuery。
4.SceneManager中的SceneQuery
上面提过SceneQuery其实就是包装场景管理器的查询接口。下面代码证明了这个说法:
class DefaultRaySceneQuery : public RaySceneQuery
{
public:
DefaultRaySceneQuery(SceneManager* creator);
~DefaultRaySceneQuery();