Tutorial 4 - Tuple Data Node

Tuple data, also referred to as fixed array data, consists of multiple elements of a simple type. For example float[3] or double[4]. This node creates one input attribute and one output attribute of each of the simple data types with an element count greater than 1.

OgnTutorialTupleData.ogn

The ogn file shows the implementation of a node named “omni.tutorials.TupleData”, which has one input and one matching output attribute of each simple type with element counts greater than one.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
{
    "omni.tutorials.TupleData" : {
        "version": 1,
        "categories": "tutorials",
        "description": [
            "This is a tutorial node. It creates both an input and output attribute of some of the supported ",
            "tuple types. The values are modified in a simple way so that the compute can be tested."
        ],
        "metadata":
        {
           "uiName": "Tutorial Node: Tuple Attributes"
        },
        "tags": ["tuple", "tutorial", "internal"],
        "inputs": {
            "a_double2": {
                "type": "double[2]",
                "description": "This is an attribute with two double values",
                "default": [1.1, 2.2]
            },
            "a_float2": {
                "type": "float[2]",
                "description": "This is an attribute with two float values",
                "default": [4.4, 5.5]
            },
            "a_half2": {
                "type": "half[2]",
                "description": "This is an attribute with two 16-bit float values",
                "default": [7.0, 8.0]
            },
            "a_int2": {
                "type": "int[2]",
                "description": "This is an attribute with two 32-bit integer values",
                "default": [10, 11]
            },
            "a_float3": {
                "type": "float[3]",
                "description": "This is an attribute with three float values",
                "default": [6.6, 7.7, 8.8]
            },
            "a_double3": {
                "type": "double[3]",
                "description": "This is an attribute with three double values",
                "default": [1.1, 2.2, 3.3]
            }
        },
        "outputs": {
            "a_double2": {
                "type": "double[2]",
                "description": "This is a computed attribute with two double values"
            },
            "a_float2": {
                "type": "float[2]",
                "description": "This is a computed attribute with two float values"
            },
            "a_half2": {
                "type": "half[2]",
                "description": "This is a computed attribute with two 16-bit float values"
            },
            "a_int2": {
                "type": "int[2]",
                "description": "This is a computed attribute with two 32-bit integer values"
            },
            "a_float3": {
                "type": "float[3]",
                "description": "This is a computed attribute with three float values"
            },
            "a_double3": {
                "type": "double[3]",
                "description": "This is a computed attribute with three double values"
            }
        },
        "tests": [
            {
                "description": "Verification that proper outputs are computed when inputs are all defaults.",
                "outputs:a_double2": [2.1, 3.2],
                "outputs:a_float2": [5.4, 6.5],
                "outputs:a_half2": [8.0, 9.0],
                "outputs:a_int2": [11, 12],
                "outputs:a_float3": [7.6, 8.7, 9.8],
                "outputs:a_double3": [2.1, 3.2, 4.3]
            },
            {
                "description": "Check computation of all outputs using non-default input values",
                "inputs:a_double2": [2.1, 3.2],
                "outputs:a_double2": [3.1, 4.2],
                "inputs:a_float2": [5.1, 6.2],
                "outputs:a_float2": [6.1, 7.2],
                "inputs:a_half2": [8.0, 9.0],
                "outputs:a_half2": [9.0, 10.0],
                "inputs:a_int2": [11, 12],
                "outputs:a_int2": [12, 13],
                "inputs:a_float3": [7.1, 8.2, 9.3],
                "outputs:a_float3": [8.1, 9.2, 10.3],
                "inputs:a_double3": [10.1, 11.2, 12.3],
                "outputs:a_double3": [11.1, 12.2, 13.3]
            }
        ]
    }
}

New Concept - Tags

Often it is helpful to group nodes with common functionality together in some way in the UI. To help with this you can specific values for the tags keyword. The values can either be a comma-separated string, or a list, that will be rendered into a comma-separated string when added to the metadata.

