.. _ogn_attribute_types:

Attribute Data Types
====================

The attribute data type is the most important part of the attribute. It describes the type of data the attribute
references, and the type of generated interface the node writer will use to access that data.

Attribute data at its core consists of a short list of data types, called `Base Data Types`_. These types encapsulate
a single value, such as a float or integer.

.. warning::

    Not all attribute types may be supported by the code generator. For a list of currently supported types
    use the command ``generate_node.py --help``.

.. note::

    The information here is for the default type definitions. You can override the type definitions using a
    configuration file whose format is show in `Type Definition Overrides`_.

.. important::

    When extracting bundle members in C++ you'll be passing in a template type to get the value. That is not the type
    shown here, these are the types you'll get as a return value. (e.g. pass in _OgnToken_ to get a return value of
    _NameToken_, or _float[]_ to get a return value of _ogn::array<float>_.)

Base Data Types
---------------

This table shows the conversion of the **Type Name**, which is how the attribute type appears in the .ogn file
*type* value of the attribute, to the various data types of the other locations the attribute might be referenced:

+-----------+--------+--------------+--------------+--------+------------------------------------------------------------+
| Type Name | USD    |  C++         | CUDA         | Python | JSON    |                   Description                    |
+===========+========+==============+==============+========+=========+==================================================+
| bool      | bool   | bool         | bool*        | bool   | bool    | True/False value                                 |
+-----------+--------+--------------+--------------+--------+---------+--------------------------------------------------+
| double    | double | double       | double*      | float  | float   | 64 bit floating point                            |
+-----------+--------+--------------+--------------+--------+---------+--------------------------------------------------+
| float     | float  | float        | float*       | float  | float   | 32 bit floating point                            |
+-----------+--------+--------------+--------------+--------+---------+--------------------------------------------------+
| half      | half   | pxr::GfHalf  | __half*      | float  | float   | 16 bit floating point                            |
+-----------+--------+--------------+--------------+--------+---------+--------------------------------------------------+
| int       | int    | int32_t      | int*         | int    | integer | 32-bit signed integer                            |
+-----------+--------+--------------+--------------+--------+---------+--------------------------------------------------+
| int64     | int64  | int64_t      | longlong*    | int    | integer | 64-bit signed integer                            |
+-----------+--------+--------------+--------------+--------+---------+--------------------------------------------------+
| path      | path   | ogn::string  | ogn::string  | str    | string  | Path to another node or attribute                |
+-----------+--------+--------------+--------------+--------+---------+--------------------------------------------------+
| string    | string | ogn::string  | ogn::string  | str    | string  | Standard string                                  |
+-----------+--------+--------------+--------------+--------+---------+--------------------------------------------------+
| token     | token  | NameToken    | NameToken*   | str    | string  | Interned string with fast comparison and hashing |
+-----------+--------+--------------+--------------+--------+---------+--------------------------------------------------+
| uchar     | uchar  | uchar_t      | uchar_t*     | int    | integer | 8-bit unsigned integer                           |
+-----------+--------+--------------+--------------+--------+---------+--------------------------------------------------+
| uint      | uint   | uint32_t     | uint32_t*    | int    | integer | 32-bit unsigned integer                          |
+-----------+--------+--------------+--------------+--------+---------+--------------------------------------------------+
| uint64    | uint64 | uint64_t     | uint64_t*    | int    | integer | 64-bit unsigned integer                          |
+-----------+--------+--------------+--------------+--------+---------+--------------------------------------------------+

.. note::

    For C++ types on input attributes a `const` is prepended to the simple types.

Here are samples of base data type values in the various languages:

**USD Type**
++++++++++++
The type of data as it would appear in a .usd file

.. code-block:: usd

    custom int inputs:myInt = 1
    custom float inputs:myFloat = 1

**C++ Type**
++++++++++++
The value type a C++ node implementation uses to access the attribute's data

.. code-block:: c++

    static bool compute(OgnMyNodeDatabase& db)
    {
        const int& iValue = db.inputs.myInt();
        const float& fValue = db.inputs.myFloat();
    }

**CUDA Type**
+++++++++++++
The value type a C++ node implementation uses to pass the attribute's data to CUDA code. Note the use of attribute
type definitions to make the function declarations more consistent.

.. code-block:: c++

    extern "C" void runCUDAcompute(inputs::myInt_t*, inputs::myFloat_t*);
    static bool compute(OgnMyNodeDatabase& db)
    {
        const int* iValue = db.inputs.myInt();
        const float* fValue = db.inputs.myFloat();
        runCUDAcompute( iValue, fValue );
    }

.. code-block:: c++

    extern "C" void runCUDAcompute(inputs::myInt_t* intValue, inputs::myFloat_t* fValue)
    {
    }

**Python Type Hint**
++++++++++++++++++++
The value used by the Python typing system to provide a hint about the expected data type

.. code-block:: python

    @property
    def myInt(self) -> int:
        return attributeValues.myInt

**JSON Type**
+++++++++++++
The value type that the .ogn file expects from test or default data from the attribute

.. code-block:: json

    {
        "myNode" : {
            "description" : ["This is my node with one integer and one float input"],
            "version" : 1,
            "inputs" : {
                "myInt" : {
                    "description" : ["This is an integer attribute"],
                    "type" : "int",
                    "default" : 0
                },
                "myFloat" : {
                    "description" : ["This is a float attribute"],
                    "type" : "float",
                    "default" : 0.0
                }
            }
        }
    }

Array Data Types
----------------

