.. _ogn_tutorial_simpleData:


Tutorial 2 - Simple Data Node
=============================

The simple data node creates one input attribute and one output attribute of each of the simple types, where "simple"
refers to data types that have a single component and are not arrays. (e.g. "float" is simple, "float[3]" is not, nor is
"float[]"). See also :ref:`ogn_tutorial_simpleDataPy` for a similar example in Python.

OgnTutorialSimpleData.ogn
-------------------------
The *ogn* file shows the implementation of a node named "omni.graph.tutorials.SimpleData", which has one
input and one output attribute of each simple type.

.. literalinclude:: ../../../../../source/extensions/omni.graph.tutorials/tutorials/tutorial2/OgnTutorialSimpleData.ogn
    :linenos:
    :language: json

OgnTutorialSimpleData.cpp
-------------------------
The *cpp* file contains the implementation of the compute method, which modifies
each of the inputs in a simple way to create outputs that have different values.

.. literalinclude:: ../../../../../source/extensions/omni.graph.tutorials/tutorials/tutorial2/OgnTutorialSimpleData.cpp
    :linenos:
    :language: c++

Note how the attribute values are available through the OgnTutorialSimpleDataDatabase class. The generated interface
creates access methods for every attribute, named for the attribute itself. Inputs will be returned as const
references, outputs will be returned as non-const references.

Attribute Data
--------------
Two types of attribute data are created, which help with ease of access and of use - the attribute name lookup
information, and the attribute type definition.

Attribute data is accessed via a name-based lookup. This is not particularly efficient, so to facilitate this process
the attribute name is translated into a fast access token. In addition, the information about the attribute's type and
default value is constant for all nodes of the same type so that is stored as well, in static data.

Normally you would use an *auto* declaration for attribute types. Sometimes you want to pass around attribute data so
it is helpful to have access to the attribute's data type. In the generated code a ``using namespace`` is set up to
provide a very simple syntax for accessing the attribute's metadata from within the node:

.. code-block:: c++

    std::cout << "Attribute name is " << inputs::a_bool.m_name << std::endl;
    std::cout << "Attribute type is " << inputs::a_bool.m_dataType << std::endl;

    extern "C" void processAttribute(inputs::a_bool_t& value);
    // Equivalent to extern "C" void processAttribute(bool& value);

Attribute Data Access
---------------------
The attributes are automatically namespaced with *inputs* and *outputs*. In the USD file the attribute names
will appear as *inputs:XXX* or *outputs:XXX*. In the C++ interface the colon is illegal so a contained struct
is used to make use of the period equivalent, as *inputs.XXX* or *outputs.XXX*.

The minimum information provided by these wrapper classes is a reference to the underlying data, accessed by
``operator()``. For this class, these are the types it provides:

+--------------------+--------------------+
| Database Function  | Returned Type      |
+====================+====================+
| inputs.a_bool()    | const bool&        |
+--------------------+--------------------+
| inputs.a_half()    | const pxr::GfHalf& |
+--------------------+--------------------+
| inputs.a_int()     | const int&         |
+--------------------+--------------------+
| inputs.a_int64()   | const int64_t&     |
+--------------------+--------------------+
| inputs.a_float()   | const float&       |
+--------------------+--------------------+
| inputs.a_double()  | const double&      |
+--------------------+--------------------+
| inputs.a_path()    | const std::string& |
+--------------------+--------------------+
| inputs.a_string()  | const std::string& |
+--------------------+--------------------+
| inputs.a_token()   | const NameToken&   |
+--------------------+--------------------+
| outputs.a_bool()   | bool&              |
+--------------------+--------------------+
| outputs.a_half()   | pxr::GfHalf&       |
+--------------------+--------------------+
| outputs.a_int()    | int&               |
+--------------------+--------------------+
| outputs.a_int64()  | int64_t&           |
+--------------------+--------------------+
| outputs.a_float()  | float&             |
+--------------------+--------------------+
| outputs.a_double() | double&            |
+--------------------+--------------------+
| outputs.a_string() | std::string&       |
+--------------------+--------------------+
| outputs.a_token()  | NameToken&         |
+--------------------+--------------------+

The data returned are all references to the real data in the Fabric, our managed memory store, pointed to the
correct location at evaluation time.

Note how input attributes return *const* data while output attributes do not. This reinforces the restriction that
input data should never be written to, as it would cause graph synchronization problems.

The type *pxr::GfHalf* is an implementation of a 16-bit floating point value, though any other may also be used with
a runtime cast of the value. *omni::graph::core::NameToken* is a simple token through which a unique string can be
looked up at runtime.

Helpers
-------
A few helpers are provided in the database class definition to help make coding with it more natural.

initializeType
++++++++++++++
Function signature ``static void initializeType(const NodeTypeObj& nodeTypeObj)``
is an implementation of the :cpp:member:`ABI function<omni::graph::core::INodeType::initializeType>` that is called once for each node type,
initializing such things as its mandatory attributes and their default values.

