编写Ogre自定义场景管理器插件的方法

转载请注明出处!http://www.cnblogs.com/pulas/

 

下面开始讲述怎样在一个插件中实现一个自定义的场景管理器。Ogre的场景管理器都是通过工厂方法创建的,所以它不仅提供了SceneManager基类,还提供了SceneManagerFactory基类。

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   /** Class which will create instances of a given SceneManager. */
class _OgreExport SceneManagerFactory : public SceneMgtAlloc
{
protected:
    mutable SceneManagerMetaData mMetaData;
    mutable bool mMetaDataInit;
    /// Internal method to initialise the metadata, must be implemented
    virtual void initMetaData(void) const = 0;
public:
    SceneManagerFactory() : mMetaDataInit(true) {}
    virtual ~SceneManagerFactory() {}
    /** Get information about the SceneManager type created by this factory. */
    virtual const SceneManagerMetaData& getMetaData(void) const
    {
        if (mMetaDataInit)
        {
            initMetaData();
            mMetaDataInit = false;
        }
        return mMetaData;
    }
    /** Create a new instance of a SceneManager.
    @remarks
    Don't call directly, use SceneManagerEnumerator::createSceneManager.
    */
    virtual SceneManager* createInstance(const String& instanceName) = 0;
    /** Destroy an instance of a SceneManager. */
    virtual void destroyInstance(SceneManager* instance) = 0;
 
};

 

下面以四叉树场景管理器为例阐述怎样实现一个自定义的场景管理器。

1. 首先从SceneManagerFactory基类派生出一个QuadtreeSceneManagerFactory工厂类,实现initMetaData()、createInstance()和destroyInstance()接口。

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//-----------------------------------------------------------------------
const String QuadtreeSceneManagerFactory::FACTORY_TYPE_NAME = "QuadtreeSceneManager";
//-----------------------------------------------------------------------
void QuadtreeSceneManagerFactory::initMetaData(void) const
{
    mMetaData.typeName = FACTORY_TYPE_NAME;
    mMetaData.description = "Scene manager organising the scene on the basis of an quadtree.";
    mMetaData.sceneTypeMask = 0xFFFF; // support all types
    mMetaData.worldGeometrySupported = false;
}
//-----------------------------------------------------------------------
SceneManager* QuadtreeSceneManagerFactory::createInstance(
    const String& instanceName)
{
    return OGRE_NEW QuadtreeSceneManager(instanceName);
}
//-----------------------------------------------------------------------
void QuadtreeSceneManagerFactory::destroyInstance(SceneManager* instance)
{
    OGRE_DELETE instance;
}

 

2. 然后从Plugin基类派生出一个QuadtreePlugin类,并在其中添加一个QuadtreeSceneManagerFactory数据成员指针。首先实现插件的install()接口创建一个QuadtreeSceneManagerFactory实例,然后实现initialise()接口在Root对象中添加该工厂实例(实际上最终是在场景管理枚举器中注册了该工厂实例)。实现插件shutdown()接口,以在卸载插件时在Root对象中移除该工厂实例(实际上是在场景管理枚举器中注销该工厂实例),最后实现uninstall()接口,删除QuadtreeSceneManagerFactory实例。

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//---------------------------------------------------------------------
void QuadtreePlugin::install()
{
    // Create objects
    mQuadtreeSMFactory = OGRE_NEW QuadtreeSceneManagerFactory();
}
//---------------------------------------------------------------------
void QuadtreePlugin::initialise()
{
    // Register
    Root::getSingleton().addSceneManagerFactory(mQuadtreeSMFactory);
}
//---------------------------------------------------------------------
void QuadtreePlugin::shutdown()
{
    // Unregister
    Root::getSingleton().removeSceneManagerFactory(mQuadtreeSMFactory);
}
//---------------------------------------------------------------------
void QuadtreePlugin::uninstall()
{
    // destroy
    OGRE_DELETE mQuadtreeSMFactory;
    mQuadtreeSMFactory = 0;
}

 

3. 在程序初始化创建自定义的场景管理器时,即调用Root::createSceneManager()方法时,Root对象通过场景管理枚举器调用QuadtreeSceneManagerFactory实例的createInstance()方法,创建一个QuadtreeSceneManager实例。

4. QuadtreeSceneManager是从SceneManager基类派生出来的一个四叉树场景管理器。Ogre提供的SceneManager类,其内部实现了场景管理的一些通用功能。该类负责组织场景中的物体,并负责把物体发送到渲染系统进行渲染。SceneManager的数据成员较多,基本上都用一个List来管理各种对象。另外,Camera, Light, SceneNode, Entity, BillboardSet, Animation, AnimationState, StaticGeometry等创建都是通过SceneManager实现的,并且创建后直接放入相应的List。

5. 由于在程序初始化时需要创建场景节点和Camera,而自定义的场景管理器为了高效管理场景,需要用到自定义的场景节点和自定义的Camera,所以QuadtreeSceneManager需要覆盖基类SceneManager的createSceneNodeImpl()和createCamera()方法。

 

