Controller Class

The primary interface you can use for interacting with OmniGraph is the omni.graph.core.Controller. It is accessible through the main module like this:

import omni.graph.core as og

keys = og.Controller.Keys  # Usually handy to keep this name short as it is used often
controller = og.Controller()

Note

For the examples below, the above initialization code will be assumed to be present.

Structure

The omni.graph.core.Controller class is an amalgam of several other classes with specific subsets of functionality. It derives from each of them, so that all of their functionality can be accessed through a single class.

For the most part the controller functions can be accessed as either static class methods or as regular object methods:

await og.Controller.evaluate()
await controller.evaluate()

The main difference between the two is that the object-based calls can maintain state information to simplify the arguments to future calls. Each of the base classes have their own constructor parameters. The ones accepted by the main controller constructor are as follows:

"""Set up state information. You only need to create an instance of the Controller if you are going to
use the edit() function more than once, when it needs to remember the node mapping used for creation.
Args are passed on to the parent classes who have inits and interpreted by them as they see fit.

Args:
    graph_id: If specified then operations are performed on this graph_id unless it is overridden in a
              particular function call. See GraphController.create_graph() for the data types
              accepted for the graph description.
    path_to_object_map: Dictionary of relative paths mapped on to their full path after creation so that the
                        edit_commands can use either full paths or short-forms to specify the nodes.
    update_usd: If specified then override whether to update the USD after the operations (default False)
    undoable: If True the operation is added to the undo queue, else it is done immediately and forgotten
              (default True)
    allow_exists_attr: If True then attribute creation operation won't fail when the attribute already exists
                       on the node it was being added to (default False)
    allow_exists_node: If True then node creation operation won't fail when the node already exists in the
                       scene graph (default False)
    allow_exists_prim: If True then prim creation operation won't fail when the prim already exists in the
                       scene graph (default False)

Check the help information for GraphController.__init__(), NodeController.__init__(), DataView.__init__(), and
ObjectLookup.__init__() for details on what other constructor arguments are accepted.
"""

Controller Functions

In addition to the functions inherited from the other classes the Controller has some of its own functions.

evaluate()/evaluate_sync()

These are sync and async versions of a function to evaluate a graph or list of graphs.

# This version evaluates all graphs asynchronously
await og.Controller.evaluate()
# This version evaluates the graph defined by the controller object synchronously
controller.evaluate_sync()
# This version evaluates the single named graph synchronously
og.Controller.evaluate_sync(graph)

edit()

The workhorse of the controller class is the edit() method. It provides simple access to a lot of the underlying functionality for manipulating the contents of OmniGraph.

One of the most typical operations you will perform in a script is to create a predefined graph and populate it with connections and values. Here is a comprehensive example that illustrates how to use all of the features in an edit call:

# Keywords are shown for the edit arguments, however they are not required
(graph, nodes, prims, name_to_object_map) = og.Controller.edit(
    # First parameter is a graph reference, created if it doesn't already exist.
    # See omni.graph.core.GraphController.create_graph() for more options
    graph_id="/World/MyGraph",
    # Second parameter is a dictionary
    edit_commands={
        # Delete a node or list of nodes that already existed in the graph
        # See omni.graph.core.GraphController.delete_nodes() for more options
        keys.DELETE_NODES: ["MyNode"],
        # Create new nodes in the graph - the resulting og.Nodes are returned in the "nodes" part of the tuple
        # See omni.graph.core.GraphController.create_node() for more options
        keys.CREATE_NODES: [
            ("src", "omni.graph.tutorials.SimpleData"),
            ("dst", "omni.graph.tutorials.SimpleData"),
            # Create a new compound node in the graph. Note that the nodes created in the compound are not
            # returned in the "nodes" part of the tuple, but are inserted into name_to_object_map.
            (
                "compound",
                {
                    keys.CREATE_NODES: [("subNode", "omni.graph.tutorials.SimpleData")],
                    # Expose the compound node's subNode ports as part of the compound node
                    # by specifying the attribute to promote and the name of the attribute on the
                    # compound node
                    keys.PROMOTE_ATTRIBUTES: [("subNode.inputs:a_float", "inputs:attr0")],
                },
            ),
        ],
        # Create new (dynamic) attributes on some nodes.
        # See omni.graph.core.NodeController.create_attribute() for more options
        keys.CREATE_ATTRIBUTES: [
            ("src.inputs:dyn_float", "float"),
            ("dst.inputs:dyn_any", "any"),
        ],
        # Create new prims in the stage - the resulting Usd.Prims are returned in the "prims" part of the tuple
        # See omni.graph.core.GraphController.create_prims() for more options
        keys.CREATE_PRIMS: [
            ("Prim1", {"attrFloat": ("float", 2.0)}),
            ("Prim2", {"attrBool": ("bool", True)}),
        ],
        # Expose on of the prims to OmniGraph by creating a USD import node to read it as a bundle.
        # The resulting node is in the "nodes" part of the tuple, after any native OmniGraph node types.
        # See omni.graph.core.GraphController.expose_prims() for more options
        keys.EXPOSE_PRIMS: [(og.Controller.PrimExposureType.AS_BUNDLE, "Prim1", "Prim1Exposed")],
        # Connect a source output to a destination input to create a flow of data between the two nodes
        # See omni.graph.core.GraphController.connect() for more options
        keys.CONNECT: [("src.outputs:a_int", "dst.inputs:a_int")],
        # Disconnect an already existing connection.
        # See omni.graph.core.GraphController.disconnect() for more options
        keys.DISCONNECT: [
            ("/World/MyGraph/MyClassNode.outputs:a_bool", "/World/MyGraph/KnownNode.inputs:a_bool")
        ],
        # Define an attribute's value (inputs and state attributes only - outputs are computed)
        # See omni.graph.core.DataView.set() for more options
        keys.SET_VALUES: [("src.inputs:a_int", 5)],
        # Create graph-local variable values
        # See omni.graph.core.GraphController.create_variables() for more options
        keys.CREATE_VARIABLES: [("a_float_var", og.Type(og.BaseDataType.FLOAT)), ("a_bool_var", "bool")],
    },
    # Parameters from here down could also be saved as part of the object and reused repeatedly if you had
    # created a controller rather than calling edit() as a class method
    path_to_object_map=None,  # Saved object-to-path map, bootstraps any created as part of the call
    update_usd=True,  # Immediately echo the changes to the underlying USD
    undoable=True,  # If False then do not remember previous state - useful for writing tests
    allow_exists_node=False,  # If True then silently succeed requests to create already existing nodes
    allow_exists_prim=False,  # If True then silently succeed requests to create already existing prims
)

