Add New Functionality To Our Experience#
Now that you understand client/server setup and communication, you’ll go through an example of adding a new button. First, you’ll add the button to our SwiftUI panel and add the functionality to our state and client management. Once you’re happy with your UI on the client, you’ll jump over to Omniverse and add the required ActionGraph to our stage.
Client#
On the client-side, you will develop a new button in the SwiftUI panel to trigger the randomize function. This button will send a message to the server, initiating the randomization process for variant colors and styles. This function will work once we’ve added the needed code to the following components:
ConfigureView
OmniverseStateManager
OmniverseClientEvents
ConfigureView#
In the ConfigureView
file, replace the current var purseActions
with the following code starting at line 86.
// Defines a view that contains actions related to the asset
var purseActions: some View {
VStack {
// toggle the visibility of the purse
LabelButton(
label: "Visibility",
onText: "On",
offText: "Off",
toggleView: true,
icon: AnyView(dashed),
isOn: true
) { isOn in
// Updates the state of purse visibility
appModel.stateManager["purseVisibility"] = isOn
? PurseVisibility.visible
: PurseVisibility.hidden
} textCondition: { _ in
// Condition to determine the text displayed on the button
viewModel.purseVisible
}
// Horizontal stack to place the Rotate and Randomize buttons
// side by side
HStack {
// The Rotate button to rotate the purse
LabelButton(
label: "Rotate", // The button's label
toggleView: false, // not a toggle button
icon: AnyView(dashed) // Icon for the button
) { isOn in
// Sends the appropriate rotation action
if isOn {
appModel.stateManager.send(RotationAction.rotateCCW)
} else {
appModel.stateManager.send(RotationAction.rotateCW)
}
}
// The Randomize button to randomize the purse
LabelButton(
label: "Randomize", // The button's label
toggleView: false, // not a toggle button
icon: AnyView(dashed) // Icon for the button
) { _ in
// Sends a request to randomize the purse
appModel.stateManager.send(Randomize.randomize)
}
}
Spacer() // Adds space below the buttons to push them up
}
}
This code inserts a new Randomize button into a VStack with our existing buttons. You may get an error that Randomize has no member, we’ll fix that next.
OmniverseClientEvents#
In the OmniverseClientEvents
file, insert the following code at the end of the file, after line 267””
// Represents an input event for a randomize action
public struct RandomizeClientInputEvent: EncodableInputEvent {
// Message data indicating the type of action
public let message: [String: String]
// Type of event, always set to "executeAction"
public let type = "executeAction"
// Initializes the event with a message indicating a randomize action
public init(_ randomize: Randomize) {
self.message = ["actionType": "randomize"]
}
}
// Represents different types of randomize actions
public enum Randomize: String, OmniverseMessageProtocol {
case randomize = "True"
// Provides an encodable representation of the randomize action
public var encodable: any EncodableInputEvent {
return RandomizeClientInputEvent(self)
}
}
This code adds a Randomize button. You can now build your application and run it in the simulator, and you should see the Randomize button added to your UI.
Omniverse#
In Omniverse, we now need to add the ActionGraph logic associated with this function. We’ll add a graph to our stage, insert some python code to randomize the variants, and listen for a message event from the client to trigger the script.
Action Graph Logic Setup#
Open the Purse dataset USD in Omniverse Kit Editor.
Create a new action graph using one of the following methods:
Right-click (RMB) within the stage panel, navigate to Create > Visual Scripting and click Action Graph
Navigate to Window > Visual Scripting > Action Graph and click New Action Graph
Right-click (RMB) on the new action graph and select “rename” from the dropdown menu. Rename it to “randomizeController”.
Take note of the Nodes column to the right of the Graph, you’ll create some actionGraph nodes within this graph that will define the randomize logic.
- On MessageBus Event
- Add+ an Attribute from the Property Panel.
Set the Attribute name to message
Set the Attribute Type to string
Set the Event name to executeAction
Uncheck Only Simulate On Play
- Script Node:
- Add+ an Attribute from the Property Panel.
Set the Name to primPath.
Set the Port Type to input
Set the Data Type to token
Add the following prim path to the new primPath Attribute:
/World/SM_LuxuryBag_A01_01
- Add+ an Attribute from the Property Panel:
Set the Name to randomize
Set the Port Type to input
Set the Data Type to token
After selecting Randomize on the client, you should see this populate to say
{ "actionType":"randomize"}
Connect the Received event from On MessageBus Event to Exec In on the Script Node.
- To Token
Connect on_messagebus:message to input:value
Connect token to script_node:input:randomize
Insert the following code block into the script node that you created earlier:
def setup(db: og.Database):
pass
def cleanup(db: og.Database):
pass
def compute(db: og.Database):
import carb
from omni.usd import get_context
import random
# Check if the randomize input is triggered
if not db.inputs.randomize:
return False
# Get the USD stage
stage = get_context().get_stage()
# Retrieve the prim path input
primPath = db.inputs.primPath # primPath as string
# Define the variant sets and variants
variantOptions = {
"Gold": ["Yellow", "White", "Pink"],
"Locker": ["Triangle", "Ring"],
"KeyRing": ["ON", "OFF"],
"Handle": ["Round", "Square"],
"Leather": ["Beige", "Black", "BlackEmboss", "Orange", "Tan", "White"]
}
# Get the prim using the prim path
prim = stage.GetPrimAtPath(primPath)
if not prim:
carb.log_error(f"Not valid prim: {primPath}")
return False
# Get the variant sets from the prim
variantSets = prim.GetVariantSets()
if not variantSets:
carb.log_error(f"Prim does not have Variant Sets: {primPath}")
return False
# Randomly select a variant for each variant set and set it
for variantSetName, variants in variantOptions.items():
variantSet = variantSets.GetVariantSet(variantSetName)
if not variantSet:
carb.log_error(
f"Prim `{primPath}` does not have"
+ f" variant set: {variantSetName}"
)
continue
# Randomly select a variant
selectedVariant = random.choice(variants)
variantSet.SetVariantSelection(selectedVariant)
print(f"Set {variantSetName} to {selectedVariant}")
return True
For brevity, we won’t add any ack logic to this setup, but you can if you’d like to notify the client or another event that randomize has completed.
Test#
Now we’re ready to test! You can Start AR with your modified stage in Omniverse, and you can launch your client from Xcode into the Simulator or deploy to the Vision Pro. Connect to your Omniverse server and try the Randomize button.