New Concept - Namespaced Node Type Name

The standard naming convention uses a simple CamelCase name, with the extension of origin prepended onto the name to ensure uniqueness. Sometimes you may wish to manage your own namespace, e.g. when you anticipate moving nodes between extensions so the extension name will not be consistent. All you have to do to override the default behaviour is to specify a namespace for the node type name (i.e. include a . separator in it).

Warning

Once you have overridden the node type name with such an absolute value you are now responsible for ensuring uniqueness so be sure you have some scheme that will help you with that. The prefix omni. is reserved for NVIDIA nodes. Everything else is legal, so long as the entire name itself is legal.

OgnTutorialTupleData.cpp

The cpp file contains the implementation of the compute method, which modifies each of the inputs in a simple way to create outputs that have different values.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto.  Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//
#include <OgnTutorialTupleDataDatabase.h>
#include <algorithm>
#include <iostream>

// This class exercises access to the DataModel through the generated database class for supported data types
// with element counts greater than 1.

class OgnTutorialTupleData
{
public:
    static bool compute(OgnTutorialTupleDataDatabase& db)
    {
        // For each piece of data apply a transforming operation so that compute does something testable.
        // The transformation adds 1 to the input to compute the matching output. Notice how the recognized
        // USD types can make use of the built-in manipulation functions while the generic types have to
        // manually apply the algorithm.
        db.outputs.a_double2() = db.inputs.a_double2() + GfVec2d(1.0, 1.0);
        db.outputs.a_double3() = db.inputs.a_double3() + GfVec3d(1.0, 1.0, 1.0);
        db.outputs.a_float2() = db.inputs.a_float2() + GfVec2f(1.0f, 1.0f);
        db.outputs.a_half2() = db.inputs.a_half2() + GfVec2h(1.0, 1.0);
        db.outputs.a_int2() = db.inputs.a_int2() + GfVec2i(1, 1);

        // If you have your own data types which are memory-layout-compatible with the defaults provided
        // you can use a simple cast operation to force a specific data type. Be careful not to cast away
        // the "const" on inputs or your data could get out of sync.
        const carb::Float3& inFloat3 = reinterpret_cast<const carb::Float3&>(db.inputs.a_float3());
        carb::Float3& outFloat3 = reinterpret_cast<carb::Float3&>(db.outputs.a_float3());
        outFloat3.x = inFloat3.x + 1.0f;
        outFloat3.y = inFloat3.y + 1.0f;
        outFloat3.z = inFloat3.z + 1.0f;

        return true;
    }
};

REGISTER_OGN_NODE()

Note how by default some of the attribute value types are USD types and some are generic ogn::tuple types. See Attribute Data Types for the full set of type definitions.

Tuple Attribute Access

The attribute access is as described in Tutorial 2 - Simple Data Node except that the exact return types of the attributes are different in order to support tuple member access. In practice you would use an auto declaration. The types are shown only for illustrative purposes.

The data types for tuples that correspond to existing USD types use the pxr::gf versions of those types, so the database accessors in this node will return these types:

Database Function

Returned Type

inputs.a_double2()

const GfVec2d&

inputs.a_float2()

const GfVec2f&

inputs.a_half2()

const GfVec2h&

inputs.a_int2()

const GfVec2i&

inputs.a_float3()

const GfVec3f&

outputs.a_double2()

GfVec2d&

outputs.a_float2()

GfVec2f&

outputs.a_half2()

GfVec2h&

outputs.a_int2()

GfVec2i&

outputs.a_float3()

GfVec3f&

Tuple Data Compute Validation

As with simple data types the existence of the mandatory inputs is confirmed before proceeding to the compute method.

Tuple Data Node Computation Tests

In the “tests” section of the .ogn file there are some simple tests exercising the basic functionality of the compute method. In practice it is a good idea to include more thorough tests which exercise different data values, especially potential edge cases.