Action Graph Car Customizer Tutorial

Welcome to OmniGraph Action Graph, Car Customizer Tutorial.

Action Graphs are a special graph type that allows the graph to respond to events, such as those from keyboard and mouse in addition to others.

In this tutorial, we will be looking into user interaction through keyboard events, as well as performing smooth motions using maneuver nodes.

For this tutorial, we require the extension bundle omni.graph.bundle.action.

The USD files required for this tutorial can be downloaded from:

To start things off, let’s run an existing graph already assembled. Please open car_customizer_tutorial_finished.usda.

This scene consists of a simple car customizer. Try pressing the key S. You should see the car changes color. Repeatedly press S to cycle through five different colors available for the car.

Next, try pressing the key D. You should see the camera moving into the car. Repeatedly press D to transition between four different camera viewpoints around the car.


Now let’s try to build this graph from scratch. Open car_customizer_tutorial_start.usda.

Since we want our graph to respond to user keyboard events, we should use an Action Graph. Open the new Action Graph Editor UI by clicking on Window -> Visual Scripting -> Action Graph. Click New Action Graph to create an empty action graph at the default path.

Detour: Action Graph Evaluation

You might be wondering: How does action graph work behind the scene? Let’s take a slight detour to investigate that.

In an action graph, each chain of nodes starts with an event source node.

An event source node listens for a particular event. When the event occurs, it fires a signal through one of its execution pins.

The action graph’s evaluator then follows the execution connections downstream and computes nodes it encounters until there are no more downstream connections to follow.

If any of these nodes have upstream data-dependencies, all of its data-dependencies will also be computed. No other nodes will be computed.

As an example, let’s take a look at the simple action graph below.


Here the On Keyboard Input node is the event source node. When the specified key is pressed, it would fire a signal through its Pressed execution pin.

The evaluator then follows the execution connections downstream, and finds out that the Write Prim Attribute node needs to be computed.

Before Write Prim Attribute can be computed, all its upstream data-dependencies need to be computed.

Hence the evaluator will proceed to compute the Read Prim Attribute, Constant Double and Multiply nodes, and then compute the Write Prim Attribute node.

If there are any other nodes in the graph, they will not be computed, because Write Prim Attribute does not need their data.

Part 1: Moving Camera Viewpoints

Let’s start with the D key, which controls the camera angles.

Step 1:

First, use the node browser on the left to add the On Keyboard Input node to the graph. In the property window on the right, set Key In to the key D, and unselect Only Simulate On Play.

Step 2:

Next, we need a Multigate node. This node can have any number of outputs. You can add more outputs to the node by clicking the + button in the property window. Add 3 more outputs to the Multigate node so that it has 4 outputs in total. And then wire up On_Keyboard_Input.Pressed to Multigate.Execute_In


The functionality of the Multigate node is that, it cycles through each of its outputs one by one. On each execution input, exactly one of its outputs will be activated, and the outputs will be activated in sequence. For example, when the user first presses the D key, the first output will be activated. When the user presses D again, the second output will be activated, and so on.

Step 3:

Next, we will add the nodes to perform camera motions. To do this, we need a new set of nodes called maneuver nodes. A maneuver node accepts a source prim as input, and smoothly transitions the source prim to a target orientation/position/scale.

Add a maneuver node called Move To Target. This node takes a source prim and a target prim as inputs. When it receives an execution signal, it would smoothly move the source prim to the target prim. At the end of the maneuver, the source prim will have the same translation, rotation and scale as the target prim.

Since we want to move our own camera, we need to set the source prim to the camera that is bounded to our viewport. Look at the top left corner of the scene, notice that our camera is set to CamFree, so we should set the source prim to CamFree using the property window. And then set the target prim to CamDriver. Finally wire Multigate.Output0 to Move_To_Target.Execute_In.

Step 4:

Next, add a second Move To Target node. Set its source prim to CamFree, and set its target prim to CamFront. Connect its Execute In to Output1.

Add a third Move To Target node. Set source prim to CamFree and target prim to CamWheel. Connect its Execute In to Output2.

Add a fourth Move To Target node. Set source prim to CamFree and target prim to CamBack. Connect its Execute In to Output3.


Right now, if you go back to the scene and press D, you will see that you can cycle through the four different camera angles.

Part 2: Opening and Closing the Door

Next, we want to open the door of the car before moving into the car, and then close the door after moving into the car. So we want to chain a sequence of maneuvers together.

In order to open the door, we need to set the orientation of the Door_DriverSide Xform, which is a child of the World Xform.


Add a maneuver node called Rotate To Orientation. Upon receiving an execution signal, this node would rotate the source prim to a desired orientation. Set its source prim to Door_DriverSide, and set its target orientation to (0,0,-90). Disconnect the wire at Multigate.Output0. Wire up Multigate.Output0 to Rotate_To_Orientation.Execute_In. Wire up Rotate_To_Orientation.Finished to Move_To_Target.Execute_In.

Add another Rotate To Orientation node. Set its source prim to Door_DriverSide, and set its target orientation to (0,0,0). Wire up Move_To_Target.Finished to Rotate_To_Orientation_01.Execute_In


Now if you go back to the scene and press D, you will see that the door opens before you move into the car, and the door closes itself after you move into the car.

Part 3: Changing the Color of the Car

Next, we want to change the color of the car whenever we press the key S.

Step 1:

In order to cycle through the five different colors, we will use a helper prim that we have defined directly in the USD file. Take a look at the CarOptions prim. It has an attribute called color, which is an array of five different colors specified in RGB format. Drag the CarOptions into the graph editor and select Read Attribute to read its attribute. Set Attribute Name to color.


Step 2:

Next, add another On Keyboard Input node to the graph. Set its Key In to the key S, and unselect Only Simulate On Play.

Step 3:

Next, add a new node called Counter. This node maintains an integer counter, and increments the counter whenever it receives an execution signal. Wire up On_Keyboard_Input_01.Pressed to Counter.Execute_In.

Step 4:

Next, add a new node called Modulo. This node computes the modulo of A%B. Wire up Counter.Count to Modulo.A.

Step 5:

Create a Constant Int node. Set its Value to 5 in the property window. Wire up Constant_Int.Value to Modulo.B.


Step 6:

We can see that the output of the Modulo node will be an index of the color array. Whenever we press the key S, the counter will increment to move to the next index of the color array. We then perform modulo 5 to reset the index to 0 if it goes out of bound.

As such, we want to add a new node called Array Index. This node takes an array and an index as inputs, and retrieves the element at the specified index. Wire up Modulo.Result to Array_Index.Index. Wire up Read_Prim_Attribute.Value to Array_Index.Array.

Step 7:

Now that we have a color that cycles, we want to use it to set the color of the car. Go to Stage -> Looks -> OmniSurface_CarPaint -> Shader. Drag the Shader into the graph editor to write its attribute.


The attribute we want to write is diffuse_reflection_color, so we should click on the new Write Prim Attribute node and set its Attribute Name to diffuse_reflection_color. Then wire up Array_Index.Value to Write_Prim_Attribute.Value.

Step 8:

The Write Prim Attribute node also needs an execution signal, so we can wire up Counter.Execute_Out to Write_Prim_Attribute.Execute_In.


That’s it! If you go back to the scene and press the S key, you will see that the car cycles through five different colors!