Compound Nodes
Compound nodes are nodes whose execution is defined by the evaluation of an OmniGraph
. Compound nodes can be used to
encapsulate complex functionality into a single node, or to create a node that can be reused. The
existing release of OmniGraph supports Compound Subgraphs, which allow the user to collapse a subnetwork of nodes into
a separate OmniGraph
that is represented by a node in the owning graph.
USD Representation of Compound Nodes
Similar to non-compound nodes, Compound Nodes are represented in USD using a prim with the schema type
OmniGraphSchema.OmniGraphNode
. A Compound Node also contains attributes and a node type. When a Compound Node
represents a Compound Subgraph, the node type is fixed to omni.graph.nodes.CompoundSubgraph.
For the prim to be recognized by OmniGraph as a compound, a USD schemaAPI is applied:
OmniGraphSchema.OmniGraphCompoundNodeAPI
. This allows the compound node to inherit an attribute representing the
compound node type (currently only “subgraph” is supported), and a USD relationship attribute
(omni:graph:compoundGraph) that references the location of the graph Prim on the stage that represents the definition
of the Compound Nodes execution.
The Prim representing the graph is a standard OmniGraph Prim. The Prim is inserted as a child of the Compound Node. Connections are made between the Compound Node and the nodes in the Compound Graph. The attributes of the Compound Node generally act as a passthrough, moving data between the owning graph and Compound Graph.
def OmniGraphNode "compound" (
prepend apiSchemas = ["OmniGraphCompoundNodeAPI"]
)
{
custom token inputs:a
custom token inputs:b
token node:type = "omni.graph.nodes.CompoundSubgraph"
int node:typeVersion = 1
rel omni:graph:compoundGraph = </World/PushGraph/compound/Subgraph>
token omni:graph:compoundType = "subgraph"
custom token outputs:sum
prepend token outputs:sum.connect = </World/PushGraph/compound/Subgraph/add.outputs:sum>
def OmniGraph "Subgraph"
{
def OmniGraphNode "add"
{
custom token inputs:a
prepend token inputs:a.connect = </World/PushGraph/compound.inputs:a>
custom token inputs:b
prepend token inputs:b.connect = </World/PushGraph/compound.inputs:b>
token node:type = "omni.graph.nodes.Add"
int node:typeVersion = 2
custom token outputs:sum
}
}
}
Using the og.Controller Class to Create Compound Nodes in Scripts
Creating Compound Nodes
The og.Controller class can be used to create Compound Nodes in scripts. The og.Controller
class provides a set of
methods useful in defining OmniGraphs using python scripting. The most straightforward way to create a compound node
using the og.Controller.edit
function is to define the Compound Subgraph when defining the Compound Node by using a
recursive definion, as demonstrated in the following sample:
keys = og.Controller.Keys
controller = og.Controller()
(graph, nodes, _, name_to_object_map) = controller.edit(
"/World/MyGraph1",
{
keys.CREATE_NODES: [
(
"CompoundNode",
{ # Creates the compound nodes
# Defines the subgraph of the compound node
keys.CREATE_NODES: [
("Constant1", "omni.graph.nodes.ConstantDouble"),
("Constant2", "omni.graph.nodes.ConstantDouble"),
("Add", "omni.graph.nodes.Add"),
],
keys.CONNECT: [
("Constant1.inputs:value", "Add.inputs:a"),
("Constant2.inputs:value", "Add.inputs:b"),
],
},
),
],
},
)
This will generate a graph that contains a Compound Node containing a Subgraph with two Constant nodes and an Add Node.
Note that in the returned values from the edit
function, the list represented by the nodes
variable will only
contain the Compound Node, and not the nodes defined inside the Compound Subgraph. In other words, the list of returned nodes only
contains nodes in the top-level graph. However, the nodes inside the Compound Subgraph can be accessed via the returned
name_to_object_map
dictionary using the defined names of each of the nodes in the Subgraph. For example, to access the
Constant1 node, use name_to_object_map["Constant1"]
. This does mean that when creating complex graph structures, all
node names need to be unique, even if they are defined in different Compound Subgraphs.
Promoting Compound Node Attributes
In the example above, the generated compound node has neither input nor output attributes. In practice, a Compound Node
will use data from, and produce data for, adjacent nodes in the graph. This is accomplished
using the og.Controller.keys.PROMOTE_ATTRIBUTES
key. The following example demonstrates how to promote attributes:
keys = og.Controller.Keys
controller = og.Controller()
(graph, nodes, _, name_to_object_map) = controller.edit(
"/World/MyGraph2",
{
keys.CREATE_NODES: [
(
"CompoundNode",
{
keys.CREATE_NODES: [
("Add", "omni.graph.nodes.Add"),
],
keys.PROMOTE_ATTRIBUTES: [
("Add.inputs:a", "inputs:one"),
("Add.inputs:b", "inputs:two"),
("Add.outputs:sum", "outputs:result"),
("Add.outputs:sum", "outputs:alt_result"),
],
},
),
("Constant1", "omni.graph.nodes.ConstantDouble"),
("Constant2", "omni.graph.nodes.ConstantDouble"),
("Consumer", "omni.graph.nodes.Add"),
],
keys.CONNECT: [
# Connect to the promoted attributes
("Constant1.inputs:value", "CompoundNode.inputs:one"),
("Constant2.inputs:value", "CompoundNode.inputs:two"),
("CompoundNode.outputs:result", "Consumer.inputs:a"),
("CompoundNode.outputs:alt_result", "Consumer.inputs:b"),
],
},
)
While the sample above does not demonstrate a particularly useful Compound Node, it does demonstrates a few important concepts about promotion. When promoting an attribute, the source attribute and the compound attribute name are supplied as a tuple. The source attribute is a path to an attribute in the Compound Subgraph. The second element of the tuple specifies the promoted attribute name in the compound. Note the name does not have to match the source attribute path.
The promoted attribute can then be accessed in the owning graph using the name specified in the promotion in the same
manner as one would use any other node attributes. If required, an attribute can be promoted multiple times, as demonstrated
by the promotion of the Add.outputs:sum. Attribute promotion can also be accomplished using the
og.NodeController.promote_attribute
function.
The inputs and outputs prefix is optional in the compound attribute name. This means that the type of promoted attribute is determined solely by the port type of the source attribute, and not the prefix of the compound attribute name. The is demonstrated in the following example:
keys = og.Controller.Keys
controller = og.Controller()
(graph, nodes, _, name_to_object_map) = controller.edit(
"/World/MyGraph3",
{
keys.CREATE_NODES: [
(
"CompoundNode",
{
keys.CREATE_NODES: [
("Add", "omni.graph.nodes.Add"),
("Constant1", "omni.graph.nodes.ConstantDouble"),
],
keys.PROMOTE_ATTRIBUTES: [
("Add.inputs:a", "one"), # Promoted as inputs:one
("Add.inputs:b", "outputs:two"), # Promoted as inputs:outputs:two
("Add.outputs:sum", "inputs:result"), # Promoted as outputs:inputs:result
("Constant1.inputs:value", "const"), # Promoted as outputs:const
],
},
),
],
},
)
Here, the appropriate prefix is appended to each of the promoted attribute names. In the case of Constants, which used input attributes that are marked as output-only, they become output attributes on the Compound Node.