Creating Your First Node¶
As we now understand how to work with the graph using existing nodes, this section of the tutorials walks through how to create new nodes to augment the existing functionality. OmniGraph uses a framework we call OGN (OmniGraph Nodes) for node creation. OGN is a powerful framework that takes a high level description of the node and generates most of the implementation required for the node. In this tutorial, it will be generating Python code, but it is capable of generating code in other languages like C++ as well. In addition, it’s capable of generating documentation and even tests for your node.
We’ll create two simple nodes, NegateDouble and AddDouble, to replace the functionality of the SubtractDouble node.
Please open the Node Description Editor:
First, we’ll create the NegateDouble node. Please fill in the first section of the Node Description Editor as seen below.
It is strongly recommended to leave the Extension Location field to its default value, as the application only looks for extensions in a fixed number of locations (for the precise list and instructions on how to alter it, please refer to general Omniverse docs). The default location above should be one of these locations. If you choose something else, make sure it’s a location where Omniverse is set to look for extensions, else your nodes will not load.
Next, please fill out the Inputs and Outputs sections as depicted blow:
Notice that as you’re filling out the information, the editor is showing you the raw .ogn file it is generating:
The .ogn file is what really matters here. It is a json file that describes what the node looks like from a high level. It is treated as a source file, which is parsed by our OGN framework, that then generates source files in Python (or C++), as well as documentation and tests. These generated source files provide functionality like accessing data, checking validity, and generally reduces boilerplate code to simplify the task for programmers to work with the system. The Node Description Editor UI you are using merely simplifies the task of writing the .ogn file, as new users may not be familiar with its syntax.
After filling out the information, click first on the “Populate Extension” button, followed by “Save Node”, followed by “Generate Blank Implementation”. The first button sets up the new extension. The second button generates the .ogn file within the extension. The third button generates a sample implementation file.
If all went well, you should already be able to place our new node into the system, even though at this time, with only the default implementation, it won’t do anything useful yet. Place a NegateDouble node into the graph and wire it up like so:
Let’s now navigate to the generated extension directory to take a look at the generated implementation file. If things were left at their default, this directory would be something like:
This is the source directory. Notice also the
directory. This is the generated code / documentation by the OGN framework. This is considered a build directory and not source, so do not check it in to your source code control system.
Open up the OgnNewNode.py file. It should look like the following:
""" This is the implementation of the OGN node defined in OgnNewNode.ogn """ # Array or tuple values are accessed as numpy arrays so you probably need this import import numpy class OgnNewNode: """ Takes a double as input and negates it """ @staticmethod def compute(db) -> bool: """Compute the outputs from the current input""" try: # By putting the compute in a try block you can fail the compute by raising an exception pass except Exception as error: # If anything causes your compute to fail report the error and return False db.log_error(str(error)) return False # Even if inputs were edge cases like empty arrays, correct outputs mean success return True
Alternatively, we can also just click on the Edit Node button in the Node Description Editor to open up the implementation file OgnNewNode.py.
To complete our NegateDouble node, we only need to change a line or two:
""" This is the implementation of the OGN node defined in OgnNewNode.ogn """ # Array or tuple values are accessed as numpy arrays so you probably need this import import numpy class OgnNewNode: """ Takes a double as input and negates it """ @staticmethod def compute(db) -> bool: """Compute the outputs from the current input""" try: # By putting the compute in a try block you can fail the compute by raising an exception input_value = db.inputs.in_double db.outputs.out_double = -1.0*input_value except Exception as error: # If anything causes your compute to fail report the error and return False db.log_error(str(error)) return False # Even if inputs were edge cases like empty arrays, correct outputs mean success return True
Of course it is good form to remove the redundant and unnecessary information, which will also server to illustrate how lean a Python node implementation can be:
"""This is the implementation of the OGN node defined in OgnNewNode.ogn""" class OgnNewNode: """Takes a double as input and negates it""" @staticmethod def compute(db) -> bool: db.outputs.out_double = - db.inputs.in_double return True
Once you save the file, notice that your node magically starts functioning:
This demonstrates the idea of “hot reloading”, which is very handy for Python nodes, and allows programmers to iterate rapidly.
In some versions of Kit there may be a problem when hot reloading a node with an existing instance in the scene. To work around this, select the nodes from the reloaded extension and use CTL-X/CTL-Z to delete the nodes and undo the delete, restoring them with the new functionality.
Now that our NegateDouble node is working, let’s move on to our next node, AddDouble. In the Node Description Editor, please click on File -> New Node to start the creation process for a new node:
Fill in the node details as follows:
Since the extension has already been populated, we do not need to click “Populate Extension” this time. Instead, click “Save Node” to save the ogn file as “OgnAddDouble.ogn” in the default directory, and then click “Generate Blank Implementation”:
Click on “Edit Node” to open the implementation file, and alter the implementation of the AddDouble node as follows:
""" This is the implementation of the OGN node defined in OgnAddDouble.ogn """ class OgnAddDouble: """ Add two doubles """ @staticmethod def compute(db) -> bool: """Compute the outputs from the current input""" db.outputs.sum = db.inputs.input_a + db.inputs.input_b return True
Place the AddDouble node into the graph, wire it up and set its input value like so:
Go ahead and disconnect the SubtractDouble node from the ClampDouble node, by right clicking on the connection:
Wire the AddDouble node to the ClampDouble node as follows:
That’s it - as you move the sphere around, the graph should function as before. As a cleanup, consider deleting the dangling SubtractDouble node.
Note that to successfully run these nodes in a future session, you need to now enable your new extension (omni.new.extension) by loading it from the extension manager like the others. You may want to enable auto-load on the extension for convenience.
In the next chapter, we’ll cover how to create a more complex node - a deformer.