USD Tools

Core utilities for USD file creation and manipulation.

Module Reference

This module contains the USDTools class for manipulating USD objects and files.

This module provides utilities for working with Universal Scene Description (USD) files in the context of medical visualization. It includes functions for merging USD files, arranging objects in grids, computing bounding boxes, and preserving materials and animations for visualization in NVIDIA Omniverse.

The tools are specifically designed for medical imaging workflows where multiple anatomical structures need to be organized and visualized together.

class physiomotion4d.usd_tools.USDTools(log_level=20)[source]

Utilities for manipulating Universal Scene Description (USD) files.

This class provides tools for working with USD files in medical visualization contexts, including merging multiple USD files, arranging objects in spatial grids, computing bounding boxes, and preserving materials and animations.

USD (Universal Scene Description) is the foundation for 3D content in NVIDIA Omniverse and other modern 3D pipelines. This class facilitates the creation of complex medical visualizations by organizing anatomical structures from multiple sources.

Key capabilities: - Merge multiple USD files while preserving hierarchy and materials - Arrange objects in spatial grids for comparison or overview - Compute bounding boxes for spatial layout - Preserve time-varying animation data - Handle material bindings and shader networks

The class is designed to work with USD files generated from medical imaging data, particularly anatomical structures extracted from CT and MR images.

Example

>>> usd_tools = USDTools()
>>> # Merge multiple anatomical USD files
>>> usd_tools.merge_usd_files(
...     'combined_anatomy.usd', ['heart.usd', 'lungs.usd', 'bones.usd']
... )
>>> # Create grid arrangement for comparison
>>> usd_tools.save_usd_file_arrangement(
...     'comparison_grid.usd', ['patient1.usd', 'patient2.usd', 'patient3.usd']
... )
__init__(log_level=20)[source]

Initialize the USDTools class.

Parameters:

log_level (int | str) – Logging level (default: logging.INFO)

load_usd_as_vtk(usd_file, prim_path='/World', time_code=None)[source]

Load USD mesh geometry as a PyVista PolyData.

Evaluates mesh points at time_code, applies each mesh prim’s local-to-world transform, and stores RGB colors in point_data['openusd_rgb']. Authored displayColor is used when available; otherwise points are colored red. Coordinates are returned in the USD stage coordinate system.

Parameters:
  • usd_file (str | Path) – Path to a USD file.

  • prim_path (str) – Root prim path to traverse. Defaults to /World.

  • time_code (float | None) – Optional time code for animated meshes. None reads default values and falls back to the first authored point time sample.

Return type:

PolyData

Returns:

A merged PyVista PolyData containing all mesh prims under prim_path.

Raises:
get_subtree_bounding_box(prim)[source]

Compute the axis-aligned bounding box of a USD primitive subtree.

Recursively traverses a USD primitive hierarchy and computes the combined bounding box of all mesh geometry within the subtree. This is useful for spatial layout and positioning operations.

Parameters:

prim (UsdGeom.Xform) – The root primitive of the subtree to analyze. Should be a UsdGeom.Xform or similar transformable primitive

Returns:

Tuple containing:
  • bbox_min: Minimum corner of the bounding box

  • bbox_max: Maximum corner of the bounding box

Return type:

tuple[Gf.Vec3f, Gf.Vec3f]

Example

>>> stage = Usd.Stage.Open('anatomy.usd')
>>> heart_prim = stage.GetPrimAtPath('/World/Heart')
>>> bbox_min, bbox_max = usd_tools.get_subtree_bounding_box(heart_prim)
>>> center = (bbox_min + bbox_max) / 2
save_usd_file_arrangement(new_stage_name, usd_file_names)[source]

Create a spatial grid arrangement of objects from multiple USD files.

Takes a list of USD files and arranges them in a regular grid pattern for comparison or overview visualization. Each USD file is referenced into the new stage and positioned to avoid overlap. This is useful for comparing anatomical structures from different patients or time points.

The grid layout is automatically computed based on the number of input files, creating approximately square arrangements. Objects are centered at their computed positions and spaced to avoid overlap.

Parameters:
  • new_stage_name (str) – Path for the output USD file containing the arranged objects

  • usd_file_names (list[str]) – List of paths to USD files to arrange. Each file should contain anatomical structures under /World

Return type:

None

Note

The method preserves material bindings from the source files and applies spatial transforms to position objects in the grid. The first USD file in the list is used as the template for the new stage structure.

Example