An array type is a list of another data type with indeterminate length, analagous to a ``std::vector`` in C++ or a
``list`` type in Python.

Any of the base data types can be made into array types by appending square brackets (`[]`) to the type name. For
example an array of integers would have type `int[]` and an array of floats would have type `float[]`.

The JSON schema type is "array" with the type of the array's "items" being the base type, although in the file it will
just look like ``[VALUE, VALUE, VALUE]``.

Python uses the _numpy_ library to return both tuple and array data types.

+-----------+----------+-------------------------+-------------------+------------------------------+-----------+
| Type Name | USD      |   C++                   | CUDA              | Python                       |  JSON     |
+===========+==========+=========================+===================+==============================+===========+
| bool[]    | bool[]   | ogn::array<bool>        | bool*,size_t      | numpy.ndarray[numpy.bool]    | bool[]    |
+-----------+----------+-------------------------+-------------------+------------------------------+-----------+
| double[]  | double[] | ogn::array<double>      | double*,size_t    | numpy.ndarray[numpy.float64] | float[]   |
+-----------+----------+-------------------------+-------------------+------------------------------+-----------+
| float[]   | float[]  | ogn::array<float>       | float*,size_t     | numpy.ndarray[numpy.float64] | float[]   |
+-----------+----------+-------------------------+-------------------+------------------------------+-----------+
| half[]    | half[]   | ogn::array<pxr::GfHalf> | __half*,size_t    | numpy.ndarray[numpy.float64] | float[]   |
+-----------+----------+-------------------------+-------------------+------------------------------+-----------+
| int[]     | int[]    | ogn::array<int32_t>     | int*,size_t       | numpy.ndarray[numpy.int32]   | integer[] |
+-----------+----------+-------------------------+-------------------+------------------------------+-----------+
| int64[]   | int64[]  | ogn::array<int64_t>     | longlong*,size_t  | numpy.ndarray[numpy.int32]   | integer[] |
+-----------+----------+-------------------------+-------------------+------------------------------+-----------+
| token[]   | token[]  | ogn::array<NameToken>   | NameToken*,size_t | numpy.ndarray[numpy.str]     | string[]  |
+-----------+----------+-------------------------+-------------------+------------------------------+-----------+
| uchar[]   | uchar[]  | ogn::array<uchar_t>     | uchar_t*,size_t   | numpy.ndarray[numpy.int32]   | integer[] |
+-----------+----------+-------------------------+-------------------+------------------------------+-----------+
| uint[]    | uint[]   | ogn::array<uint32_t>    | uint32_t*,size_t  | numpy.ndarray[numpy.int32]   | integer[] |
+-----------+----------+-------------------------+-------------------+------------------------------+-----------+
| uint64[]  | uint64[] | ogn::array<uint64_t>    | uint64_t*,size_t  | numpy.ndarray[numpy.int32]   | integer[] |
+-----------+----------+-------------------------+-------------------+------------------------------+-----------+

.. note::

    For C++ types on input attributes the array type is `ogn::const_array`.

Here are samples of array data type values in the various languages:

**USD Array Type**
++++++++++++++++++

.. code-block:: usd

    custom int[] inputs:myIntArray = [1, 2, 3]
    custom float[] inputs:myFloatArray = [1.0, 2.0, 3.0]

**C++ Array Type**
++++++++++++++++++

.. code-block:: c++

    static bool compute(OgnMyNodeDatabase& db)
    {
        const ogn::const_array& iValue = db.inputs.myIntArray();
        const auto& fValue = db.inputs.myFloatArray();
    }

**CUDA Array Type**
+++++++++++++++++++

.. code-block:: c++

    extern "C" runCUDAcompute(inputs::myIntArray_t*, size_t, inputs::myFloatArray_t*, size_t);
    static bool compute(OgnMyNodeDatabase& db)
    {
        const int* iValue = db.inputs.myIntArray();
        auto iSize = db.inputs.myIntArray.size();

        const auto fValue = db.inputs.myFloatArray();
        auto fSize = db.inputs.myFloatArray.size();

        runCUDAcompute( iValue, iSize, fValue, fSize );
    }

.. code-block:: c++

    extern "C" void runCUDAcompute(inputs::myIntArray_t* iArray, size_t iSize, inputs::myFloat_t* fArray, size_t fSize)
    {
        // In here it is true that the number of elements in iArray = iSize
    }

**Python Array Type Hint**
++++++++++++++++++++++++++

.. code-block:: python

    import numpy as np
    @property
    def myIntArray(self) -> np.ndarray[np.int32]:
        return attributeValues.myIntArray

**JSON Array Type**
+++++++++++++++++++

.. code-block:: json

    {
        "myNode" : {
            "description" : ["This is my node with one integer array and one float array input"],
            "version" : 1,
            "inputs" : {
                "myIntArray" : {
                    "description" : ["This is an integer array attribute"],
                    "type" : "int[]",
                    "default" : [1, 2, 3]
                },
                "myFloatArray" : {
                    "description" : ["This is a float array attribute"],
                    "type" : "float[]",
                    "default" : [1.0, 2.0, 3.0]
                }
            }
        }
    }

Tuple Data Types
----------------

An tuple type is a list of another data type with fixed length, analagous to a ``std::array`` in C++ or a
``tuple`` type in Python. Not every type can be a tuple, and the tuple count is restricted to a small subset of those
supported by USD. They are denoted with by appending square brackets containing the tuple count to the
type name. For example a tuple of two integers would have type `int[2]` and a tuple of three floats would have type
`float[3]`.