See the method documentation for more details on what types of arguments can be accepted for each of the parameters.

ObjectLookup

This class contains the functions you will probably use the most. It provides an extremely flexible method for looking up OmniGraph objects from the information you have on hand. The specs it accepts as arguments can be seen in the class documentation.

The subsection titles link to the actual method documentation, while the content consists of a simple example of how to use that method in a typical script. Each subsection assumes a graph set up through the following initialization code:

import omni.graph.core as og
import omni.usd
from pxr import OmniGraphSchema, Sdf

keys = og.Controller.Keys
# Note that when you extract the parameters this way it is important to have the trailing "," in the node
# and prim tuples so that Python doesn't try to interpret them as single objects.
(graph, (node,), (prim,), _) = og.Controller.edit(
    "/World/MyGraph",
    {
        keys.CREATE_NODES: ("MyNode", "omni.graph.test.TestAllDataTypes"),
        keys.CREATE_PRIMS: ("MyPrim", {"myFloat": ("float", 0)}),
        keys.CREATE_VARIABLES: ("MyVariable", "float"),
    },
)
assert prim.IsValid()
attribute = node.get_attribute("inputs:a_bool")
relationship_attribute = node.get_attribute("inputs:a_target")
attribute_type = attribute.get_resolved_type()
node_type = node.get_node_type()
variable = graph.get_variables()[0]
stage = omni.usd.get_context().get_stage()

graph()

Finds an og.Graph object.

# Look up the graph directly from itself (to simplify code)
assert graph == og.Controller.graph(graph)

# Look up the graph by path
assert graph == og.Controller.graph("/World/MyGraph")

# Look up the graph by Usd Prim
graph_prim = stage.GetPrimAtPath("/World/MyGraph")
assert graph == og.Controller.graph(graph_prim)

# Look up by a Usd Schema object
graph_schema = OmniGraphSchema.OmniGraph(graph_prim)
assert graph == og.Controller.graph(graph_schema)

# Look up the graph by SdfPath
path = Sdf.Path("/World/MyGraph")
assert graph == og.Controller.graph(path)

# Look up a list of graphs by passing a list of any of the above
for new_graph in og.Controller.graph([graph, "/World/MyGraph", path]):
    assert graph == new_graph

node()

Finds an og.Node object

# Look up the node directly from itself (to simplify code)
assert node == og.Controller.node(node)

# Look up the node by path
assert node == og.Controller.node("/World/MyGraph/MyNode")

# Look up the node by partial path and graph.
# The graph parameter can be any of the ones supported by og.Controller.graph()
assert node == og.Controller.node(("MyNode", graph))

# Look up the node by SdfPath
node_path = Sdf.Path("/World/MyGraph/MyNode")
assert node == og.Controller.node(node_path)

# Look up the node from its underlying USD prim backing, if it exists
node_prim = stage.GetPrimAtPath("/World/MyGraph/MyNode")
assert node == og.Controller.node(node_prim)

# Look up a list of nodes by passing a list of any of the above
for new_node in og.Controller.node([node, "/World/MyGraph/MyNode", ("MyNode", graph), node_path, node_prim]):
    assert node == new_node

attribute()

Finds an og.Attribute object

# Look up the attribute directly from itself (to simplify code)
assert attribute == og.Controller.attribute(attribute)

# Look up the attribute by path
assert attribute == og.Controller.attribute("/World/MyGraph/MyNode.inputs:a_bool")

# Look up the attribute by SdfPath
assert attribute == og.Controller.attribute(Sdf.Path("/World/MyGraph/MyNode.inputs:a_bool"))

# Look up the attribute by name and node
assert attribute == og.Controller.attribute(("inputs:a_bool", node))

