Tutorial 21 - Adding Bundled Attributes

Sometimes instead of simply copying data from an input or input bundle into an output bundle you might want to construct a bundle from some other criteria. For example a bundle construction node could take in an array of names and attribute types and output a bundle consisting of those attributes with some default values.

The bundle accessor provides a simple method that can accomplish this task. Adding a new attribute is as simple as providing those two values to the bundle for every attribute you wish to add.

There is also a complementary function to remove named bundle attributes.

OgnTutorialBundleAddAttributes.ogn

The ogn file shows the implementation of a node named “omni.graph.tutorials.BundleData”, which has one input bundle and one output bundle.

 1{
 2    "BundleAddAttributes": {
 3        "description": [
 4            "This is a tutorial node. It exercises functionality for adding and removing attributes on",
 5            "output bundles."
 6        ],
 7        "version": 1,
 8        "categories": "tutorials",
 9        "uiName": "Tutorial Node: Bundle Add Attributes",
10        "inputs": {
11            "typesToAdd": {
12                "type": "token[]",
13                "description": [
14                    "List of type descriptions to add to the bundle. The strings in this list correspond to the",
15                    "strings that represent the attribute types in the .ogn file (e.g. float[3][], colord[3], bool"
16                ],
17                "uiName": "Attribute Types To Add"
18            },
19            "addedAttributeNames": {
20                "type": "token[]",
21                "description": [
22                    "Names for the attribute types to be added. The size of this array must match the size",
23                    "of the 'typesToAdd' array to be legal."
24                ]
25            },
26            "removedAttributeNames": {
27                "type": "token[]",
28                "description": "Names for the attribute types to be removed. Non-existent attributes will be ignored."
29            },
30            "useBatchedAPI": {
31                "type": "bool",
32                "description": "Controls whether or not to used batched APIS for adding/removing attributes"
33            }
34        },
35        "outputs": {
36            "bundle": {
37                "type": "bundle",
38                "description": ["This is the bundle with all attributes added by compute."],
39                "uiName": "Constructed Bundle"
40             }
41        }
42    }
43}

OgnTutorialBundleAddAttributes.cpp

The cpp file contains the implementation of the compute method. It accesses the attribute descriptions on the inputs and creates a bundle with attributes matching those descriptions as its output.

 1// SPDX-FileCopyrightText: Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
 2// SPDX-License-Identifier: LicenseRef-NvidiaProprietary
 3//
 4// NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
 5// property and proprietary rights in and to this material, related
 6// documentation and any modifications thereto. Any use, reproduction,
 7// disclosure or distribution of this material and related documentation
 8// without an express license agreement from NVIDIA CORPORATION or
 9// its affiliates is strictly prohibited.
10#include <omni/graph/core/IAttributeType.h>
11
12#include <OgnTutorialBundleAddAttributesDatabase.h>
13
14using omni::graph::core::Type;
15
16
17class OgnTutorialBundleAddAttributes
18{
19public:
20    static bool compute(OgnTutorialBundleAddAttributesDatabase& db)
21    {
22        const auto& attributeTypeNames = db.inputs.typesToAdd();
23        const auto& attributeNames = db.inputs.addedAttributeNames();
24        const auto& useBatchedAPI = db.inputs.useBatchedAPI();
25        auto& outputBundle = db.outputs.bundle();
26
27        // The usual error checking. Being diligent about checking the data ensures you will have an easier time
28        // debugging the graph if anything goes wrong.
29        if (attributeTypeNames.size() != attributeNames.size())
30        {
31            db.logWarning("Number of attribute types (%zu) does not match number of attribute names (%zu)",
32                          attributeTypeNames.size(), attributeNames.size());
33            return false;
34        }
35
36        // Make sure the bundle is starting from empty
37        outputBundle.clear();
38
39        if (useBatchedAPI)
40        {
41            //
42            // unfortunately we need to build a vector of the types
43            //
44            auto typeNameIt = std::begin(attributeTypeNames);
45            std::vector<Type> types;
46            types.reserve(attributeTypeNames.size());
47            for (; typeNameIt != std::end(attributeTypeNames); ++typeNameIt)
48            {
49                auto typeName = *typeNameIt;
50                types.emplace_back(db.typeFromName(typeName));
51            }
52            outputBundle.addAttributes(attributeTypeNames.size(), attributeNames.data(), types.data());
53
54            // Remove attributes from the bundle that were already added. This is a somewhat contrived operation that
55            // allows testing of both adding and removal within a simple environment.
56            if (db.inputs.removedAttributeNames().size())
57            {
58                outputBundle.removeAttributes(
59                    db.inputs.removedAttributeNames().size(), db.inputs.removedAttributeNames().data());
60            }
61        }
62        else
63        {
64            // Since the two arrays are the same size a dual loop can be used to walk them in pairs
65            auto typeNameIt = std::begin(attributeTypeNames);
66            auto attributeNameIt = std::begin(attributeNames);
67            for (; typeNameIt != std::end(attributeTypeNames) && attributeNameIt != std::end(attributeNames);
68                 ++typeNameIt, ++attributeNameIt)
69            {
70                auto typeName = *typeNameIt;
71                auto attributeName = *attributeNameIt;
72                Type newType = db.typeFromName(typeName);
73                // Ignore the output since for this example there will not be any values set on the new attribute
74                (void)outputBundle.addAttribute(attributeName, newType);
75            }
76
77            // Remove attributes from the bundle that were already added. This is a somewhat contrived operation that
78            // allows testing of both adding and removal within a simple environment.
79            for (const auto& toRemove : db.inputs.removedAttributeNames())
80            {
81                outputBundle.removeAttribute(toRemove);
82            }
83        }
84
85        return true;
86    }
87};
88
89REGISTER_OGN_NODE()

OgnTutorialBundleAddAttributesPy.py

The py file contains the same algorithm as the C++ node, with only the implementation language being different.

 1"""
 2Implementation of the Python node adding attributes with a given description to an output bundle.
 3"""
 4
 5import omni.graph.core as og
 6
 7
 8class OgnTutorialBundleAddAttributesPy:
 9    """Exercise the bundled data types through a Python OmniGraph node"""
10
11    @staticmethod
12    def compute(db) -> bool:
13        """Implements the same algorithm as the C++ node OgnTutorialBundleAddAttributes.cpp using the Python
14        bindings to the bundle method
15        """
16        # Start with an empty output bundle.
17        output_bundle = db.outputs.bundle
18        output_bundle.clear()
19
20        if db.inputs.useBatchedAPI:
21            attr_types = [og.AttributeType.type_from_ogn_type_name(type_name) for type_name in db.inputs.typesToAdd]
22            output_bundle.add_attributes(attr_types, db.inputs.addedAttributeNames)
23
24            output_bundle.remove_attributes(db.inputs.removedAttributeNames)
25
26        else:
27            for attribute_type_name, attribute_name in zip(db.inputs.typesToAdd, db.inputs.addedAttributeNames):
28                attribute_type = og.AttributeType.type_from_ogn_type_name(attribute_type_name)
29                output_bundle.insert((attribute_type, attribute_name))
30
31            # Remove attributes from the bundle that were already added. This is a somewhat contrived operation that
32            # allows testing of both adding and removal within a simple environment.
33            for attribute_name in db.inputs.removedAttributeNames:
34                output_bundle.remove(attribute_name)
35
36        return True