Since tuple types are implemented in C++ as raw data there is no differentiation between the types returned by input
versus output attributes, just a *const* clause.

+-----------+-------------------------------+--------------+----------+----------------------------------+------------------------------+
| Type Name | USD                           |  C++         | CUDA     | Python                           | JSON                         |
+===========+===============================+==============+==========+==================================+==============================+
| double[2] | (double,double)               | pxr::GfVec2d | double2* | numpy.ndarray[numpy.float64](2,) | [float, float]               |
+-----------+-------------------------------+--------------+----------+----------------------------------+------------------------------+
| double[3] | (double,double,double)        | pxr::GfVec3d | double3* | numpy.ndarray[numpy.float64](3,) | [float, float, float]        |
+-----------+-------------------------------+--------------+----------+----------------------------------+------------------------------+
| double[4] | (double,double,double,double) | pxr::GfVec4d | double4* | numpy.ndarray[numpy.float64](4,) | [float, float, float, float] |
+-----------+-------------------------------+--------------+----------+----------------------------------+------------------------------+
| float[2]  | (float,float)                 | pxr::GfVec2f | float2*  | numpy.ndarray[numpy.float](2,)   | [float, float]               |
+-----------+-------------------------------+--------------+----------+----------------------------------+------------------------------+
| float[3]  | (float,float,float)           | pxr::GfVec3f | float3*  | numpy.ndarray[numpy.float](3,)   | [float, float, float]        |
+-----------+-------------------------------+--------------+----------+----------------------------------+------------------------------+
| float[4]  | (float,float,float,float)     | pxr::GfVec4f | float4*  | numpy.ndarray[numpy.float](4,)   | [float, float, float, float] |
+-----------+-------------------------------+--------------+----------+----------------------------------+------------------------------+
| half[2]   | (half,half)                   | pxr::GfVec2h | __half2* | numpy.ndarray[numpy.float16](2,) | [float, float]               |
+-----------+-------------------------------+--------------+----------+----------------------------------+------------------------------+
| half[3]   | (half,half,half)              | pxr::GfVec3h | __half3* | numpy.ndarray[numpy.float16](3,) | [float, float, float]        |
+-----------+-------------------------------+--------------+----------+----------------------------------+------------------------------+
| half[4]   | (half,half,half,half)         | pxr::GfVec4h | __half4* | numpy.ndarray[numpy.float16](4,) | [float, float, float, float] |
+-----------+-------------------------------+--------------+----------+----------------------------------+------------------------------+
| int[2]    | (int,int)                     | pxr::GfVec2i | int2*    | numpy.ndarray[numpy.int32](2,)   | [float, float]               |
+-----------+-------------------------------+--------------+----------+----------------------------------+------------------------------+
| int[3]    | (int,int,int)                 | pxr::GfVec3i | int3*    | numpy.ndarray[numpy.int32](3,)   | [float, float, float]        |
+-----------+-------------------------------+--------------+----------+----------------------------------+------------------------------+
| int[4]    | (int,int,int,int)             | pxr::GfVec4i | int4*    | numpy.ndarray[numpy.int32](4,)   | [float, float, float, float] |
+-----------+-------------------------------+--------------+----------+----------------------------------+------------------------------+

.. note::

    Owing to this implementation of a wrapper around raw data all of these types can also be safely
    cast to other types that have an equivalent memory layout. For example:

        ``MyFloat3& f3 = reinterpret_cast<MyFloat3&>(db.inputs.myFloat3Attribute());``

Here's an example of how the class, typedef, USD, and CUDA types relate:

.. code-block:: c++

    const GfVec3f& fRaw = db.inputs.myFloat3();
    const ogn::float3& fOgn = reinterpret_cast<const ogn::float3&>(fRaw);
    const carb::Float3& fCarb = reinterpret_cast<const carb::Float3>(fOgn);
    vectorOperation( fCarb.x, fCarb.y, fCarb.z );
    callCUDAcode( fRaw );

.. code-block:: c++

    extern "C" void callCUDAcode(float3 myFloat3) {...}

Here are samples of tuple data type values in the various languages:

**USD Tuple Type**
++++++++++++++++++

.. code-block:: usd

    custom int2 inputs:myIntTuple = (1, 2)
    custom float3 inputs:myFloatTuple = (1.0, 2.0, 3.0)

**C++ Tuple Type**
++++++++++++++++++

.. code-block:: c++

    static bool compute(OgnMyNodeDatabase& db)
    {
        const GfVec2i& iValue = db.inputs.myIntTuple();
        const GfVec3f& fValue = db.inputs.myFloatTuple();
    }

**CUDA Tuple Type**
+++++++++++++++++++

.. code-block:: c++

    // Note how the signatures are not identical between the declaration here and the
    // definition in the CUDA file. This is possible because the data types have identical
    // memory layouts, in this case equivalent to int[2] and float[3].
    extern "C" runCUDAcompute(pxr::GfVec2i* iTuple, pxr::GfVec3f* fTuple);
    static bool compute(OgnMyNodeDatabase& db)
    {
        runCUDAcompute( db.inputs.myIntTuple(), db.inputs.myFloatTuple() );
    }

.. code-block:: c++

    extern "C" void runCUDAcompute(float3* iTuple, float3* fTuple)
    {
        // In here it is true that the number of elements in iArray = iSize
    }

**Python Tuple Type Hint**
++++++++++++++++++++++++++

