Import TetMesh prims through add_usd#2414
Conversation
Traverse TetMesh prims during USD import and route them through ModelBuilder.add_soft_mesh while preserving prim transforms and material-derived tet parameters. Add regression coverage for soft-mesh import counts, material propagation, and transform composition so TetMesh USD stages import correctly.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughImports USD UsdGeom.TetMesh prims as soft meshes via ModelBuilder.add_usd(); adds a cached TetMesh loader, uniform-scale detection, per-prim transform handling, suppresses tetmesh warnings, and adds tests for material, instancing, transforms, and custom-attribute immutability. Changes
Sequence DiagramsequenceDiagram
participant User
participant ModelBuilder
participant parse_usd
participant USDStage
participant TetMeshCache
participant BuilderAddSoftMesh
User->>ModelBuilder: add_usd(asset_path / stage, xform=...)
ModelBuilder->>parse_usd: parse stage (root_path, ignore_paths, xform)
parse_usd->>USDStage: traverse prims for UsdGeom.TetMesh (incl. instances)
loop per TetMesh prim
parse_usd->>TetMeshCache: _get_tetmesh_cached(prim_path)
TetMeshCache-->>parse_usd: TetMesh data
parse_usd->>parse_usd: compute world transform & decompose scale
parse_usd->>parse_usd: filter custom attributes (copy if needed)
alt uniform scale
parse_usd->>BuilderAddSoftMesh: add_soft_mesh(tetmesh, scale=uniform)
else non-uniform scale
parse_usd->>parse_usd: scale vertices per-axis
parse_usd->>BuilderAddSoftMesh: add_soft_mesh(vertices=scaled, scale=1)
end
BuilderAddSoftMesh-->>parse_usd: confirmation
end
parse_usd-->>ModelBuilder: builder updated with soft meshes
ModelBuilder-->>User: return builder/state
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (3)
CHANGELOG.md (1)
121-121: Move this changelog item fromFixedtoAdded.Line 121 describes new functionality, not a bug fix, so it should be listed under
### Addedin[Unreleased].Suggested changelog adjustment
### Added @@ +- Import `UsdGeom.TetMesh` prims through `ModelBuilder.add_usd()` as soft meshes @@ ### Fixed @@ -- Import `UsdGeom.TetMesh` prims through `ModelBuilder.add_usd()` as soft meshesAs per coding guidelines: user-facing changes must be placed under the correct
[Unreleased]category inCHANGELOG.md.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@CHANGELOG.md` at line 121, The changelog entry "Import `UsdGeom.TetMesh` prims through `ModelBuilder.add_usd()` as soft meshes" is under `Fixed` but describes new functionality; move that line from the `### Fixed` section into the `### Added` section under the `[Unreleased]` header in CHANGELOG.md so it appears as an addition, not a bug fix, and ensure formatting/indentation matches other `### Added` items.newton/tests/test_import_usd.py (1)
7751-7792: Consider extending this transform test to include rotation and non-uniform scale.Current coverage is translation-only. Adding one case with nested parent xforms + non-uniform scale would better lock in the TetMesh transform decomposition behavior introduced by this PR.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@newton/tests/test_import_usd.py` around lines 7751 - 7792, Update the test_add_usd_imports_tetmesh_with_transforms case to cover rotation and non-uniform scale by adding nested Xform hierarchy and non-uniform scale ops in the USD payload and by calling builder.add_usd with a wp.transform that includes a non-identity quaternion and non-uniform scale (instead of wp.quat_identity and uniform translation only); specifically modify the stage.GetRootLayer().ImportFromString USD to include a parent Xform with rotate and scale ops (unique symbol: the TetMesh "SoftBody" under World) and change the builder.add_usd(xform=...) invocation to pass a transform with rotation and non-uniform scale, then update the expected positions array to the transformed vertex coordinates and assert against builder.particle_q as before.newton/_src/utils/import_usd.py (1)
2409-2414: Clarify precedence when bothmeshandverticesare provided.When scale is non-uniform, the code passes both
mesh=tetmeshandvertices=tetmesh.vertices * scale. This relies onadd_soft_meshpreferring the explicitverticeskwarg overmesh.vertices.Consider adding a brief inline comment to clarify this expected behavior, or verify the contract with
add_soft_mesh.if _is_uniform_scale(soft_mesh_scale): add_soft_mesh_kwargs["scale"] = float(np.array(soft_mesh_scale, dtype=np.float32)[0]) else: + # When non-uniform scale is present, pre-scale vertices. + # add_soft_mesh uses explicit `vertices` kwarg over mesh.vertices. add_soft_mesh_kwargs["vertices"] = tetmesh.vertices * np.array(soft_mesh_scale, dtype=np.float32)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@newton/_src/utils/import_usd.py` around lines 2409 - 2414, The code currently passes both mesh=tetmesh and vertices=tetmesh.vertices * scale when _is_uniform_scale(soft_mesh_scale) is False, relying on add_soft_mesh to prefer the explicit vertices kwarg; add a short inline comment next to the add_soft_mesh_kwargs construction (referencing _is_uniform_scale, add_soft_mesh_kwargs, tetmesh, mesh, vertices, and builder.add_soft_mesh) stating that add_soft_mesh must prefer vertices over mesh.vertices, and optionally add a defensive check (e.g., assert or explicit override) to verify the add_soft_mesh contract before calling builder.add_soft_mesh.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@CHANGELOG.md`:
- Line 121: The changelog entry "Import `UsdGeom.TetMesh` prims through
`ModelBuilder.add_usd()` as soft meshes" is under `Fixed` but describes new
functionality; move that line from the `### Fixed` section into the `### Added`
section under the `[Unreleased]` header in CHANGELOG.md so it appears as an
addition, not a bug fix, and ensure formatting/indentation matches other `###
Added` items.
In `@newton/_src/utils/import_usd.py`:
- Around line 2409-2414: The code currently passes both mesh=tetmesh and
vertices=tetmesh.vertices * scale when _is_uniform_scale(soft_mesh_scale) is
False, relying on add_soft_mesh to prefer the explicit vertices kwarg; add a
short inline comment next to the add_soft_mesh_kwargs construction (referencing
_is_uniform_scale, add_soft_mesh_kwargs, tetmesh, mesh, vertices, and
builder.add_soft_mesh) stating that add_soft_mesh must prefer vertices over
mesh.vertices, and optionally add a defensive check (e.g., assert or explicit
override) to verify the add_soft_mesh contract before calling
builder.add_soft_mesh.
In `@newton/tests/test_import_usd.py`:
- Around line 7751-7792: Update the test_add_usd_imports_tetmesh_with_transforms
case to cover rotation and non-uniform scale by adding nested Xform hierarchy
and non-uniform scale ops in the USD payload and by calling builder.add_usd with
a wp.transform that includes a non-identity quaternion and non-uniform scale
(instead of wp.quat_identity and uniform translation only); specifically modify
the stage.GetRootLayer().ImportFromString USD to include a parent Xform with
rotate and scale ops (unique symbol: the TetMesh "SoftBody" under World) and
change the builder.add_usd(xform=...) invocation to pass a transform with
rotation and non-uniform scale, then update the expected positions array to the
transformed vertex coordinates and assert against builder.particle_q as before.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: ae610161-b68c-4ea3-ac49-0090b056d522
📒 Files selected for processing (4)
CHANGELOG.mdnewton/_src/sim/builder.pynewton/_src/utils/import_usd.pynewton/tests/test_import_usd.py
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
## Motivation The entry "Import `UsdGeom.TetMesh` prims through `ModelBuilder.add_usd()` as soft meshes" was placed in the `### Fixed` section, but it describes new functionality, not a bug fix. Per the project's CHANGELOG conventions, new capabilities belong in `### Added` and should use imperative present tense starting with "Add". ## What changed - Removed the entry from `### Fixed` (was at line 121). - Added a rephrased entry to `### Added`: "Add import of `UsdGeom.TetMesh` prims as soft meshes through `ModelBuilder.add_usd()`". ## Verification - `uv run --extra dev python eval/code-fixer/validate_fixes.py` — confirms the entry is in the Added section and not in Fixed.
…utes
## Motivation
Two bugs in the new TetMesh import path inside `parse_usd()`:
1. **Prototype duplication** (#58567, #58599, #58601): The `Usd.PrimRange` loop at line 2387 traverses instance proxies but never skips `/Prototypes/` paths. With the default `root_path="/"`, stages that keep reusable assets under `/Prototypes/...` and reference them as instanceable prims import the authored prototype TetMesh *in addition to* every instance proxy, silently multiplying particle/tet counts and mass.
2. **Unregistered primvar crash** (#58568, #58600, #58602): `usd.get_tetmesh()` stores all USD primvars/custom attributes on the returned `TetMesh`, and `add_soft_mesh()` raises `ValueError` for any attribute not pre-registered in `builder.custom_attributes`. Common authored primvars like `displayColor` or per-vertex fields thus crash `add_usd()`, unlike the rigid-body/joint paths which filter through `usd.get_custom_attribute_values()` and silently drop unknown attributes.
## What changed
- Added `if path.startswith("/Prototypes/"): continue` immediately after extracting the prim path, matching the pattern used by the neighboring Gaussian splat traversal (line 2425+).
- Before calling `builder.add_soft_mesh()`, filter `tetmesh.custom_attributes` to only keys present in `builder.custom_attributes`, matching the existing `add_usd()` behavior for other prim types.
## Scoping
- Both fixes are minimal additions inside the existing TetMesh loop, touching only `newton/_src/utils/import_usd.py`.
- The custom attribute filter mutates the cached `TetMesh` object, but this is safe: each prim path is visited once, `tetmesh_cache` is local to `parse_usd`, and filtering is idempotent.
## Verification
- `uv run --extra dev python eval/code-fixer/validate_fixes.py` — validates prototype skip (expects 8 particles for 2 instances, not 12) and primvar tolerance (no `ValueError` for `displayColor`/`temperature`).
- `uv run --extra dev -m newton.tests -k TestTetMesh` — all 36 TetMesh tests pass.
adenzler-nvidia
left a comment
There was a problem hiding this comment.
PR Review: Import TetMesh prims through add_usd
Nice addition — the feature follows existing patterns well (caching, transform decomposition, visual shape suppression, /Prototypes/ filtering). The scope is tight and the happy-path test coverage is solid. A few issues worth addressing below.
Strengths
- Smart uniform vs. non-uniform scale handling — pre-baking non-uniform scale into vertices since
add_soft_meshonly supports uniformscaleis the right approach. - Good material propagation test — validates the full chain from USD physics material → Lamé parameters → total mass.
- Clean visual shape suppression — adding
"tetmesh"to the warning exclusion set prevents noisy warnings without over-suppressing.
Avoid mutating cached TetMesh custom attributes during USD import\nby filtering on a shallow copy before calling add_soft_mesh.\n\nExpand TetMesh import coverage for instance proxies, composed\ntransforms, and the custom-attribute regression so the reviewed\nbehavior stays locked in.
adenzler-nvidia
left a comment
There was a problem hiding this comment.
Looks good — prior review items are all properly addressed. The cache mutation fix via copy.copy, instancing test, expanded transform coverage, and type annotation update are solid.
One nit for a follow-up: in the non-uniform scale branch, tetmesh.vertices should be tetmesh_for_builder.vertices:
add_soft_mesh_kwargs["vertices"] = tetmesh.vertices * np.array(soft_mesh_scale, dtype=np.float32)This works today because copy.copy is shallow (both reference the same numpy array), but it's fragile — if the copy semantics ever change, this silently passes stale vertices. Using tetmesh_for_builder.vertices makes the intent clear and is more robust.
## Motivation In the non-uniform scale branch of the TetMesh import loop, the code read vertices from `tetmesh` (the cached original) instead of `tetmesh_for_builder` (the potentially-copied variant): ```python add_soft_mesh_kwargs["vertices"] = tetmesh.vertices * np.array(soft_mesh_scale, dtype=np.float32) ``` This works today because `copy.copy` is shallow — both `tetmesh` and `tetmesh_for_builder` share the same underlying numpy array. However, it is fragile: if the copy semantics ever change (e.g., to a deep copy or if `TetMesh` gains a `__copy__` override), this line would silently pass stale vertices. ## Fix - Changed `tetmesh.vertices` → `tetmesh_for_builder.vertices` on line 2428 of `newton/_src/utils/import_usd.py`. - This makes the intent clear: the non-uniform scale path operates on the same object that is passed as `mesh` to `add_soft_mesh`. ## Verification - `uv run --extra dev -m newton.tests -k test_add_usd_imports_tetmesh` — 2 tests pass (basic import + transforms with non-uniform scale) - `uv run --extra dev -m newton.tests -k test_import_usd.TestTetMesh` — all 38 TetMesh tests pass
## Motivation
The `ruff format` pre-commit hook was failing because line 2428 of `newton/_src/utils/import_usd.py` exceeded the configured line-length limit. The offending line was:
```python
add_soft_mesh_kwargs["vertices"] = tetmesh_for_builder.vertices * np.array(soft_mesh_scale, dtype=np.float32)
```
## Fix
Wrap the `np.array(...)` call across multiple lines, matching the style `ruff format` produces automatically:
```python
add_soft_mesh_kwargs["vertices"] = tetmesh_for_builder.vertices * np.array(
soft_mesh_scale, dtype=np.float32
)
```
## Verification
- `uvx pre-commit run -a` — all 5 hooks pass (ruff lint, ruff format, uv-lock, typos, warp array syntax)
- `uv run --extra dev -m newton.tests -k test_add_usd_imports_tetmesh` — both TetMesh import tests pass
There was a problem hiding this comment.
🧹 Nitpick comments (1)
newton/_src/utils/import_usd.py (1)
2425-2429: Minor cleanup: avoid repeated scale conversion per TetMesh prim.
np.array(soft_mesh_scale, dtype=np.float32)is constructed multiple times in the same block; compute once for clarity and less allocation churn.♻️ Suggested refactor
add_soft_mesh_kwargs = { "pos": soft_mesh_pos, "rot": soft_mesh_rot, "scale": 1.0, "vel": wp.vec3(0.0, 0.0, 0.0), "mesh": tetmesh_for_builder, } + soft_mesh_scale_np = np.asarray(soft_mesh_scale, dtype=np.float32) if _is_uniform_scale(soft_mesh_scale): - add_soft_mesh_kwargs["scale"] = float(np.array(soft_mesh_scale, dtype=np.float32)[0]) + add_soft_mesh_kwargs["scale"] = float(soft_mesh_scale_np[0]) else: - add_soft_mesh_kwargs["vertices"] = tetmesh_for_builder.vertices * np.array(soft_mesh_scale, dtype=np.float32) + add_soft_mesh_kwargs["vertices"] = tetmesh_for_builder.vertices * soft_mesh_scale_np🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@newton/_src/utils/import_usd.py` around lines 2425 - 2429, Compute the NumPy array for soft_mesh_scale once and reuse it to avoid repeated allocations: create a local variable (e.g., scale_arr = np.array(soft_mesh_scale, dtype=np.float32)) before the if checking _is_uniform_scale(soft_mesh_scale), then use scale_arr[0] when setting add_soft_mesh_kwargs["scale"] and multiply tetmesh_for_builder.vertices by scale_arr when setting add_soft_mesh_kwargs["vertices"]; update the logic around _is_uniform_scale, add_soft_mesh_kwargs, and tetmesh_for_builder.vertices to reference this single precomputed array.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@newton/_src/utils/import_usd.py`:
- Around line 2425-2429: Compute the NumPy array for soft_mesh_scale once and
reuse it to avoid repeated allocations: create a local variable (e.g., scale_arr
= np.array(soft_mesh_scale, dtype=np.float32)) before the if checking
_is_uniform_scale(soft_mesh_scale), then use scale_arr[0] when setting
add_soft_mesh_kwargs["scale"] and multiply tetmesh_for_builder.vertices by
scale_arr when setting add_soft_mesh_kwargs["vertices"]; update the logic around
_is_uniform_scale, add_soft_mesh_kwargs, and tetmesh_for_builder.vertices to
reference this single precomputed array.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: b303c5ff-f729-4747-b765-6c575e0602a9
📒 Files selected for processing (1)
newton/_src/utils/import_usd.py
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@newton/_src/utils/import_usd.py`:
- Around line 359-360: The TetMesh cache currently keys by str(prim.GetPath())
in get_tetmesh(), which fails to deduplicate instance proxies traversed via
Usd.TraverseInstanceProxies(); change the cache key logic to use the prototype
identity instead — e.g., if prim.GetPrototype() returns non-None use
str(prim.GetPrototype().GetPath()) (or the prototype prim itself) as the key,
otherwise fall back to str(prim.GetPath()); update the lookup and insertion into
tetmesh_cache to use this normalized key so all proxies of the same prototype
share the cached TetMesh.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: 13f52d8b-273c-4f5e-a377-e9ce4a503ee3
📒 Files selected for processing (1)
newton/_src/utils/import_usd.py
Thanks, addressed this now. Can you please reapprove @adenzler-nvidia? |
Description
Add support for importing
UsdGeom.TetMeshprims as soft meshes throughModelBuilder.add_usd(). When a USD stage contains TetMesh prims, they are now automatically converted to soft meshes viaadd_soft_mesh(), with proper handling of:Checklist
CHANGELOG.mdhas been updated (if user-facing change)Test plan
Two new tests verify:
.usdafile with a TetMesh prim and physics material is imported correctly — particle count, tet/tri indices, material parameters (µ, λ), and total mass are validated.New feature / API change
Summary by CodeRabbit
New Features
Documentation
Tests