From 02bc4e0b70587f71faa40fa5b7eb64a288840bf4 Mon Sep 17 00:00:00 2001 From: Stephen Aylward Date: Wed, 5 Aug 2026 18:57:32 -0400 Subject: [PATCH 1/2] ENH: Clean-up docs and add lighting to USD stages --- README.md | 6 +- docs/_static/custom.css | 18 --- docs/architecture.rst | 5 - docs/cli_scripts/byod_tutorials.rst | 11 +- docs/cli_scripts/heart_gated_ct.rst | 10 +- docs/developer/usd_generation.rst | 10 +- docs/faq.rst | 7 +- docs/index.rst | 26 +-- docs/installation.rst | 3 +- docs/quickstart.rst | 21 +-- docs/troubleshooting.rst | 12 +- docs/tutorials.rst | 4 +- docs/viewing_usd.rst | 153 +++++------------- .../convert_vtk_to_usd_using_class.py | 4 +- src/physiotwin4d/vtk_to_usd/usd_utils.py | 22 ++- ...7_lung_fit_statistical_model_to_patient.py | 1 + 16 files changed, 111 insertions(+), 202 deletions(-) diff --git a/README.md b/README.md index 95d2ec72..ad4e6663 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,9 @@ PhysioTwin4D typically begins with a 3D medical image of a subject, extracts anatomic models from that image, and then uses AI surrogates to estimate the subject's physiological processes — initially focusing on cardiac and respiratory motion, and expanding to electrophysiology, blood flow, and organ perfusion. The package provides methods for forming these physiological AI surrogates and for finetuning the segmentation and registration AI methods that power them, with special emphasis on statistical shape models: they capture subject-specific characteristics that help determine subject-specific physiological function, and establish correspondence across subjects to aid AI surrogate generalization and simplify the application of traditional solvers. -> **Not validated for clinical use.** PhysioTwin4D is a research toolkit. It -> is not a medical device and must not be used for diagnosis, treatment -> planning, or clinical decision-making. +PhysioTwin4D is not validated for clinical use. It is a research and +visualization toolkit, not a medical device, and must not be used for +diagnosis, treatment planning, or clinical decision-making. ## Documentation diff --git a/docs/_static/custom.css b/docs/_static/custom.css index 445512a6..b20db8a2 100644 --- a/docs/_static/custom.css +++ b/docs/_static/custom.css @@ -338,24 +338,6 @@ dt.sig { z-index: 2; } -.pt4d-clinical-notice { - margin: -3.5rem 0 2rem; - padding: 1rem 1.25rem; - position: relative; - z-index: 3; - color: #111111; - background: #fff7d1; - border: 2px solid #8a6500; - border-left: 6px solid #8a6500; - border-radius: 8px; - box-shadow: 0 12px 28px rgba(17, 17, 17, 0.14); -} - -.pt4d-clinical-notice strong { - display: block; - margin-bottom: 0.25rem; -} - .pt4d-card, .pt4d-card:visited { display: flex; diff --git a/docs/architecture.rst b/docs/architecture.rst index b44c19f2..e7226ae6 100644 --- a/docs/architecture.rst +++ b/docs/architecture.rst @@ -8,11 +8,6 @@ personalized physiological digital twins from 3D medical images. Runtime workflow classes inherit from :class:`PhysioTwin4DBase` for logging and common runtime configuration. -.. warning:: - - PhysioTwin4D {{ pt4d_project_version }} beta is not validated for clinical - use. It is a research and visualization toolkit, not a medical device. - Data Flow ========= diff --git a/docs/cli_scripts/byod_tutorials.rst b/docs/cli_scripts/byod_tutorials.rst index f8c0173d..269be127 100644 --- a/docs/cli_scripts/byod_tutorials.rst +++ b/docs/cli_scripts/byod_tutorials.rst @@ -9,12 +9,6 @@ of 3D or 4D DICOM data, a single 3D or 4D file in a common medical image format such as MHA, NRRD, or NIfTI, or a list of 3D image files representing a time series. VTK inputs may be one mesh file or a mesh sequence. -.. note:: - - PhysioTwin4D is a research tool and has **not** been validated for - clinical use. Outputs must not be used for diagnostic or therapeutic - decisions without independent validation. - Installation ------------ @@ -291,8 +285,9 @@ support ``.usd``, ``.usda``, or ``.usdc`` output files directly. Open **Omniverse USD Composer**, drag your ``.usd`` file onto the viewport, then press **Play** (spacebar) to watch the animation. For 4D cardiac data, -use the **Timeline** panel to scrub through phases. ``usdview`` works the same -way and is lighter to install — :doc:`../viewing_usd` covers both. +use the **Timeline** panel to scrub through phases. Set the viewport renderer +to RTX and switch to the scene's ``/World/Camera`` first — +:doc:`../viewing_usd` covers both. See Also -------- diff --git a/docs/cli_scripts/heart_gated_ct.rst b/docs/cli_scripts/heart_gated_ct.rst index 51bed856..13fb7b7e 100644 --- a/docs/cli_scripts/heart_gated_ct.rst +++ b/docs/cli_scripts/heart_gated_ct.rst @@ -329,11 +329,13 @@ Approximate times on typical workstation (32GB RAM, NVIDIA GPU): Viewing Results =============== -1. Open the ``.usd`` file in ``usdview`` or an Omniverse Kit application -2. Press Play to view the cardiac motion animation -3. Adjust the timeline speed for the visualization you want +1. Open the ``.usd`` file in an Omniverse Kit application, with the viewport + renderer set to RTX +2. Switch the viewport to the scene's ``/World/Camera`` +3. Press Play to view the cardiac motion animation +4. Adjust the timeline speed for the visualization you want -:doc:`../viewing_usd` covers installing either viewer and what to look at once +:doc:`../viewing_usd` covers installing the viewer and what to look at once the scene is open. Next Steps diff --git a/docs/developer/usd_generation.rst b/docs/developer/usd_generation.rst index 25033781..4384e498 100644 --- a/docs/developer/usd_generation.rst +++ b/docs/developer/usd_generation.rst @@ -140,14 +140,18 @@ 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 --------------- +Framing Camera and Light +------------------------ 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 +medical-scale meshes (~0.03 m wide). Alongside it, ``add_framing_camera`` +authors a ``/World/DistantLight`` sharing the camera's transform — a +DistantLight emits along its local -Z, the same direction the camera looks, +so the anatomy is lit from the viewing direction in stages that carry no +other light. 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. diff --git a/docs/faq.rst b/docs/faq.rst index 974c0762..f606d904 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -26,11 +26,10 @@ What data formats are supported? Do I need NVIDIA Omniverse? ---------------------------- -No, Omniverse is optional for visualization. You can also use: +Omniverse is the recommended way to view the USD scenes: its RTX renderer is +what evaluates the material properties assigned to each tissue. See +:doc:`viewing_usd`. For the intermediate results you can also use: -* ``usdview``, the viewer that ships with OpenUSD — note that it is **not** - part of the ``usd-core`` package installed with PhysioTwin4D; see - :doc:`viewing_usd` * PyVista, for the intermediate ``.vtp`` / ``.vtu`` meshes * ParaView, likewise for the VTK files diff --git a/docs/index.rst b/docs/index.rst index 52429079..e1167a04 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -14,85 +14,71 @@ PhysioTwin4D is a collection of methods, workflows, tutorials, and CLI tools for creating personalized physiological digital twins from 3D medical images. Install it, clone the repository for the tutorial - scripts, and work through the cards below — each tutorial runs on + scripts, and work through the cards below — each tutorial runs on downloadable data and ends with the constants to change for your own. + It is not validated for clinical use: PhysioTwin4D is a research and + visualization toolkit, not a medical device, and must not be used for + diagnosis, treatment planning, or clinical decision-making.

