Frequently Used Python Snippets
A collection of python utility snippets that can be used in the script editor or pure python applications
Create A Physics Scene
| import omni
from pxr import Gf, Sdf, UsdPhysics
stage = omni.usd.get_context().get_stage()
# Add a physics scene prim to stage
scene = UsdPhysics.Scene.Define(stage, Sdf.Path("/World/physicsScene"))
# Set gravity vector
scene.CreateGravityDirectionAttr().Set(Gf.Vec3f(0.0, 0.0, -1.0))
scene.CreateGravityMagnitudeAttr().Set(981.0)
|
The following can be added to set specific settings, in this case use CPU physics and the TGS solver
| from pxr import PhysxSchema
PhysxSchema.PhysxSceneAPI.Apply(stage.GetPrimAtPath("/World/physicsScene"))
physxSceneAPI = PhysxSchema.PhysxSceneAPI.Get(stage, "/World/physicsScene")
physxSceneAPI.CreateEnableCCDAttr(True)
physxSceneAPI.CreateEnableStabilizationAttr(True)
physxSceneAPI.CreateEnableGPUDynamicsAttr(False)
physxSceneAPI.CreateBroadphaseTypeAttr("MBP")
physxSceneAPI.CreateSolverTypeAttr("TGS")
|
Adding a ground plane to a stage can be done via the following code:
It creates a Z up plane with a size of 100 cm at a Z coordinate of -100
| import omni
from pxr import PhysicsSchemaTools
stage = omni.usd.get_context().get_stage()
PhysicsSchemaTools.addGroundPlane(stage, "/World/groundPlane", "Z", 100, Gf.Vec3f(0, 0, -100), Gf.Vec3f(1.0))
|
Enable Physics And Collision For a Mesh
The script below assumes there is a physics scene in the stage.
| import omni
from omni.physx.scripts import utils
# Create a cube mesh in the stage
stage = omni.usd.get_context().get_stage()
result, path = omni.kit.commands.execute("CreateMeshPrimCommand", prim_type="Cube")
# Get the prim
cube_prim = stage.GetPrimAtPath("/Cube")
# Enable physics on prim
# If a tighter collision approximation is desired use convexDecomposition instead of convexHull
utils.setRigidBody(cube_prim, "convexHull", False)
|
If a tighter collision approximation is desired use convexDecomposition
| import omni
from omni.physx.scripts import utils
# Create a cube mesh in the stage
stage = omni.usd.get_context().get_stage()
result, path = omni.kit.commands.execute("CreateMeshPrimCommand", prim_type="Cube")
# Get the prim
cube_prim = stage.GetPrimAtPath("/Cube")
# Enable physics on prim
# If a tighter collision approximation is desired use convexDecomposition instead of convexHull
utils.setRigidBody(cube_prim, "convexDecomposition", False)
|
To verify that collision meshes have been successfully enabled, click the “eye” icon > “Show By Type” >
“Physics Mesh” > “All”. This will show the collision meshes as pink outlines on the objects.
Set Mass Properties for a Mesh
The snippet below shows how to set the mass of a physics object. Density can also be specified as an alternative
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | import omni
from pxr import UsdPhysics
from omni.physx.scripts import utils
stage = omni.usd.get_context().get_stage()
result, path = omni.kit.commands.execute("CreateMeshPrimCommand", prim_type="Cube")
# Get the prim
cube_prim = stage.GetPrimAtPath(path)
# Make it a rigid body
utils.setRigidBody(cube_prim, "convexHull", False)
mass_api = UsdPhysics.MassAPI.Apply(cube_prim)
mass_api.CreateMassAttr(10)
### Alternatively set the density
mass_api.CreateDensityAttr(1000)
|
Traverse a stage and assign collision meshes to children
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39 | import omni
from pxr import Usd, UsdGeom, Gf
from omni.physx.scripts import utils
stage = omni.usd.get_context().get_stage()
def add_cube(stage, path, size: float = 10, offset: Gf.Vec3d = Gf.Vec3d(0, 0, 0)):
cubeGeom = UsdGeom.Cube.Define(stage, path)
cubeGeom.CreateSizeAttr(size)
cubeGeom.AddTranslateOp().Set(offset)
### The following prims are added for illustrative purposes
result, path = omni.kit.commands.execute("CreateMeshPrimCommand", prim_type="Torus")
# all prims under AddCollision will get collisions assigned
add_cube(stage, "/World/Cube_0", offset=Gf.Vec3d(100, 100, 0))
# create a prim nested under without a parent
add_cube(stage, "/World/Nested/Cube", offset=Gf.Vec3d(100, 0, 100))
###
# Traverse all prims in the stage starting at this path
curr_prim = stage.GetPrimAtPath("/")
for prim in Usd.PrimRange(curr_prim):
# only process shapes and meshes
if (
prim.IsA(UsdGeom.Cylinder)
or prim.IsA(UsdGeom.Capsule)
or prim.IsA(UsdGeom.Cone)
or prim.IsA(UsdGeom.Sphere)
or prim.IsA(UsdGeom.Cube)
):
# use a ConvexHull for regular prims
utils.setCollider(prim, approximationShape="convexHull")
elif prim.IsA(UsdGeom.Mesh):
# "None" will use the base triangle mesh if available
# Can also use "convexDecomposition", "convexHull", "boundingSphere", "boundingCube"
utils.setCollider(prim, approximationShape="None")
pass
pass
|
Do Overlap Test
These snippets detect and report when objects overlap with a specified cubic/spherical region.
The following is assumed: the stage contains a physics scene, all objects have collision meshes enabled,
and the play button has been clicked.
The parameters: extent, origin and rotation (or origin and radius) define the cubic/spherical region to check overlap against.
The output of the physX query is the number of objects that overlaps with this cubic/spherical region.
| def check_overlap_box(self):
# Defines a cubic region to check overlap with
extent = carb.Float3(20.0, 20.0, 20.0)
origin = carb.Float3(0.0, 0.0, 0.0)
rotation = carb.Float4(0.0, 0.0, 1.0, 0.0)
# physX query to detect number of hits for a cubic region
numHits = get_physx_scene_query_interface().overlap_box(extent, origin, rotation, self.report_hit, False)
# physX query to detect number of hits for a spherical region
# numHits = get_physx_scene_query_interface().overlap_sphere(radius, origin, self.report_hit, False)
self.kit.update()
return numHits > 0:
|
| import omni.physx
from omni.physx import get_physx_scene_query_interface
from pxr import UsdGeom, Gf, Vt
import carb
def report_hit(self, hit):
# When a collision is detected, the object color changes to red.
hitColor = Vt.Vec3fArray([Gf.Vec3f(180.0 / 255.0, 16.0 / 255.0, 0.0)])
usdGeom = UsdGeom.Mesh.Get(self.stage, hit.rigid_body)
usdGeom.GetDisplayColorAttr().Set(hitColor)
return True
|
Do Raycast Test
This snippet detects the closest object that intersects with a specified ray.
The following is assumed: the stage contains a physics scene, all objects have collision meshes enabled,
and the play button has been clicked.
The parameters: origin, rayDir and distance define a ray along which a ray hit might be detected.
The output of the query can be used to access the object’s reference, and its distance from the raycast origin.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | import omni.physx
from omni.physx import get_physx_scene_query_interface
from pxr import UsdGeom
def check_raycast(self):
# Projects a raycast from 'origin', in the direction of 'rayDir', for a length of 'distance' cm
# Parameters can be replaced with real-time position and orientation data (e.g. of a camera)
origin = carb.Float3(0.0, 0.0, 0.0)
rayDir = carb.Float3(1.0, 0.0, 0.0)
distance = 100.0
# physX query to detect closest hit
hit = get_physx_scene_query_interface().raycast_closest(origin, rayDir, distance)
if(hit["hit"]):
# Change object color to yellow and record distance from origin
usdGeom = UsdGeom.Mesh.Get(self.stage, hit["rigidBody"])
hitColor = Vt.Vec3fArray([Gf.Vec3f(255.0 / 255.0, 255.0 / 255.0, 0.0)])
usdGeom.GetDisplayColorAttr().Set(hitColor)
distance = hit["distance"]
return usdGeom.GetPath().pathString, distance
return None, 10000.0
|
Creating, Modifying, Assigning Materials
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 | import omni
from pxr import UsdShade, Sdf, Gf
mtl_created_list = []
# Create a new material using OmniGlass.mdl
omni.kit.commands.execute(
"CreateAndBindMdlMaterialFromLibrary",
mdl_name="OmniGlass.mdl",
mtl_name="OmniGlass",
mtl_created_list=mtl_created_list,
)
# Get reference to created material
stage = omni.usd.get_context().get_stage()
mtl_prim = stage.GetPrimAtPath(mtl_created_list[0])
# Set material inputs, these can be determined by looking at the .mdl file
# or by selecting the Shader attached to the Material in the stage window and looking at the details panel
omni.usd.create_material_input(mtl_prim, "glass_color", Gf.Vec3f(0, 1, 0), Sdf.ValueTypeNames.Color3f)
omni.usd.create_material_input(mtl_prim, "glass_ior", 1.0, Sdf.ValueTypeNames.Float)
# Create a prim to apply the material to
result, path = omni.kit.commands.execute("CreateMeshPrimCommand", prim_type="Cube")
# Get the path to the prim
cube_prim = stage.GetPrimAtPath(path)
# Bind the material to the prim
cube_mat_shade = UsdShade.Material(mtl_prim)
UsdShade.MaterialBindingAPI(cube_prim).Bind(cube_mat_shade, UsdShade.Tokens.strongerThanDescendants)
|
Assigning a texture to a material that supports it can be done as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 | import omni
import carb
from pxr import UsdShade, Sdf
# Change the server to your Nucleus install, default is set to localhost in omni.isaac.sim.base.kit
default_server = carb.settings.get_settings().get("/isaac/nucleus/default")
mtl_created_list = []
# Create a new material using OmniPBR.mdl
omni.kit.commands.execute(
"CreateAndBindMdlMaterialFromLibrary",
mdl_name="OmniPBR.mdl",
mtl_name="OmniPBR",
mtl_created_list=mtl_created_list,
)
stage = omni.usd.get_context().get_stage()
mtl_prim = stage.GetPrimAtPath(mtl_created_list[0])
# Set material inputs, these can be determined by looking at the .mdl file
# or by selecting the Shader attached to the Material in the stage window and looking at the details panel
omni.usd.create_material_input(
mtl_prim,
"diffuse_texture",
default_server + "/Isaac/Samples/DR/Materials/Textures/marble_tile.png",
Sdf.ValueTypeNames.Asset,
)
# Create a prim to apply the material to
result, path = omni.kit.commands.execute("CreateMeshPrimCommand", prim_type="Cube")
# Get the path to the prim
cube_prim = stage.GetPrimAtPath(path)
# Bind the material to the prim
cube_mat_shade = UsdShade.Material(mtl_prim)
UsdShade.MaterialBindingAPI(cube_prim).Bind(cube_mat_shade, UsdShade.Tokens.strongerThanDescendants)
|
Align two USD prims
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 | import omni
from pxr import UsdGeom, Gf
stage = omni.usd.get_context().get_stage()
# Create a cube
result, path_a = omni.kit.commands.execute("CreateMeshPrimCommand", prim_type="Cube")
prim_a = stage.GetPrimAtPath(path_a)
# change the cube pose
xform = UsdGeom.Xformable(prim_a)
transform = xform.AddTransformOp()
mat = Gf.Matrix4d()
mat.SetTranslateOnly(Gf.Vec3d(10.0, 1.0, 1.0))
mat.SetRotateOnly(Gf.Rotation(Gf.Vec3d(0, 1, 0), 290))
transform.Set(mat)
# Create a second cube
result, path_b = omni.kit.commands.execute("CreateMeshPrimCommand", prim_type="Cube")
prim_b = stage.GetPrimAtPath(path_b)
# Get the transform of the first cube
pose = omni.usd.utils.get_world_transform_matrix(prim_a)
# Clear the transform on the second cube
xform = UsdGeom.Xformable(prim_b)
xform.ClearXformOpOrder()
# Set the pose of prim_b to that of prim_b
xform_op = xform.AddXformOp(UsdGeom.XformOp.TypeTransform, UsdGeom.XformOp.PrecisionDouble, "")
xform_op.Set(pose)
|
Save current stage to USD
This can be useful if generating a stage in python and you want to store it to reload later to debugging
| import omni
import carb
# Change server below to your nucleus install
default_server = carb.settings.get_settings().get("/isaac/nucleus/default")
# Create a prim
result, path = omni.kit.commands.execute("CreateMeshPrimCommand", prim_type="Cube")
# Change the path as needed
omni.usd.get_context().save_as_stage(default_server + "/Users/test/saved.usd", None)
|
Simple Async Task
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | import asyncio
import omni
# Async task that pauses simulation once the incoming task is complete
async def pause_sim(task):
done, pending = await asyncio.wait({task})
if task in done:
print("Waited until next frame, pausing")
omni.timeline.get_timeline_interface().pause()
# Start simulation, then wait a frame and run the pause_sim task
omni.timeline.get_timeline_interface().play()
task = asyncio.ensure_future(omni.kit.app.get_app().next_update_async())
asyncio.ensure_future(pause_sim(task))
|
Multi-Camera
The below script will create multiple viewports, set active camera, resolution for each viewport and camera positions.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 | import carb
import omni.kit
import numpy as np
import asyncio
import omni.syntheticdata._syntheticdata as gt
from PIL import Image
stage = omni.usd.get_context().get_stage()
settings_interface = carb.settings.acquire_settings_interface()
viewport_interface = omni.kit.viewport.get_viewport_interface()
# Create camera
camera_path = "/World/Camera"
camera_prim = stage.DefinePrim(camera_path, "Camera")
# Create cube
cube_path = "/World/Cube"
cube_prim = stage.DefinePrim(cube_path, "Cube")
# Create new viewport, set active camera and resolution
viewport_handle = viewport_interface.create_instance()
viewport_window = viewport_interface.get_viewport_window(viewport_handle)
viewport_window.set_active_camera(camera_path)
viewport_window.set_texture_resolution(500, 300)
viewport_window.set_window_pos(300, 500)
viewport_window.set_camera_position(camera_path, 0.0, 0.0, 50.0, True)
viewport_window.set_camera_target(camera_path, 0.0, 0.0, 0.0, True)
# Get existing viewport, set active camera
viewport_handle_2 = viewport_interface.get_instance('Viewport')
viewport_window_2 = viewport_interface.get_viewport_window(viewport_handle_2)
viewport_window_2.set_active_camera("OmniverseKit_Persp")
viewport_window_2.set_camera_position("/OmniverseKit_Persp", 50.0, 50.0, 50.0, True)
viewport_window_2.set_camera_target("/OmniverseKit_Persp", 0.0, 0.0, 0.0, True)
|
Next, to create, initialize RGB sensor, get ground truth and save to disk, append the below snippet.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 | def save_rgb(rgb_data, file_name):
rgb_image_data = np.frombuffer(rgb_data, dtype=np.uint8).reshape(*rgb_data.shape, -1)
rgb_img = Image.fromarray(rgb_image_data, "RGBA")
rgb_img.save(file_name+".png")
async def get_synthetic_data(sensor_dict):
global count
tick_count = 1
# Sensor creation
while True:
flag = 0
await omni.kit.app.get_app().next_update_async()
for sensor_name, sensor_details in sensor_dict.items():
if not sensor_details[1]:
sensor_details[1] = syntheticdata_interface.create_sensor(sensor_details[0], sensor_details[2])
flag = 1
if flag == 0:
break
tick_count = tick_count + 1
# Sensor initialization
while True:
flag = 0
await omni.kit.app.get_app().next_update_async()
for sensor_name, sensor_details in sensor_dict.items():
if not syntheticdata_interface.is_sensor_initialized(sensor_details[1]):
flag = 1
if flag == 0:
break
tick_count = tick_count + 1
# Get Sensor data
rgb_data = None
for sensor_name, sensor_details in sensor_dict.items():
if "RGB" in sensor_name:
sensor_data = syntheticdata_interface.get_sensor_host_uint32_texture_array(sensor_details[1])
save_rgb(sensor_data, sensor_name)
syntheticdata_interface = gt.acquire_syntheticdata_interface()
rgb_sensor = syntheticdata_interface.create_sensor(gt.SensorType.Rgb, viewport_window)
rgb_sensor_2 = syntheticdata_interface.create_sensor(gt.SensorType.Rgb, viewport_window_2)
sensor_dict = {
"RGB" : [gt.SensorType.Rgb, rgb_sensor, viewport_window],
"RGB2" : [gt.SensorType.Rgb, rgb_sensor, viewport_window_2]
}
asyncio.ensure_future(get_synthetic_data(sensor_dict))
|
Convert Asset to USD
The below script will convert a non-USD asset like OBJ/STL/FBX to USD.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 | import carb
import omni
import asyncio
async def convert_asset_to_usd(input_obj: str, output_usd: str):
import omni.kit.asset_converter
def progress_callback(progress, total_steps):
pass
converter_context = omni.kit.asset_converter.AssetConverterContext()
# setup converter and flags
# converter_context.ignore_material = False
# converter_context.ignore_animation = False
# converter_context.ignore_cameras = True
# converter_context.single_mesh = True
# converter_context.smooth_normals = True
# converter_context.preview_surface = False
# converter_context.support_point_instancer = False
# converter_context.embed_mdl_in_usd = False
# converter_context.use_meter_as_world_unit = True
# converter_context.create_world_as_default_root_prim = False
instance = omni.kit.asset_converter.get_instance()
task = instance.create_converter_task(input_obj, output_usd, progress_callback, converter_context)
success = await task.wait_until_finished()
if not success:
carb.log_error(task.get_status(), task.get_detailed_error())
print("converting done")
asyncio.ensure_future(
convert_asset_to_usd(
"</path/to/mesh.obj>",
"</path/to/mesh.usd>",
)
)
|
The details about the optional import options in lines 13-23 can be found here.
Get Camera Parameters
The below script show how to get the camera parameters associated with a viewport.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 | import omni
from omni.syntheticdata import helpers
import math
stage = omni.usd.get_context().get_stage()
viewport = omni.kit.viewport.get_viewport_interface()
# acquire the viewport window
viewport_handle = viewport.get_instance("Viewport")
viewport_window = viewport.get_viewport_window(viewport_handle)
# Set viewport resolution, changes will occur on next frame
viewport_window.set_texture_resolution(512, 512)
width = 512
height = 512
aspect_ratio = width / height
# get camera prim attached to viewport
camera = stage.GetPrimAtPath(viewport_window.get_active_camera())
focal_length = camera.GetAttribute("focalLength").Get()
horiz_aperture = camera.GetAttribute("horizontalAperture").Get()
vert_aperture = camera.GetAttribute("verticalAperture").Get()
# Pixels are square so we can also do:
# vert_aperture = height / width * horiz_aperture
near, far = camera.GetAttribute("clippingRange").Get()
fov = 2 * math.atan(horiz_aperture / (2 * focal_length))
# helper to compute projection matrix
proj_mat = helpers.get_projection_matrix(fov, aspect_ratio, near, far)
# compute focal point and center
focal_x = height * focal_length / vert_aperture
focal_y = width * focal_length / horiz_aperture
center_x = height * 0.5
center_y = width * 0.5
|
Get Size of a Mesh
The snippet below shows how to get the size of a mesh.
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | import omni
from pxr import Usd, UsdGeom, Gf
stage = omni.usd.get_context().get_stage()
result, path = omni.kit.commands.execute("CreateMeshPrimCommand", prim_type="Cone")
# Get the prim
prim = stage.GetPrimAtPath(path)
# Get the size
bbox_cache = UsdGeom.BBoxCache(Usd.TimeCode.Default(), includedPurposes=[UsdGeom.Tokens.default_])
bbox_cache.Clear()
prim_bbox = bbox_cache.ComputeWorldBound(prim)
prim_range = prim_bbox.ComputeAlignedRange()
prim_size = prim_range.GetSize()
print(prim_size)
|