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

How can the OGRE3D SceneManager really find *any* SceneNode?

TL;DR;

SceneManager 如何才能真正找到任何 SceneNode ,而不管它恰好在图表中的什么位置 当:

  1. SceneManager::createSceneNode(...) 方法明确声明创建的节点 不是 图形的一部分?¹,
  2. SceneNode可以在 SceneManager 不知情的情况下独立创建自己的 children?²

¹ SM不会自动将自己创建的场景节点变成children的其他节点(例如root);你必须在节点上手动调用 addChild

² 客户端只要写sceneManager->getRootSceneNode()->createChildSceneNode("Child");,SM就不知道新child的存在


背景

我在查看 OGRE3D 中的源代码时发现了以下关于 SceneManager class 的文档(>> << 添加了重点):

/** 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 在它自己的名为 mSceneNodesSceneNodeList 中搜索您请求的 SceneNode。我想弄清楚的部分是文档声称它可以找到一个节点 "wherever it is in the scene graph"。新 SceneNode 仅在使用 SceneManager::createSceneNode(...) 时添加到 mSceneNodes 列表中。 SM 的 createSceneNode 方法的文档说 (>> << 添加了重点):

/** 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);

同时,如果你看SceneNodeclass,它有自己的createChild(const String& name, ...)方法,显然不是 将自己的children加入到SceneManager的列表中,如下图:

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不会 知道新 child 的存在节点,据我所知,所以它永远找不到它。

我已经研究了一段时间的源代码,但我没有找到这个问题的答案。我查看了 BspSceneManagerBspSceneNode 只是想看看我是否能发现其他东西,但结果是空的。


为了 completeness/reference,master 分支中当前可用的最新提交是:

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

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

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

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

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

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

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

mCreator 是一个 SceneManager 指针变量。所以你去了:当通过 createChildSceneNode.

创建 SceneNode 时,SceneManager 确实会收到通知

但是请注意,一个实现(例如 BspSceneManager 中的 BspSceneNode)可能会使 createChildImpl 过载并且不会通知 SceneManager;或者他们可能。

SceneManager::_createSceneNode 中放置一个断点并检查调用堆栈会让您省去很多麻烦。