# These can chain, so you can also look up the attribute by name and node, where node is further looked up by
# relative path and graph
assert attribute == og.Controller.attribute(("inputs:a_bool", ("MyNode", graph)))

# Look up the attribute by SdfPath
attr_path = Sdf.Path("/World/MyGraph/MyNode.inputs:a_bool")
assert attribute == og.Controller.attribute(attr_path)

# Look up the attribute through its Usd counterpart
stage = omni.usd.get_context().get_stage()
node_prim = stage.GetPrimAtPath("/World/MyGraph/MyNode")
usd_attribute = node_prim.GetAttribute("inputs:a_bool")
assert attribute == og.Controller.attribute(usd_attribute)

# Look up a list of attributes by passing a list of any of the above
for new_attribute in og.Controller.attribute(
    [
        attribute,
        "/World/MyGraph/MyNode.inputs:a_bool",
        Sdf.Path("/World/MyGraph/MyNode.inputs:a_bool"),
        ("inputs:a_bool", node),
        ("inputs:a_bool", ("MyNode", graph)),
        attr_path,
        usd_attribute,
    ]
):
    assert attribute == new_attribute

attribute_type()

Finds an og.Type object

attribute_type = og.Type(og.BaseDataType.FLOAT, tuple_count=3, array_depth=1, role=og.AttributeRole.POSITION)
# Look up the attribute type by OGN type name
assert attribute_type == og.Controller.attribute_type("pointf[3][]")

# Look up the attribute type by SDF type name
assert attribute_type == og.Controller.attribute_type("point3f[]")

# Look up the attribute type directly from itself (to simplify code)
assert attribute_type == og.Controller.attribute_type(attribute_type)

# Look up the attribute type from the attribute with that type
point_attribute = og.Controller.attribute(("inputs:a_pointf_3_array", node))
assert attribute_type == og.Controller.attribute_type(point_attribute)

# Look up the attribute type from the attribute data whose attribute has that type (most commonly done with
# attributes that have extended types or attributes belonging to bundles)
point_attribute_data = point_attribute.get_attribute_data()
assert attribute_type == og.Controller.attribute_type(point_attribute_data)

node_type()

Finds an og.NodeType object

node_type = node.get_node_type()
# Look up the node type directly from itself (to simplify code)
assert node_type == og.Controller.node_type(node_type)

# Look up the node type from the string that uniquely identifies it
assert node_type == og.Controller.node_type("omni.graph.test.TestAllDataTypes")

# Look up the node type from the node with that type
assert node_type == og.Controller.node_type(node)

# Look up the node type from the USD Prim backing a node of that type
assert node_type == og.Controller.node_type(node_prim)

# Look up a list of node types by passing a list of any of the above
for new_node_type in og.Controller.node_type(
    [
        node_type,
        "omni.graph.test.TestAllDataTypes",
        node,
        node_prim,
    ]
):
    assert node_type == new_node_type

prim()

Finds a Usd.Prim object

# Look up the prim directly from itself (to simplify code)
assert node_prim == og.Controller.prim(node_prim)

# Look up the prim from the prim path as a string
assert node_prim == og.Controller.prim("/World/MyGraph/MyNode")

# Look up the prim from the Sdf.Path pointing to the prim
assert node_prim == og.Controller.prim(node_path)

# Look up the prim from the OmniGraph node for which it is the backing
assert node_prim == og.Controller.prim(node)

# Look up the prim from the (node_path, graph) tuple defining the OmniGraph node for which it is the backing
assert node_prim == og.Controller.prim(("MyNode", graph))

# Look up the prim from an OmniGraph graph
assert graph_prim == og.Controller.prim(graph)

# Look up a list of prims by passing a list of any of the above
for new_prim in og.Controller.prim(
    [
        node_prim,
        "/World/MyGraph/MyNode",
        node_path,
        node,
        ("MyNode", graph),
    ]
):
    assert node_prim == new_prim

usd_attribute()

Finds a Usd.Attribute object

# USD attributes can be looked up with the same parameters as for looking up an OmniGraph attribute

# Look up the USD attribute directly from itself (to simplify code)
assert usd_attribute == og.Controller.usd_attribute(usd_attribute)

# Look up the USD attribute by path
assert usd_attribute == og.Controller.usd_attribute("/World/MyGraph/MyNode.inputs:a_bool")

# Look up the USD attribute by name and node
assert usd_attribute == og.Controller.usd_attribute(("inputs:a_bool", node))

# These can chain, so you can also look up the USD attribute by name and node, where node is further looked up by
# relative path and graph
assert usd_attribute == og.Controller.usd_attribute(("inputs:a_bool", ("MyNode", graph)))

# Look up the USD attribute by SdfPath
attr_path = Sdf.Path("/World/MyGraph/MyNode.inputs:a_bool")
assert usd_attribute == og.Controller.usd_attribute(attr_path)

# Look up the USD attribute through its OmniGraph counterpart
assert usd_attribute == og.Controller.usd_attribute(attribute)

# Look up a list of attributes by passing a list of any of the above
for new_usd_attribute in og.Controller.usd_attribute(
    [
        usd_attribute,
        "/World/MyGraph/MyNode.inputs:a_bool",
        ("inputs:a_bool", node),
        ("inputs:a_bool", ("MyNode", graph)),
        attr_path,
        attribute,
    ]
):
    assert usd_attribute == new_usd_attribute