.. code-block:: python

    import numpy as np
    @property
    def myIntTuple(self) -> np.ndarray[nd.int]:
        return attributeValues.myIntTuple
    @property
    def myFloatTuple(self) -> np.ndarray[nd.float]:
        return attributeValues.myFloatTuple

**JSON Tuple Type**
+++++++++++++++++++

.. code-block:: json

    {
        "myNode" : {
            "description" : ["This is my node with one integer tuple and one float tuple input"],
            "version" : 1,
            "inputs" : {
                "myIntTuple" : {
                    "description" : ["This is an integer tuple attribute"],
                    "type" : "int[2]",
                    "default" : [1, 2]
                },
                "myFloatTuple" : {
                    "description" : ["This is a float tuple attribute"],
                    "type" : "float[3]",
                    "default" : [1.0, 2.0, 3.0]
                }
            }
        }
    }

Arrays of Tuple Data Types
--------------------------

Like base data types, there can also be arrays of tuples by appending '[]' to the data type. For now the only
ones supported are the above special types, supported natively in USD. Once the USD conversions are sorted out,
all tuple types can be arrays using these rules.

The type names will have the tuple specification followed by the array specification, e.g. *float[3][]* for an
array of three-floats. This will also extend to arrays of arrays in the future by appending another '[]'.

JSON makes no distinction between arrays and tuples so it will be a multi-dimensional list.

USD uses parentheses **()** for tuples and square brackets **[]** for arrays so both are used to specify the
data values. The types are specified according to the USD Type column in the table above with square brackets appended.

Both the Python and C++ tuple and array types nest for arrays of tuple types.

+-------------+--------------------------------------+--------------------------+--------------------------------------------------+----------------------------+-----------+
| Type Name   |   C++                                | CUDA                     |  Python                                          |  JSON                      | Direction |
+=============+======================================+==========================+==================================================+============================+===========+
| TYPE[N][]   | ogn::array<TUPLE_TYPE>               | TUPLE_TYPE*,size_t       | numpy.ndarray[numpy.TUPLE_TYPE](N,TUPLE_COUNT,)  | array of array of JSONTYPE |   Output  |
+-------------+--------------------------------------+--------------------------+--------------------------------------------------+----------------------------+-----------+

Here are samples of arrays of tuple data type values in the various languages:

**USD Tuple Array Type**
++++++++++++++++++++++++

.. code-block:: usd

    custom int2[] inputs:myIntTuple = [(1, 2), (3, 4), (5, 6)]
    custom float3[] inputs:myFloatTuple = [(1.0, 2.0, 3.0)]

**C++ Tuple Array Type**
++++++++++++++++++++++++

.. code-block:: c++

    static bool compute(OgnMyNodeDatabase& db)
    {
        const ogn::const_array<GfVec2i>& iValue = db.inputs.myIntTupleArray();
        const ogn::const_array<GfVec3f> &fValue = db.inputs.myFloatTupleArray();
        // or const auto& fValue = db.inputs.myFloatTupleArray();
    }

**CUDA Tuple Array Type**
+++++++++++++++++++++++++

.. code-block:: c++

    extern "C" runCUDAcompute(inputs::myIntTupleArray_t* iTuple, size_t iSize,
                              inputs::myFloatTupleArray_t* fTuple, size_t fSize);
    static bool compute(OgnMyNodeDatabase& db)
    {
        runCUDAcompute( db.inputs.myIntTupleArray(), db.inputs.myIntTupleArray.size(),
                        db.inputs.myFloatTupleArray(), db.inputs.myFloatTupleArray.size() );
    }

.. code-block:: c++

    extern "C" void runCUDAcompute(float3** iTuple, size_t iSize, float3** fTuple, size_t fSize)
    {
    }

**Python Tuple Array Type Hint**
++++++++++++++++++++++++++++++++

.. code-block:: python

    import numpy as np
    @property
    def myIntTupleArray(self) -> np.ndarray:
        return attributeValues.myIntTupleArray
    @property
    def myFloatTupleArray(self) -> np.ndarray:
        return attributeValues.myFloatTupleArray

**JSON Tuple Array Type**
+++++++++++++++++++++++++

.. code-block:: json

    {
        "myNode" : {
            "description" : ["This is my node with one integer tuple array and one float tuple array input"],
            "version" : 1,
            "inputs" : {
                "myIntTuple" : {
                    "description" : ["This is an integer tuple array attribute"],
                    "type" : "int[2][]",
                    "default" : [[1, 2], [3, 4], [5, 6]]
                },
                "myFloatTuple" : {
                    "description" : ["This is a float tuple array attribute"],
                    "type" : "float[3][]",
                    "default" : []
                }
            }
        }
    }

.. _ogn_attribute_roles:

Attribute Types With Roles
--------------------------

Some attributes have specific interpretations that are useful for determining how to use them at runtime. These
roles are encoded into the names for simplicity.

.. note::

    The fundamental data in the attributes when an AttributeRole is set are unchanged. Adding the role just allows
    the interpretation of that data as a first class object of a non-trivial type. The "C++ Type" column in the
    table below shows how the underlying data is represented.

For simplicity of specification, the type of base data is encoded in the type name, e.g. *colord* for colors
using double values and *colorf* for colors using float values.

