Omniverse KIT cheat sheet

Michael T. Wagner
9 min readNov 25, 2021

--

here are some snippets or pieces of information which helped me to get things going in KIT, will be extended as we go:

Files

FileDialog

There is a convenience wrapper for the FileDialog, very helpful:

Check if file exists on Nucleus

we can’t use os.path.exists with files on the Nucleus — like

omniverse://localhost/myfile.usd

check it like this :

def is_file_exists(filename: str):
“””Returns True if the given file exists”””
result, stat = omni.client.stat(filename)
return result == omni.client.Result.OK

Navigation Mesh

If you dont want a footprint on the navigation mesh make sure to disable “instanceable” in your components (Thanks @Thomas for finding out)

Debugging

Launching App with Developer Menu

You can add a developer menu to you app (KIT 106) by starting with the -d command line parameter. (Thanks to richard3d from Omniverse discord)

Developer Menu
repo launch -d 

Launching Test with Selection GUI and VS-Code Debugger

It’s a little tricky to use the VS-Code Debugger with test-batch files in KIT 106 — my approach is to activate the test selection GUI by adding the — dev option and enabling the omni.kit.debug.vscode extension for the test GUI frame ( note the additional “— —” before “- — enable “omni.kit.debug.vscode”, this forwards the parameters to the launched new GUI process)

_build\windows-x86_64\release\tests-[your extension].bat --dev -- --enable omni.kit.debug.vscode

Logging messages with your extension name

In order to be able filter the debug messages of your extension, create a logger with logger.getLogger(__name__).

import logging
# logger with extension name e.g. My.Great.Extension
logger = logging.getLogger(__name__)
logger.info("123") #-> [Info][My.Great.Extension] 123
logger.warning("456")#-> [Warning][My.Great.Extension] 456
logger.error("789")#-> [Error][My.Great.Extension] 789

Use the filter in console window to just display your messages by clicking “Select None” and checking your extension, in this case

[x] My.Great.Extension.

https://docs.omniverse.nvidia.com/kit/docs/kit-manual/latest/guide/logging.html

Enable break points in VS-Code:

In order to make break points work during test execution I had to add the “args” section to my launch.json (thanks Matias Codesal for the hint):

"pathMappings": [
{
"localRoot": "${workspaceFolder}",
"remoteRoot": "${workspaceFolder}",
}
],
"args": [
"--preserve-symlinks",
"--preserve-symlinks-main"
],

Running tests with GUI:
add “— dev” to command line

Setting Log-Level to info in command line
add “ — /log/level=info” to command line (warn is default)

Using the debugger when running tests from extensions tab
make sure to disable autoload for the vscode extension and to restart composer if you want to use the debugger with “RUN EXTENSION TESTS”

Reference
https://docs.omniverse.nvidia.com/kit/docs/carbonite/latest/docs/omni.log/Logging.html#logging-settings-reference

Ctrl + T to goto definition of a class in different extension in VS-Code

You can use Ctrl+T to lookup the definition of a class in a different extension for example if F12 doesnt find the symbol

Test Logfiles

Log-files from tests are located in %APPDATA%, it’s better to read than the console output — e.g. if your extension is called my_extension, after you’ve run the tests, the test logfile is located in

C:\Users\[your_user]\AppData\Local\ov\data\_testoutput\exttest_my_extensions\

There is a new logfile for each test-run.

Prevent tests from loading dependencies

If tests take lot of time to execute they may try to resolve additional dependencies which may be not possible in a testing environment — you can prevent that by editing the omni.app.test_ext.kit used for testing and adding the setting app.extensions.registryEnabled to false.

C:\Users\[user]\AppData\Local\ov\pkg\create-2023.2.3\kit\apps\omni.app.test_ext.kit : 

add this:

[settings]
# Make sure extensions doesn't pull anything accidentally from downstream repos (circular dependency prevention)
app.extensions.registryEnabled = false

Omniverse Microservices

Great introduction:

https://www.nvidia.com/en-us/on-demand/session/gtcspring21-s32071/?playlistId=playList-c2dcf6dd-6a25-41c6-b07e-d842f19df727

SampleCode from the Talk:

[settings]
# Setting the port for the embedded http server
exts."omni.services.transport.server.http".port = 8111

How to retrieve the url for the OpenAPI docs from settings:

settings = carb.settings.get_settings()
local_host = settings.get_as_string("exts/omni.services.transport.server.http/host")
local_port = settings.get_as_int("exts/omni.services.transport.server.http/port")
print(f"Local Docs - http://{local_host}:{local_port}/docs")

set listen port from the command line with repo launch:

repo launch your_application.kit -- --/exts/omni.services.transport.server.http/port=8041

using other python libraries