Version {{ pt4d_project_version }}

-
- Not validated for clinical use. PhysioTwin4D is a - research and visualization toolkit, not a medical device. Do not use it - for diagnosis, treatment planning, or clinical decision-making. -
-
00

Install and Clone

-

Install the package, then clone the repository — the tutorial scripts do not ship in the wheel.

- Start here +

Install the package, then clone the repository — the tutorial scripts do not ship in the wheel.

01

Gated 4D CT to Animated USD

Segment, register and assemble a 4D CT series into an animated OpenUSD scene.

- Slicer-Heart-CT · DIR-Lab
02

Finetune ICON Registration

Adapt uniGradICON to your own cohort and measure what the finetuning bought you.

- DIR-Lab
03

Reconstruct High-Resolution 4D CT

Register every phase to one reference and reconstruct the series at its resolution.

- Slicer-Heart-CT · DIR-Lab
04

CT Segmentation to VTK Surfaces

Segment one CT phase and export patient anatomy as VTK PolyData surfaces.

- Slicer-Heart-CT · DIR-Lab
05

VTK Surfaces to Animated USD

Convert meshes into a time-sampled USD scene for Omniverse playback.

- Tutorial 4 output
06

Create a PCA Shape Model

Turn a population of meshes into a statistical shape model and its modes.

