Skip to content

Import USD deformable bodies (cable, cloth, volume) in add_usd() - #3192

Merged
mzamoramora-nvidia merged 209 commits into
newton-physics:mainfrom
mzamoramora-nvidia:mzamoramora/usd-deformable-import
Jul 8, 2026
Merged

Import USD deformable bodies (cable, cloth, volume) in add_usd()#3192
mzamoramora-nvidia merged 209 commits into
newton-physics:mainfrom
mzamoramora-nvidia:mzamoramora/usd-deformable-import

Conversation

@mzamoramora-nvidia

@mzamoramora-nvidia mzamoramora-nvidia commented Jun 16, 2026

Copy link
Copy Markdown
Member

Description

Closes #3178
ModelBuilder.add_usd() now imports deformable bodies authored with the AOUSD UsdPhysics Deformables proposal.

This is an initial implementation of a subset of the proposal: not fully proposal-compliant, and not compatible with native OmniPhysics/PhysX deformable assets (details in docs/concepts/usd_parsing.rst).

What imports as what

USD Newton
GeomBasisCurves + PhysicsCurvesDeformableSimAPI Rod: capsule bodies joined by cable joints, wrapped in one articulation
GeomMesh + PhysicsSurfaceDeformableSimAPI Cloth: FEM triangles with bending edges
UsdGeom.TetMesh + PhysicsVolumeDeformableSimAPI Soft body
PhysicsAttachment (hard) Ball joints. A hard cable-to-cable attachment at the same point welds the cables into one rod
PhysicsElementCollisionFilter Collision-filter pairs

Materials and mass

  • Attributes are read from the standard physics: namespace. A schema resolver (e.g. SchemaResolverPhysx) only adds the same proposal-shaped attributes under vendor namespaces on bound materials; it does not translate OmniPhysics applied schemas or asset structure, so a native Omni deformable without the AOUSD sim APIs imports as ordinary static geometry. Exception: old vendor TetMesh materials keep working for now, with a DeprecationWarning.
  • Mass precedence per the proposal: per-point physics:masses, then body mass/density, then material density.
  • No authored thickness? A default applies, with a warning: 2 mm cloth shell, 2.5 mm cable radius.

Collision

  • A deformable collides when its simulation geometry has an enabled PhysicsCollisionAPI. A dedicated collider in the body hierarchy also enables it, approximated by the simulation geometry with a warning.
  • Cables without a collider import as dynamics-only rods. Cloth and volume cannot disable particle collision yet; they warn and collide.
  • physics:filteredPairs works for shape-backed participants (colliders, rigid bodies, cables, deformable bodies owning a cable); cloth/volume pairs warn. UsdPhysicsCollisionGroup is not applied to deformables.

Edge cases warn, never silently change the model

  • Compliant (finite-stiffness) attachments are kept as metadata, not hardened. Damping does not affect hardness.
  • Kinematic or malformed deformables are skipped.
  • A disabled body keeps its collision geometry as static colliders, like a disabled rigid body.
  • PhysicsDeformableBodyAPI + RigidBodyAPI on one prim imports as rigid.
  • Graphics geometry that cannot be embedded is skipped.

Full details: docs/concepts/usd_parsing.rst (part of this PR).

API

One experimental, keyword-only option: add_usd(..., return_deformable_results=True) (off by default). It adds prim-path index maps and authored material values to the returned mapping. These results may change or be removed without notice. Nothing else is added to Model or ModelBuilder. Batched selection is the stacked follow-up #3326.

Addresses the deformable-import scope of #3178 and the linked #3036 / #3037 / #3038.

Checklist

  • New or existing tests cover these changes
  • The documentation is up to date with these changes
  • CHANGELOG.md has been updated (if user-facing change)

Test plan

uv run --extra dev -m newton.tests -k test_import_usd

One filter runs the whole area: the per-family suites, attachments and filters, group bookkeeping, a mixed-scene CUDA smoke test, and the pre-existing rigid/TetMesh import tests.