variable()

Finds an og.IVariable object

# Look up the variable directly from itself (to simplify code)
assert variable == og.Controller.variable(variable)

# Look up the variable from a tuple with the variable name and the graph to which it belongs
assert variable == og.Controller.variable((graph, "MyVariable"))

# Look up the variable from a path string pointing directly to it
assert variable == og.Controller.variable(variable.source_path)

# Look up the variable from an Sdf.Path pointing directly to it
variable_path = Sdf.Path(variable.source_path)
assert variable == og.Controller.variable(variable_path)

# Look up a list of variables by passing a list of any of the above
for new_variable in og.Controller.variable(
    [
        variable,
        (graph, "MyVariable"),
        variable.source_path,
        variable_path,
    ]
):
    assert variable == new_variable

Utilities

In addition to type lookups there are a few methods that provide utility functions related to these lookups.

# Look up the path to an attribute given any of the types an attribute lookup recognizes
for attribute_spec in [
    attribute,
    "/World/MyGraph/MyNode.inputs:a_bool",
    ("inputs:a_bool", node),
    ("inputs:a_bool", ("MyNode", graph)),
    attr_path,
    usd_attribute,
]:
    assert attribute.get_path() == og.Controller.attribute_path(attribute_spec)

# Look up the path to a node given any of the types a node lookup recognizes
for node_spec in [node, "/World/MyGraph/MyNode", ("MyNode", graph), node_path, node_prim]:
    assert node.get_prim_path() == og.Controller.node_path(node_spec)

# Look up the path to a prim given any of the types a prim lookup recognizes
for prim_spec in [node_prim, "/World/MyGraph/MyNode", node_path, node, ("MyNode", graph)]:
    assert node_prim.GetPrimPath() == og.Controller.prim_path(prim_spec)

# Look up the path to a prim given any of the types a graph lookup recognizes
graph_path = graph.get_path_to_graph()
for graph_spec in [graph, graph_path, Sdf.Path(graph_path)]:
    assert graph_path == og.Controller.prim_path(graph_spec)

# Look up a list of paths to prims given a list of any of the types a prim lookup recognizes
for new_path in og.Controller.prim_path(
    [node_prim, "/World/MyGraph/MyNode", node_path, node, ("MyNode", graph)]
):
    assert node_prim.GetPrimPath() == new_path

# Separate the graph name from the node name in a full path to a node
assert (graph, "MyNode") == og.Controller.split_graph_from_node_path("/World/MyGraph/MyNode")

# Separate the graph name from the node name in an Sdf.Path to a node
assert (graph, "MyNode") == og.Controller.split_graph_from_node_path(node_path)

GraphController

This class contains the functions that manipulate the structure of the graph, including creating a graph. The class documentation describe the details of what it can do.

The subsection titles link to the actual method documentation, while the content consists of a simple example of how to use that method in a typical script. Each subsection assumes a set up through the following initialization code:

import omni.graph.core as og
import omni.kit
from pxr import Sdf

node_type_name = "omni.graph.test.TestAllDataTypes"
node_type = og.Controller.node_type(node_type_name)

__init____()

A few parameters can be shared among multiple calls to the controller if it is instantiated as an object rather than calling the functions as class methods.

# Explanation of the non-default values in the constructor
controller = og.GraphController(
    update_usd=False,  # Only update Fabric when paths are added are removed, do not propagate to USD
    undoable=False,  # Do not save information on changes for later undo (most applicable to testing)
    # If a node specification in og.GraphController.create_node() exists then silently succeed instead of
    # raising an exception
    allow_exists_node=True,
    # If a prim specification in og.GraphController.create_prim() exists then silently succeed instead of
    # raising an exception
    allow_exists_prim=True,
)
assert controller is not None
# The default values are what is assumed when class methods are called. Where they apply to any of the
# functions they can also be passed to the class method functions to specify non-default values.

create_graph()

Creates a new og.Graph, similar to the first parameter to the og.Controller.edit() function.

# Simple method of creating a graph just passes the desire prim path to it. This creates a graph using all
# of the default parameters.
graph = og.GraphController.create_graph("/World/MyGraph")
assert graph.is_valid()

# If you want to customize the type of graph then instead of passing just a path you can pass a dictionary
# graph configuration values. See the developer documentation of omni.graph.core.GraphController.create_graph
# for details on what each parameter means
action_graph = og.GraphController.create_graph(
    {
        "graph_path": "/World/MyActionGraph",
        "node_name": "MyActionGraph",
        "evaluator_name": "execution",
        "is_global_graph": True,
        "backed_by_usd": True,
        "fc_backing_type": og.GraphBackingType.GRAPH_BACKING_TYPE_FABRIC_SHARED,
        "pipeline_stage": og.GraphPipelineStage.GRAPH_PIPELINE_STAGE_SIMULATION,
        "evaluation_mode": og.GraphEvaluationMode.GRAPH_EVALUATION_MODE_AUTOMATIC,
    }
)

# Also accepts the "update_usd" and "undoable" shared construction parameters

create_node()

Creates a new og.Node.

This performs the same function and takes the same parameters as the og.Controller.edit() keyword og.Controller.Keys.CREATE_NODES.

