Tutorial 12 - Python ABI Override Node

Although the .ogn format creates an easy-to-use interface to the ABI of the OmniGraph node and the associated data model, there may be cases where you want to override the ABI to perform special processing.

OgnTutorialABIPy.ogn

The ogn file shows the implementation of a node named “omni.graph.tutorials.AbiPy”, in its first version, with a simple description.

 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
{
    "AbiPy" : {
        "version": 1,
        "categories": ["tutorials", { "internal:abiPy": "Internal nodes that override the Python ABI functions" }],
        "language": "python",
        "description": ["This tutorial node shows how to override ABI methods on your Python node.",
                        "The algorithm of the node converts an RGB color into HSV components."],
        "metadata": {
            "uiName": "Tutorial Python Node: ABI Overrides"
        },
        "inputs": {
            "color": {
                "type": "colord[3]",
                "description": ["The color to be converted"],
                "default": [0.0, 0.0, 0.0],
                "metadata": {
                    "$comment": "Metadata is key/value pairs associated with the attribute type.",
                    "$specialNames": "Kit may recognize specific keys. 'uiName' is a human readable version of the attribute name",
                    "uiName": "Color To Convert",
                    "multipleValues": ["value1", "value2", "value3"]
                }
            }
        },
        "outputs": {
            "h": {
                "type": "double",
                "description": ["The hue component of the input color"]
            },
            "s": {
                "type": "double",
                "description": ["The saturation component of the input color"]
            },
            "v": {
                "type": "double",
                "description": ["The value component of the input color"]
            }
        },
        "tests": [ {"inputs:color": [0.2, 0.4, 0.4], "outputs:h": 0.5, "outputs:s": 0.5, "outputs:v": 0.4} ]
    }
}

OgnTutorialABIPy.py

The py file contains the implementation of the node class with every possible ABI method replaced with customized processing. The node still functions the same as any other node, although it is forced to write a lot of extra boilerplate code to do so.

  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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
"""
Implementation of the Python node accessing all of the simple data types.
This class exercises access to the DataModel through the generated database class for all simple data types.
It implements the same algorithm as the C++ node OgnTutorialABI.cpp
"""
import colorsys
from contextlib import suppress

import carb


class OgnTutorialABIPy:
    """Illustrate overrides of the various ABI functions available to a Python OmniGraph node"""

    @staticmethod
    def compute(context_helper, node) -> bool:
        """
        Convert a color into components using the raw ABI function without the nice Database interface.

        Rarely Overridden:
            Usually you will implement the much more friendly and Pythonic compute(OgnTutorialABIDatabasePy)
            method so that you can have easier access to your data.
        """
        # Manually acquire the data on the known attributes
        input_color_attr = node.get_attribute("inputs:color")

        # Extract the input value from the helper
        input_color = context_helper.get_attr_value(input_color_attr)

        output_hue_attr = node.get_attribute("outputs:h")
        output_saturation_attr = node.get_attribute("outputs:s")
        output_value_attr = node.get_attribute("outputs:v")

        (h, s, v) = colorsys.rgb_to_hsv(*input_color)

        # This exception is triggered if you accidentally reverse the parameters to set_attr_value.
        # The error isn't recovered, to prevent proliferation of inconsistent calls. The exception is
        # thrown to help with debugging. (As this is an example the exception is caught and ignored here.)
        with suppress(TypeError):
            context_helper.set_attr_value(output_hue_attr, h)

        context_helper.set_attr_value(h, output_hue_attr)
        context_helper.set_attr_value(s, output_saturation_attr)
        context_helper.set_attr_value(v, output_value_attr)

        #
        # For comparison, here is the same algorithm implemented using "compute(db)"
        #
        # def compute(db) -> bool:
        #     (db.outputs.h, db.outputs.s, db.outputs.v) = colorsys.rgb_to_hsv(*db.inputs.color)
        return True

    # ----------------------------------------------------------------------
    @staticmethod
    def get_node_type() -> str:
        """
        Rarely overridden

        This should almost never be overridden as the auto-generated code will handle the name
        """
        carb.log_info("Python ABI override of get_node_type")
        return "omni.graph.tutorials.AbiPy"

    # ----------------------------------------------------------------------
    @staticmethod
    def initialize(graph_context, node):
        """
        Occasionally overridden

        This method might be overridden to set up initial conditions when a node of this type is created.
        Note that overridding this puts the onus on the node writer to set up initial conditions such as
        attribute default values and metadata.

        When a node is created this will be called
        """
        carb.log_info("Python ABI override of initialize")
        # There is no default behaviour on initialize so nothing else is needed for this tutorial to function

    # ----------------------------------------------------------------------
    @staticmethod
    def initialize_type(node_type) -> bool:
        """
        Rarely overridden

        This method might be overridden to set up initial conditions when a node type is registered.
        Note that overriding this puts the onus on the node writer to initialize the attributes and metadata.

        By returning "True" the function is requesting that the attributes and metadata be initialized upon return,
        otherwise the caller will assume that this override has already done that.
        """
        carb.log_info("Python ABI override of initialize_type")
        return True

    # ----------------------------------------------------------------------
    @staticmethod
    def release(node):
        """
        Occasionally overridden

        After a node is removed it will get a release call where anything set up in initialize() can be torn down
        """
        carb.log_info("Python ABI override of release")
        # There is no default behaviour on release so nothing else is needed for this tutorial to function

    # ----------------------------------------------------------------------
    @staticmethod
    def update_node_version(graph_context, node, old_version: int, new_version: int):
        """
        Occasionally overridden

        This is something you do want to override when you have more than version of your node.
        In it you would translate attribute information from older versions into the current one.
        """
        carb.log_info(f"Python ABI override of update_node_version from {old_version} to {new_version}")
        # There is no default behaviour on update_node_version so nothing else is needed for this tutorial to function
        return old_version < new_version

    # ----------------------------------------------------------------------
    @staticmethod
    def on_connection_type_resolve(node):
        """
        Occasionally overridden

        When there is a connection change to this node which results in an extended type attribute being automatically
        resolved, this callback gives the node a change to resolve other extended type attributes. For example a generic
        'Increment' node can resolve its output to an int only after its input has been resolved to an int. Attribute
        types are resolved using omni.graph.attribute.set_resolved_type(), or the utility functions such as
        og.resolve_fully_coupled().
        """
        carb.log_info("Python ABI override of on_connection_type_resolve")
        # There is no default behaviour for on_connection_type_resolve so nothing else is needed for this
        # tutorial to function

Metadata Attached To Attributes

This file introduces the metadata keyword to attributes, whose value is a dictionary of key/value pairs associated with the attribute in which it appears that may be extracted using the ABI metadata functions. These are not persisted in any files and so must be set either in the .ogn file or in an override of the initialize() method in the node definition.