Attribute Type Definition

Attributes in OmniGraph all have a type, which indicates what kind of data they can represent. The type definitions are attached to the attributes so that their associated data can be correctly stored and interpreted.

In addition, the type definition can be used to disambiguate values that can be interpreted as multiple types For example the number 5 could be interpreted as integer, floating point, double-precision, etc. The correct interpretation is needed in order to decide how much storage this value will occupy, as well as how the bits representing that number are to be arranged.

The Type Structure

The attribute type is a simple structure that contains a few pieces that uniquely identify the entire type. The type definition in C++ is inherited from Fabric since that is where the data is stored. There is an OmniGraph wrapper around it that allows creation of some specific utilities, as well as interpretation of some OmniGraph-specific role types.

Full documentation of the type structure is available for both C++ and Python.

Base Data Type

The base data type is the low level data type that stores a single value of the given attribute type. There are some simple values that are plain-old-data (POD) types and a few that represent USD-specific data such as prims. In C++ the value is specified by the enum omni::fabric::BaseDataType, and in Python it is in omni.graph.core.BaseDataType.

import omni.graph.core as og
all_base_data_types = {
    og.BaseDataType.ASSET: "Data represents an Asset",
    og.BaseDataType.BOOL: "Data is a boolean",
    og.BaseDataType.CONNECTION: "Data is a special value representing a connection",
    og.BaseDataType.DOUBLE: "Data is a double precision floating point value",
    og.BaseDataType.FLOAT: "Data is a single precision floating point value",
    og.BaseDataType.HALF: "Data is a half precision floating point value",
    og.BaseDataType.INT: "Data is a 32-bit integer",
    og.BaseDataType.INT64: "Data is a 64-bit integer",
    og.BaseDataType.PRIM: "Data is a reference to a USD prim",
    og.BaseDataType.RELATIONSHIP: "Data is a relationship to a USD prim",
    og.BaseDataType.TAG: "Data is a special Fabric tag",
    og.BaseDataType.TOKEN: "Data is a reference to a unique shared string",
    og.BaseDataType.UCHAR: "Data is an 8-bit unsigned character",
    og.BaseDataType.UINT: "Data is a 32-bit unsigned integer",
    og.BaseDataType.UINT64: "Data is a 64-bit unsigned integer",
    og.BaseDataType.UNKNOWN: "Data type is currently unknown",
}
enum class BaseDataType : uint8_t
{
    eUnknown = 0, //!< The base type is not defined
    eBool,  //!< Boolean type
    eUChar,  //!< Unsigned character (8-bit)
    eInt,  //!< 32-bit integer
    eUInt,  //!< 32-bit unsigned integer
    eInt64,  //!< 64-bit integer
    eUInt64,  //!< 64-bit unsigned integer

    eHalf,  //!< Half-precision floating point number
    eFloat,  //!< Full-precision floating point number
    eDouble,  //!< Double-precision floating point number

    eToken,  //!< Unique token identifying a constant shared string

    // RELATIONSHIP is stored as a 64-bit integer internally, but shouldn't be
    // treated as an integer type by nodes.
    eRelationship,  //!< 64-bit handle to a USD relationship

    // For internal use only
    eAsset,  //!< (INTERNAL) Handle to a USD asset
    eDeprecated1,  //!< (INTERNAL) Handle to a USD prim
    eConnection,  //!< (INTERNAL) Special type connecting to USD elements

    // eTags are attributes that have a name but no type or value
    eTag,

    ePath, //!< To use for scalar PathC attributes

    eCompactTransform, //!< A mixed precision TRS struct. Translation is double precision.

    eCount // for compile-time checks
};

Component Count

A component count corresponds to the number of repeated elements of the base data type that appear in the type as a whole. Only a small subset of component counts are supported, corresponding to those supported by USD. (Every type supports a component count of 1, meaning only a single element is present.)

Base Type

Component Counts Supported

float

2, 3, 4

double

2, 3, 4, 9, 16

half

2, 3, 4

int

2, 3, 4

The above indicate support when there is no role specified. When there is a role there are fewer component counts allowed, as follows:

Role

Component Counts Supported

Color