# Creates a new node in an existing graph.
# The two mandatory parameters are node path and node type. The node path can be any of the types recognized
# by the omni.graph.core.ObjectLookup.node_path() method and the node type can be any of the types recognized
# by the omni.graph.core.ObjectLookup.node_type() method.
node_by_path = og.GraphController.create_node(
    node_id="/World/MyGraph/MyNode",
    node_type_id=node_type_name,
)
assert node_by_path.is_valid()
node_by_name = og.GraphController.create_node(
    node_id=("MyNodeByName", graph),
    node_type_id=node_type,
)
assert node_by_name.is_valid()
node_by_sdf_path = Sdf.Path("/World/MyGraph/MyNodeBySdf")
node_by_sdf = og.GraphController.create_node(node_id=node_by_sdf_path, node_type_id=node_by_name)
assert node_by_sdf.is_valid()

# Also accepts the "update_usd", "undoable", and "allow_exists_node" shared construction parameters

create_prim()

Creates a new Usd.Prim.

This performs the same function and takes the same parameters as the og.Controller.edit() keyword og.Controller.Keys.CREATE_PRIMS.

# Creates a new prim on the USD stage
# You can just specify the prim path to get a default prim type with no attributes. The prim_path argument
# can accept any value accepted by omni.graph.core.ObjectLookup.prim_path
prim_empty = og.GraphController.create_prim(prim_path="/World/MyEmptyPrim")
assert prim_empty.IsValid()

# You can add a prim type if you want the prim to be a specific type, including schema types
prim_cube = og.GraphController.create_prim(prim_path=Sdf.Path("/World/MyCube"), prim_type="Cube")
assert prim_cube.IsValid()

# You can also populate the prim with some attributes and values using the attribute_values parameter, which
# accepts a dictionary of Name:(Type,Value). An attribute named "Name" will be created with type "Type"
# (specified in either USD or SDF type format), and initial value "Value" (specified in any format compatible
# with the Usd.Attribute.Set() function). The names do not have to conform to the usual OGN standards of
# starting with one of the "inputs", "outputs", or "state" namespaces, though they can. The "Type" value is
# restricted to the USD-native types, so things like "any", "bundle", and "execution" are not allowed.
prim_with_values = og.GraphController.create_prim(
    prim_path="/World/MyValuedPrim",
    attribute_values={
        "someFloat": ("float", 3.0),
        "inputs:float3": ("float3", [1.0, 2.0, 3.0]),
        "someFloat3": ("float[3]", [4.0, 5.0, 6.0]),
        "someColor": ("color3d", [0.5, 0.6, 0.2]),
    },
)
assert prim_with_values.IsValid()

# Also accepts the "undoable" and "allow_exists_prim" shared construction parameters

create_variable()

Creates a new og.IVariable.

This performs the same function and takes the same parameters as the og.Controller.edit() keyword og.Controller.Keys.CREATE_VARIABLES.

# To construct a variable the graph must be specified, along with the name and type of variable.
# The variable type can only be an omni.graph.core.Type or a string representing one of those types.
float_variable = og.GraphController.create_variable(graph_id=graph, name="FloatVar", var_type="float")
assert float_variable.valid

color3_type = og.Type(og.BaseDataType.FLOAT, 3, role=og.AttributeRole.COLOR)
color3_variable = og.GraphController.create_variable(graph_id=graph, name="Color3Var", var_type=color3_type)
assert color3_variable.valid

# Also accepts the "undoable" shared construction parameter

delete_node()

Deletes an existing og.Node.

This performs the same function and takes the same parameters as the og.Controller.edit() keyword og.Controller.Keys.DELETE_NODES.

# To delete a node you can pass in a node_id, as accepted by omni.graph.core.ObjectLookup.node, or a node
# name and a graph_id as accepted by omni.graph.core.ObjectLookup.graph.
og.GraphController.delete_node(node_by_sdf)
# The undo flag was the global default so this operation is undoable
omni.kit.undo.undo()
# HOWEVER, you must get the node reference back as it may have been altered by the undo
node_by_sdf = og.Controller.node(node_by_sdf_path)

# Try it a different way
og.GraphController.delete_node(node_id="MyNodeBySdf", graph_id=graph)

# If you do not know if the node exists or not you can choose to ignore that case and silently succeed
og.GraphController.delete_node(node_id=node_by_sdf_path, ignore_if_missing=True)

# Also accepts the "update_usd" and "undoable" shared construction parameters

expose_prim()

Makes a Usd.Prim visible to OmniGraph through a read node.

This performs the same function and takes the same parameters as the og.Controller.edit() keyword og.Controller.Keys.EXPOSE_PRIMS.

# USD prims cannot be directly visible in OmniGraph so instead you must expose them through an import or
# export node. See the documentation of the function for details on the different ways you can expose a prim
# to OmniGraph.

# A prim is exposed as a new OmniGraph node of a given type where the exposure process creates the node and
# the necessary links to the underlying prim. The OmniGraph node can then be used as any others might.
# The prim_id can accept any type accepted by omni.graph.core.ObjectLookup.prim() and the node_path_id can
# accept any type accepted by omni.graph.core.ObjectLookup.node_path()
exposed_empty = og.GraphController.expose_prim(
    exposure_type=og.GraphController.PrimExposureType.AS_BUNDLE,
    prim_id="/World/MyEmptyPrim",
    node_path_id="/World/MyActionGraph/MyEmptyNode",
)
assert exposed_empty is not None