Validation

  • The companion branch mzamoramora/usd-deformable-examples builds each cable/cloth/soft-body example twice, from code and from USD, and checks that both models match.
  • The cable assets from this comment and the Isaac Lab scene from this comment import and finalize.
  • Deformable-free stages import at the same speed as main. The scouting overhead from this comment is gone.

New feature / API change

import newton

builder = newton.ModelBuilder()
result = builder.add_usd("scene_with_deformables.usda", return_deformable_results=True)

# Find an imported deformable by prim path:
p0, p1 = result["path_cloth_map"]["/World/Cloth"]["particle"]

# Material values exactly as authored (including ones the built model cannot express):
cable_attrs = result["path_cable_attrs"]["/World/Cable"]  # {material, resolved_density, closed}

model = builder.finalize()  # imported cables are already wrapped in articulations

Summary by CodeRabbit

  • New Features
    • Added experimental USD deformable-body import (cables, cloth, soft/volume) with optional deformable_results returning prim-path maps and build-time attribute snapshots.
    • Added import support for deformable PhysicsAttachment and PhysicsElementCollisionFilter, including collision-filter pairing behavior.
    • Added compat_namespaces (keyword-only) for TetMesh deformable material parsing with canonical-first fallback behavior.
  • Bug Fixes
    • Improved validation for deformable parameters/topology, collision filtering, and fixed-joint remapping; skip unsupported/invalid authored cases more reliably.
  • Documentation
    • Expanded the USD parsing guide for deformables and documented DEFORMABLE_LEGACY_NAMESPACES.
  • Deprecated
    • Legacy vendor-namespaced deformable material attributes are still supported during a warning window; canonical physics: attributes are preferred.
  • Tests
    • Added/updated USD deformable test suites and assets (including mixed scenes, attachments, and collision filtering).

Recognize a GeomBasisCurves carrying PhysicsCurvesDeformableSimAPI and
import it as a VBD cable via ModelBuilder.add_rod (capsule bodies + cable
joints). Discovery is metadata-based (has_applied_api_schema) so it works
without the deformable schema being registered with the USD runtime,
matching the NewtonSDFCollisionAPI approach.

