USD Generation

PhysioMotion4D uses physiomotion4d.ConvertVTKToUSD as the application-level API for VTK-to-USD conversion. Workflows, command-line tools, and experiments should use this class instead of importing physiomotion4d.vtk_to_usd directly.

physiomotion4d.vtk_to_usd remains a public advanced low-level package for external users who need file readers, data containers, or direct USD writer primitives.

ConvertVTKToUSD

Use physiomotion4d.ConvertVTKToUSD for application-level conversion. The full API reference is in VTK to USD Conversion.

Single File

from physiomotion4d import ConvertVTKToUSD

stage = ConvertVTKToUSD.from_files(
    data_basename='Heart',
    vtk_files=['heart.vtp'],
    extract_surface=True,
).convert('heart.usd')

Time Series

from physiomotion4d import ConvertVTKToUSD

stage = ConvertVTKToUSD.from_files(
    data_basename='Heart',
    vtk_files=['heart_t0.vtp', 'heart_t1.vtp', 'heart_t2.vtp'],
    time_codes=[0.0, 1.0, 2.0],
    times_per_second=24.0,
).convert('animated_heart.usd')

In-Memory Meshes

from physiomotion4d import ConvertVTKToUSD
import pyvista as pv

meshes = [pv.read(path) for path in vtk_files]
converter = ConvertVTKToUSD(
    data_basename='CardiacModel',
    input_polydata=meshes,
    compute_normals=True,
)
stage = converter.convert('cardiac_model.usd')

Labeled Meshes with Anatomy Grouping

When the input has anatomical labels (mask_ids and a boundary_labels cell array), pass a physiomotion4d.SegmentAnatomyBase instance through the segmenter argument so labeled prims are grouped by anatomy type. The output layout becomes:

  • Meshes: /World/{basename}/{group}/{organ_name}

  • Materials: /World/Looks/{group}/{organ_name}_material

with one Xform per group (heart, lung, bone, …) that only appears if at least one labeled organ from the input falls into that group.

from physiomotion4d import (
    ConvertVTKToUSD,
    SegmentChestTotalSegmentator,
)

seg = SegmentChestTotalSegmentator()
converter = ConvertVTKToUSD(
    data_basename='Patient',
    input_polydata=meshes,
    mask_ids=seg.taxonomy.all_labels(),
    segmenter=seg,
)
stage = converter.convert('patient.usd')
# /World/Patient/heart/heart, /World/Patient/lung/lung_upper_lobe_left, ...
# /World/Looks/heart/heart_material, ...

If no segmenter is supplied, labels are grouped under a single Anatomy Xform (/World/{basename}/Anatomy/{organ_name}) so the hierarchy stays two-deep regardless.

The classification logic is delegated to the segmenter’s AnatomyTaxonomy; new groups added there flow into the prim layout without any change to ConvertVTKToUSD. See Segmentation Base Class for the taxonomy contract and Segmentation Developer Guide for how to add a new segmenter.

Colormaps

converter.set_colormap(
    color_by_array='pressure',
    colormap='viridis',
    intensity_range=(0.0, 1.0),
)
stage = converter.convert('pressure.usd')

Derived Scalars: von Mises Stress

For finite-element outputs that carry a 9-component stress tensor field (e.g. cardiac valve simulations), call compute_von_mises_stress between from_files and convert to add a scalar von_mises_stress primvar suitable for colormap rendering:

stage = (
    ConvertVTKToUSD.from_files(
        data_basename='Valve',
        vtk_files=valve_files,
        time_codes=time_codes,
        separate_by='connectivity',
    )
    .compute_von_mises_stress('stress')   # source array name in the VTK
    .convert('valve.usd')
)

The default output array name is "von_mises_stress"; the method finds the source array in either point_data or cell_data and writes the scalar back to the same data dict so the convert step picks it up as a USD primvar.

Framing Camera

Every USD stage that ConvertVTKToUSD (and the lower-level convert_vtk_file facade) writes gets a /World/Camera prim with a tight clippingRange sized to the geometry’s bounding box. This avoids the common “Omniverse Kit near-plane clips small geometry” problem for medical-scale meshes (~0.03 m wide). The camera is also baked into stages produced by TransformTools.convert_transform_to_usd_visualization and USDTools.merge_usd_files; the helper is idempotent so re-merging a USD that already has a Camera does not produce a duplicate transform op.

Anatomy Materials with USDAnatomyTools

physiomotion4d.USDAnatomyTools applies OmniSurface materials to labeled meshes after conversion. It reads AnatomyTaxonomy from the segmenter to find which prim names map to which group, and looks up the material parameters in its render_params dict (initialized from the module-level physiomotion4d.usd_anatomy_tools.DEFAULT_RENDER_PARAMS).

from physiomotion4d import USDAnatomyTools

tools = USDAnatomyTools(stage)
tools.enhance_meshes(seg)
stage.Export('painted.usd')

Add a custom anatomy look without subclassing by mutating either the module-level defaults (affects future instances) or a single instance:

from physiomotion4d.usd_anatomy_tools import DEFAULT_RENDER_PARAMS

DEFAULT_RENDER_PARAMS["brain"] = {
    "name": "Brain",
    "diffuse_reflection_color": (0.85, 0.75, 0.7),
    # ... full parameter list mirrors the existing entries ...
}

# ...or per-instance:
tools.render_params["brain"] = {...}

enhance_meshes falls back to render_params["other"] for any group without a registered entry, so newly registered groups still render — just with the generic “other” look until a dedicated entry is added.

Advanced Low-Level Facade

Use the low-level facade only when the high-level class is not appropriate:

from physiomotion4d.vtk_to_usd import convert_vtk_file

stage = convert_vtk_file('mesh.vtp', 'mesh.usd')