validate
++++++++
Function signature ``bool validate()``. If any of the mandatory attributes do not have values then the generated code
will exit early with an error message and not actually call the node's compute method.

token
+++++
Function signature ``NameToken token(const char* tokenName)``.

Provides a simple conversion from a string to the unique token representing that string, for fast comparison of
strings and for use with the attributes whose data types are *token*.

Compute Status Logging
++++++++++++++++++++++
Two helper functions are providing in the database class to help provide more information when the compute method of
a node has failed. Two methods are provided, both taking printf-like variable sets of parameters.

:cpp:member:`void logError(Args...)<omni::graph::core::ogn::Database::logError>` is used when the compute has run into some
inconsistent or unexpected data, such as two
input arrays that are supposed to have the same size but do not, like the normals and vertexes on a mesh.

:cpp:member:`void logWarning(Args...)<omni::graph::core::ogn::Database::logWarning>` can be used when the compute has hit an
unusual case but can still provide a consistent
output for it, for example the deformation of an empty mesh would result in an empty mesh and a warning since that is
not a typical use for the node.

typedefs
++++++++
Although not part of the database class per se, a typedef alias is created for every attribute so that you can
use its type directly without knowing the detailed type; a midway point between exact types and *auto*. The main
use for such types might be passing attribute data between functions.

Here are the corresponding typedef names for each of the attributes:

+--------------------+--------------------+
| Typedef Alias      | Actual Type        |
+====================+====================+
| inputs.a_bool_t    | const bool&        |
+--------------------+--------------------+
| inputs.a_half_t    | const pxr::GfHalf& |
+--------------------+--------------------+
| inputs.a_int_t     | const int&         |
+--------------------+--------------------+
| inputs.a_int64_t   | const int64_t&     |
+--------------------+--------------------+
| inputs.a_float_t   | const float&       |
+--------------------+--------------------+
| inputs.a_double_t  | const double&      |
+--------------------+--------------------+
| inputs.a_token_t   | const NameToken&   |
+--------------------+--------------------+
| outputs.a_bool_t   | bool&              |
+--------------------+--------------------+
| outputs.a_half_t   | pxr::GfHalf&       |
+--------------------+--------------------+
| outputs.a_int_t    | int&               |
+--------------------+--------------------+
| outputs.a_int64_t  | int64_t&           |
+--------------------+--------------------+
| outputs.a_float_t  | float&             |
+--------------------+--------------------+
| outputs.a_double_t | double&            |
+--------------------+--------------------+
| outputs.a_token_t  | NameToken&         |
+--------------------+--------------------+

Notice the similarity between this table and the one above. The typedef name is formed by adding the extension *_t*
to the attribute accessor name, similar to C++ standard type naming conventions. The typedef should always correspond
to the return value of the attribute's ``operator()``.


Direct ABI Access
+++++++++++++++++
All of the generated database classes provide access to the underlying *INodeType* ABI for those rare situations
where you want to access the ABI directly. There are two methods provided, which correspond to the objects passed
in to the ABI compute method.

Context function signature :cpp:member:`const GraphContextObj& abi_context() const<omni::graph::core::ogn::Database::abi_context>`,
for accessing the underlying OmniGraph evaluation context and its interface.

Node function signature :cpp:member:`const NodeObj& nodeObj abi_node() const<omni::graph::core::ogn::Database::abi_node>`,
for accessing the underlying OmniGraph node object and its interface.

In addition, the attribute ABI objects are extracted into a shared structure so that they can be accessed in a
manner similar to the attribute data. For example ``db.attributes.inputs.a_bool()`` returns the `AttributeObj` that
refers to the input attribute named `a_bool`. It can be used to directly call ABI functions when required, though
again it should be emphasized that this will be a rare occurrence - all of the common operations can be performed
more easily using the database interfaces.

Node Computation Tests
----------------------
The "tests" section of the .ogn file contains a list of tests consisting of a description and attribute values,
both inputs and outputs, that will be used for the test.

The test runs by setting all of the named input attributes to their values, running the compute, then comparing the
resulting output attribute values against those specified by the test.

For example to test the computation of the boolean attribute, whose output is the negation of the input, these
two test values could be specified:

.. code::json

    {
        "tests": [
            {
                "description": "Check that true becomes false",
                "inputs": {
                    "a_bool": true
                },
                "outputs": {
                    "a_bool": false
                }
            }
        ]
    }

The "description" field is optional, though highly recommended to aid in debugging which tests are failing.
Any unspecified inputs take their default value, and any unspecified outputs do not get checked after the compute.

For simple attribute lists an abbreviated version of the syntax can be used, where the inputs and outputs get their
fully namespaced names so that there is no need for the "inputs" and "outputs" objects.

.. code::json

    {
        "tests": [
            {
                "description": "Check that false becomes true",
                "inputs:a_bool": false,
                "outputs:a_bool": true
            }
        ]
    }
