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.

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

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

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.

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 );
}
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

@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

{
    "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

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

C++ Array Type

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

CUDA Array Type

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 );
}
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

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

JSON Array Type

{
    "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:

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 );
extern "C" void callCUDAcode(float3 myFloat3) {...}

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

USD Tuple Type

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

C++ Tuple Type

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

CUDA Tuple Type

// 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() );
}
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

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

{
    "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

typing.List[typing.Tuple[TYPE, TYPE…]]

array of array of JSONTYPE

Output

TYPE[N][]

ogn::const_array<const TUPLE_TYPE>

const TUPLE_TYPE*,size_t

typing.List[typing.Tuple[TYPE, TYPE…]]

array of array of JSONTYPE

Input

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

USD Tuple Array Type

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

C++ Tuple Array Type

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

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() );
}
extern "C" void runCUDAcompute(float3** iTuple, size_t iSize, float3** fTuple, size_t fSize)
{
}

Python Tuple Array Type Hint

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

JSON Tuple Array Type

{
    "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" : []
            }
        }
    }
}

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

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

C++ Color Role Attribute

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

CUDA Color Role Type

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

Python Color Role Attribute Hint

from typing import Tuple
@property
def myColorRole(self) -> Tuple[float, float, float]:
    return attributeValues.myColorRole

JSON Color Role Attribute

{
    "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

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

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.getAttributeByName(db.stringToToken("myFloat3"));
    if (auto asFloat3Array = myFloat3Attribute.value<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.value<int>() );
        }
    }
}

See the tutorials on Tutorial 15 - Bundle Manipulation and Tutorial 16 - Bundle Data for more details on manipulating the bundle and its attributes.

CUDA Bundled Attribute

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

Python Bundled Attribute Hint

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 Tutorial 15 - Bundle Manipulation and Tutorial 16 - Bundle Data for more details on manipulating the bundle and its attributes.

JSON Bundled Attribute

{
    "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

custom rel inputs:floatOrInt
custom rel inputs:floatArrayOrIntArray
custom rel inputs:anyType

C++ Extended Attribute

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.value<float>());
    bool isInt = (nullptr != floatOrInt.value<int>());

    // Different types are cast in the same way as bundle attributes
    const auto& floatOrIntArray = db.inputs.floatOrIntArray();
    bool isFloatArray = (nullptr != floatOrIntArray.value<float[]>());
    bool isIntArray = (nullptr != floatOrIntArray.value<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.value<int>() );
    }
}

CUDA Extended Attribute

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.value<float[][3]>())
    {
        runCUDAcomputeFloat(asFloat3Array.data(), asFloat3Array.size());
    }
    else if (auto asInt3Array = float3OrInt3Array.value<int[][3]>())
    {
        runCUDAcomputeint(asInt3Array.data(), asInt3Array.size());
    }
}
extern "C" void runCUDAcomputeFloat(float3** value, size_t iSize)
{
}
extern "C" void runCUDAcomputeInt(int3** value, size_t iSize)
{
}

Python Extended Attribute Hint

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

JSON Extended Attribute

{
    "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:

{
    "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 omni.graph.core.resolve_base_coupled() and omni.graph.core.resolve_fully_coupled() which allow you to match unresolved inputs to resolved inputs.

@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 omni.graph.core.Type for more information about creating custom types.

@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 Tutorial 19 - Extended Attribute Types for more examples on how to perform attribute resolution in C++ and Python.

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.

{
    "typeDefinitions": {
        "$description": [
            "This file contains the casting information that will tell the OGN code generator what kind of data",
            "types it should generate for all of the attribute types. Any that do not explicitly appear in this file",
            "will use the default USD types (e.g. float[3] or int[][2]). As with regular .ogn files the keywords",
            "starting with a '$' are ignored by the parser and can be used for adding explanatory comments, such as",
            "this one.",
            "",
            "This file provides the type cast configuration for using POD data types. Note that as this is no",
            "POD equivalent for special types, including the 'half' float, they are left as their defaults. So long",
            "as none of your nodes use them they will not bring in USD. Other types such as 'any', 'bundle', 'string',",
            "and 'token' have explicit OGN types."
        ],
        "c++": {
            "$description": [
                "This section contains cast information for C++ data types. These are the types returned by the",
                "database generated for nodes written in C++. Each entry consists of a key value corresponding to",
                "the attribute type as it appears in .ogn files, and a value pair consisting of the raw data type",
                "to which the attribute value should be cast and the include file required to define it. Though there",
                "may be considerable duplication of include file definitions only one will be emitted by the generated code.",
                "Every supported type must be present in this file, using an empty list as the implementation if there",
                "is no explicit definition for them in this library. In those cases the hardcoded defaults will be",
                "used. If supported types are missing a warning will be logged as it may indicate an oversight. One",
                "caveat is allowed - if an array type is not specified but the non-array base type is then it is",
                "assumed that the array type information is the same as the non-array type information. e.g. the",
                "information for bool[] is the same as for bool[]."
            ],
            "any": [],
            "bool": ["bool"],
            "bundle": [],
            "colord[3]": ["double[3]"],
            "colord[4]": ["double[4]"],
            "colorf[3]": ["float[3]"],
            "colorf[4]": ["float[4]"],
            "colorh[3]": [],
            "colorh[4]": [],
            "double": ["double"],
            "double[2]": ["double[2]"],
            "double[3]": ["double[3]"],
            "double[4]": ["double[4]"],
            "execution": ["uint32_t"],
            "float": ["float"],
            "float[2]": ["float[2]"],
            "float[3]": ["float[3]"],
            "float[4]": ["float[4]"],
            "frame[4]": ["double[4][4]"],
            "half": [],
            "half[2]": [],
            "half[3]": [],
            "half[4]": [],
            "int": ["int"],
            "int[2]": ["int[2]"],
            "int[3]": ["int[3]"],
            "int[4]": ["int[4]"],
            "int64": ["int64_t"],
            "matrixd[2]": ["double[2][2]"],
            "matrixd[3]": ["double[3][3]"],
            "matrixd[4]": ["double[4][4]"],
            "normald[3]": ["double[3]"],
            "normalf[3]": ["float[3]"],
            "normalh[3]": [],
            "objectId": ["uint64_t"],
            "path": ["uint64_t"],
            "pointd[3]": ["double[3]"],
            "pointf[3]": ["float[3]"],
            "pointh[3]": [],
            "$Quaternion layout": "[i, j, k, r] must be used to match the memory layout used by GfQuat4d et. al.",
            "quatd[4]": ["double[4]"],
            "quatf[4]": ["float[4]"],
            "quath[4]": [],
            "string": [],
            "texcoordd[2]": ["double[2]"],
            "texcoordd[3]": ["double[3]"],
            "texcoordf[2]": ["float[2]"],
            "texcoordf[3]": ["float[3]"],
            "texcoordh[2]": [],
            "texcoordh[3]": [],
            "timecode": ["double"],
            "token": [],
            "transform[4]": ["double[4][4]"],
            "uchar": ["uint8_t"],
            "uint": ["uint32_t"],
            "uint64": ["uint64_t"],
            "vectord[3]": ["double[3]"],
            "vectorf[3]": ["float[3]"],
            "vectorh[3]": []
        }
    }
}