>>> # Create comparison grid of cardiac models
>>> usd_tools.save_usd_file_arrangement(
...     'cardiac_comparison.usd',
...     [
...         'patient_001_heart.usd',
...         'patient_002_heart.usd',
...         'patient_003_heart.usd',
...         'patient_004_heart.usd',
...     ],
... )
merge_usd_files(output_filename, input_filenames_list)[source]

Merge multiple USD files into a single comprehensive USD file.

Combines multiple USD files while preserving all essential data including object hierarchies, transforms, materials, shaders, and time-varying animation data. This is useful for creating complete anatomical scenes from individually processed structures.

The merging process: 1. Creates a new USD stage with proper metadata 2. Copies all primitive hierarchies from input files 3. Preserves all attributes including time-sampled data 4. Maintains material bindings and shader networks 5. Handles coordinate system and units consistently

Parameters:
  • output_filename (str) – Path for the merged output USD file. Should have .usd or .usda extension

  • input_filenames_list (list[str]) – List of input USD file paths to merge. Each should be a valid USD file with compatible coordinate systems

Return type:

None

Note

The merged file stores coordinates in meters (metersPerUnit=1.0) with upAxis=”Y”, which are standard for Omniverse. Time-varying data (animations) are preserved across all time samples.

Example

>>> # Merge anatomical components into complete scene
>>> usd_tools.merge_usd_files(
...     'complete_anatomy.usd', ['heart_dynamic.usd', 'lungs_static.usd', 'skeleton.usd']
... )
merge_usd_files_flattened(output_filename, input_filenames_list)[source]

Merge multiple USD files using references and flattening.

This method uses USD’s native composition system (references) and then flattens the result into a self-contained file. This approach is simpler (~50 lines vs ~150 lines) and leverages USD’s built-in composition engine.

The method properly preserves:
  • All materials and MDL shader networks

  • Time-varying animation data with correct time codes

  • Material bindings to geometry

  • Stage metadata (TimeCodesPerSecond, time range, etc.)

Parameters:
  • output_filename (str) – Path for the merged output USD file. Should have .usd or .usda extension

  • input_filenames_list (list[str]) – List of input USD file paths to merge. Each should be a valid USD file with compatible coordinate systems

Return type:

None

Comparison to merge_usd_files():
  • merge_usd_files(): More control, can skip specific attributes

  • merge_usd_files_flattened(): Simpler, faster, USD-native approach

Both methods produce equivalent results for most use cases. Use the flattened method unless you need fine-grained control over what gets copied.

Example

>>> usd_tools = USDTools()
>>> usd_tools.merge_usd_files_flattened(
...     'complete_anatomy.usd', ['heart_dynamic.usd', 'lungs_static.usd']
... )
list_mesh_primvars(stage_or_path, mesh_path, time_code=None)[source]

List all primvars on a USD mesh with metadata.

Inspects a mesh and returns information about each primvar including name, type, interpolation, time samples, and value range when feasible. This is useful for understanding what simulation data is available on the mesh for visualization.

Parameters:
  • stage_or_path (Usd.Stage | str) – USD Stage or path to USD file

  • mesh_path (str) – Path to mesh prim (e.g., “/World/Meshes/MyMesh”)

  • time_code (float | None) – Optional time code to sample values. If None, uses default.

Returns:

List of primvar metadata dictionaries containing:
  • name: Primvar name

  • type_name: USD type name (e.g., “float[]”, “color3f[]”)

  • interpolation: Interpolation mode (“vertex”, “uniform”, “constant”)

  • num_time_samples: Number of time samples (0 if static)

  • elements: Number of elements in the array

  • range: Tuple (min, max) for numeric arrays, None otherwise

Return type:

list[dict]

Example

>>> usd_tools = USDTools()
>>> primvars = usd_tools.list_mesh_primvars("valve.usd", "/World/Meshes/Valve")
>>> for pv in primvars:
...     print(f"{pv['name']}: {pv['interpolation']}, {pv['elements']} elements")
pick_color_primvar(primvar_infos, keywords=('strain', 'stress'))[source]

Select a primvar for coloring based on keywords and preferences.

Examines a list of primvar metadata and picks the best candidate for default coloring visualization. Prefers primvars containing keywords like “strain” or “stress” that are commonly used in biomechanical simulations.

Selection priority: 1. Name contains first keyword (“strain”) over later keywords (“stress”) 2. Vertex interpolation preferred over uniform (face) interpolation 3. Alphabetically first if multiple candidates tie

Parameters:
  • primvar_infos (list[dict[str, Any]]) – List of primvar metadata dicts (from list_mesh_primvars)

  • keywords (tuple[str, ...]) – Tuple of keywords to search for in primvar names (case-insensitive)