+--------------+------------+------------+----------+-----------------------------------------------------------+
| Type Name    |  USD       | C++        | CUDA     | Description                                               |
+==============+============+============+==========+===========================================================+
| colord[3]    | color3d    | GfVec3d    | double3  | Color value with 3 members of type double                 |
+--------------+------------+------------+----------+-----------------------------------------------------------+
| colorf[3]    | color3f    | GfVec3f    | float3   | Color value with 3 members of type float                  |
+--------------+------------+------------+----------+-----------------------------------------------------------+
| colorh[3]    | color3h    | GfVec3h    | __half3  | Color value with 3 members of type 16 bit float           |
+--------------+------------+------------+----------+-----------------------------------------------------------+
| colord[4]    | color4d    | GfVec4d    | double4  | Color value with 4 members of type double                 |
+--------------+------------+------------+----------+-----------------------------------------------------------+
| colorf[4]    | color4f    | GfVec4f    | float4   | Color value with 4 members of type float                  |
+--------------+------------+------------+----------+-----------------------------------------------------------+
| colorh[4]    | color4h    | GfVec4h    | __half4  | Color value with 4 members of type 16 bit float           |
+--------------+------------+------------+----------+-----------------------------------------------------------+
| normald[3]   | normal3d   | GfVec3d    | double3  | Normal vector with 3 members of type double               |
+--------------+------------+------------+----------+-----------------------------------------------------------+
| normalf[3]   | normal3f   | GfVec3f    | float3   | Normal vector with 3 members of type float                |
+--------------+------------+------------+----------+-----------------------------------------------------------+
| normalh[3]   | normal3h   | GfVec3h    | __half3  | Normal vector with 3 members of type 16 bit float         |
+--------------+------------+------------+----------+-----------------------------------------------------------+
| pointd[3]    | pointd     | GfVec3d    | double3  | Cartesian point value with 3 members of type double       |
+--------------+------------+------------+----------+-----------------------------------------------------------+
| pointf[3]    | pointf     | GfVec3f    | float3   | Cartesian point value with 3 members of type float        |
+--------------+------------+------------+----------+-----------------------------------------------------------+
| pointh[3]    | pointh     | GfVec3h    | __half3  | Cartesian point value with 3 members of type 16 bit float |
+--------------+------------+------------+----------+-----------------------------------------------------------+
| quatd[4]     | quat4d     | GfQuatd    | double3  | Quaternion with 4 members of type double as IJKR          |
+--------------+------------+------------+----------+-----------------------------------------------------------+
| quatf[4]     | quat4f     | GfQuatf    | float3   | Quaternion with 4 members of type float as IJKR           |
+--------------+------------+------------+----------+-----------------------------------------------------------+
| quath[4]     | quat4h     | GfQuath    | __half3  | Quaternion with 4 members of type 16 bit float as IJKR    |
+--------------+------------+------------+----------+-----------------------------------------------------------+
| texcoordd[2] | texCoord2d | GfVec2d    | double2  | Texture coordinate with 2 members of type double          |
+--------------+------------+------------+----------+-----------------------------------------------------------+
| texcoordf[2] | texCoord2f | GfVec2f    | float2   | Texture coordinate with 2 members of type float           |
+--------------+------------+------------+----------+-----------------------------------------------------------+
| texcoordh[2] | texCoord2h | GfVec2h    | __half2  | Texture coordinate with 2 members of type 16 bit float    |
+--------------+------------+------------+----------+-----------------------------------------------------------+
| texcoordd[3] | texCoord3d | GfVec3d    | double3  | Texture coordinate with 3 members of type double          |
+--------------+------------+------------+----------+-----------------------------------------------------------+
| texcoordf[3] | texCoord3f | GfVec3f    | float3   | Texture coordinate with 3 members of type float           |
+--------------+------------+------------+----------+-----------------------------------------------------------+
| texcoordh[3] | texCoord3h | GfVec3h    | __half3  | Texture coordinate with 3 members of type 16 bit float    |
+--------------+------------+------------+----------+-----------------------------------------------------------+
| timecode     | timecode   | double     | double   | Double value representing a timecode                      |
+--------------+------------+------------+----------+-----------------------------------------------------------+
| vectord[3]   | vector3d   | GfVec3d    | double3  | Vector with 3 members of type double                      |
+--------------+------------+------------+----------+-----------------------------------------------------------+
| vectorf[3]   | vector3f   | GfVec3f    | float3   | Vector with 3 members of type float                       |
+--------------+------------+------------+----------+-----------------------------------------------------------+
| vectorh[3]   | vector3h   | GfVec3h    | __half3  | Vector with 3 members of type 16 bit float                |
+--------------+------------+------------+----------+-----------------------------------------------------------+
| matrixd[2]   | matrix2d   | GfMatrix2d | Matrix2d | Transform matrix with 4 members of type double            |
+--------------+------------+------------+----------+-----------------------------------------------------------+
| matrixd[3]   | matrix3d   | GfMatrix3d | Matrix3d | Transform matrix with 9 members of type double            |
+--------------+------------+------------+----------+-----------------------------------------------------------+
| matrixd[4]   | matrix4d   | GfMatrix4d | Matrix4d | Transform matrix with 16 members of type double           |
+--------------+------------+------------+----------+-----------------------------------------------------------+

Python and JSON do not have special types for role-based attributes, although that may change for Python once its
interface is fully defined.

The roles are all tuple types so the Python equivalents will all be of the form **Tuple[TYPE, TYPE...]**, and JSON data
will be of the form **[TYPE, TYPE, TYPE]**. The types corresponding to the *Equivalent* column base types are seen above
in `Base Data Types`_.

The color role will serve for our example types here:

**USD Color Role Attribute**
+++++++++++++++++++++++++++++

.. code-block:: usd

    custom color3d inputs:myColorRole = (1.0, 0.5, 1.0)

**C++ Color Role Attribute**
+++++++++++++++++++++++++++++

