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