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
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 |
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¶
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¶
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¶
{
"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¶
import numpy as np
@property
def myColorRole(self) -> np.ndarray:
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.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 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.attributeByName(db.stringToToken("myFloat3"));
if (auto asFloat3Array = myFloat3Attribute.get<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 token inputs:floatOrInt
custom token inputs:floatArrayOrIntArray
custom token 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.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¶
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());
}
}
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.
Use the typeDefinitions flag on the generate_node.py script to point to the file containing the configuration.
Use the “typeDefinitions”: “ConfigurationFile.json” keyword in the .ogn file to point a single node to a configuration.
Use the “typeDefintitions”: {TypeConfigurationDictionary} keyword in the .ogn file to implement simple targeted overrides in a single node.
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]": []
}
}
}