Skip to content

Anatomy of a Node

Viktor Kovacs edited this page Sep 20, 2020 · 24 revisions

Introduction

Nodes are the most important objects in the engine. You can create your own nodes to extend the functionality. You can define the visualization and almost all kind of behaviour of a node.

A node is a black box for the engine. The engine doesn't know anything about the internal calculations, it just calls the node functions when they are needed.

Basic Concepts

These are the most important concepts:

  • Node: A node is basically a black box which can calculate some output values from some input values. It is possible to write a node without input or output values, but usually nodes have both of them.
  • Slot: A slot is where you can connect other nodes to make the data flow through the whole graph. Input slots are always on the left side of the node, and output slots are always on the right side.
  • Connection: A connection represents the data flow between nodes. Connections are always connected to slots.

The below example shows a possible visualization of a node. The node name is Addition, A and B are input slots and Result is an output slot.

Node

Implementing a Node

To implement a node you have to create a new class inherited from BI::BasicUINode. You can implement several functions to modify the behaviour of the node, but there are some mandatory.

  • Initialize: This function is called when the node is added to the graph. Here you can register input and output slots for the node.
  • Calculate: This function is called when the node should calculate its value. Here you can evaluate the values of the input slots and do the calculation based on these values.
  • Read/Write: These are for serialization. They are called not only for import/export, but for some internal functionalities as well (undo/redo for example).

The below example shows a possible implementation of a node. It registers two input slots and an output slot. It calculates the addition of the two input values and returns the result as an output value.

class MyNode : public BI::BasicUINode
{
    DYNAMIC_SERIALIZABLE (MyNode);

public:
    MyNode ()
    {
    
    }

    MyNode (const NE::String& name, const NUIE::Point& position) :
        BI::BasicUINode (name, position)
    {
    
    }

    virtual ~MyNode ()
    {
    
    }

    virtual void Initialize () override
    {
        RegisterUIInputSlot (NUIE::UIInputSlotPtr (new NUIE::UIInputSlot (
            NE::SlotId ("a"), NE::String (L"A"), NE::ValuePtr (new NE::DoubleValue (0.0)),
            NE::OutputSlotConnectionMode::Single
        )));
        RegisterUIInputSlot (NUIE::UIInputSlotPtr (new NUIE::UIInputSlot (
            NE::SlotId ("b"), NE::String (L"B"), NE::ValuePtr (new NE::DoubleValue (0.0)),
            NE::OutputSlotConnectionMode::Single
        )));
        RegisterUIOutputSlot (NUIE::UIOutputSlotPtr (new NUIE::UIOutputSlot (
            NE::SlotId ("result"), NE::String (L"Result")
        )));
    }

    virtual NE::ValueConstPtr Calculate (NE::EvaluationEnv& env) const override
    {
        NE::ValueConstPtr aValue = EvaluateInputSlot (NE::SlotId ("a"), env);
        NE::ValueConstPtr bValue = EvaluateInputSlot (NE::SlotId ("b"), env);
        if (!NE::IsSingleType<NE::NumberValue> (aValue) ||
            !NE::IsSingleType<NE::NumberValue> (bValue))
        {
            return nullptr;
        }

        double aDouble = NE::NumberValue::ToDouble (aValue);
        double bDouble = NE::NumberValue::ToDouble (bValue);
        return NE::ValueConstPtr (new NE::DoubleValue (aDouble + bDouble));
    }

    virtual NE::Stream::Status Read (NE::InputStream& inputStream) override
    {
        NE::ObjectHeader header (inputStream);
        BasicUINode::Read (inputStream);
        return inputStream.GetStatus ();
    }

    virtual NE::Stream::Status Write (NE::OutputStream& outputStream) const override
    {
        NE::ObjectHeader header (outputStream, serializationInfo);
        BasicUINode::Write (outputStream);
        return outputStream.GetStatus ();
    }
};

DYNAMIC_SERIALIZATION_INFO (MyNode, 1, "{8DC2C2F6-C1D1-451A-96C5-D64B3DEDB764}");