3, 4

Frame

16

Matrix

4, 9, 16

Normal

3

Point

3

Quaternion

4

TexCoord

2, 3

Vector

3

Important

While component counts not appearing in the above tables can be specified in a type definition they will not be supported in OmniGraph or Fabric.

Array Depth

Many of the types also support arrays of the basic types. The value in the type is the array depth, where a depth of 0 is a single value, a depth of 1 is an array of values. Other depth levels are not currently supported.

In Fabric every type except for string and path can be an array since a single string value is already represented as an array of depth 1. OmniGraph also does not use arrays of type bundle, execution, and target.

Role

A role provides an interpretation of the data in the underlying type. For data storage the value is irrelevant because it does not alter the size of data specified by the type.

Note

OmniGraph Unsupported Roles Although all of the roles are supported in Fabric some of them are not used by OmniGraph. These are the ones whose base type is tag (applied schema, prim type name, instanced attribute, and ancestor prim type).

In C++ the value is specified by the enum omni::fabric::AttributeRole, and in Python it is in omni.graph.core.AttributeRole.

import omni.graph.core as og
all_roles = {
    og.AttributeRole::APPLIED_SCHEMA: "Data is to be interpreted as an applied schema",
    og.AttributeRole::BUNDLE: "Data is to be interpreted as an OmniGraph Bundle",
    og.AttributeRole::COLOR: "Data is to be interpreted as RGB or RGBA color",
    og.AttributeRole::EXECUTION: "Data is to be interpreted as an Action Graph execution pin",
    og.AttributeRole::FRAME: "Data is to be interpreted as a 4x4 matrix representing a reference frame",
    og.AttributeRole::MATRIX: "Data is to be interpreted as a square matrix of values",
    og.AttributeRole::NONE: "Data has no special role",
    og.AttributeRole::NORMAL: "Data is to be interpreted as a normal vector",
    og.AttributeRole::OBJECT_ID: "Data is to be interpreted as a unique object identifier",
    og.AttributeRole::PATH: "Data is to be interpreted as a path to a USD element",
    og.AttributeRole::POSITION: "Data is to be interpreted as a position or point vector",
    og.AttributeRole::PRIM_TYPE_NAME: "Data is to be interpreted as the name of a prim type",
    og.AttributeRole::QUATERNION: "Data is to be interpreted as a rotational quaternion",
    og.AttributeRole::TARGET: "Data is to be interpreted as a relationship target path",
    og.AttributeRole::TEXCOORD: "Data is to be interpreted as texture coordinates",
    og.AttributeRole::TEXT: "Data is to be interpreted as a text string",
    og.AttributeRole::TIMECODE: "Data is to be interpreted as a time code",
    og.AttributeRole::TRANSFORM: "Deprecated",
    og.AttributeRole::VECTOR: "Data is to be interpreted as a simple vector",
    og.AttributeRole::UNKNOWN: "Data role is currently unknown",
}
/**
 * @brief The role enum provides an interpretation of the base data as a specific type
 *
 * The roles are meant to provide some guidance on how to use the data after extraction from Fabric.
 * For example a length calculation makes sense for a "vector" role but not for a "position" role.
 * Some of the roles correspond to an equivalent role in USD, others are Fabric-specific.
 */
