How to create a new omnigraph environment

Hello,

we would like to make a “Scene Creation Graph” for our current research project.

This should ideally be an extension, in which the user can build the scene by connecting different omnigraph nodes, each representing a different scene object. Those scene objects, for example a robot cell or a robot arm, have different mounting points. Connecting these object nodes together should allow for an automatic positioning and if needed creation of joints between those objects based on those mounting points.

We could probably implement this with custom nodes in a normal action or push graph, but we would like to make a custom user interface for that (for example with a button to create the scene and with a defined selection of nodes to chose from).

Is it possible to make such a custom omnigraph environment? And is there a documentation or a tutorial for that?

Kind regards

Hi @axel.goedrich. In the Code app, we have a the Omni.UI Graph Docs which would be a good place for you to start:

Dev team is telling me that Omni.UI graph is more advanced and that they strongly recommend starting with custom nodes for Action/Generic graph. You could customize the property panel for your custom nodes, that might serve your needs.

If you choose explore custom graphs and backends, Anim Graph is an interesting example to look at. It uses ActionGraph with custom nodes, but the state machine is currently a different backend that also uses Omni.UI Graph, and shares some widgets with OmniGraph.

1 Like

Thanks for the hint. I will look into it.

I have already experimented with custom omnigraph nodes. The process was pretty straight forward and easy to set up.

The only thing that was missing for our application is access to omnigraph node events, like node creation, destruction or a change of node inputs. It would be very helpful to have access to such events within a node itself.

Hi @axel.goedrich. That sounds interesting. If you have some example of how you’d find that functionality useful. I can share it with the dev team.

Hi @mati-nvidia,

one interesting use case for us is scene creation.

The video below is from a quick prototype I created for actiongraph.

The idea behind this is that individual components of the scene can be created through Omnigraph nodes and automatically positioned and possibly parameterized by connecting them.

This is motivated by our current research project, in which we want to train robots for assembly applications on industrial plants. The plants we are looking at mostly consist of the same or at least similar components connected in different configurations.

Omnigraph would theoretically be a nice and user-friendly solution to assemble such systems from components.

The following functions would be very helpful for that:

#gets called on node creation
def on_startup(db): 
    #load the USD


#gets called on node destruction
def on_shutdown(db): 
    #remove usd prim


#gets called when node inputs change
def on_input_change(db): 
    #for example position object based on inputs

In addition, it would be useful if the node object from the respective node class is initiated during node creation. This would open up the benefits of object-oriented programming.

Hi @axel.goedrich. I heard back and it sounds like we do have these functions. They are:

  • initialize()
  • release()
  • register_on_connected_callback()

Hi @mati-nvidia,

thank you for the Info.

The initialize() and the release() functions work as expected.
“def initialize():” is called when the node is created and “def release():” is called when the node is destroyed.

But the register_on_connected_callback() never gets called (tried to connect different inputs and change values, but nothings seems to activate it).

This is the code I used:

class EGK_40_EL_M_B:

    def initialize(arg1, arg2):
         print("Initialized!!!!!!!!!!!!!!!!")

    def release(arg1):
        print("Released!!!!!!!!!!!!!!!!")

    def register_on_connected_callback(arg1):
        print("Connection Change!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
    
    @staticmethod
    def compute(db) -> bool:
        """Compute the outputs from the current input"""

        try:
            db.outputs.out_exec = True
            stage = omni.usd.get_context().get_stage()

            # Add a physics scene prim to stage
            if not stage.GetPrimAtPath(f"{objRoot}/physicsScene"):
                scene = UsdPhysics.Scene.Define(stage, Sdf.Path("/World/physicsScene"))
                scene.CreateGravityDirectionAttr().Set(Gf.Vec3f(0.0, 0.0, -1.0))
                scene.CreateGravityMagnitudeAttr().Set(981.0)

            if not stage.GetPrimAtPath(f"{objRoot}{objXform}"):
                xformable = UsdGeom.Xform.Define(stage, f"{objRoot}{objXform}")
                xform_op_translate = xformable.AddXformOp(UsdGeom.XformOp.TypeTranslate, UsdGeom.XformOp.PrecisionDouble, "")
                xform_op_translate = UsdGeom.XformOp(xformable.GetPrim().GetAttribute('xformOp:translate'))
                scale = xformable.AddXformOp(UsdGeom.XformOp.TypeScale, UsdGeom.XformOp.PrecisionDouble, "")
                scale.Set(Gf.Vec3f(objScale, objScale, objScale))

            if not stage.GetPrimAtPath(f"{objRoot}{objXform}{objRobot}"):
                xform = stage.GetPrimAtPath(f"{objRoot}{objXform}")
                xform.GetReferences().AddReference(UR10_path)
            pass

        except Exception as error:
            # If anything causes your compute to fail report the error and return False
            #db.log_error(str(error))
            print("ERROR!!!")
            return False

        # Even if inputs were edge cases like empty arrays, correct outputs mean success
        return True

Do I need to implement the “register_on_connected_callback()” function differently than the other two?

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.

Sorry for the delay on this. You don’t need to override register_on_connected_callback(). You just need to call it and provide it the callback function. I think you can do it within your initialize() function:

def initialize():
   self.conn1_cb = self.register_on_connected_callback(on_connection)

def on_connection(attr1, attr2):
    # do something
    pass
1 Like