OGRE3D场景管理器如何真正找到任何场景节点

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

本文关键字:任何场 节点 管理器 OGRE3D 何真正      更新时间:2023-10-16

TL;DR

SceneManager如何实际找到任何SceneNode,而不管它在图中的位置当:

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

SM不会自动将其创建的场景节点转换为其他节点(例如根节点)的子节点;您必须在该的节点上手动调用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在其名为mSceneNodesSceneNodeList中搜索您请求的SceneNode。我想弄清楚的是,文档声称它可以"在场景图中的任何位置"找到节点。当使用SceneManager::createSceneNode(...)时,新的SceneNode仅被添加到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);

同时,如果您查看SceneNode类,它有自己的createChild(const String& name, ...)方法,它显然不会将自己的子级添加到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知道新子节点AFAIK的存在,因此永远无法找到它。

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


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

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

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

然而,如果一旦你知道该寻找什么,答案就很简单:Node::createChild的代码如下:

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

它实际上将创建委托给createChildImpl("实现者")。此函数是一个纯虚拟函数,因此SceneNode必须过载。

当我们进入SceneNode::createChildImpl时,我们得到:

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

CCD_ 27是一个CCD_ 28指针变量。因此:当通过createChildSceneNode创建SceneNode时,SceneManager确实会得到通知。

然而要注意的是,一种实现方式(例如BspSceneManager中的BspSceneNode)可以使createChildImpl过载并且不通知SceneManager;或者它们可能

不过,在SceneManager::_createSceneNode中放置断点并检查调用堆栈将为您省去很多麻烦。