.. code-block:: c++

    static bool compute(OgnMyNodeDatabase& db)
    {
        const GfVec3d& colorValue = db.inputs.myColorRole();
        // or const auto& colorValue = db.inputs.myColorRole();
    }

**CUDA Color Role Type**
++++++++++++++++++++++++

.. code-block:: c++

    extern "C" runCUDAcompute(pxr::GfVec3d* color);
    static bool compute(OgnMyNodeDatabase& db)
    {
        runCUDAcompute( db.inputs.myColorRole() );
    }

.. code-block:: c++

    extern "C" void runCUDAcompute(double3* color)
    {
    }

**Python Color Role Attribute Hint**
+++++++++++++++++++++++++++++++++++++

.. code-block:: python

    import numpy as np
    @property
    def myColorRole(self) -> np.ndarray:
        return attributeValues.myColorRole

**JSON Color Role Attribute**
++++++++++++++++++++++++++++++

.. code-block:: json

    {
        "myNode" : {
            "description" : ["This is my node with one color role input"],
            "version" : 1,
            "inputs" : {
                "myColorRole" : {
                    "description" : ["This is color role attribute"],
                    "type" : "colord[3]",
                    "default" : [0.0, 0.5, 1.0]
                }
            }
        }
    }

Bundle Type Attributes
----------------------

There is a special type of attribute whose type is *bundle*. This attribute represents a set of attributes whose
contents can only be known at runtime. It can still be in a tuple, array, or both. In itself it has no data in Fabric.
Its purpose is to be a container to a description of other attributes of any of the above types, or even
other bundles.

**USD Bundled Attribute**
++++++++++++++++++++++++++

.. code-block:: usd

    custom rel inputs:inBundle (
        doc="""The input bundle is a relationship, which could come from a prim or another bundle attribute""")

    def Output "outputs_outBundle" (
        doc="""Output bundles are represented as empty prims, with any namespace colons replaced by underscores""")
    {
    }

**C++ Bundled Attribute**
++++++++++++++++++++++++++

.. code-block:: c++

    static bool compute(OgnMyNodeDatabase& db)
    {
        // The simplest method of breaking open a bundle is to get an attribute by name
        const auto& inBundle = db.inputs.inBundle();
        auto myFloat3Attribute = inBundle.attributeByName(db.stringToToken("myFloat3"));
        if (auto asFloat3Array = myFloat3Attribute.get<float[][3]>())
        {
            handleFloat3Array(asFloat3Array); // The data is of type float[][3]
        }

        // The bundle has iteration capabilities
        for (auto& bundledAttribute : inBundle)
        {
            // Use the type information to find the actual data type and then cast it
            if ((bundledAttribute.type().baseType == BaseDataType::eInt)
            &&  (bundledAttribute.type().componentCount == 1)
            &&  (bundledAttribute.type().arrayDepth == 0))
            {
                CARB_ASSERT( nullptr != bundledAttribute.get<int>() );
            }
        }
    }

See the tutorials on :ref:`ogn_tutorial_bundle_manipulation` and :ref:`ogn_tutorial_bundle_data` for more
details on manipulating the bundle and its attributes.

**CUDA Bundled Attribute**
+++++++++++++++++++++++++++

.. code-block:: c++

    extern "C" runCUDAcompute(float3* value, size_t iSize);
    static bool compute(OgnMyNodeDatabase& db)
    {
        const auto& myBundle = db.inputs.myBundle();
        auto myFloat3Attribute = myBundle.attributeByName(db.stringToToken("myFloat3"));
        if (auto asFloat3Array = myFloat3Attribute.get<float[][3]>())
        {
            runCUDAcompute(asFloat3Array.data(), asFloat3Array.size());
        }
    }

.. code-block:: c++

    extern "C" void runCUDAcompute(float3** value, size_t iSize)
    {
    }

**Python Bundled Attribute Hint**
++++++++++++++++++++++++++++++++++

.. code-block:: python

    from typing import Union
    from omni.graph.core.types import AttributeTypes, Bundle, BundledAttribute
    @property
    def myBundle(self) -> Bundle:
        return attributeValues.myBundle

    attribute_count = myNode.myBundle.attribute_count()
    for bundled_attribute in myNode.myBundle.attributes():
        if bundled_attribute.type.base_type == AttributeTypes.INT:
            deal_with_integers(bundled_attribute.value)

See the tutorials on :ref:`ogn_tutorial_bundle_manipulation` and :ref:`ogn_tutorial_bundle_data` for more
details on manipulating the bundle and its attributes.

**JSON Bundled Attribute**
++++++++++++++++++++++++++

.. code-block:: json

    {
        "myNode" : {
            "description" : ["This is my node with one bundled input"],
            "version" : 1,
            "inputs" : {
                "myBundle" : {
                    "description" : ["This is input bundle attribute"],
                    "type" : "bundle"
                }
            }
        }
    }

It's worth noting here that as a bundle does not represent actual data these attributes are not allowed to have a
default value.

If a bundle attribute is defined to live on the GPU, either at all times or as a decision at runtime, this is
equivalent to stating that any attributes that exist inside the bundle will be living on the GPU using the same
criteria.

Extended Attribute Types
------------------------

Some attribute types are only determined at runtime by the data they receive. These types include the "any" type, which
is a single attribute that can be any of the above types, and the "union" type, which specifies a subset of the above
types it can take on. (The astute will notice that "union" is a subset of "any".)