enum class AttributeRole : uint8_t
{
    eNone = 0, //!< No special role
    eVector,  //!< A vector in space
    eNormal,  //!< A surface normal
    ePosition,  //!< A position in space
    eColor,  //!< A color representation
    eTexCoord,  //!< Texture coordinates
    eQuaternion,  //!< A 4d quaternion vector
    eTransform,  //!< For instances, TRS components
    eFrame,  //!< A 4x4 matrix representing a coordinate frame
    eTimeCode,  //!< A double value representing a time code
    // eText is not a USD role. If a uchar[] attribute has role eText then
    // the corresponding USD attribute will have type "string", and be human
    // readable in USDA. If it doesn't, then it will have type "uchar[]" in USD
    // and appear as an array of numbers in USDA.
    eText,  //!< (Non-USD) Interpret uchar-array as a string
    eAppliedSchema,  //!< (Non-USD) eTag representing a USD applied schema
    ePrimTypeName,  //!< (Non-USD) eTag representing a USD prim type
    eExecution,  //!< (Non-USD) UInt value used for control flow in OmniGraph Action Graphs
    eMatrix,  //!< A 4x4 or 3x3 matrix
    eObjectId,  //!< (Non-USD) UInt64 value used for Python object identification
    eBundle,  //!< (Non-USD) Representation of the OmniGraph "bundle" type, which is a set of attributes
    ePath,  //!< (Non-USD) uchar-array representing a string that is interpreted as a USD Sdf Path
    eInstancedAttribute,  //!< (Non-USD) eTag used in place of attribute types on instanced prims
    eAncestorPrimTypeName,  //!< (Non-USD) eTag representing an ancestor type of a USD prim type
    eTarget,  //!< (Non-USD) eRelationship representing path data in OmniGraph
    eUnknown,

    eCount // for compile-time checks
};

Special Types

While most of the correlations between data types and roles are obvious, e.g. a float[3] and a pointf[3] are basically the same thing, there are a few combinations that require more explanation.

String

String types have base data type unsigned character, a component count of 1, and array depth of 1, and a role of text. You can probably see why they are defined this way, but the implication is that even though it is literally an array type, conceptually it is a single value of type string so there is often special case code to handle this specific type.

Arrays of strings are not possible as that would require the currently unsupported array depth of 2. There is an alternative that provides a similar data type, which is an array of tokens. The only downside is that tokens require conversion back into a string for any kind of manipulation, and they are immutable so you have to create a brand new string and convert it back into a token if you modify it.

Path

A path type is the same as a string described above, except that it has a role of path instead of text. While use of it is the same the interpretation of it is more restrictive in that it can be considered to be an Sdf Path representing a reference to some other data in Fabric or in USD.

Bundle

A bundle type has a base data type of relationship because the bundle data itself is just a reference to a collection of other data. The data value is an opaque handle to the omni::graph::core::IBundle2/ omni.graph.core.IBundle2 object, which provides an interface to all of the data referenced by a bundle.

Execution

An execution type is a special type only recognized by an Action Graph node. Attributes with this type are trigger points that either accept a signal from another node to begin executing, or send a signal to another node to begin executing. It has a numeric value so that some small amount of information can be encoded into the signal.

Target

A target represents a path object that points to an object in Fabric or on the USD stage. It is similar to the path attribute except that its underlying data provides a direct handle to the object so that if the object is renamed or moved around in the scene hierarchy the path object stays intact and still points to the same underlying object. The base data type is relationship, as it is for the bundle role.

Union and Any

These types are not explicitly represented as data types because they are not data types themselves, they are representations of possible data types an attribute can take. All of that is handled at a higher level though, with the data type being a simple token for both of them.

The attributes created with either of these types will have an extended attribute type (C++/Python) set. The union type will further have added data which defines the set of attribute types it can accept.

The list of accepted types for unions includes the base types, with combinations built up from a configuration file

{
	"unionDefinitions" : {
		"$description": [
			"This file contains the definitions for attribute union groups. Attribute Union Groups are convenient groupings of related attribute types",
			"Each entry contains an union name as the key, and a list of the string representations for corresponding ogn types, or the names of other", 
			"unions to include. Valid dictionary values are strings, lists of strings, or dictionaries.",
			"In the case of dictionaries, it must contain two keys:",
			"   'entries', whose values are attribute type names or union names, and",
			"   'append', whose value is a string to append to each resolved value in 'entries.'"
		],
		"integral_scalers": ["uchar", "int", "uint", "uint64", "int64"],
		"integral_tuples": ["int[2]", "int[3]", "int[4]"],
		"decimal_scalers": ["double", "float", "half", "timecode"],
		"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]"
		],
		"matrices": ["matrixd[2]", "matrixd[3]", "matrixd[4]", "transform[4]", "frame[4]"],
		"integral_array_elements": ["integral_scalers", "integral_tuples"],
		"integral_arrays" : {
			"entries": ["integral_array_elements"],
			"append": "[]"
		},
		"integrals" : ["integral_array_elements", "integral_arrays"],
		"decimal_array_elements": ["decimal_scalers", "decimal_tuples"],
		"decimal_arrays": {
			"entries" : ["decimal_array_elements"],
			"append" : "[]"
		},
		"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": {
			"entries": ["numeric_array_elements"],
			"append": "[]"
		},
		"numerics": ["numeric_array_elements", "numeric_arrays"],
		"array_elements": ["numeric_array_elements", "token"],
		"arrays": ["numeric_arrays", "token[]"],
		"string_types": ["path", "string", "token", "token[]"]
	}
}

