I've been using Pixar's USD in a CG pipeline for over a year now. Here's some helpful things I've noted.
Most of these are following are python snippets. The function interface is mostly the same with the C++ API with some noticeable language differences:
- Wrap values with
pxr::VtValue(10.0f)
pxr::VtValue("Value")
- Paths with
pxr::SdfPath("/path")
Code | |
---|---|
memory | stage = Usd.Stage.CreateInMemory() |
file | stage = Usd.Stage.CreateNew("/path/to/stage.usda") |
Code | |
---|---|
comment | stage.SetMetadata('comment', 'This will be the comment') |
documentation | stage.SetMetadata('documentation', 'This will be the documentation') |
documentation | stage.GetPseduoRoot().SetDocumentation('This will be the documentation") |
renderSettingsPrimPath | stage.SetMetadata('renderSettingsPrimPath', '/RenderSettings/Prim') |
Interesting note is that there is no current API to set render settings prim path except explicitly via token...
Code | Docs | |
---|---|---|
save | stage.GetRootLayer().Save() |
docs |
save as | stage.Export( "/path/to/output.usda" ) |
docs |
print( stage.ExportToString() ) |
docs |
To traverse recursively through every prim:
Python
for prim in stage.Traverse():
print(prim)
C++
for (pxr::UsdPrim prim: usd_stage->Traverse()) {
std::cout << "Prim path: " << prim.GetPath().GetText() << '\n';
}
or
pxr::UsdPrimRange prim_range = usd_stage->Traverse();
for (auto it = prim_range.begin(); it!=prim_range.end(); ++it) {
std::cout << "Found prim: " << it->GetPath().GetText() << '\n';
}
C++
for (const pxr::UsdPrim &childPrim : prim.GetChildren()) {
// Do something...
}
Layers are core part of USD composition. A usd file can have "SubLayers". A Stage can have multiple "Layers". Both "In Memory Layers" and "On Disk Layers".
There is not as nice of an API for layers, and it actaully differs whether in Python or C++. (Link)[https://groups.google.com/g/usd-interest/c/usUeP1USk04/m/ENTOmMsBBwAJ]
Code | Docs | Tips | |
---|---|---|---|
add sublayer | stage.GetRootLayer().subLayerPaths.append(usd_file) |
(python) | |
get layer stack | stage.GetLayerStack() |
(python) |
Code | Docs | Tips | |
---|---|---|---|
define | stage.DefinePrim("/primpath") |
docs | Gets or creates prim at primpath. To transform, prim must be an xform (or sub type) |
exists | stage.GetPrimAtPath("/primpath") |
docs | Return prim object. Can be invalid (Check with IsValid) |
valid | prim.IsValid() |
docs | False if not valid (Doesn't exist) |
attribute | prim.GetAttribute('attribName') |
docs | Use .Get() and .Set(val) |
type (string) | prim.GetTypeName() |
docs | |
prim path | prim.GetPath() |
docs | Explicitly as C++ String prim.GetPath().GetString() |
prim name | prim.GetName() |
docs | Explicitly as C++ String prim.GetName().GetString() |
USD Tokens are a handle for a registered string. Basically all strings must be tokens. In python these are usually converted automagically.
In order to "sanitize" a string to be a valid usd token you can use this function: docs
Python
from pxr import Tf
token = Tf.MakeValidIdentifier("some.illegal/string")
C++
TfToken token = TfMakeValidIdentifier("some.illegal/string");
Note:
Value Types for C++: pxr::SdfValueTypeNames->String
Value Types for Python: Sdf.ValueTypeNames.String
For userProperties, append userProperties:
to attribute name.
Code | Docs | Tips | |
---|---|---|---|
create attribute | prim.CreateAttribute(name, Sdf.ValueTypeNames.Int, isCustom) |
docs | |
get attribute | prim.GetAttribute(name) |
docs | |
set attribute (value) | attribute.Set() |
docs | In C++ you need to use the specific template Set<float>(0.5f) |
get attribute (value) | attribute.Get() |
docs | In C++ you need to use a template Get<float>() |
is time varying | attribute.ValueMightBeTimeVarying() |
docs | Return true if value might change. If false, it's certain that this value remains constant. |
The idea of composition is one of the key parts of what makes USD so flexible. There are a few core ways to compose a USD stage.
USD References can either be prepended or appended. Combined with this, the reference's order in said list is used in stage composition.
Code | Docs | Tips | |
---|---|---|---|
Add Reference | prim.GetReferences().AddReference("C:/file/on/disk.usd") |
docs | Adds a reference to back of prepend list |
Add Reference (specific) | prim.GetReferences().AddReference("C:/file/on/disk.usd", position = Usd.ListPositionFrontOfAppendList) |
docs | Adds a reference to back of prepend list |
Get References | prim.GetPrimStack() |
docs | There is no proper way yet to retrieve a list of references. This will return the composed stack, it is not recommended. |
from pxr import UsdGeom
Code | Docs | Tips | |
---|---|---|---|
define xform | xform = stage.DefinePrim("/primpath", "Xform") |
docs | Gets or creates an xform prim at primpath. To transform a prim, it must be an Xform or child type |
define xform | xform = UsdGeom.Xform.Define(stage, "/primpath") |
docs | Same as above. |
set position | UsdGeom.XformCommonAPI(xform).SetTranslate(Gf.Vec3d(x, y, z), timecode) |
docs | |
set rotation (euler) | UsdGeom.XformCommonAPI(xform).SetRotate(Gf.Vec3d(x, y, z), timecode) |
docs | Degrees |
set rotation (quaternion) | UsdGeom.XformCommonAPI(xform).SetRotate(Gf.Rotation(Gf.Vec3d(x, y, z), w), timecode) |
docs | |
get transform (local) | UsdGeom.XformCommonAPI(xform).GetXformVectors(timecode) |
docs | Returns tuple of (translation, rotation, scale, pivot, rotationOrder) |
get transform (world) | mat4 = UsdGeom.XformCache(timecode).GetLocalToWorldTransform(prim) |
docs | Returns matrix 4x4 |
set transform | xform.AddTransformOp().Set(transform_matrix) |
docs | Returns tuple of (translation, rotation, scale, pivot, rotationOrder) |
Graphics Foundations
from pxr import Gf
Code | Docs | Tips | |
---|---|---|---|
define matrix4 | mat4 = Gf.Matrix4d(2) |
docs | Creates identity matrix |
define translate matrix4 | mat4 = Gf.Matrix4d(2).SetTranslate(Gf.Vec3d(x, y, z)) |
docs | Creates translate matrix |
define per element matrix4 | mat4 = Gf.Matrix4d(2).SetTranslate(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1) |
docs | Creates translate matrix |
get translation | mat4.ExtractTranslation() |
docs | Get vec3 translation |
get rotation | mat4.ExtractRotation() |
docs | Get rotation |
get scale | scale = (mat4.GetRow3(0).GetLength(), mat4.GetRow3(1).GetLength(), mat4.GetRow3(2).GetLength()) |
Get all axis scale |
from pxr import UsdLux
Code | Docs | Tips | |
---|---|---|---|
define light | light_prim = stage.DefinePrim("/primpath", "DomeLight") |
docs | This will return a prim object, no light methods attached |
access light | light = UsdLux.DomeLight(light_prim) |
docs | |
set dome texture | dome_light.CreateTextureFileAttr().Set("/path/to/texture.exr") |
docs |
from pxr import UsdShade
Code | Docs | Tips | |
---|---|---|---|
get shader from prim | shader = UsdShade.Shader(prim) |
docs | This will return a shader object |
get filename | shader.GetInput("filename").Get() |
docs | Gets the texture filename string (Has '@' at start and end. |
set existing filename | shader.GetInput("filename").Set("C:/new_path.png") |
docs |
Sometimes you may need to zip a usd scene to a .usdz without running the provided usdzip command line tool. Turns out this is easy.
from pxr import Sdf, UsdUtils
UsdUtils.CreateNewARKitUsdzPackage(Sdf.AssetPath(usd_path), usdzip_path)
# or
UsdUtils.CreateNewUsdzPackage(Sdf.AssetPath(usd_path), usdzip_path)
- Most API functions are CamelCase.
Source: usd/match_size.py
These are somethings that got me when working with USDZips, particularly on iOS quicklook.
- If there are multiple root prims and no default prim, iOS quicklook freaks out. Add a defaultPrim to the stage
- "File size is too large" I've found usdzips work best when small.
- Beware large textures. The usdzip can be small but the texture budget could be 100x as much...
- If a texture path has an invalid path it can bundle in empty 1kb files with no extension
- Too many (Any?) of these can sometimes cause quicklook to not display it at all
- Scene size is important, as is the sceneUnits layer property
- USD Apple Schemas: https://developer.apple.com/documentation/arkit/usdz_schemas_for_ar
There are some hidden URL parameters when linking to usdz from the web
- #allowsContentScaling=0 stops use scaling (https://developer.apple.com/forums/thread/124558)
- Call to action: https://developer.apple.com/documentation/arkit/adding_an_apple_pay_button_or_a_custom_action_in_ar_quick_look