OGRE3D SceneManager如何真正找到* any * SceneNode?

时间:2016-05-29 06:14:00

标签: c++ ogre

TL; DR;

SceneManager如何实际找到任何SceneNode ,无论它在图表中的位置如何:

  1. SceneManager::createSceneNode(...)方法明确声明创建的节点是图表的一部分?¹,
  2. SceneNode s可以在没有SceneManager知识的情况下独立创建自己的孩子吗?²
  3. ¹ SM不会自动将其创建的场景节点转换为其他节点的子节点(例如root);您必须在该

    的节点上手动调用addChild

    ²客户可以简单地写sceneManager->getRootSceneNode()->createChildSceneNode("Child");,而SM不会知道新孩子的存在

    背景

    我正在浏览OGRE3D中的源代码,并在SceneManager类(>>><<强调添加)中遇到了以下文档:

    /** Retrieves a named SceneNode from the scene graph.
        @remarks
            If you chose to name a SceneNode as you created it, or if you
            happened to make a note of the generated name, you can look it
            up >>wherever it is in the scene graph<< using this method.
            @note Throws an exception if the named instance does not exist
    */
    virtual SceneNode* getSceneNode(const String& name) const;
    

    当您查看实施时,您会看到:

    SceneNode* SceneManager::getSceneNode(const String& name) const
    {
        SceneNodeList::const_iterator i = mSceneNodes.find(name);
    
        if (i == mSceneNodes.end())
        {
            OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "SceneNode '" + name + "' not found.",
                "SceneManager::getSceneNode");
        }
    
        return i->second;
    }
    

    到目前为止,这么好。我们可以看到,SM会在名为SceneNode的{​​{1}}个SceneNodeList中搜索您请求的mSceneNodes。我想弄清楚的部分是文档声称它可以找到一个节点“它在场景图中的任何地方”。新SceneNode仅在使用mSceneNodes时添加到SceneManager::createSceneNode(...)列表中。 SM的createSceneNode方法的文档说(&gt;&gt;&lt;&lt;强调添加):

    /** Creates an instance of a SceneNode with a given name.
        @remarks
            Note that this >>does not add the SceneNode to the scene hierarchy<<.
            This method is for convenience, since it allows an instance to
            be created for which the SceneManager is responsible for
            allocating and releasing memory, which is convenient in complex
            scenes.
        @par
            To include the returned SceneNode in the scene, use the addChild
            method of the SceneNode which is to be it's parent.
        @par
            Note that this method takes a name parameter, which makes the node easier to
        retrieve directly again later.
    */
    virtual SceneNode* createSceneNode(const String& name);
    

    同时,如果您查看SceneNode类,它有自己的createChild(const String& name, ...)方法,显然将自己的子项添加到SceneManager中1}}的列表,如下所示:

    SceneNode* SceneNode::createChildSceneNode(const Vector3& inTranslate,
        const Quaternion& inRotate)
    {
        return static_cast<SceneNode*>(this->createChild(inTranslate, inRotate));
    }
    //-----------------------------------------------------------------------
    SceneNode* SceneNode::createChildSceneNode(const String& name, const Vector3& inTranslate,
        const Quaternion& inRotate)
    {
        return static_cast<SceneNode*>(this->createChild(name, inTranslate, inRotate));
    }
    

    这意味着,如果客户端程序显示node.createChildSceneNode(...);,则SceneManager意识到新子节点AFAIK的存在,所以它永远不会能够找到它。

    我已经研究了一段时间的源代码,但我没有找到这个问题的答案。我查看了BspSceneManagerBspSceneNode只是为了看看我是否能发现其他内容,但却显得空洞。

    为了完整性/参考,master分支中当前可用的最新提交是:

    commit 3b13abbdcce146b2813a6cc3bedf16d1d6084340
    Author: mkultra333 <unknown>
    Date:   Sun May 8 19:31:39 2016 +0800
    

1 个答案:

答案 0 :(得分:4)

难怪这部分让你感到困惑,因为它是十多年前OOP的一部分。有些人喜欢它,有些人讨厌它。

但是,如果您知道要查找的内容,则答案非常简单: Node::createChild的代码如下:

Node* newNode = createChildImpl( sceneType );
//...
return newNode;

它实际上将创建委托给createChildImpl(“实施者”)。此函数是纯虚函数,因此SceneNode 必须重载。

当我们转到SceneNode::createChildImpl时,我们得到:

Node* SceneNode::createChildImpl(const String& name)
{
    return mCreator->_createSceneNode( name );
}

mCreatorSceneManager指针变量。您就去了:SceneManager通过SceneNode创建createChildSceneNode时会收到通知。

但请注意,实施(例如BspSceneNode中的BspSceneManager)可能会超载createChildImpl而不会通知SceneManager;或者他们可能。

SceneManager::_createSceneNode内放置断点并检查callstack会为您节省很多麻烦。