.. _ogn_tutorial_bundle_data:

Tutorial 16 - Bundle Data
=========================

Attribute bundles are a construct that packages up groups of attributes into a single entity that can be passed
around the graph. These attributes have all of the same properties as a regular attribute, you just have to go
through an extra step to access their values. This node illustrates how to break open a bundle to access and
modify values in the bundled attributes.

OgnTutorialBundleData.ogn
-------------------------
The *ogn* file shows the implementation of a node named "omni.graph.tutorials.BundleData", which has one input bundle and one
output bundle.

.. literalinclude:: OgnTutorialBundleData.ogn
    :linenos:
    :language: json

OgnTutorialBundleData.cpp
-------------------------
The *cpp* file contains the implementation of the compute method. It accesses any attributes in the bundle that
have integral base types and doubles the values of those attributes.

.. literalinclude:: OgnTutorialBundleData.cpp
    :linenos:
    :language: c++

Bundled Attribute Data Manipulation Methods
-------------------------------------------
These are the methods for accessing the data that the bundled attributes encapsulate.
In regular attributes the code generated from the .ogn file provides accessors with predetermined data types.
The data types of attributes within bundles are unknown until compute time so it is up to the node writer to
explicitly cast to the correct data type.

Extracting Bundled Attribute Data - Simple Types
++++++++++++++++++++++++++++++++++++++++++++++++
For reference, simple types, tuple types, array types, tuple array types, and role types are all described in
:ref:`ogn_attribute_types`. However, unlike normal attributes the bundled attributes are always accessed as their
raw native data types. For example instead of ``pxr::GfVec3f`` you will access with ``float[3]``, which can always
be cast to the explicit types if desired.

.. note::

    One exception to the type casting is tokens. In normal attributes you retrieve tokens as ``NameToken``.
    Due to certain compiler restrictions the bundled attributes will be retrieved as the helper type
    ``OgnToken``, which is castable to ``NameToken`` for subsequent use.


.. code-block:: cpp
    :emphasize-lines: 5

    // As the attribute data types are only known at runtime you must perform a type-specific cast
    // to get the data out in its native form.
    const auto& inputBundle = db.inputs.bundle();
    // Note the "const" here, to ensure we are not inadvertently modifying the input data.
    const auto weight = inputBundle.attributeByName(weightToken);
    const float* weightValue = weight.value<float>();

    // nullptr return means the data is not of the requested type
    asssert( nullptr == weight.value<int>() );

Extracting Bundled Attribute Data - Tuple Types
+++++++++++++++++++++++++++++++++++++++++++++++
.. code-block:: cpp
    :emphasize-lines: 4

    // The tuple data types can be accessed in exactly the same way as simple data types, with the proper cast.
    const auto& inputBundle = db.inputs.bundle();
    const auto weight3 = inputBundle.attributeByName(weight3Token);
    const auto weight3Value = weight3.value<float[3]>();
    // type of weight3Value == const float[3]*

    // If you have a preferred library for manipulating complex types you can cast to them if they are compatible.
    static_assert( std::is_convertible(pxr::GfVec3f, float[3]) );
    const pxr::GfVec3f* usdWeight = reinterpret_cast<const pxr::GfVec3f*>(weight3Value);

Extracting Bundled Attribute Data - Array Types
+++++++++++++++++++++++++++++++++++++++++++++++
.. code-block:: cpp
    :emphasize-lines: 4,11

    // As with tuple types, the array types are extracted directly with the native array cast
    const auto& inputBundle = db.inputs.bundle();
    const auto weights = inputBundle.attributeByName(weightsToken);
    const auto weightsValue = weights.value<float[]>();
    // type == const float[]*

    auto& outputBundle = db.outputs.bundle();
    // As this is an output, the const is omitted so that the data can be modified
    auto nWeights = outputBundle.attributeByName(nWeightsToken);
    // As with regular attributes, bundled array outputs must be resized to allocate space before filling them.
    // These array types also have the normal array capabilities, with a size() method and range-based for loops.
    nWeights.resize( weights.size() );
    size_t index = 0;
    for (const auto& weightValue : *weightsValue)
    {
        nWeights[index++] = weightValue / 256.0f;
    }

Extracting Bundled Attribute Data - Tuple Array Types
+++++++++++++++++++++++++++++++++++++++++++++++++++++
.. code-block:: cpp
    :emphasize-lines: 4

    // Tuple-arrays behave as you would expect, using the native tuple-array as the cast type
    const auto& inputBundle = db.inputs.bundle();
    const auto weights3 = inputBundle.attributeByName(weights3Token);
    const auto weights3Value = weights.value<float[][3]>();
    // type == const float[][3]*

OgnTutorialBundleDataPy.py
--------------------------
This is a Python version of the above C++ node with exactly the same set of attributes and a similar algorithm.
The main difference is that for the Python version the type definitions are much more flexible so the algorithm
can be applied to every type of bundled attribute with minimal code. (The .ogn file is omitted for
brevity, being identical to the previous one save for the addition of a ``"language": "python"`` property.)

.. literalinclude:: OgnTutorialBundleDataPy.py
    :linenos:
    :language: python
