Ogreカスタムシーンマネージャプラグインの作成方法

10612 ワード

転載は出典を明記してください!http://www.cnblogs.com/pulas/
 
次に、プラグインでカスタムシーンマネージャを実装する方法について説明します.Ogreのシーンマネージャは、ファクトリメソッドによって作成されるため、SceneManagerベースクラスだけでなく、SceneManagerFactoryベースクラスも提供されます.
 
       /** 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()インタフェースを実現する.
 
//-----------------------------------------------------------------------

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インスタンスを作成し、次に、Rootオブジェクトにファクトリインスタンスを追加するinitialise()インタフェースを実装します(実際には、最終的にはシーン管理列挙器にファクトリインスタンスが登録されています).プラグインshutdown()インタフェースを実装し、プラグインのアンインストール時にRootオブジェクトからファクトリインスタンスを削除し(実際にはシーン管理列挙器でファクトリインスタンスをログアウト)、最後にuninstall()インタフェースを実装し、QuadtreeSceneManagerFactoryインスタンスを削除します.
 
	//---------------------------------------------------------------------

	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はデータメンバーが多く、基本的にはリスト1つで様々なオブジェクトを管理しています.なお、Camera,Light,SceneNode,Entity,BillboardSet,Animation,AnimationState,StaticGeometry等の作成は、いずれもSceneManagerにより実現され、作成後はそのまま該当リストに入れる.
5.プログラムの初期化時にシーンノードとCameraを作成する必要があり、カスタムのシーンマネージャは効率的にシーンを管理するためにカスタムのシーンノードとカスタムのCameraを使用する必要があるため、QuadtreeSceneManagerはベースクラスSceneManagerのcreateSceneNodeImpl()とcreateCamera()メソッドを上書きする必要がある.
 
    /// @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の次の方法により、OGERはSceneNodeにEntityを掛けます.
clip_image006
EntityとSceneNodeの関係を下図に示します.
clip_image008
前述のシーンマネージャの呼び出し順序の第2のステップでは、ルートノードからすべてのSceneNodeを再帰的に呼び出す必要がある_update()は、ノードの包囲ボックスを更新します.
 
void SceneNode::_update(bool updateChildren, bool parentHasChanged)

{

    Node::_update(updateChildren, parentHasChanged);

    _updateBounds();

}

 
四叉木シーンノードに接続されているオブジェクトが変化するため、カスタムシーンノードはベースクラスSceneNodeの_を上書きする必要があります.updateBounds()メソッド.
 
//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()は、この方法の主な方法は、まず、シーンノードがシーンツリーに追加されたかどうかを判断し、そうでなければ、追加することである.既に追加されている場合は、ノードがシーンサブツリーに移動する必要があるかどうかを判断します.
 
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 );

    }

}