Omniverse KIT cheat sheet
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)
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”
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:
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:
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)
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:
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.
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
You can detach any window in KIT by clicking with the right mouse button on the window titlebar.