Extended attribute types allow a single node to handle several different attribute-type configurations.  For example a
generic 'Cos' node may be able to compute the cosine of any decimal type.

**USD Extended Attribute**
++++++++++++++++++++++++++

.. code-block:: usd

    custom token inputs:floatOrInt
    custom token inputs:floatArrayOrIntArray
    custom token inputs:anyType

**C++ Extended Attribute**
++++++++++++++++++++++++++

.. code-block:: c++

    static bool compute(OgnMyNodeDatabase& db)
    {
        // Casting can be used to find the actual data type the attribute contains
        const auto& floatOrInt = db.inputs.floatOrInt();
        bool isFloat = (nullptr != floatOrInt.get<float>());
        bool isInt = (nullptr != floatOrInt.get<int>());

        // Different types are cast in the same way as bundle attributes
        const auto& floatOrIntArray = db.inputs.floatOrIntArray();
        bool isFloatArray = (nullptr != floatOrIntArray.get<float[]>());
        bool isIntArray = (nullptr != floatOrIntArray.get<int[]>());

        const auto& anyType = db.inputs.anyType();
        std::cout << "Any type is " << anyType.type() << std::endl;
        // Like bundled attributes, use the type information to find the actual data type and then cast it
        if ((anyType.type().baseType == BaseDataType::eInt)
        &&  (anyType.type().componentCount == 1)
        &&  (anyType.type().arrayDepth == 0))
        {
            CARB_ASSERT( nullptr != anyType.get<int>() );
        }
    }

**CUDA Extended Attribute**
+++++++++++++++++++++++++++

.. code-block:: c++

    extern "C" runCUDAcomputeFloat(float3* value, size_t iSize);
    extern "C" runCUDAcomputeInt(int3* value, size_t iSize);
    static bool compute(OgnMyNodeDatabase& db)
    {
        const auto& float3OrInt3Array = db.inputs.float3OrInt3Array();
        if (auto asFloat3Array = float3OrInt3Array.get<float[][3]>())
        {
            runCUDAcomputeFloat(asFloat3Array.data(), asFloat3Array.size());
        }
        else if (auto asInt3Array = float3OrInt3Array.get<int[][3]>())
        {
            runCUDAcomputeint(asInt3Array.data(), asInt3Array.size());
        }
    }

.. code-block:: c++

    extern "C" void runCUDAcomputeFloat(float3** value, size_t iSize)
    {
    }
    extern "C" void runCUDAcomputeInt(int3** value, size_t iSize)
    {
    }

**Python Extended Attribute Hint**
++++++++++++++++++++++++++++++++++

.. code-block:: python

    from typing import Union
    @property
    def myIntOrFloatArray(self) -> List[Union[int, float]]:
        return attributeValues.myIntOrFloatArray

**JSON Extended Attribute**
+++++++++++++++++++++++++++

.. code-block:: json

    {
        "myNode" : {
            "description" : "This is my node with some extended inputs",
            "version" : 1,
            "inputs" : {
                "anyType" : {
                    "description" : "This attribute accepts any type of data, determined at runtime",
                    "type" : "any"
                },
                "someNumber": {
                    "description": "This attribute accepts either float, double, or half values",
                    "type": ["float", "double", "half"]
                },
                "someNumberArray": {
                    "description": ["This attributes accepts an array of float, double, or half values.",
                                    "All values in the array must be of the same type, like a regular array attribute."],
                    "type": ["float[]", "double[]", "half[]"],
                },
            }
        }
    }

**Extended Attribute Union Groups**
+++++++++++++++++++++++++++++++++++

As described above, union extended types are specified by providing a list of types in the OGN definition. These lists can become
quite long if a node can handle a large subset of the possible types. For convenience there are special type names that can be
used inside the JSON list to denote groups of types. For example:

.. code-block:: json

    {
        "myNode" : {
            "description" : "This is my node using union group types",
            "version" : 1,
            "inputs" : {
                "decimal" : {
                    "description" : "This attribute accepts double, float and half",
                    "type" : ["decimal_scalers"]
                }
            }
        }
    }

**List of Attribute Union Groups**
++++++++++++++++++++++++++++++++++