exposed_cube = og.GraphController.expose_prim(
    exposure_type=og.GraphController.PrimExposureType.AS_ATTRIBUTES,
    prim_id=prim_cube,
    node_path_id=("MyCubeNode", action_graph),
)
assert exposed_cube is not None

# Also accepts the "update_usd" and "undoable" shared construction parameters

connect()

Connects two og.Attributes together.

This performs the same function and takes the same parameters as the og.Controller.edit() keyword og.Controller.Keys.CONNECT.

# Once you have more than one node in a graph you will want to connect them so that the results of one node's
# computation can be passed on to another for further computation - the true power of OmniGraph. The connection
# sends data from the attribute in "src_spec" and sends it to the attribute in "dst_spec". Both of those
# parameters can accept anything accepted by omni.graph.core.ObjectLookup.attribute
og.GraphController.connect(
    src_spec=("outputs:a_bool", ("MyNode", graph)),
    dst_spec="/World/MyGraph/MyNodeByName/outputs:a_bool",
)

# Also accepts the "update_usd" and "undoable" shared construction parameters

disconnect()

Breaks an existing connection between two og.Attributes.

This performs the same function and takes the same parameters as the og.Controller.edit() keyword og.Controller.Keys.DISCONNECT.

# As part of wiring the nodes together you may also want to break connections, either to make a new connection
# elsewhere or just to leave the attributes unconnected. The disconnect method is a mirror of the connect
# method, taking the same parameters and breaking any existing connection between them. It is any error to try
# to disconnect two unconnected attributes.
og.GraphController.disconnect(
    src_spec=("outputs:a_bool", ("MyNode", graph)),
    dst_spec="/World/MyGraph/MyNodeByName/outputs:a_bool",
)
omni.kit.undo.undo()

# Also accepts the "update_usd" and "undoable" shared construction parameters

disconnect_all()

Disconnects everything from an existing og.Attribute.

# Sometimes you don't know or don't care what an attribute is connected to, you just want to remove all of its
# connections, both coming to and going from it. The single attribute_spec parameter tells which attribute is to
# be disconnected, accepting any value accepted by omni.graph.ObjectLookup.attribute
og.GraphController.disconnect_all(attribute_spec=("outputs:a_bool", ("MyNode", graph)))

# As this just disconnects "all" if an attribute is not connected to anything it will silently succeed
og.GraphController.disconnect_all(attribute_spec=("outputs:a_bool", ("MyNode", graph)))

# Also accepts the "update_usd" and "undoable" shared construction parameters

set_variable_default_value()

Sets the default value of an og.IVariable.

# After creation a graph variable will have zeroes as its default value. You may want to set some other default
# so that when the graph is instantiated a second time the defaults are non-zero. The variable_id parameter
# accepts anything accepted by omni.graph.core.ObjectLookup.variable() and the value must be a data type
# compatible with the type of the (already existing) variable

# For example you might have a color variable that you wish to initialize in all subsequent graphs to red
og.GraphController.set_variable_default_value(variable_id=(graph, "Color3Var"), value=(1.0, 0.0, 0.0))

get_variable_default_value()

Gets the default value of an og.IVariable.

# If you are using variables to configure your graphs you probably want to know what the default values are,
# especially if someone else created them. You can read the default for a given variable, where the variable_id
# parameter accepts anything accepted by omni.graph.core.ObjectLookup.variable().
color_default = og.GraphController.get_variable_default_value(variable_id=color3_variable)
assert color_default == (1.0, 0.0, 0.0)

NodeController

This class contains the functions that manipulate the contents of a node. It only has a few functions. The class documentation outlines its areas of control.

The subsection titles link to the actual method documentation, while the content consists of a simple example of how to use that method in a typical script. Each subsection assumes a graph set up through the following initialization code:

import omni.graph.core as og

keys = og.Controller.Keys
(_, (node,), _, _) = og.Controller.edit(
    "/World/MyGraph",
    {
        keys.CREATE_NODES: ("MyNode", "omni.graph.test.TestAllDataTypes"),
    },
)
assert node.is_valid()

__init____()

A few parameters can be shared among multiple calls to the controller if it is instantiated as an object rather than calling the functions as class methods.

# The NodeController constructor only recognizes one parameter
controller = og.NodeController(
    update_usd=False,  # Only update Fabric when attributes are added or removed, do not propagate to USD
)
assert controller is not None

create_attribute()

Creates a new dynamic og.Attribute.

This performs the same function and takes the same parameters as the og.Controller.edit() keyword og.Controller.Keys.CREATE_ATTRIBUTES.

# Creating new, or "dynamic", attributes on a node requires the same information you would find in a .ogn
# description of the attribute. The mandatory pieces are "node" on which it is to be created, accepting
# anything accepted by omni.graph.core.ObjectLookup.node, the name of the attribute not including the port
# namespace (i.e. without the "inputs:", "outputs:", or "state:" prefix, though you can leave it on if you
# prefer), the type "attr_type" of the attribute, accepting anything accepted by
# omni.graph.core.ObjectLookup.attribute_type.

