Official Murl Engine Forum

Full Version: My own xml nodes
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Hi!

Is it possible to create my own nodes (with custom name and attributes) and use them inside a graph resource xml along with the other nodes?

Thanks in advance Smile
Hi!

You can do so by deriving from Graph::Node.

In the header file, write:
#include "murl_graph_node.h"
#include "app_i_my_node.h"

namespace Murl
{
    namespace App
    {
        class MyNode : public IMyNode, public Graph::Node
        {
            // Declare the attributes here:

            MURL_DECLARE_FACTORY_OBJECT_BEGIN(App::MyNode)
                MURL_DECLARE_FACTORY_OBJECT_PROPERTY(PROPERTY_MY_ATTR)
            MURL_DECLARE_FACTORY_OBJECT_END(App::MyNode)
            
            static INode* Create(const Graph::IFactory* factory);

        public:
            virtual Graph::INode* GetNodeInterface();
            virtual const Graph::INode* GetNodeInterface() const;

        protected:
            MyNode(const Graph::IFactory* factory);
            virtual ~MyNode();
            virtual Bool DeserializeBaseAttribute(Graph::IDeserializeAttributeTracker* tracker);

            // Use member variables to store attribute values.

            SInt32 mMyAttr;
        };
    }
}


Then implement in the CPP file:
using namespace Murl;

// Define the attributes here:

MURL_DEFINE_FACTORY_OBJECT_BEGIN(App::MyNode)
    MURL_DEFINE_FACTORY_OBJECT_PROPERTY(PROPERTY_MY_ATTR, "myAttr")
MURL_DEFINE_FACTORY_OBJECT_END(App::MyNode)

Graph::INode* App::MyNode::Create(const Graph::IFactory* factory)
{
    return new MyNode(factory);
}

App::MyNode::MyNode(const Graph::IFactory* factory)
: Graph::Node(factory)
, mMyAttr(0)
{
}

App::MyNode::~MyNode()
{
}

Graph::INode* App::MyNode::GetNodeInterface()
{
    return this;
}

const Graph::INode* App::MyNode::GetNodeInterface() const
{
    return this;
}

Bool App::MyNode::DeserializeBaseAttribute(Graph::IDeserializeAttributeTracker* tracker)
{
    switch(tracker->GetBaseAttributeProperty(GetProperties()))
    {
        case PROPERTY_MY_ATTR:
            tracker->GetAttributeValue(mMyAttr);
            return true;

        // handle other attributes here

        default:
            return Graph::Node::DeserializeBaseAttribute(tracker);
    }
}


You may want to override the InitSelf, ProcessLogicSelf, etc. methods from Graph::Node to add custom behavior.

Finally, you need to register/unregister the node in your App class by overriding these methods from AppBase:
Bool App::MyApp::RegisterCustomFactoryClasses(IAppFactoryRegistry* factoryRegistry)
{
    factoryRegistry->GetGraphFactoryRegistry()->RegisterNodeClass(App::MyNode::GetClassInfo());
    return true;
}

Bool App::MyApp::UnregisterCustomFactoryClasses(IAppFactoryRegistry* factoryRegistry)
{
    factoryRegistry->GetGraphFactoryRegistry()->UnregisterNodeClass(App::MyNode::GetClassInfo());
    return true;
}


Now you can use:

<App::MyNode myAttr="42"/>


Or:

<MyNode myAttr="42"/>


Wink
Thank you very much for your help!
This is exactly what I wanted.

I knew there has to be an easy way. Smile
Is this information still valid, or does it need to be updated slightly?

I tried this with murl_1.00.5225beta, however I get a bunch of compiler errors with the macros (MURL_DECLARE_FACTORY_OBJECT_BEGIN, MURL_DECLARE_FACTORY_OBJECT_PROPERTY and MURL_DECLARE_FACTORY_OBJECT_END)


P.S. I know some forums frown upon resurrecting old threads like this, if that's the case just let me know and I'll create a new thread for my questions Smile.
Thanks for the info; the way how custom nodes are defined has changed indeed. Here's an updated version of the above class that reflects the necessary changes:


Header file snippet:
#include "murl_graph_node.h"

namespace Murl
{
    namespace App
    {
        class MyNode : public Graph::Node
        {
            // Define the class, including its base class
            MURL_FACTORY_OBJECT_DERIVED_CLASS(App::MyNode, Murl::Graph::Node)

            // Define node properties, this creates a member variable for each property and assigns a given default value.
            MURL_FACTORY_OBJECT_PROPERTIES(App::MyNode,
                                           (PROPERTY_MY_ATTR_1, mMyAttr1, 42,  Graph::SInt32Property),
                                           (PROPERTY_MY_ATTR_2, mMyAttr2, "42", Graph::StringProperty))

            // Define the attributes for deserialization; these bind to individual properties defined above
            MURL_FACTORY_OBJECT_ATTRIBUTES(App::MyNode,
                                           (ATTRIBUTE_MY_ATTR_1, "myAttr1", mMyAttr1, COMPONENT_SINGLE, ACCESS_SINGLE, ELEMENT_SINGLE),
                                           (ATTRIBUTE_MY_ATTR_2, "myAttr2", mMyAttr2, COMPONENT_SINGLE, ACCESS_SINGLE, ELEMENT_SINGLE))

            // The static creation method
            static INode* Create(const Graph::IFactory* factory);

        public:
            virtual Graph::INode* GetNodeInterface();
            virtual const Graph::INode* GetNodeInterface() const;

        protected:
            MyNode(const Graph::IFactory* factory);
            virtual ~MyNode();

            virtual Bool DeserializeBaseAttribute(Graph::IDeserializeAttributeTracker* tracker);
        };
    }
}


CPP file snippet:
using namespace Murl;

// No need for macros in the CPP file anymore, this is all done in the
// header file now.

Graph::INode* App::MyNode::Create(const Graph::IFactory* factory)
{
    return new MyNode(factory);
}

App::MyNode::MyNode(const Graph::IFactory* factory)
: Graph::Node(factory)
{
    // Also, no need to initialize members. this is done in the
    // MURL_FACTORY_OBJECT_PROPERTIES macro.
}

App::MyNode::~MyNode()
{
}

Graph::INode* App::MyNode::GetNodeInterface()
{
    return this;
}

const Graph::INode* App::MyNode::GetNodeInterface() const
{
    return this;
}

Bool App::MyNode::DeserializeBaseAttribute(Graph::IDeserializeAttributeTracker* tracker)
{
    switch (tracker->GetBaseAttribute(GetAttributeInfo()))
    {
        case PROPERTY_MY_ATTR_1:
            return mMyAttr1.DeserializeValue(tracker);

        case PROPERTY_MY_ATTR_2:
            return mMyAttr2.DeserializeValue(tracker);

            // handle other attributes here

        default:
            return Graph::Node::DeserializeBaseAttribute(tracker);
    }
}


This is just a basic example using two simple properties/attributes. There's a bunch of other data types available, see the file murl/base/include/engine/graph/murl_graph_property.h; however we are still lacking thorough documentation on the whole topic of user-defined node creation.

Feel free to ask if you need something more specific.

As for resurrecting old threads: We're quite relaxed about doing so as long as it's not totally off-topic or exhaustive Wink

Best regards,

dizzy