TL; DR;
在以下情况下,不管如何SceneManager
实际上如何找到任何SceneNode
:
SceneManager::createSceneNode(...)
方法明确声明所创建的节点不是图的一部分?¹和SceneNode
可以在不了解SceneManager
的情况下独立创建自己的子级?²¹SM不会自动将其创建的场景节点转换为其他节点的子节点(例如,根节点);您必须为此在节点上手动调用
addChild
²客户端只需写
sceneManager->getRootSceneNode()->createChildSceneNode("Child");
,SM就不会知道新 child 的存在背景
我查看了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
的SceneNodeList
中搜索您请求的mSceneNodes
。我要弄清楚的部分是文档声称它可以“在场景图中的任何位置”找到一个节点。仅当使用SceneNode
时,新的mSceneNodes
才添加到SceneManager::createSceneNode(...)
列表中。 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的存在,因此它将永远无法找到它。我已经研究源代码已有一段时间了,但没有找到这个问题的答案。我查看了
BspSceneManager
和BspSceneNode
只是看我是否还能发现其他东西,但是空了出来。为了完整性/引用,master分支中当前可用的最新提交是:
commit 3b13abbdcce146b2813a6cc3bedf16d1d6084340
Author: mkultra333 <unknown>
Date: Sun May 8 19:31:39 2016 +0800
最佳答案
难怪这部分会使您感到困惑,因为它是十多年前的OOP的一部分。有些喜欢它,有些讨厌它。
但是,一旦知道了要查找的内容,答案就非常简单:Node::createChild
的代码如下:
Node* newNode = createChildImpl( sceneType );
//...
return newNode;
它实际上将创建委托(delegate)给
createChildImpl
(“实现器”)。此函数是纯虚函数,因此SceneNode
必须重载。当我们转到
SceneNode::createChildImpl
时,我们得到:Node* SceneNode::createChildImpl(const String& name)
{
return mCreator->_createSceneNode( name );
}
mCreator
是SceneManager
指针变量。因此,您可以执行以下操作:通过SceneManager
创建SceneNode
时,会通知createChildSceneNode
。但是请注意,一种实现方式(例如
BspSceneNode
中的BspSceneManager
)可能会使createChildImpl
重载而不会通知SceneManager
;或者他们可能会。不过,在
SceneManager::_createSceneNode
中放置一个断点并 check out 调用栈将为您省去很多麻烦。