add dependencies to “requirements” under[python.pipapi] in extension.toml:

[python.pipapi]
requirements = ['numpy', 'scipy']
use_online_index = true

installing python library in code

from omni.kit.pipapi import installomni.kit.pipapi.install('pandas')

omni.kit.pipapi Documentation

https://docs.omniverse.nvidia.com/py/kit/source/extensions/omni.kit.pipapi/docs/index.html

Sample Code

make a valid identifier

Python:
from pxr import Tf
Tf.MakeValidIdentifier(prim_name)

C++
TF_API std::string TfMakeValidIdentifier (const std::string &in)

see https://graphics.pixar.com/usd/release/api/group__group__tf___string.html

scenegraph instancing

usd has the concept of implicit prototypes and explicit instances — you need to set instanceable flag on each product instance like that :

product_instance_prim.SetInstanceable(True)

see documentation here:

Prims that share parts of the scenegraph through instancing are called “instance” prims. Each of these instance prims are associated with a “prototype” prim that serves as the root of the shared scenegraph. A single prototype prim may have many associated instances, all of which share the same scenegraph.

Users do not explicitly set up the network of prototype and instance prims. Instead, users mark the prims they want to be instanced with metadata that tags them as “instanceable.”

Instancing prims in same stage

target_prim.GetReferences().AddInternalReference(
referenced_prim.GetPath()
)

see https://graphics.pixar.com/usd/dev/api/class_usd_references.html#Usd_Internal_References

Traversing all children and descendants of a Prim:

use Usd.PrimRange (https://graphics.pixar.com/usd/release/api/class_usd_prim_range.html)

for prim in Usd.PrimRange(root_prim):
print(prim.GetPath())

Manipulating the Camera

There is a great utility class to manipulate the camera in the omni.kit.viewport.utility extension (Thank you Thomas for finding out):
\omni\kit\viewport\utility\camera_state.py

Physics

Useful scripts & utilities

There are a lot of usefull methods in the utils scripts in the physx extension, great learning resource:

app\kit\extsPhysics\omni.physx-1.4.7–5.1\omni\physx\scripts

Movie Capture
when rendering to movie set FPS to 60 given that physics scene has 60 steps per second, otherwise the simulation is not in sync with rest of animation (e.g. Camera) when rendering to movie

ffmpeg -r 60 -f image2 -s 1920x1080 -i pic%04d.png -vcodec libx264 -crf 25  -pix_fmt yuv420p test.mp4

Behavior Scripts

here is some information about behavior scripts used to check collisions

https://www.youtube.com/watch?v=YLEjYHzs2Qo

Layers

The LayerUtils module provides lots of nice methods when working with layers:

https://docs.omniverse.nvidia.com/kit/docs/omni.kit.usd.layers/latest/omni.kit.usd.layers/omni.kit.usd.layers.LayerUtils.html

Payloads

Loading a file & creating a new stage in KIT

context = omni.usd.get_context()# load file 
context.open_stage(“path_to_stage.usd”)
# new
context.new_stage()
# get stage
stage = context.get_stage()

Using AssetConverter

AssetConverter is an extension, so make sure to add dependency in extension.toml to get it loaded with your extension:

[dependencies]
“omni.kit.asset_converter” = {}

see https://docs.omniverse.nvidia.com/extensions/latest/ext_asset-converter.html

OmniClient Authentication (C++)

If you get connection errors your cached authentication token may have expired — signing out deletes the locally cached token, reconnect should work again.

omniClientSignOut(url); // <- also clears local token cache omniClientReconnect(url);

setting prims visibility(python)

TfToken is set with string from python :

cube_prim.GetAttribute(“visibility”).Set(“invisible”)

Thank you Karo and Thomas !

getting the extension script directory

SCRIPT_ROOT = os.path.dirname(os.path.realpath(__file__))

getting other extensions directory

extmgr = omni.kit.app.get_app().get_extension_manager()
ext_path = extmgr.get_extension_path_by_module("my.module.name")

getting the bounding box of a prim

# Method 1 with Imageable 
UsdGeom.Imageable(prim).ComputeWorldBound(
Usd.TimeCode(1), purpose1="default"))
If you need to compute bounds for multiple prims on a stage, it will be much, much more efficient to instantiate a UsdGeomBBoxCache and query it directly; doing so will reuse sub-computations shared by the prims:

# Method 2: using cache
cache = UsdGeom.BBoxCache(
Usd.TimeCode.Default(), ["default", "render"])
cache.ComputeWorldBound(prim)