OGN and SDF type names

Though the underlying Type representations are consistent, the string representation of the type name varies slightly depending on whether you are accessing it for OmniGraph or for USD. For example a floating point value with three components in OmniGraph is represented as float[3] but in USD is represented as float3.

In most places that accept type names the OmniGraph style is preferred but many also accept the USD style. A .ogn file will always use the OmniGraph style names. There is also a utility that yields a slightly different variation dubbed the Fabric style in omni::fabric::Type::getTypeName().

This is the complete list of the differences between OmniGraph and USD style names.

OmniGraph

USD

float[2], float[3], float[4]

float2, float3, float4

float[2][], float[3][], float[4][]

float2[], float3[], float4[]

double[2], double[3], double[4]

double2, double3, double4

double[2][], double[3][], double[4][]

double2[], double3[], double4[]

half[2], half[3], half[4]

half2, half3, half4

half[2][], half[3][], half[4][]

half2[], half3[], half4[]

int[2], int[3], int[4]

int2, int3, int4

half[2][], half[3][], half[4][]

half2[], half3[], half4[]

matrixd[2], matrixd[3], matrixd[4]

matrix2d, matrix3d, matrix4d

matrixd[2][], matrixd[3][], matrixd[4][]

matrix2d[], matrix3d[], matrix4d[]

frame[4]

frame

frame[4][]

frame[]

colord[3], colord[4]

color3d, color4d

colord[3][], colord[4][]

color3d[], color4d[]

colorf[3], colorf[4]

color3f, color4f

colorf[3][], colorf[4][]

color3f[], color4f[]

colorh[3], colorh[4]

color3h, color4h

colorh[3][], colorh[4][]

color3h[], color4h[]

normald[3]

normal3d

normald[3][]

normal3d[]

normalf[3]

normal3f

normalf[3][]

normal3f[]

normalh[3]

normal3h

normalh[3][]

normal3h[]

pointd[3]

point3d

pointd[3][]

point3d[]

pointf[3]

point3f

pointf[3][]

point3f[]

pointh[3]

point3h

pointh[3][]

point3h[]

quatd[4]

quatd

quatd[4][]

quatd[]

quatf[4]

quatf

quatf[4][]

quatf[]

quath[4]

quath

quath[4][]

quath[]

texcoordd[2], texcoordd[3]

texCoord2d, texCoord3d

texcoordd[2][], texcoordd[3][]

texCoord2d[], texCoord3d[]

texcoordf[2], texcoordf[3]

texCoord2f, texCoord3f

texcoordf[2][], texcoordf[3][]

texCoord2f[], texCoord3f[]

texcoordh[2], texcoordh[3]

texCoord2h, texCoord3h

texcoordh[2][], texcoordh[3][]

texCoord2h[], texCoord3h[]

vectord[3], vectord[4]

vector3d, vector4d

vectord[3][], vectord[4][]

vector3d[], vector4d[]

vectorf[3], vectorf[4]

vector3f, vector4f

vectorf[3][], vectorf[4][]

vector3f[], vector4f[]

vectorh[3], vectorh[4]

vector3h, vector4h

vectorh[3][], vectorh[4][]

vector3h[], vector4h[]

See also the OGN documentation on attribute types for how these types are used in a .ogn file and in running code.

IAttributeType Interface

In addition to the basic structure there is a Carbonite interface class defined that provides a few simple utilities for converting between OGN and Sdf representations of the type name, as well as processing the strings that define the various union type descriptions.

The documentation for omni::graph::core::IAttributeType/omni.graph.core.AttributeType has full details on what it can do.