- KCL-Heart-Model · DIR-Lab
07

Fit the Shape Model to a Patient

Fit the shape model to one routine clinical scan, PCA coefficients and all.

- Chest-CT · Tutorial 6 output
08

Propagate the Model Through 4D

Fit each case at its reference phase and carry the mesh through every phase.

- DIR-Lab · Tutorials 2 and 6
09

Train a PhysicsNeMo Surrogate

Train a MeshGraphNet to predict per-vertex motion from shape and phase.

- Tutorial 8 output
10

Predict Motion With the Surrogate

Replace the registration solve with one forward pass, then export to USD.

- Tutorials 8 and 9 output
@@ -120,7 +106,7 @@

Viewing USD Files

-

Set up usdview or an Omniverse Kit app and open the scenes the workflows produce.

+

Set up an Omniverse Kit app with RTX rendering and open the scenes the workflows produce.

Bring Your Own Data

diff --git a/docs/installation.rst b/docs/installation.rst index 4738282f..7f85c339 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -254,7 +254,8 @@ Solution: TotalSegmentator downloads models on first use. Ensure you have: Solution: 1. Ensure NVIDIA Omniverse is installed -2. Check USD file integrity with ``usdview`` (included with usd-core) +2. Set the viewport renderer to RTX and switch to the scene's + ``/World/Camera``; see :doc:`viewing_usd` 3. Verify file paths are accessible to Omniverse Getting Help diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 55b2fd6b..4396d9f2 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -4,13 +4,6 @@ Quick Start This guide will help you get started with PhysioTwin4D quickly. -.. warning:: - - **Not validated for clinical use.** PhysioTwin4D - {{ pt4d_project_version }} beta is a research and visualization toolkit, not - a medical device. Do not use it for diagnosis, treatment planning, or - clinical decision-making. - Prerequisites ============= @@ -277,13 +270,15 @@ Python >= 3.11. Visualizing Results =================== -The workflows write OpenUSD scenes, and viewing them needs a USD viewer — -``usdview`` for inspection and debugging, or an Omniverse Kit application for -real-time RTX playback. Note that the ``usd-core`` package installed with -PhysioTwin4D provides the OpenUSD *libraries* only and contains no viewer. +The workflows write OpenUSD scenes, and viewing them needs a USD viewer — use +an Omniverse Kit application with RTX rendering, which is what evaluates the +material properties assigned to each tissue. Note that the ``usd-core`` +package installed with PhysioTwin4D provides the OpenUSD *libraries* only and +contains no viewer. -:doc:`viewing_usd` covers where to get each one, how to set it up, and how to -open a PhysioTwin4D scene. +:doc:`viewing_usd` covers where to get it, how to set it up, and how to open a +PhysioTwin4D scene — including switching to the camera defined in the scene, +whose clipping planes are fitted to the anatomy's scale. The intermediate meshes need no USD tooling: diff --git a/docs/troubleshooting.rst b/docs/troubleshooting.rst index 07fbee59..e473cfe0 100644 --- a/docs/troubleshooting.rst +++ b/docs/troubleshooting.rst @@ -120,14 +120,12 @@ USD Not Animating usdchecker model.usd -2. Check time samples: + ``usdchecker`` is not part of the ``usd-core`` package installed with + PhysioTwin4D; it ships with the OpenUSD toolset, available pre-built from + https://developer.nvidia.com/usd. - .. code-block:: bash - - usdview model.usd - - ``usdchecker`` and ``usdview`` are not part of the ``usd-core`` package - installed with PhysioTwin4D; see :doc:`viewing_usd` for how to get them. +2. Open the scene in an Omniverse Kit application, switch the viewport to the + scene's ``/World/Camera``, and press Play; see :doc:`viewing_usd`. 3. Verify that the generated USD contains time samples. diff --git a/docs/tutorials.rst b/docs/tutorials.rst index c3456bf1..22a4de07 100644 --- a/docs/tutorials.rst +++ b/docs/tutorials.rst @@ -815,8 +815,8 @@ Adapt to your data Where to Go Next ================ -- :doc:`viewing_usd` — installing ``usdview`` or an Omniverse Kit application - and opening the scenes these tutorials produce. +- :doc:`viewing_usd` — installing an Omniverse Kit application and opening the + scenes these tutorials produce. - :doc:`cli_scripts/byod_tutorials` — running the workflows on your own DICOM, NRRD or VTK data, including directory layout and conversion. - :doc:`api/index` — every workflow, segmenter, registrar and utility class. diff --git a/docs/viewing_usd.rst b/docs/viewing_usd.rst index af27f97d..c7173863 100644 --- a/docs/viewing_usd.rst +++ b/docs/viewing_usd.rst @@ -8,138 +8,69 @@ commands — writes an OpenUSD scene: anatomy split into per-organ prims, painte with OmniSurface materials, and time-sampled when the input was a series. To see the motion you need a USD viewer. -Two are worth knowing. Use **usdview** for day-to-day inspection and debugging, -and an **Omniverse Kit application** when you want real-time ray tracing or to -build on the scene. +Use an **NVIDIA Omniverse Kit application**. It is built on OpenUSD, renders +with RTX in real time, and is the only viewer that shows these scenes as the +workflows intend them. .. important:: ``pip install physiotwin4d`` pulls in `usd-core `_, which is the OpenUSD *libraries* only — enough to write and read stages, but it contains no viewer. - ``usdview`` needs a build that includes USD Imaging, from one of the two - routes below. - -usdview -======= - -``usdview`` is the canonical viewer that ships with OpenUSD. It is a -lightweight application for opening a stage, walking its scene graph, -inspecting prim properties and composition, scrubbing the timeline, and -switching between renderers — the tool to reach for when you want to know what -is actually in the file. See the `OpenUSD toolset documentation -`_ for the full feature list. - -Getting it: pre-built binaries ------------------------------- - -The quickest route is NVIDIA's pre-built OpenUSD libraries and tools, which -include ``usdview`` for Windows and Linux and are matched to specific Python -versions: https://developer.nvidia.com/usd - -Download the package matching your Python version, unpack it, and put its -``bin`` and ``lib`` directories on your path. On Windows: - -.. code-block:: bat - - set USD_ROOT=C:\usd - set PATH=%USD_ROOT%\bin;%USD_ROOT%\lib;%PATH% - set PYTHONPATH=%USD_ROOT%\lib\python;%PYTHONPATH% - - usdview tutorials\output\tutorial_01_heart\cardiac_model.usd - -On Linux: - -.. code-block:: bash - - export USD_ROOT=$HOME/usd - export PATH=$USD_ROOT/bin:$PATH - export PYTHONPATH=$USD_ROOT/lib/python:$PYTHONPATH - - usdview tutorials/output/tutorial_01_heart/cardiac_model.usd - -Use a **separate environment** from the one PhysioTwin4D runs in, or at least -be deliberate about ordering: the ``PYTHONPATH`` above puts a second copy of -``pxr`` ahead of the ``usd-core`` wheel, and mixing two OpenUSD builds in one -interpreter causes import errors that are tedious to diagnose. - -Getting it: building from source --------------------------------- - -To build OpenUSD yourself — needed if no pre-built package matches your Python, -or you want a specific release — clone -https://github.com/PixarAnimationStudios/OpenUSD and run its build script: - -.. code-block:: bash - - git clone https://github.com/PixarAnimationStudios/OpenUSD.git - python OpenUSD/build_scripts/build_usd.py ~/usd - -The script fetches and builds the dependencies as well, so expect it to take a -while. USD Imaging and ``usdview`` are included by default; ``usdview`` also -needs PySide and PyOpenGL in the Python environment you launch it from. The -repository's build instructions list the per-platform prerequisites. - -Using it --------- - -.. code-block:: bash - - usdview cardiac_model.usd - -- The **viewport** opens on frame one. Press the play button, or scrub the - timeline at the bottom, to see the cardiac or respiratory motion — a static - scene means the workflow wrote a single time sample. -- The **scene graph** on the left is the anatomy hierarchy the workflow built - (``/World///``). Select a prim to isolate an organ. -- The **property panel** shows the attributes on the selected prim, including - the time-sampled ``points`` that carry the motion and the bound material. -- The **interpreter** (``Window > Interpreter``) gives you a Python prompt on - the live stage, which is the fastest way to check an attribute's values at a - given time code. - -For a non-interactive sanity check that a file is valid USD, the same toolset -ships ``usdchecker``: - -.. code-block:: bash - - usdchecker cardiac_model.usd Omniverse Kit applications ========================== -NVIDIA Omniverse is built on OpenUSD and renders it with RTX in real time. Use -it when you want photorealistic playback of the anatomy, to compose a -PhysioTwin4D scene with other assets, or to drive a downstream simulation or -XR workflow rather than to inspect the file. - -Download the Omniverse launcher and applications from -https://www.nvidia.com/en-us/omniverse/download/. The relevant Kit-based apps -are **USD Composer** for authoring and layout, and **USD Presenter** for -review and playback; the Kit SDK and the `USD Viewer -`_ sample are the starting points if you -want to embed a viewer in your own tool. +The recommended application is **USD Composer**, built from the +`usd_composer template +`_ +in NVIDIA's `kit-app-template +`_ repository. Clone the +repository and follow its README: ``repo template new`` to create an app from +the ``usd_composer`` template, ``repo build`` to build it, and ``repo launch`` +to run it. The same repository holds a ``usd_viewer`` template if you want a +review-and-playback app or a starting point for embedding a viewer in your own +tool. -Omniverse needs an RTX-capable NVIDIA GPU and a current driver, which is a -heavier requirement than ``usdview``'s GL preview — that is the main reason to -keep both around. +Omniverse needs an RTX-capable NVIDIA GPU and a current driver. Opening a PhysioTwin4D scene: -1. Launch **USD Composer** (or **USD Presenter**). +1. Launch your **USD Composer** app. 2. ``File > Open`` and select the generated ``.usd`` file — for the tutorials, under ``tutorials/output//``. -3. Press **Play** on the timeline to run the animation. The frame rate is the +3. Switch the viewport to the **camera defined in the USD scene** + (``/World/Camera``) — see below. +4. Press **Play** on the timeline to run the animation. The frame rate is the ``frames_per_second`` the workflow was given, so a value of ``1.0`` plays one phase per second; raise it for smoother playback. -4. Anatomy materials are already bound, so the organs arrive colored. Select a +5. Anatomy materials are already bound, so the organs arrive colored. Select a prim in the stage tree to adjust its material, or to hide organs that occlude the structure you care about. -If a scene opens but appears empty, check the units and the camera: the -workflows write millimetre-scale geometry in USD's right-handed Y-up frame, and -a viewport whose near plane is set for metre-scale content will clip it. See -:doc:`developer/usd_generation` for the conversion details. +Use RTX rendering +----------------- + +The workflows assign each tissue an OmniSurface material carrying its visual +properties — color, roughness, transmission and subsurface scattering for +translucent tissue. Those properties are only evaluated by the **RTX** +renderers (``RTX - Real-Time`` or ``RTX - Interactive``). In a preview or +Storm-style render mode the organs fall back to flat approximate shading, so +tissues that should read as translucent or wet look uniformly opaque. Set the +viewport renderer to RTX before judging how a scene looks. + +Use the camera in the scene +--------------------------- + +Each scene ships a ``/World/Camera`` prim framing the anatomy, with clipping +planes and focus distance fitted to the anatomy's scale — the near plane is set +from the geometry's bounding-box diagonal, so you can zoom in close without the +surfaces vanishing. The default Omniverse perspective camera is set up for +room- and building-sized content, so on an organ-sized scene it clips the +anatomy away and navigates awkwardly. In the viewport camera menu, select the +scene's ``Camera`` rather than ``Perspective``. If a scene opens but appears +empty, this is almost always why. See :doc:`developer/usd_generation` for the +coordinate and unit details. Before USD: viewing the meshes directly ======================================= diff --git a/experiments/Convert_VTK_To_USD/convert_vtk_to_usd_using_class.py b/experiments/Convert_VTK_To_USD/convert_vtk_to_usd_using_class.py index 44c44637..0c57dc2f 100644 --- a/experiments/Convert_VTK_To_USD/convert_vtk_to_usd_using_class.py +++ b/experiments/Convert_VTK_To_USD/convert_vtk_to_usd_using_class.py @@ -376,11 +376,11 @@ def verify_usd_file(usd_path): # 7. **Data Preservation**: All VTK arrays preserved as USD primvars # 8. **Coordinate Systems**: Automatic LPS to USD right-handed Y-up conversion # -# The library is production-ready and can be used for converting medical imaging data, simulation results, and other VTK-based datasets to USD for visualization in Omniverse, USDView, or other USD-compatible applications. +# The library is production-ready and can be used for converting medical imaging data, simulation results, and other VTK-based datasets to USD for visualization in Omniverse or other USD-compatible applications. # # ### Next Steps # -# - View the generated USD files in USDView or Omniverse +# - View the generated USD files in Omniverse with RTX rendering enabled # - Experiment with different conversion settings # - Test with your own VTK datasets # - Explore advanced features like custom colormaps and transfer functions diff --git a/src/physiotwin4d/vtk_to_usd/usd_utils.py b/src/physiotwin4d/vtk_to_usd/usd_utils.py index 7d066261..fdbfc6a4 100644 --- a/src/physiotwin4d/vtk_to_usd/usd_utils.py +++ b/src/physiotwin4d/vtk_to_usd/usd_utils.py @@ -10,7 +10,7 @@ import numpy as np from numpy.typing import NDArray -from pxr import Gf, Sdf, Usd, UsdGeom, Vt +from pxr import Gf, Sdf, Usd, UsdGeom, UsdLux, Vt from .data_structures import DataType, GenericArray @@ -430,6 +430,8 @@ def add_framing_camera( *, parent_path: str = "/World", name: str = "Camera", + light_name: str = "DistantLight", + light_intensity: float = 3000.0, bounds_min: tuple[float, float, float] | None = None, bounds_max: tuple[float, float, float] | None = None, focal_length_mm: float = 50.0, @@ -443,6 +445,10 @@ def add_framing_camera( ``clippingRange`` so users can zoom close in Omniverse Kit and other USD viewers without geometry vanishing at the near plane. + A ``UsdLux.DistantLight`` is added at ``{parent_path}/{light_name}`` sharing + the camera's orientation, so the anatomy is lit from the viewing direction + in stages that carry no other light. + Bounds must be expressed in stage coordinates (post axis-swap and unit scaling). For time-varying stages, bounds are sampled at the start time code. @@ -452,6 +458,9 @@ def add_framing_camera( supplied; world bounds are then computed from the stage. parent_path: Parent prim path. Defaults to ``"/World"``. name: Camera prim name. Defaults to ``"Camera"``. + light_name: Distant light prim name. Defaults to ``"DistantLight"``. + light_intensity: Distant light intensity. Defaults to ``3000.0``, which + reads as a neutral key light under the RTX renderers. bounds_min: Optional min corner ``(x, y, z)`` in stage coordinates. bounds_max: Optional max corner ``(x, y, z)`` in stage coordinates. focal_length_mm: Camera focal length. USD camera lens parameters are @@ -528,4 +537,15 @@ def add_framing_camera( camera.CreateHorizontalApertureAttr().Set(float(horizontal_aperture_mm)) camera.CreateFocusDistanceAttr().Set(float(distance)) + # A DistantLight emits along its local -Z, the same direction the camera + # looks, so reusing the camera transform lights whatever the camera frames. + light_path = f"{parent_path.rstrip('/')}/{light_name}" + light = UsdLux.DistantLight.Define(stage, light_path) + light_xformable = UsdGeom.Xformable(light.GetPrim()) + light_xformable.ClearXformOpOrder() + light_xformable.AddTransformOp().Set(camera_to_world) + light.CreateIntensityAttr().Set(float(light_intensity)) + # Sun-like angular diameter: soft enough to avoid hard-edged shadows. + light.CreateAngleAttr().Set(0.53) + return camera diff --git a/tutorials/tutorial_07_lung_fit_statistical_model_to_patient.py b/tutorials/tutorial_07_lung_fit_statistical_model_to_patient.py index f62893c9..fac26c14 100644 --- a/tutorials/tutorial_07_lung_fit_statistical_model_to_patient.py +++ b/tutorials/tutorial_07_lung_fit_statistical_model_to_patient.py @@ -122,6 +122,7 @@ workflow.set_use_pca_registration( use_pca_registration=True, pca_model=pca_model, + pca_number_of_modes=6, use_surface=False, ) From ecac9c38f48d6e0ba7d55cc3544ac7a83e3366ee Mon Sep 17 00:00:00 2001 From: Stephen Aylward Date: Wed, 5 Aug 2026 19:44:44 -0400 Subject: [PATCH 2/2] BUG: coderabbit --- docs/cli_scripts/byod_tutorials.rst | 2 +- docs/developer/usd_generation.rst | 3 ++- docs/index.rst | 4 ++-- docs/viewing_usd.rst | 14 +++++++++++--- src/physiotwin4d/vtk_to_usd/usd_utils.py | 3 ++- 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/docs/cli_scripts/byod_tutorials.rst b/docs/cli_scripts/byod_tutorials.rst index 269be127..a9554632 100644 --- a/docs/cli_scripts/byod_tutorials.rst +++ b/docs/cli_scripts/byod_tutorials.rst @@ -287,7 +287,7 @@ Open **Omniverse USD Composer**, drag your ``.usd`` file onto the viewport, then press **Play** (spacebar) to watch the animation. For 4D cardiac data, use the **Timeline** panel to scrub through phases. Set the viewport renderer to RTX and switch to the scene's ``/World/Camera`` first — -:doc:`../viewing_usd` covers both. +:doc:`../viewing_usd` covers why both matter. See Also -------- diff --git a/docs/developer/usd_generation.rst b/docs/developer/usd_generation.rst index 4384e498..02a86955 100644 --- a/docs/developer/usd_generation.rst +++ b/docs/developer/usd_generation.rst @@ -151,7 +151,8 @@ medical-scale meshes (~0.03 m wide). Alongside it, ``add_framing_camera`` authors a ``/World/DistantLight`` sharing the camera's transform — a DistantLight emits along its local -Z, the same direction the camera looks, so the anatomy is lit from the viewing direction in stages that carry no -other light. The camera is also baked into stages +other light. The light is created only when valid, non-degenerate bounds +exist; otherwise no light is authored. 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. diff --git a/docs/index.rst b/docs/index.rst index e1167a04..88de13a8 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -14,7 +14,7 @@ PhysioTwin4D is a collection of methods, workflows, tutorials, and CLI tools for creating personalized physiological digital twins from 3D medical images. Install it, clone the repository for the tutorial - scripts, and work through the cards below — each tutorial runs on + scripts, and work through the cards below — each tutorial runs on downloadable data and ends with the constants to change for your own. It is not validated for clinical use: PhysioTwin4D is a research and visualization toolkit, not a medical device, and must not be used for @@ -28,7 +28,7 @@
00

