Add and transform USD assets in python

I am new to the Omniverse/USD world and after some experimenting, I’ve written a python script that generates a USD file that mimics the layout of an area using cubes. I’m happy with the positioning but I’d like to swap the cubes out for a 3D model that we have in a USD file. The cubes get translated, have the size set, rotated, and scaled. Here is the generic version of the method I’m using to create the cubes. How do I update this to use my 3d model?

def create_cube(stage, input_json):
    # Create cube
    cubePrim = UsdGeom.Cube.Define(stage, f'/world/cube_{name}')
    # Move cube (translate)
    centerX = input_json['CenterX']
    centerY = input_json['CenterY'] * - 1
    cubePrim.AddTranslateOp().Set(value=(centerX, 0, centerY))
    # Set size of cube
    inchesInMeter = 39.3701
    # Recalculate extent of cube
    extentAttr = cubePrim.GetExtentAttr()
    extentAttr.Set(extentAttr.Get() * inchesInMeter)
    # Rotate
    rotationAngle = calculate_rotation(input_json)
    cubePrim.AddRotateXYZOp().Set(Gf.Vec3d(0, rotationAngle, 0))
    # Change length, width, height of cube (scale)
    length = input_json['LengthX'] / inchesInMeter
    width = input_json['WidthY'] / inchesInMeter
    height = input_json['HeightZ'] / inchesInMeter
    cubePrim.AddScaleOp().Set((length, height, width))

After some digging, I’ve found some code to add a reference as an xform but xforms don’t have the same methods as the cube prim. I’ve only figured out how to translate the model so far

stage = Usd.Stage.CreateNew('AddReference.usda')
prim = stage.DefinePrim("/world/new", "Xform")

transformable = UsdGeom.Xformable(prim)
centerX = 10
centerY = 12
transformable.AddTranslateOp().Set(value=(centerX, 0, centerY))

Hi @archer-kgraves. Looks like you’re on the right track. Which methods are you needing? You can calculate the bounding box of your asset with this: Compute the Bounding Box for a Prim — Omniverse Kit documentation

I’m trying to figure out how to do the following commands, but on the referenced asset instead of the cube
cubePrim.GetExtentAttr().Set(extentAttr.Get() * size)
cubePrim.AddRotateXYZOp().Set(Gf.Vec3d(0, rotationAngle, 0))
cubePrim.AddScaleOp().Set((length, height, width))

I actually realized that I can transform, scale, and rotate the referenced model (code was breaking before I got there :) ). What is left is figuring out how to set the size and the extents.

from pxr import Gf, Usd, UsdGeom, UsdLux, Sdf

stage = Usd.Stage.CreateNew('AddReference.usda')
itemPrim = stage.DefinePrim("/world/item", "Xform")
    assetPath = 'MyFile.usda',
    primPath = '/World/MyModel')

xformable = UsdGeom.Xformable(itemPrim)
# Clear transforms

# Move cube (translate)
xformable.AddTranslateOp().Set(value=(10, 0, 12))

# Rotate
xformable.AddRotateXYZOp().Set(Gf.Vec3d(0, 1, 0))

# Scale
xformable.AddScaleOp().Set((5, 10, 5))

Ah, ok. I realize you figured some of this out before I finished, but I’ll post the whole thing for completeness. :)

  1. Size is specific to the Cube prim. To resize or scale your asset, you should do it with “xformOp:scale” like your last line.
  2. Extent is an attribute that is only available to UsdGeomBoundable prims. Xforms do not inherit from that. Instead, you can calculate the bbox for you xform and all of its descendants using the snippet I provided: Compute the Bounding Box for a Prim — Omniverse Kit documentation. You can use the UsdGeomModelAPI to cache that computed bbox for an asset if that makes sense for your pipeline. These are known as extent hints: Universal Scene Description: UsdGeomModelAPI Class Reference I wouldn’t worry to much about setting the extents as you’re prototyping, but definitely worth understanding and authoring it as you scale up your pipeline.
  3. Both the AddRotateXYZOp and AddScaleOp should be available to you on the UsdGeomXformable object you’re using: Universal Scene Description: UsdGeomXformable Class Reference

Awesome, thank you! That helps a lot

I’m going to need many copies of the 3d model. In my code, I’m calling the create method in a loop that will be executed 100s of times. Should I add the reference each time (.GetReferences().AddReference()) or should I add it once and then somehow refer to that in the loop? My code is currently adding the reference repeatedly but I’m wondering if that’s necessary/good practice.

Yes, that’s the correct way. By adding a reference to a prim, you can think of it as though you’re saying, “This prim uses this blueprint”. And the blueprint may just give the prim a type and set some properties or it might add some additional children prims to it. USD isn’t fetching the referenced later multiple times if that’s what you’re worried about.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.