How can the SceneManager
actually find any SceneNode
regardless of where it happens to be in the graph when:
SceneManager::createSceneNode(...)
method explicitly claims that the nodes created are not part of the graph?¹, andSceneNode
s can independently create their own children without the SceneManager
's knowledge?²¹ The SM does not automatically turn the scene nodes it creats into children of other nodes (e.g. root); you have to manually call addChild
on a node for that
² The client can simply write sceneManager->getRootSceneNode()->createChildSceneNode("Child");
, and the SM wouldn't know about the new child's existence
I was going over the source code in OGRE3D and came across the following piece of documentation on the SceneManager
class (>> << emphasis added):
/** 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;
When you look at the implementation, you see:
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;
}
So far, so good. We can see that the SM searches for your requested SceneNode
in its own SceneNodeList
named mSceneNodes
. The part I'm trying to figure out is that the documentation claims it can find a node "wherever it is in the scene graph". New SceneNode
s are only added into the mSceneNodes
list when using SceneManager::createSceneNode(...)
. The documentation for the SM's createSceneNode
method says (>> << emphasis added):
/** 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);
At the same time, if you look at the SceneNode
class, it has its own createChild(const String& name, ...)
method, which clearly does not add its own children into the SceneManager
's list, as shown below:
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));
}
This means that if the client program says node.createChildSceneNode(...);
, the SceneManager
would not be aware of the existence of the new child node, AFAIK, so it'd never be able to find it.
I've been studying the source code for a while now and I've not found an answer to this question. I looked at the BspSceneManager
and the BspSceneNode
just to see if I could spot something else, but came up empty.
For the sake of completeness/reference, the most recent commit currently available in the master branch is:
commit 3b13abbdcce146b2813a6cc3bedf16d1d6084340
Author: mkultra333 <unknown>
Date: Sun May 8 19:31:39 2016 +0800
It's no wonder this part confuses you because it's part of over-OOP from more than a decade ago. Some like it, some hate it.
The answer however, is quite simple if once you know what to look for:
The code for Node::createChild
is the following:
Node* newNode = createChildImpl( sceneType );
//...
return newNode;
It actually delegates creation to createChildImpl
(the "implementer"). This function is a pure virtual function, thus SceneNode
must overload.
When we go to SceneNode::createChildImpl
, we get:
Node* SceneNode::createChildImpl(const String& name)
{
return mCreator->_createSceneNode( name );
}
mCreator
is a SceneManager
pointer variable. So there you go: The SceneManager
does get informed when a SceneNode
is created via createChildSceneNode
.
Note however an implementation (e.g. BspSceneNode
in BspSceneManager
) may overload createChildImpl
and not inform the SceneManager
; or they may.
Placing a breakpoint inside SceneManager::_createSceneNode
and checking out the callstack will save you a lot of headaches though.