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'] ... )
- 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 inpoint_data['openusd_rgb']. AuthoreddisplayColoris used when available; otherwise points are colored red. Coordinates are returned in the USD stage coordinate system.- Parameters:
- Return type:
PolyData- Returns:
A merged PyVista
PolyDatacontaining all mesh prims underprim_path.- Raises:
FileNotFoundError – If
usd_filedoes not exist.ValueError – If the stage, prim path, or mesh geometry is invalid.
- 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:
- Return type:
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:
- Return type:
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:
- Return type:
- 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:
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:
- 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:
ValueError – If mesh or primvar not found
ImportError – If matplotlib is not available
- 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