Official Murl Engine Forum

Full Version: The Great Node Creation Problem
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Hey guys!

I have another question for you Wink

This time, I want to create nodes just from my logic class. I want to do this because the amount of nodes I need can vary greatly and I want to be able to dynamically create and manipulate them during runtime.

I tried out some ways to accomplish this, but unfortunately I was quite unsuccessful.

I expected these lines to work:
Logic::PlaneGeometryNode pgn ;
AddGraphNode(pgn.GetReference(root->CreateNode("SOME_CLASS_NAME")));


What exactly is the deal with this classname String?
I tried typing "Logic::PlaneGeometryNode" or getting the classname in some other way but didn't manage to do so.

Is this the way to go or is there any other way I could make this work?

Your help is very much appreciated! Thank you Smile
Hi!

You are almost there Wink

The Graph::IRoot::CreateNode() method can be used to create a scene graph node at runtime. Here, the className parameter represents the actual name of the class you want to create an instance from. This name must be equal to the element tag you would specify in an XML scene graph description; in your case you would create a plane geometry node from the Graph::PlaneGeometry class by stating
root->CreateNode("PlaneGeometry");

As a convenience, you might also pass the node's desired ID with the following call (optional):
root->CreateNode("PlaneGeometry", "myPlaneGeom1");

Both methods return a Graph::INode* object, which represents the base interface of all scene graph nodes. In order to access the specific functionality of the node you created, you need to cast that base interface to the actual interface. With that interface in hand, you can directly access its methods, and also the methods of any of the available other base interfaces for this class:
Graph::IPlaneGeometry* geom = dynamic_cast<Graph::IPlaneGeometry*>(root->CreateNode("PlaneGeometry", "myPlaneGeom1"));
geom->SetFrameSize(42.0, 21.0);
geom->GetTransformInterface()->SetPositionX(27.0);
geom->GetNodeInterface()->SetVisible(false);
geom->GetNodeInterface()->SetId("myPlaneGeom1"); // redundant, because we already passed an ID at root->CreateNode().

Now we have a newly created node with some parameters set. We must place this node somewhere in the scene graph and initialize it, which is done using the base Graph::INode interface. If we want to add it to the graph's root node, we might write:
root->InitGraph(geom->GetNodeInterface(), root->GetRootNode());

In practice, you will most likely need to put the node at some other place in the graph, for example
root->InitGraph(geom->GetNodeInterface(), root->FindNode("/myNamespace1/blah"));

will add the node as a child of the existing node found at "/myNamespace1/blah", and initialize it.
If you do not only want to create a single node but a more complex sub-graph, you do not need to explicitly perform root->InitGraph() on every single node in that sub-graph. You might do something like this:
Graph::INode* subGraph = root->CreateNode("FixedParameters", "testParams"));
Graph::IFixedParameters* params = dynamic_cast<Graph::IFixedParameters*>(subGraph);
params->SetColor(IEnums::LIGHTING_COMPONENT_DIFFUSE, Color(1.0f, 0.5f, 0.0f, 1.0f));

Graph::IMaterialState* matState = dynamic_cast<Graph::IMaterialState*>(root->CreateNode("MaterialState", "testMatState"));
matState->GetMaterialNodeTarget()->SetNumberOfNodes(1);
matState->GetMaterialNodeTarget()->SetNodeId("/common_materials/mat_white", 0);
subGraph->AddChild(matState->GetNodeInterface());

Graph::ICubeGeometry* cube = dynamic_cast<Graph::ICubeGeometry*>(root->CreateNode("CubeGeometry", "testCube"));
cube->GetTransformInterface()->SetPosition(Real(3.0), Real(1.5), Real(0.1));
cube->SetScaleFactor(Real(0.25));
subGraph->AddChild(cube->GetNodeInterface());

root->InitGraph(subGraph, root->FindNode("/myNamespace1/blah"));

However, as soon as you manually create (a) node(s) at runtime, you are also responsible for correct destruction! Every root->InitGraph() call must have a corresponding root->DeInitGraph() call along with root->DestroyGraph(). Here's a more detailed example:
App::MyLogic::MyLogic(Logic::IFactory* factory)
: BaseProcessor(factory)
, mSubGraph(0)
{
}
Bool App::MyLogic::CreateMyGraph(Graph::IRoot* root)
{
	if (mSubGraph == 0)
	{
		mSubGraph = root->CreateNode("FixedParameters", "testParams"));
		Graph::IFixedParameters* params = dynamic_cast<Graph::IFixedParameters*>(mSubGraph);
		params->SetColor(IEnums::LIGHTING_COMPONENT_DIFFUSE, Color(1.0f, 0.5f, 0.0f, 1.0f));

		Graph::IMaterialState* matState = dynamic_cast<Graph::IMaterialState*>(root->CreateNode("MaterialState", "testMatState"));
		matState->GetMaterialNodeTarget()->SetNumberOfNodes(1);
		matState->GetMaterialNodeTarget()->SetNodeId("/common_materials/mat_white", 0);
		mSubGraph->AddChild(matState->GetNodeInterface());

		Graph::ICubeGeometry* cube = dynamic_cast<Graph::ICubeGeometry*>(root->CreateNode("CubeGeometry", "testCube"));
		cube->GetTransformInterface()->SetPosition(Real(3.0), Real(1.5), Real(0.1));
		cube->SetScaleFactor(Real(0.25));
		mSubGraph->AddChild(cube->GetNodeInterface());

		root->InitGraph(mSubGraph, root->FindNode("/myNamespace1/blah"));
	}
	return true;
}
Bool App::MyLogic::DestroyMyGraph(Graph::IRoot* root)
{
	if (mSubGraph != 0)
	{
	    root->DeInitGraph(mSubGraph);
	    root->DestroyGraph(mSubGraph);
	    mSubGraph = 0;
	}
}
Bool App::MyLogic::OnDeInit(const Logic::IState* state)
{
	return DestroyMyGraph(state->GetGraphRoot());
}
void App::MyLogic::OnProcessTick(const Logic::IState* state)
{
    if (state->GetDeviceHandler()->WasTouchReleased())
    {
    	if (mSubGraph == 0)
    	{
 			CreateMyGraph(state->GetGraphRoot());
    	}
    	else
    	{
 			DestroyMyGraph(state->GetGraphRoot());
		}
    }
}

If you want to use the newly created node with a Logic::PlaneGeometryNode observable, you would write something like that:
Graph::IPlaneGeometry* geom = dynamic_cast<Graph::IPlaneGeometry*>(root->CreateNode("PlaneGeometry", "myPlaneGeom1"));
root->InitGraph(geom->GetNodeInterface(), root->FindNode("/myNamespace1/blah"));

AddGraphNode(mPgn.GetReference(geom->GetNodeInterface())); // define in header file: Logic::PlaneGeometryNode mPgn; 
...

again followed by the following code on destruction:
...
Graph::INode* geom = mPgn.GetNodeInterface();
RemoveGraphNode(mPgn);

root->DeInitGraph(geom);
root->DestroyGraph(geom);


Hope that helps!

dizzy
Thank you very much for your detailed explanation!

It helped me very much Smile