1
2
3
4
5
6
7
8
/// @copydoc SceneManager::getTypeName
const String& getTypeName(void) const;
/** Creates a specialized QuadtreeNode */
virtual SceneNode * createSceneNodeImpl ( void );
/** Creates a specialized QuadtreeNode */
virtual SceneNode * createSceneNodeImpl ( const String &name );
/** Creates a specialized QuadtreeCamera */
virtual Camera * createCamera( const String &name );

 

6. 在循环渲染时,场景管理器的调用顺序大致如下:

1) SceneManager::_renderScene();

2) SceneManager:: _updateSceneGraph(); 从根节点开始递归的调用所有SceneNode的_update(),主要是计算了transform;

3) SceneManager::prepareRenderQueue(); 准备渲染队列RenderQueue。在SceneManager类有一个RenderQueue数据成员,RenderQueue主要是为了把Objects按照材质分组,它还将管理对象的渲染优先权;

4) QuadtreeSceneManager::_findVisibleObjects(); 查找可见物体。自定义场景管理器需要覆盖基类的该方法,并在其中实现自己的物体是否可见的判断算法。对于QuadtreeSceneManager,通过递归调用QuadtreeSceneManager::walkOctree()遍历四叉树来对所有的SceneNode进行了可见性判断,如果可能可见,则通过调用QuadtreeNode::_addToRenderQueue()加入到RenderQueue中;

在以上调用过程中,有些需要调用场景节点的方法。对于自定义场景节点,需要覆盖其基类SceneNode的一些方法,以实现自定义的算法。下面开始讲解怎样实现自定义的场景节点。

 

7. Ogre中同场景树有关的类图如下图所示。在图的顶部是一个抽象类Renderable,作为场景中可以渲染的object的父类,场景树中的每个节点都是可渲染的,所以都是从Renderable继承而来。

clip_image002

同场景树相关的类主要是Node、SceneNode。SceneNode的基类Node提供如下方法用于建立一棵Node树:

clip_image004

通过SceneNode的如下方法,OGRE把Entity挂接到SceneNode上。

clip_image006

Entity和SceneNode的关系如下图所示。

clip_image008

在前述的场景管理器的调用顺序的第二步中,需要从根节点开始递归的调用所有SceneNode的_update(),在其中将更新节点的包围盒。

 

1
2
3
4
5
void SceneNode::_update(bool updateChildren, bool parentHasChanged)
{
    Node::_update(updateChildren, parentHasChanged);
    _updateBounds();
}

 

由于在四叉树场景节点上挂接的物体会发生变化,所以自定义的场景节点需要覆盖其基类SceneNode的的_updateBounds()方法。

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//same as SceneNode, only it doesn't care about children...
void QuadtreeNode::_updateBounds( void )
{
    mWorldAABB.setNull();
    mLocalAABB.setNull();
 
    // Update bounds from own attached objects
    ObjectMap::iterator i = mObjectsByName.begin();
    AxisAlignedBox bx;
 
    while ( i != mObjectsByName.end() )
    {
 
        // Get local bounds of object
        bx = i->second ->getBoundingBox();
 
        mLocalAABB.merge( bx );
 
        mWorldAABB.merge( i->second ->getWorldBoundingBox(true) );
        ++i;
    }
 
    //update the QuadtreeSceneManager that things might have moved.
    // if it hasn't been added to the octree, add it, and if has moved
    // enough to leave it's current node, we'll update it.
    if ( ! mWorldAABB.isNull() )
    {
        static_cast < QuadtreeSceneManager * > ( mCreator ) -> _updateQuadtreeNode( this );
    }
}

 

在上述代码的最后一段调用了QuadtreeSceneManager:: _updateQuadtreeNode(),这个方法的主要是首先判断该场景节点是否已经加入到场景树中,若没有,则加入;若已加入,则再判断该节点是否需要移动到某个场景子树上。

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void QuadtreeSceneManager::_updateQuadtreeNode( QuadtreeNode * onode )
{
    const AxisAlignedBox& box = onode -> _getWorldAABB();
 
    if ( box.isNull() )
        return ;
 
    // Skip if quadtree has been destroyed (shutdown conditions)
    if (!mQuadtree)
        return;
 
    if ( onode -> getQuadtant() == 0 )
    {
        //if outside the quadtree, force into the root node.
        if ( ! onode -> _isIn( mQuadtree -> mBox ) )
            mQuadtree->_addNode( onode );
        else
            _addQuadtreeNode( onode, mQuadtree );
        return ;
    }
 
    if ( ! onode -> _isIn( onode -> getQuadtant() -> mBox ) )
    {
        _removeQuadtreeNode( onode );
 
        //if outside the quadtree, force into the root node.
        if ( ! onode -> _isIn( mQuadtree -> mBox ) )
            mQuadtree->_addNode( onode );
        else
            _addQuadtreeNode( onode, mQuadtree );
    }
}
posted @   Pulaski  阅读(602)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示