from (https://github.com/ColinKennedy/USD-Cookbook/blob/master/features/world_bounding_box/python/bounding_box.py)

omni.ui — User Interface

FormDialog — easy data input with omni.kit.window.popup_dialog

If you need to create popup dialog widgets e.g. to display and change data there are a very convenient classes in omni.kit.window.popup_dialog (documentation) to perform that with a few lines of code:

sample form dialog
from omni.kit.window.popup_dialog import FormDialog

# handle when ok is pressed:
def _on_settings_ok(self, dialog: FormDialog):
values = dialog.get_values()
self._bom_path = values["bom"]
# more getting values ...
self._setting_dialog.hide()

# build the dialog just by adding field_defs
def _build_settings_dialog(self) -> FormDialog:

field_defs = [
FormDialog.FieldDef("bom", "BOM: ", ui.StringField, self._bom_path),
FormDialog.FieldDef("templates", "Templates: ", ui.StringField, self._templates_path),
FormDialog.FieldDef("int", "Integer: ", ui.IntField, 1),
FormDialog.FieldDef("float", "Float: ", ui.FloatField, 2.0),
FormDialog.FieldDef(
"tuple", "Tuple: ", lambda **kwargs: ui.MultiFloatField(column_count=3, h_spacing=2, **kwargs), None
),
FormDialog.FieldDef("slider", "Slider: ", lambda **kwargs: ui.FloatSlider(min=0, max=10, **kwargs), 3.5),
FormDialog.FieldDef("bool", "Boolean: ", ui.CheckBox, True),

]
dialog = FormDialog(
title="Settings",
message="Please specify the following paths:",
field_defs=field_defs,
ok_handler=self._on_settings_ok,
)
# show it
dlg = self._build_settings_dialog()
dlg.show()

# see also example in create...\omni\kit\window\popup_dialog\tests\test_form_dialog.py

ComboBox updating

Matti has a nice video explaining how to update a combobox :

Window refresh manually

To refresh the UI manually you can call Frame.rebuild():

# given that it was built like this:
self._window = ui.Window("MyTestWindow")
#...
# refresh
self._window.frame.rebuild()

KIT settings

you can store app settings in config/extensions.toml like this:

[settings]
mysetting=’myval’

retrieve the value with this:

settings = carb.settings.get_settings()
result=settings.get('mysetting')

KIT focus on selection

def focus_prim(self):
# omni.kit.viewport_legacy is optional dependancy
try:
import omni.kit.viewport_legacy
viewport = omni.kit.viewport_legacy.get_viewport_interface()
if viewport:
viewport.get_viewport_window().focus_on_selected()
except:
pass

Finding sample source in KIT app directory with VS-Code

right click on “>app”, select “search in folder…” then you can search all the good source code of the NVIDIA superbrains :-)

useful python snippets in KIT

The KIT “Create” sample application contains useful snippets, I’ve created a github repo with all the files for better accessability:

https://github.com/perfectproducts/kit_snippets.git

adding KIT extensions from git repository

To add extensions directly from github to your Omniverse app:

1. Go into: Extension Manager -> Gear Icon -> Extension Search Path

2. Add this as a search path:

git://github.com/[repository-name]?branch=main&dir=exts

e.g. : `git://github.com/NVIDIA-Omniverse/kit-extension-sample-ui-scene?branch=main&dir=exts`

Getting scene statistics like triangle count

In order to get information about your scene in USD Composer open Window->Utilities->Statistics and select “RTX Scene” as Scope in the dropdown.

scene statistics include triangle count

Show file in content browser

To select a given file in the Content Browser window use this snippe

from omni.kit.window.content_browser import get_content_window
content_browser = get_content_window()
content_browser.navigate_to(path)

Setting Attributes with Sdf.ValueTypeNames

To set attributes on Prims you need the correct TypeName, here’s a list:

prim.CreateAttribute(“name”, Sdf.ValueTypeNames.Double3, False).Set(Gf.Vec3f(0,1,2))

https://openusd.org/dev/api/_usd__page__datatypes.html

more resources & cheat sheets:

my new favorite:

Tips & Tricks — Usd Survival Guide (lucascheller.github.io)

my favorite:

nice snippets

https://github.com/ColinKennedy/USD-Cookbook/

in japanese:

great example for scene-api Overlay in extensions from nvidia’s Mati :
https://github.com/mati-nvidia/scene-api-sample

Sample Code from nvidia Omniverse github repo:

Excellent collection with Python Code snippets :

Insightful tutorial on data centric product configurator workflow:

Data Structure Tutorials — Omniverse Workflows latest documentation (nvidia.com)

What else

Detach any KIT window to move to separate screen

right click on title

You can detach any window in KIT by clicking with the right mouse button on the window titlebar.

--

--

Michael T. Wagner
Michael T. Wagner

Written by Michael T. Wagner

CTO and Co-Founder @ipolog.ai & synctwin.ai, creating clever solutions for smart factory

Responses (3)