+-------------------------+------------------------------------------------------------------------------------------+
|     Group Type Name     |                                       Type Members                                       |
+=========================+==========================================================================================+
| integral_scalers        | uchar, int, uint, uint64, int64, timecode                                                |
+-------------------------+------------------------------------------------------------------------------------------+
| integral_tuples         | int[2], int[3], int[4]                                                                   |
+-------------------------+------------------------------------------------------------------------------------------+
| integral_array_elements | integral_scalers, integral_tuples                                                        |
+-------------------------+------------------------------------------------------------------------------------------+
| integral_arrays         | arrays of integral_array_elements                                                        |
+-------------------------+------------------------------------------------------------------------------------------+
| integrals               | integral_array_elements, integral_arrays                                                 |
+-------------------------+------------------------------------------------------------------------------------------+
| matrices                | matrixd[3], matrixd[4], transform[4], frame[4]                                           |
+-------------------------+------------------------------------------------------------------------------------------+
| decimal_scalers         | double, float, half                                                                      |
+-------------------------+------------------------------------------------------------------------------------------+
| decimal_tuples          | double[2], double[3], double[4], float[2], float[3], float[4], half[2], half[3], half[4] |
|                         |                                                                                          |
|                         | colord[3], colord[4], colorf[3], colorf[4], colorh[3], colorh[4]                         |
|                         |                                                                                          |
|                         | normald[3], normalf[3], normalh[3]                                                       |
|                         |                                                                                          |
|                         | pointd[3], pointf[3], pointh[3]                                                          |
|                         |                                                                                          |
|                         | texcoordd[2], texcoordd[3], texcoordf[2], texcoordf[3], texcoordh[2], texcoordh[3]       |
|                         |                                                                                          |
|                         | quatd[4], quatf[4], quath[4]                                                             |
|                         |                                                                                          |
|                         | vectord[3], vectorf[3], vectorh[3]                                                       |
+-------------------------+------------------------------------------------------------------------------------------+
| decimal_array_elements  | decimal_scalers, decimal_tuples                                                          |
+-------------------------+------------------------------------------------------------------------------------------+
| decimal_arrays          | arrays of decimal_array_elements                                                         |
+-------------------------+------------------------------------------------------------------------------------------+
| decimals                | decimal_array_elements, decimal_arrays                                                   |
+-------------------------+------------------------------------------------------------------------------------------+
| numeric_scalers         | integral_scalers, decimal_scalers                                                        |
+-------------------------+------------------------------------------------------------------------------------------+
| numeric_tuples          | integral_tuples, decimal_tuples                                                          |
+-------------------------+------------------------------------------------------------------------------------------+
| numeric_array_elements  | numeric_scalers, numeric_tuples, matrices                                                |
+-------------------------+------------------------------------------------------------------------------------------+
| numeric_arrays          | arrays of numeric_array_elements                                                         |
+-------------------------+------------------------------------------------------------------------------------------+
| numerics                | numeric_array_elements, numeric_arrays                                                   |
+-------------------------+------------------------------------------------------------------------------------------+
| array_elements          | numeric_array_elements, token                                                            |
+-------------------------+------------------------------------------------------------------------------------------+
| arrays                  | numeric_arrays, token[]                                                                  |
+-------------------------+------------------------------------------------------------------------------------------+

**Extended Attribute Resolution**
+++++++++++++++++++++++++++++++++

Extended attributes are useful to improve the usability of nodes with different types.  However the node author has an
extra responsibility to resolve the extended type attributes when possible in order to resolve possible ambiguity
in the graph. If graph connections are unresolved at execution, the node's computation will be skipped.

There are various helpful Python APIs for type resolution, including :py:func:`omni.graph.core.resolve_base_coupled` and
:py:func:`omni.graph.core.resolve_fully_coupled` which allow you to match unresolved inputs to resolved inputs.

.. code-block:: python

    @staticmethod
    def on_connection_type_resolve(node) -> None:
        aattr = node.get_attribute("inputs:a")
        resultattr = node.get_attribute("outputs:result")
        og.resolve_fully_coupled([aattr, resultattr])

You can also define your own semantics for custom type resolution. The following node takes two decimals, a and b,
and returns their product. If one input is at a lower "significance" than the other, the less significant will be "promoted"
to prevent loss of precision. For example, if inputs are `float` and `double`, the output will be a `double`.

See :class:`omni.graph.core.Type` for more information about creating custom types.

.. code-block:: python

    @staticmethod
    def on_connection_type_resolve(node) -> None:
        atype = node.get_attribute("inputs:a").get_resolved_type()
        btype = node.get_attribute("inputs:b").get_resolved_type()
        productattr = node.get_attribute("outputs:product")
        producttype = productattr.get_resolved_type()
        # we can only infer the output given both inputs are resolved and they are the same.
        if (atype.base_type != og.BaseDataType.UNKNOWN and btype.base_type != og.BaseDataType.UNKNOWN
                and producttype.base_type == og.BaseDataType.UNKNOWN):
            if atype.base_type == btype.base_type:
                base_type = atype.base_type
            else:
                decimals = [og.BaseDataType.HALF, og.BaseDataType.FLOAT, og.BaseDataType.DOUBLE]
                try:
                    a_ix = decimals.index(atype.base_type)
                except ValueError:
                    a_ix = -1
                try:
                    b_ix = decimals.index(btype.base_type)
                except ValueError:
                    b_ix = -1

                if a_ix >= 0 or b_ix >= 0:
                    base_type = atype.base_type if a_ix > b_ix else btype.base_type
                else:
                    base_type = og.BaseDataType.DOUBLE

            productattr.set_resolved_type(og.Type(base_type, max(atype.tuple_count, btype.tuple_count),
                max(atype.array_depth, btype.array_depth)))

See :ref:`ogn_tutorial_extended_types` for more examples on how to perform attribute resolution in C++ and Python.

.. _ogn_type_definition_overrides:

Type Definition Overrides
-------------------------
The generated types provide a default implementation you can use out of the box. Sometimes you might have your own
favorite library for type manipulation so you can provide a type definition configuration file that modifies the
return types used by the generated code.

There are four ways you can implement type overrides.

1. Use the `typeDefinitions` flag on the `generate_node.py` script to point to the file containing the configuration.
2. Use the `"typeDefinitions": "ConfigurationFile.json"` keyword in the .ogn file to point a single node to a configuration.
3. Use the `"typeDefintitions": {TypeConfigurationDictionary}` keyword in the .ogn file to implement simple targeted overrides in a single node.
4. Add the name of the type definitions file to your premake5.lua file in `get_ogn_project_informat("omni/test", "ConfigurationFile.json")` to modify the types for every node in your extension.

The format used for the type definition information is the same for all methods. Here is a sample, with an embedded
explanation on how it is formatted.

.. literalinclude:: ../../../../source/extensions/omni.graph.tools/ogn_config/TypeConfigurationPod.json
    :language: json