Returns:

Name of selected primvar, or None if no candidates found

Return type:

str | None

Example

>>> primvars = usd_tools.list_mesh_primvars("valve.usd", "/World/Meshes/Valve")
>>> color_primvar = usd_tools.pick_color_primvar(primvars)
>>> print(f"Selected for coloring: {color_primvar}")
apply_colormap_from_primvar(stage_or_path, mesh_path, source_primvar, *, cmap='viridis', time_codes=None, intensity_range=None, use_sigmoid_scale=False, write_default_at_t0=True, bind_vertex_color_material=True)[source]

Apply colormap visualization by converting a primvar to displayColor.

Reads numeric data from a source primvar (like vtk_cell_stress or vtk_point_displacement) and generates RGB vertex colors using a matplotlib colormap. Writes these colors to the mesh’s displayColor primvar and optionally binds a material that uses vertex colors for rendering.

This is especially useful for post-processing USD files to add default visualization colors based on simulation data like stress or strain fields.

Key features:

  • Handles multi-component data (vectors/tensors) by computing magnitude

  • Converts uniform (per-face) data to vertex data by averaging

  • Computes global value range across all time samples for consistent coloring (or uses intensity_range when provided)

  • Writes both default and time-sampled displayColor for Omniverse compatibility

Parameters:
  • stage_or_path (Usd.Stage | str) – USD Stage or path to USD file

  • mesh_path (str) – Path to mesh prim (e.g., “/World/Meshes/MyMesh”)

  • source_primvar (str) – Name of primvar to visualize (e.g., “vtk_cell_stress”)

  • cmap (str) – Matplotlib colormap name (default: “viridis”)

  • time_codes (list[float] | None) – List of time codes to process. If None, uses stage time range.

  • intensity_range (tuple[float, float] | None) – Optional (vmin, vmax) for colormap. If None, computed from data.

  • use_sigmoid_scale (bool) – If True, use sigmoid scale for colormap normalization.

  • write_default_at_t0 (bool) – If True, also write default value at t=0

  • bind_vertex_color_material (bool) – If True, create/bind material using displayColor

Raises:
Return type:

None

Example

>>> usd_tools = USDTools()
>>> usd_tools.apply_colormap_from_primvar(
...     "valve.usd",
...     "/World/Meshes/Valve",
...     "vtk_cell_stress",
...     cmap="plasma"
... )
set_solid_display_color(stage_or_path, mesh_path, color, *, time_codes=None, bind_vertex_color_material=True)[source]

Set a constant (solid) displayColor for a mesh.

Fills the mesh’s displayColor primvar with the same RGB for every vertex, optionally at each time code for animated meshes, and binds the vertex color material so the color is visible in Omniverse.

Parameters:
  • stage_or_path (Usd.Stage | str) – USD Stage or path to USD file

  • mesh_path (str) – Path to mesh prim (e.g., “/World/Meshes/MyMesh”)

  • color (tuple[float, float, float]) – RGB tuple in [0, 1] (e.g., (1, 0, 0) for red)

  • time_codes (list[float] | None) – If provided, set displayColor at each time. If None, set default only.

  • bind_vertex_color_material (bool) – If True, bind material that uses displayColor

Return type:

None

list_mesh_paths_under(stage_or_path, parent_path='/World/Meshes')[source]

List paths of all mesh prims under a parent path.

Parameters:
  • stage_or_path (Usd.Stage | str) – USD Stage or path to USD file

  • parent_path (str) – Parent prim path (default: /World/Meshes)

Return type:

list[str]

Returns:

List of mesh prim paths (e.g. [“/World/Meshes/Mesh0”, “/World/Meshes/Mesh1”])

repair_mesh_primvar_element_sizes(stage_or_path, mesh_path, *, time_code=None, save=True)[source]

Repair missing/incorrect primvar elementSize metadata for a mesh.

Some multi-component primvars (e.g. 9-component stress tensors) may be authored as a flat array (float[]) but require primvar elementSize > 1 so that viewers interpret them as tuples-per-point rather than extra points. This can prevent Omniverse/Hydra crashes during animation evaluation.

Heuristic: - For vertex primvars: infer elementSize if raw_len % n_points == 0 - For uniform primvars: infer elementSize if raw_len % n_faces == 0 - Only updates when inferred elementSize > 1

Returns:

updated (list), skipped (list)

Return type:

dict with keys

Navigation

USD Generation Modules | USD Anatomy Tools | VTK to USD Conversion