# The default here is to create an input attribute of type float
float_attr = og.NodeController.create_attribute(node, "theFloat", "float")
assert float_attr.is_valid()

# Using the namespace is okay, but redundant
double_attr = og.NodeController.create_attribute("/World/MyGraph/MyNode", "inputs:theDouble", "double")
assert double_attr.is_valid()

# Unless you want a non-default port type, in which case it will be extracted from the name
int_attr = og.NodeController.create_attribute(node, "outputs:theInt", og.Type(og.BaseDataType.INT))
assert int_attr.is_valid()

# ...or you can just specify the port explicitly and omit the namespace
int2_attr = og.NodeController.create_attribute(
    node, "theInt2", "int2", attr_port=og.AttributePortType.ATTRIBUTE_PORT_TYPE_OUTPUT
)
assert int2_attr.is_valid()

# The default will set an initial value on your attribute, though it is not remembered in the future
float_1_attr = og.NodeController.create_attribute(node, "the1Float", "float", attr_default=1.0)
assert float_1_attr.is_valid()

# Mismatching between an explicit namespace and a port type will result in a duplicated namespace so be careful
error_attr = og.NodeController.create_attribute(node, "outputs:theError", "float")
assert error_attr.get_path() == "/World/MyGraph/MyNode.inputs:outputs:theError"
assert error_attr.is_valid()

# Lastly the special "extended" types of attributes (any or union) can be explicitly specified through
# the "attr_extended_type" parameter. When this is anything other than the default then the "attr_type"
# parameter will be ignored in favor of the extended type definition, however it must still be a legal type.
# This simplest type of extended attribute is "any", whose value can be any legal type.
union_attr = og.NodeController.create_attribute(
    node, "theAny", attr_type="float", attr_extended_type=og.ExtendedAttributeType.EXTENDED_ATTR_TYPE_ANY
)
assert union_attr.is_valid()

# Note that with any extended type the "default" is invalid and will be ignored
any_other_attr = og.NodeController.create_attribute(
    node, "theOtherAny", "token", default=5, attr_extended_type=og.ExtendedAttributeType.EXTENDED_ATTR_TYPE_ANY
)
assert any_other_attr.is_valid()

# If you want a more restricted set of types you can instead use the extended union type. When specifying
# that type it will be a 2-tuple where the second value is a list of types accepted by the union. For example
# this attribute will accept either doubles or floats as value types. (See the documentation on extended
# attribute types for more information on how types are resolved.)
union_attr = og.NodeController.create_attribute(
    node,
    "theUnion",
    "token",
    attr_extended_type=(og.ExtendedAttributeType.EXTENDED_ATTR_TYPE_UNION, ["double", "float"]),
)
assert union_attr.is_valid()

# Also accepts the "undoable" shared construction parameter

Note

A dynamic attribute in this context means an attribute that can be added to the node that does not exist in the node’s .ogn file. In all other ways this attribute will behave the same as any other attribute.

remove_attribute()

Removes an existing dynamic og.Attribute from a node.

# Dynamic attributes are a powerful method of reconfiguring a node at runtime, and as such you will also want
# to remove them. The "attribute" parameter accepts anything accepted by
# omni.graph.core.ObjectLookup.attribute(). (The "node" parameter, while still functional, is only there for
# historical reasons and can be ignored.)
og.NodeController.remove_attribute(error_attr)

og.NodeController.remove_attribute(("inputs:theUnion", node))

# Also accepts the "undoable" shared construction parameter

safe_node_name()

Returns a node name based on an og.NodeType that is USD-safe. i.e. it replaces any characters that are not accepted by USD as part of a node name, such as a period or vertical bar.

# This is a utility you can use to ensure a node name is safe for use in the USD backing prim. Normally
# OmniGraph will take care of this for you but if you wish to dynamically create nodes using USD names you can
# use this to confirm that your name is safe for use as a prim.
assert og.NodeController.safe_node_name("omni.graph.node.name") == "omni_graph_node_name"

# There is also an option to use a shortened name rather than replacing dots with underscores
assert og.NodeController.safe_node_name("omni.graph.node.name", abbreviated=True) == "name"

DataView

This class contains the functions to get and set attribute values. It has a flexible init function that can optionally take an “attribute” parameter to specify either an og.Attribute or og.AttributeData to which the data operations will apply. The class documentation shows the available functionality.

The subsection titles link to the actual method documentation, while the content consists of a simple example of how to use that method in a typical script. Each subsection assumes a graph set up through the following initialization code:

import omni.graph.core as og

keys = og.Controller.Keys
(_, (node, any_node,), _, _) = og.Controller.edit(
    "/World/MyGraph",
    {
        keys.CREATE_NODES: [
            ("MyNode", "omni.graph.test.TestAllDataTypes"),
            ("MyAnyNode", "omni.graph.tutorials.ExtendedTypes"),
        ],
        keys.SET_VALUES: [
            ("MyNode.inputs:a_int", 3),
        ],
    },
)
int_attr = og.Controller.attribute("inputs:a_int", node)
union_attr = og.Controller.attribute("inputs:floatOrToken", any_node)
float_array_attr = og.Controller.attribute("outputs:a_float_array", node)
double_array_attr = og.Controller.attribute("outputs:a_double_array", node)