Install and Clone

-

Install the package, then clone the repository — the tutorial scripts do not ship in the wheel.

+

Install the package, then clone the repository — the tutorial scripts do not ship in the wheel.

01 diff --git a/docs/viewing_usd.rst b/docs/viewing_usd.rst index c7173863..b5662e4f 100644 --- a/docs/viewing_usd.rst +++ b/docs/viewing_usd.rst @@ -26,9 +26,17 @@ The recommended application is **USD Composer**, built from the `_ in NVIDIA's `kit-app-template `_ repository. Clone the -repository and follow its README: ``repo template new`` to create an app from -the ``usd_composer`` template, ``repo build`` to build it, and ``repo launch`` -to run it. The same repository holds a ``usd_viewer`` template if you want a +repository and follow its README: ``template new`` to create an app from the +``usd_composer`` template, ``build`` to build it, and ``launch`` to run it — +driven through ``./repo.sh`` on Linux and macOS, or ``.\repo.bat`` on Windows: + +.. code-block:: bat + + .\repo.bat template new + .\repo.bat build + .\repo.bat launch + +The same repository holds a ``usd_viewer`` template if you want a review-and-playback app or a starting point for embedding a viewer in your own tool. diff --git a/src/physiotwin4d/vtk_to_usd/usd_utils.py b/src/physiotwin4d/vtk_to_usd/usd_utils.py index fdbfc6a4..a56f7340 100644 --- a/src/physiotwin4d/vtk_to_usd/usd_utils.py +++ b/src/physiotwin4d/vtk_to_usd/usd_utils.py @@ -447,7 +447,8 @@ def add_framing_camera( A ``UsdLux.DistantLight`` is added at ``{parent_path}/{light_name}`` sharing the camera's orientation, so the anatomy is lit from the viewing direction - in stages that carry no other light. + in stages that carry no other light. The light is created only when valid, + non-degenerate bounds exist; otherwise no light is authored. Bounds must be expressed in stage coordinates (post axis-swap and unit scaling). For time-varying stages, bounds are sampled at the start time