This is the first seam of the USD deformable importer (issue newton-physics#3178,
cable REQ newton-physics#3037): topology only (curveVertexCounts split, wrap -> closed),
world-transformed centerline -> rod positions, and a path_cable_map entry
exposing each cable's (body, joint) indices. Material -> stiffness,
normals -> orientation, and rest-shape parsing are deferred to later
phases; defaults are used for now.
Read the bound curve-deformable material (PhysicsCurvesDeformableMaterialAPI)
and map it onto add_rod parameters:

- thickness -> capsule radius (thickness / 2)
- stretchStiffness [force/area] -> per-joint stretch_stiffness via E*A/L
- bendStiffness  [force/area] -> per-joint bend_stiffness via E*I/L

with A = pi r^2, I = pi r^4 / 4 and the per-curve mean segment length,
mirroring newton.utils.cable.create_cable_stiffness_from_elastic_moduli.

Material values are resolved via a new newton.usd.get_curve_deformable_material()
helper that reads omniphysics: / physxDeformableBody: / physics: namespaces and
drops the -inf "simulator default" sentinel and non-positive values. shearStiffness
and twistStiffness are warned-and-ignored until the VBD cable gains separate
shear/twist constraints (PR newton-physics#3122).
Lock down the prim -> body/joint mapping (REQ newton-physics#3037 addressability):

- two cables in one stage map to disjoint, fully-covering body/joint
  ranges via path_cable_map
- each cable body origin matches its authored segment start point
  (Z-up stage so points are not axis-converted), proving the map points
  at the right bodies
- the cable's articulation is labeled "<prim_path>_articulation", the
  replication-durable handle preserved through add_builder

No production change — the mapping (path_cable_map + the cable's
articulation) was already produced by the discovery scaffold; these tests
pin the contract.
- density: bound material density overrides the builder default on the
  capsule ShapeConfig, so segment mass scales with authored density.
  Per-point `masses` (a simulation-mesh concept) do not map onto rigid
  capsule bodies and are not consumed.
- normals: authored per-vertex curve normals set each segment's
  cross-section frame -> per-segment quaternion (local +Z to the segment
  tangent, local +Y to the normal), with a roll-free fallback for absent
  or degenerate normals.

Tests: density doubling doubles segment mass; authored normals orient
each segment (+Z -> tangent, +Y -> normal). Comments kept ASCII-only.
Replicate a parsed cable prototype across worlds and assert each world
gets an independent, contiguous segment block (T5 from REQ newton-physics#3037): body
count scales with world_count, the cable articulation label repeats once
per env, and per-world body ranges are disjoint and cover the model - so
state can be sliced as (num_envs, num_segments, ...). Validates the
addressability decision (sub-articulation reuse) under replication.
Commit a hand-authored cable asset (newton/tests/assets/
cable_curve_deformable.usda): a GeomBasisCurves with
PhysicsCurvesDeformableSimAPI and a bound curve-deformable material,
i.e. the USD form of the cables the examples build programmatically.

Tests load it through add_usd and (1) assert it parses to the expected
rod bodies/joints (device-free), and (2) on CUDA, run it through
SolverVBD for 20 steps and assert the cable stays finite and bounded -
the "examples work after parsing" check. Also fix a RUF017 quadratic
list-sum in the replication test.
Address Newton conventions (AGENTS.md):
- Rename get_curve_deformable_material -> _get_curve_deformable_material.
  It is an internal _src helper with a single internal caller; a public
  name without a public export is the worst of both, and exposing it would
  commit to public API on an evolving deformable schema. Keep it private.
- Add the missing [Unreleased] CHANGELOG entry for the user-facing USD
  cable import in add_usd().
A triangle GeomMesh carrying PhysicsSurfaceDeformableSimAPI is imported as
Newton cloth (particles + FEM triangles + bending edges) via
add_cloth_mesh (REQ newton-physics#3036). Discovery is metadata-based; the bound
surface-deformable material maps stretch -> tri_ke, shear -> tri_ka, bend
-> bending-edge stiffness, with density for mass. The returned dict gains
path_cloth_map: prim path -> particle / triangle / bending-edge ranges
([start, end)) for per-cloth addressability.

Non-triangulated meshes warn and skip. Adds _get_surface_deformable_material
(private, mirroring the curve reader). Tests cover discovery + ranges,
the material mapping, and the negative (plain mesh -> no cloth).
The TetMesh -> add_soft_mesh path now records path_soft_map: prim path ->
particle / tet ranges ([start, end)) in the model arrays, so individual
volume soft bodies can be located and sliced after import (REQ newton-physics#3038).
Mirrors path_cable_map / path_cloth_map.

Tests cover the single-body range and two-body disjoint/covering ranges.

Deferred: k_damp / particle_radius material parity (newton-physics#3038) needs
Newton-additive attribute names not in the base deformable schema; left
for the Newton-layered schema decision rather than inventing names here.
Andrew's newton-physics#3178 review asks us to parse the public AOUSD deformable
proposal as written, under the canonical physics: namespace, and to
handle vendor namespaces (omniphysics, physxDeformableBody) the same way
rigid bodies do -- through a schema resolver rather than hand-rolled in
the parser.

Read deformable material/geometry attributes from physics: first. Vendor
namespaces become an opt-in fallback sourced from the active resolvers'
extra_attr_namespaces (new SchemaResolverManager.compat_attr_namespaces),
which SchemaResolverPhysx now declares for deformables. A default import
reads only the canonical schema; passing the PhysX resolver re-enables
the vendor-compat path. Migrate the test assets to physics:.
The deformable proposal defines a mass precedence that was previously
unimplemented: per-point physics:masses > PhysicsDeformableBodyAPI.mass
> body density > material density, with per-element volume weighting.

Read PhysicsDeformableBodyAPI mass/density (walking up to the body root,
which may be an ancestor Xform of the simulation geometry) and the
simulation API's per-point physics:masses, then apply them across cable,
cloth, and volume imports:

- body density overrides the material density driving the builder's
  volume/area-weighted mass distribution (areal for cloth, via the
  surface thickness);
- PhysicsDeformableBodyAPI.mass rescales that distribution to a target
  total (segment masses + inertia for the rigid cable model);
- per-point masses are applied directly to the particle range, or, for
  the rigid cable model, summed to a total with a warning.

Element volume/area weighting is delegated to the add_* builders.
Mark a TetMesh as a volume deformable when it carries
PhysicsVolumeDeformableSimAPI or is the simulation child of a
PhysicsDeformableBodyAPI, matching the AOUSD proposal's discovery rule.
Only recognized volume deformables receive the deformable mass
precedence (body mass/density, per-point physics:masses); a bare TetMesh
keeps the legacy material-density import so existing assets still load.
…GELOG

Note in the curve-deformable importer that Newton-specific curve
parameters (cable damping, articulation wrapping) are intentionally left
to a future NewtonCurvesDeformable{Sim,Material}API extension layered via
a schema resolver, keeping the base parser on the public schema. Record
the canonical-namespace and mass-distribution behavior in the CHANGELOG.
The cable importer wrapped every imported cable in its own articulation.
Add an optional newton:cableWrapInArticulation bool on the curve prim
(default true): when false, the cable joints are left unwrapped so the
caller can place them itself - e.g. close a loop or attach the cable to
other bodies with extra joints before finalize, as the cross-slide-table
example does with ball joints to the table.

Flows to ModelBuilder.add_rod(wrap_in_articulation=...). Test asserts the
cable is still created but no articulation is registered when false.
newton:cableWrapInArticulation is Newton-specific, not part of the public
AOUSD curve-deformable schema. Read it through a dedicated helper that the
base parser invokes only when the Newton schema resolver is active,
mirroring how physx vendor attributes require SchemaResolverPhysx. A
default import (Newton resolver on) is unchanged; a base-only import reads
only the public schema and always wraps cables in their own articulation.
This keeps the boundary clean for a future NewtonCurvesDeformableSimAPI.
The main-rebase left both the consolidated deformable-import entry and the
original per-family bullets; remove the duplicates.
Cable was thoroughly tested while cloth had only discovery + material
mapping. Add cloth tests for per-cloth addressability (disjoint ranges),
per-point physics:masses precedence, and the volumetric->areal density
conversion via surface thickness; plus parse-and-simulate (SolverVBD)
tests for both cloth and a tet soft body, matching the existing cable
round-trip test.
The curve-material modulus -> per-joint stiffness conversion divided by the
straight-line endpoint distance / (n-1), which underestimates the segment
rest length for curved cables (e.g. a zigzag) and inflated the stiffness
(2x for the cable_twist example). Use the mean of the actual segment
lengths instead. Straight cables are unaffected.
@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds deformable USD import support for cables, cloth, and soft-body volumes, plus attachment and collision-filter lowering, resolver-scoped legacy namespace compatibility, deformable result maps, and updated docs, assets, and tests.

Changes

USD Deformable Import Enhancements

Layer / File(s) Summary
Namespace compatibility contracts
newton/_src/usd/schema_resolver.py, newton/_src/usd/schemas.py, newton/_src/usd/utils.py, newton/_src/geometry/types.py, docs/api/newton_usd.rst, docs/generate_api.py, CHANGELOG.md, newton/tests/test_import_usd.py, newton/tests/assets/tetmesh_with_material.usda, newton/tests/assets/tetmesh_with_legacy_material.usda
Adds deformable namespace declarations and fallback, keyword-only TetMesh compatibility controls, canonical-first material reads with legacy handling, related API docs/changelog updates, and TetMesh compatibility tests and assets.
Deformable orchestration and builder state
newton/_src/utils/import_usd.py, newton/_src/sim/builder.py, docs/concepts/usd_parsing.rst, newton/tests/_usd_deformable_test_utils.py, newton/tests/assets/deformables_mixed.usda, newton/tests/test_import_usd_deformable_mixed.py, newton/tests/test_import_usd_deformable_groups.py
Adds shared deformable tracking and scouting helpers, threads deformable parsing through parse_usd() and ModelBuilder, records deformable group registries and result maps, documents the import flow and return contract, and adds mixed-scene and group-registry coverage.
Cable and curve import
newton/_src/utils/import_usd_deformable_cable.py, newton/tests/test_import_usd_deformable_cable.py
Implements welded and non-welded curve import for cables, including rod-graph lowering, material and mass mapping, normals-based orientation, per-curve attributes, and cable-specific test coverage for edge cases and welding rules.
Cloth and volume import
newton/_src/utils/import_usd_deformable_cloth.py, newton/_src/utils/import_usd_deformable_volume.py, newton/tests/test_import_usd_deformable_cloth.py, newton/tests/test_import_usd_deformable_volume.py
Implements cloth mesh lowering and TetMesh soft-body lowering, including triangulation, winding repair, transform baking, thickness and density resolution, per-point mass handling, TetMesh topology validation, and soft-body test coverage.
Attachment and collision filter import
newton/_src/utils/import_usd_deformable_attachments.py, newton/tests/test_import_usd_deformable_attachments.py
Implements PhysicsAttachment lowering into ball joints, post-collapse remapping of attachment and cable indices, and PhysicsElementCollisionFilter lowering for grouped element collision filtering, with dedicated tests for attachment and filter semantics.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant parse_usd
  participant _scout_deformable_prims
  participant _DeformableImportContext
  participant ModelBuilder
  participant _deformable_import_cable
  participant _deformable_import_cloth
  participant _deformable_import_volume
  participant _deformable_import_attachments

  parse_usd->>_scout_deformable_prims: bucket deformable prims
  parse_usd->>_DeformableImportContext: build shared context
  _DeformableImportContext->>_deformable_import_cable: lower cables
  _DeformableImportContext->>_deformable_import_cloth: lower cloth
  _DeformableImportContext->>_deformable_import_volume: lower volumes
  _DeformableImportContext->>_deformable_import_attachments: lower attachments and filters
  parse_usd->>ModelBuilder: merge deformable groups and remap collapse indices
Loading

Possibly related issues

Possibly related PRs

Suggested reviewers: eric-heiden, adenzler-nvidia, nvtw

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 78.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding USD deformable-body import support to add_usd() for cable, cloth, and volume.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mzamoramora-nvidia mzamoramora-nvidia changed the title Import USD deformable bodies (cable, cloth, volume) in add_usd() [Draft] Import USD deformable bodies (cable, cloth, volume) in add_usd() Jun 16, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
newton/tests/test_usd_deformable.py (2)

301-302: ⚡ Quick win

Trim inline comments that restate obvious test steps/results.

These comments mostly narrate what adjacent code already makes explicit, which adds noise in a long test file.

As per coding guidelines, in **/*.py code comments should be brief and reserved for non-obvious intent (“why”, not “what”).

Also applies to: 312-317, 375-376

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@newton/tests/test_usd_deformable.py` around lines 301 - 302, Remove or
replace the inline comments that restate what the adjacent code already makes
explicit. In test_usd_deformable.py at lines 301-302, remove the comment about
parsing the cable into prototype builder and replication across worlds since the
code is self-explanatory. Similarly, at lines 312-317 and 375-376, trim any
comments that merely narrate what the code does rather than explaining the
non-obvious intent or reasoning behind it. Keep only comments that explain "why"
the code is written that way, not "what" it does, in accordance with coding
guidelines for Python files.

Source: Coding guidelines


169-175: ⚡ Quick win

Avoid hard-coding the importer default radius in this resolver-behavior test.

This assertion couples the test to an internal default (0.05) rather than the behavior under test (vendor namespace ignored without resolver). Prefer asserting a difference between default and compat outcomes, while keeping the compat-radius check.

Proposed test hardening
-            self.assertAlmostEqual(default_radius, 0.05, places=5)
+            # Without resolver, vendor thickness should not be applied.
+            # Keep this independent of whatever the builder's fallback default is.
+            self.assertGreater(default_radius, 0.0)
@@
             builder_compat = newton.ModelBuilder()
             builder_compat.add_usd(str(usd_path), schema_resolvers=[SchemaResolverPhysx()])
             compat_radius = builder_compat.shape_scale[builder_compat.body_shapes[0][0]][0]
+            self.assertNotAlmostEqual(default_radius, compat_radius, places=6)
             self.assertAlmostEqual(compat_radius, 0.01, places=5)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@newton/tests/test_usd_deformable.py` around lines 169 - 175, The test
hard-codes an assertion that default_radius equals 0.05, which couples the test
to an internal implementation detail rather than the behavior being tested.
Instead of using assertAlmostEqual to check if default_radius equals 0.05,
modify the assertion to compare the default_radius with the compat_radius (which
should be computed earlier in the test) to verify they produce different results
when the vendor namespace is ignored versus when it is handled. This keeps the
test focused on the actual behavior difference without relying on magic numbers.
Keep the existing compat-radius check intact to maintain validation of the
compatibility behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@newton/_src/utils/import_usd.py`:
- Around line 4472-4474: The function returning the dictionary with
path_cable_map, path_cloth_map, and path_soft_map keys has a docstring with a
Returns section that does not document these three new keys. Locate the Returns
section of the docstring for the function containing this dictionary return
statement and add documentation entries for path_cable_map, path_cloth_map, and
path_soft_map that describe what each key represents and its addressability
contract, following the same format and style as the existing documented return
keys.
- Around line 3651-3653: The path_cable_map is populated with cable body and
joint references before collapse_fixed_joints() is called, but when
collapse_fixed_joints=True, the subsequent collapse operation compacts the body
and joint arrays. Currently, only path_body_map and path_joint_map are refreshed
after the collapse, leaving path_cable_map with stale pre-collapse indices.
After the collapse_fixed_joints() call completes (in the block that handles
collapse_fixed_joints=True), add logic to remapping path_cable_map so that its
stored cable body and joint ids reference the correct post-collapse indices,
similar to how path_body_map and path_joint_map are being refreshed.
- Around line 3415-3416: In the `_apply_particle_masses()` function, before
summing the point_masses array at the line where target is assigned, add
validation to ensure the length of point_masses matches the expected number of
particles in the cable. Without this length check, a mismatched physics:masses
array can silently override the cable's total mass with an incorrect value.
Validate the array length and raise an appropriate error or warning if it does
not match the cable's expected particle count before proceeding with the
float(sum(point_masses)) operation.
- Around line 3682-3708: The code currently forces cloth scale to 1.0 when it is
non-uniform but does not apply the actual non-uniform scale values to the
mesh_points vertices passed to builder.add_cloth_mesh. This causes the imported
cloth to lose its authored USD scale. Modify the code to bake the component-wise
scale into the mesh_points vertices when _is_uniform_scale(cloth_scale) returns
false, applying cloth_scale[0], cloth_scale[1], and cloth_scale[2] to each
respective coordinate of each vertex in mesh_points before passing the scaled
vertices to builder.add_cloth_mesh. Keep scale as 1.0 in the add_cloth_mesh call
for non-uniform cases, as the scaling will already be baked into the vertices.

---

Nitpick comments:
In `@newton/tests/test_usd_deformable.py`:
- Around line 301-302: Remove or replace the inline comments that restate what
the adjacent code already makes explicit. In test_usd_deformable.py at lines
301-302, remove the comment about parsing the cable into prototype builder and
replication across worlds since the code is self-explanatory. Similarly, at
lines 312-317 and 375-376, trim any comments that merely narrate what the code
does rather than explaining the non-obvious intent or reasoning behind it. Keep
only comments that explain "why" the code is written that way, not "what" it
does, in accordance with coding guidelines for Python files.
- Around line 169-175: The test hard-codes an assertion that default_radius
equals 0.05, which couples the test to an internal implementation detail rather
than the behavior being tested. Instead of using assertAlmostEqual to check if
default_radius equals 0.05, modify the assertion to compare the default_radius
with the compat_radius (which should be computed earlier in the test) to verify
they produce different results when the vendor namespace is ignored versus when
it is handled. This keeps the test focused on the actual behavior difference
without relying on magic numbers. Keep the existing compat-radius check intact
to maintain validation of the compatibility behavior.
🪄 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: 4cf01dd6-41a4-4260-81e3-d408d6579c68

📥 Commits

Reviewing files that changed from the base of the PR and between 1f25a77 and 9040077.

📒 Files selected for processing (8)
  • CHANGELOG.md
  • newton/_src/usd/schema_resolver.py
  • newton/_src/usd/schemas.py
  • newton/_src/usd/utils.py
  • newton/_src/utils/import_usd.py
  • newton/tests/assets/cable_curve_deformable.usda
  • newton/tests/assets/tetmesh_with_material.usda
  • newton/tests/test_usd_deformable.py

Comment thread newton/_src/utils/import_usd.py Outdated
Comment thread newton/_src/utils/import_usd.py Outdated
Comment thread newton/_src/utils/import_usd.py Outdated
Comment thread newton/_src/utils/import_usd.py Outdated
@codecov

codecov Bot commented Jun 16, 2026

Copy link
Copy Markdown

get_tetmesh gained a compat_namespaces parameter and an updated namespace
docstring; mirror both on TetMesh.create_from_usd so the docstring-parity
test (test_api) passes. Fixes the CI unittest failure.
…oc return maps

- _apply_cable_masses: warn and ignore physics:masses whose length != the
  curve point count, instead of summing a mismatched array.
- Cloth import: bake a non-uniform xformOp:scale into the vertices (add_cloth_mesh
  only takes a uniform scale) so it is not silently dropped.
- Document path_cable_map / path_cloth_map / path_soft_map in the parse_usd
  return contract.
- Add regressions for both fixes.
- Periodic curves now build a body for the closing v[-1]->v[0] segment
  (close the polyline before add_rod, which makes len-1 bodies + the loop
  joint). A 4-point periodic curve now imports 4 bodies / 4 joints.
- Remap path_cable_map body/joint indices through collapse_fixed_joints'
  body_remap/joint_remap so the map stays valid when collapsing reindexes
  bodies (cables parse after rigid bodies, so their indices would shift).
- Add regressions for both.
Comment thread newton/_src/utils/import_usd_deformable_cloth.py Outdated
Comment thread newton/_src/utils/import_usd_deformable_volume.py
The default-thickness fallback only triggered when the surface material
authored cloth fields, but a volumetric density can also resolve from a
PhysicsDeformableBodyAPI override or a base physics material. Neither
source can author a surface thickness, so the volumetric value was
passed to add_cloth_mesh() as areal density, inflating the mass by
roughly 1/thickness (~500x at 1000 kg/m3 vs the 2 mm default).

Resolve the density before the thickness fallback and apply the default
(with the existing warning) whenever any volumetric source resolves
without an authored thickness.
The scout bucketed prims and assigned body ownership before
ignore_paths was applied, so an ignored first simulation child could
still claim its deformable body. The lowering pass then skipped the
ignored prim AND skipped its non-ignored sibling as additional
simulation geometry, importing neither.

Filter ignored prims at scout time so they are as-if-absent for
bucketing and ownership. The check short-circuits on an empty
ignore_paths, leaving the deformable-free import path unchanged
(scout benchmark unchanged at 3.3 ms median on 3,000 Xforms).
A body-mass-only volume deformable built its particle masses from the
TetMesh density or the builder's default_tet_density. When that
fallback is zero, the particle masses are zero and the body-mass
rescale in _apply_particle_masses() has no positive total to
distribute, silently losing the authored mass.

Pass the neutral weight density (1.0) to add_soft_mesh() when a body
mass is authored without a positive density source, as the cable and
cloth paths already do: the rescale turns the volume-proportional
masses into the proposal's density-independent m_p = m_tot * V_p /
V_tot. The resolved_density metadata keeps reporting the unmodified
resolution instead of the neutral weight.
The flag only controls whether the extra deformable result entries are
returned, not whether deformables import, and existing result-gating
kwargs use the return_ prefix (return_uv_indices, return_diagnostics).
Plain rename without a deprecation: the kwarg has not shipped in a
release.
The legacy-default deprecation warning fired only for vendor-namespaced
material attributes. A material that authors canonical physics: moduli
without PhysicsVolumeDeformableMaterialAPI is the second case where the
deprecated read-any-material default is load-bearing: the default reads
the moduli, while canonical-only (compat_namespaces=()) scopes them to
API-applied materials and silently drops them.

Warn for that case too, keeping the no-warn guarantee for API-applied
canonical and render-only materials. The add_usd() vendor-recovery gate
is unchanged: it enables vendor namespaces when it fires, which would
be wrong for canonical-attrs-only materials.
physics:filteredPairs was collected only from native colliders and
applied with direct path_shape_map indexing before the deformable
passes ran, so a pair targeting a cable raised KeyError, a pair
authored on the deformable side or on a rigid-body prim was silently
dropped, and a self-referencing pair produced an invalid self-filter.

Collect canonicalized path pairs from native colliders, rigid-body
prims (via path_body_map, covering every body creation path), and
deformable participants (simulation geometry, deformable body prims,
owned colliders), and apply them after deformable lowering through a
set-valued endpoint resolver: a collider is one shape, a rigid body or
cable is all of its shapes, and a deformable body resolves through its
simulation geometry. Cloth and volume endpoints are particles Newton's
shape filters cannot express, so those pairs warn with both paths and
are kept out of the model, as are missing or non-participating
targets. Application seeds its dedup set from the builder so pairs the
element-filter pass already added are not appended twice.
State explicitly that deformable import is an initial implementation
of a subset of the AOUSD proposal: not fully proposal-compliant and
not compatible with native OmniPhysics/PhysX deformable assets.

The vendor-namespace wording implied that a schema resolver makes
Omni assets work; SchemaResolverPhysx only enables reading the same
proposal-shaped material attributes under omniphysics: /
physxDeformableBody: namespaces. It does not translate applied
schemas, renamed attributes, attachments, pose purposes, or hierarchy
conventions, so a native Omni deformable without the AOUSD sim APIs
imports as ordinary static geometry. Say so in the parsing guide and
the resolver docstring, and document UsdPhysicsCollisionGroup
membership as not applied to deformables (per-pair filtering only).

@adenzler-nvidia adenzler-nvidia left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One leftover to clean up before merge; otherwise this looks ready to me.

Comment thread CHANGELOG.md Outdated
A main merge duplicated the two VBD damping entries and the MJCF/USD
margin-gap entry from Changed into the Deprecated section; upstream
main carries each exactly once under Changed.

@jcarius-nv jcarius-nv left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @mzamoramora-nvidia , I just checked with Alain. Looks like everything is addressed, let's get this in!

@mzamoramora-nvidia
mzamoramora-nvidia added this pull request to the merge queue Jul 8, 2026
Merged via the queue into newton-physics:main with commit 44158fc Jul 8, 2026
25 checks passed
@mzamoramora-nvidia

Copy link
Copy Markdown
Member Author

Closes #3178

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Add Newton Importer Support for USD Deformable Physics Schemas

9 participants