__init____()

A few parameters can be shared among multiple calls to the controller if it is instantiated as an object rather than calling the functions as class methods.

# The DataView constructor can take a number of parameters, mostly useful if you intend to make repeated
# calls with the same configuration.

# The most common parameter is the attribute on which you will operate. The parameter accepts anything
# accepted by omni.graph.core.ObjectLookup.attribute()
per_attr_view = og.DataView(attribute=int_attr)
assert per_attr_view
# Subsequent calls to per_attr_view functions will always apply to "int_attr"

# You can also force USD and undo configurations, as per other classes like omni.graph.core.NodeController
do_now = og.DataView(update_usd=False, undoable=False)
assert do_now is not None

# To keep memory operations on a single device you can configure the DataView to always use the GPU
gpu_view = og.DataView(on_gpu=True, gpu_ptr_kind=og.PtrToPtrKind.CPU)
# You can retrieve the GPU pointer kind (i.e. where the memory pointing to GPU arrays lives)
assert gpu_view.gpu_ptr_kind == og.PtrToPtrKind.CPU

# And if you are working with an instanced graph you can isolate the DataView to a single instance. Also
# handy for looping through different instances.
instance_view = og.DataView(instance=1)
assert instance_view is not None

get()

Fetches the current value of an attribute.

# Reading the value of an attribute is the most common operation you'll want to use.
assert og.DataView.get(attribute=int_attr) == 3

# If you've already configured the attribute you don't need to specify it, and you can reuse it
assert per_attr_view.get() == 3
assert per_attr_view.get() == 3

# As a special case, when you have array attributes that you want to write on you can specify an array size
# when you get the reference with the "reserved_element_count" parameter
array_to_write = og.DataView.get(attribute=float_array_attr)
assert len(array_to_write) == 2
array_to_write = og.DataView.get(attribute=float_array_attr, reserved_element_count=5)
assert len(array_to_write) == 5

# Only valid on GPU array attributes is the "return_type" argument. Normally array values are returned in
# numpy wrappers, however you can get the data as raw pointers as well if you want to handle processing of
# the data yourself or cast it to some other library type. This also illustrates how you can use the
# pre-configured constructed GPU view to get specific attribute values on the GPU.
raw_array = gpu_view.get(attribute=double_array_attr, return_type=og.WrappedArrayType.RAW)

# The return value is omni.graph.core.DataWrapper, which describes its device-specific data and configuration
assert raw_array.gpu_ptr_kind == og.PtrToPtrKind.CPU

# Also accepts overrides to the global parameters "on_gpu", "gpu_ptr_kind", and "instance"

get_array_size()

Fetches the number of elements in an array attribute. This is meant to be a quick read of the size-only that avoids fetching the entire array, which can be quite large in some cases.

# An array size may be set without actually allocating space for it. In the case of very large arrays this can
# be quite useful. The get_array_size function lets you find the number of elements that will be in the array
# if you request the data, either on GPU or CPU.
assert og.DataView.get_array_size(float_array_attr) == 5

# Also accepts overrides to the global parameter "instance"

set()

Sets a new value for an attribute.

This performs the same function and takes the same parameters as the og.Controller.edit() keyword og.Controller.Keys.SET_VALUES.

# The counterpart to getting values is of course setting them. Normally through this interface you will be
# setting values on input attributes, or sometimes state attributes, relying on the generated database to
# provide the interface for setting output values as part of your node's compute function.
# The "attribute" parameter accepts anything accepted by omni.graph.core.ObjectLookup.attribute(), and the
# "value" parameter must be a legal value for the attribute type.
og.DataView.set(attribute=int_attr, value=5)
assert og.DataView.get(int_attr) == 5

# An optional "update_usd" argument does what you'd expect, preventing the update of the USD backing value
# for the attribute you just set.
await og.Controller.evaluate()
og.DataView.set(int_attr, value=10, update_usd=False)
usd_attribute = og.ObjectLookup.usd_attribute(int_attr)
assert usd_attribute.Get() != 10

# The values being set are flexible as well, with the ability to use a dictionary format so that you can set
# the type for any of the extended attributes.
og.DataView.set(union_attr, value={"type": "float", "value": 3.5})
assert og.DataView.get(union_attr) == 3.5

# Also accepts overrides to the global parameters "on_gpu", "gpu_ptr_kind", and "instance"

force_usd_update()

A context manager that lets you temporarily bracket a bunch of calls to force them to update immediately to USD or not, regardless of the class or parameter values.

# Sometimes you are calling unknown code and you want to ensure USD updates are performed the way you want
# them. The DataView class provides a method that returns a contextmanager for just such a purpose.
with og.DataView.force_usd_update(False):
    int_view = og.DataView(int_attr)
    int_view.set(value=20)
    # The USD value does not update, even though normally the default is to update
    assert usd_attribute.Get() != 20
with og.DataView.force_usd_update(True):
    int_view = og.DataView(int_attr)
    int_view.set(value=30)
    # The USD value updates
    assert usd_attribute.Get() == 30