MJCF Importer Incorrectly Converts Cylinder Colliders to Capsules #333#771
Conversation
…ton-physics#333 Signed-off-by: Viktor Reutskyy <vreutskyy@nvidia.com>
📝 WalkthroughWalkthroughImports for cylinder geometries in MJCF and URDF now call builder.add_shape_cylinder instead of builder.add_shape_capsule. Capsule handling is unchanged. Tests were added to verify cylinder shape type and dimensions during import. MJCF tests include duplicate cylinder tests. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant Importer as MJCF/URDF Importer
participant Builder as ModelBuilder
User->>Importer: load(file)
Importer->>Importer: parse geometry elements
alt geom_type == "cylinder"
Importer->>Builder: add_shape_cylinder(xform, radius, half_height, up_axis/axis, ...)
Builder-->>Importer: shape_id (type=Cylinder)
else geom_type == "capsule"
Importer->>Builder: add_shape_capsule(xform, radius, half_height, up_axis/axis, ...)
Builder-->>Importer: shape_id (type=Capsule)
end
Importer-->>User: model with shapes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).Please share your feedback with us on this Discord post. 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.
Actionable comments posted: 0
🧹 Nitpick comments (5)
newton/_src/utils/import_mjcf.py (1)
386-405: Edge-case note: fromto collinearity can yield a zero rotation axisWhen start→end aligns with the reference axis, cross(...) becomes zero and normalization can NaN the quaternion. Not introduced here, but this block is susceptible. Add a small epsilon guard and pick an orthogonal fallback axis when |cross| ≈ 0.
- axis = wp.normalize(wp.cross(axis, wp.vec3(0.0, 1.0, 0.0))) + cross_axis = wp.cross(axis, wp.vec3(0.0, 1.0, 0.0)) + if wp.length(cross_axis) < 1e-6: + cross_axis = wp.vec3(0.0, 0.0, 1.0) # fallback orthogonal + axis = wp.normalize(cross_axis)newton/tests/test_import_urdf.py (2)
16-16: Prefer public API over private internals in testsImport GeoType via the public surface to avoid tight coupling to internals.
-from newton._src.geometry.types import GeoType +from newton import GeoType
251-306: Good coverage; tweak assertions to avoid order-coupling and fix misleading commentVisuals are added before colliders in parse_urdf, so “First shape … (collision)” is misleading. Also, indexing makes the test brittle across importer reorderings. Count by type instead.
- # First shape should be cylinder (collision) - self.assertEqual(shape_types[0], GeoType.CYLINDER) - - # Second shape should be cylinder (visual) - self.assertEqual(shape_types[1], GeoType.CYLINDER) - - # Third shape should be capsule - self.assertEqual(shape_types[2], GeoType.CAPSULE) + # We expect exactly two cylinders (visual + collision) and one capsule. + from collections import Counter + counts = Counter(shape_types) + self.assertEqual(counts[GeoType.CYLINDER], 2) + self.assertEqual(counts[GeoType.CAPSULE], 1)newton/tests/test_import_mjcf.py (2)
25-25: Prefer public API import for GeoTypeUse newton.GeoType to decouple tests from internal module layout.
-from newton._src.geometry.types import GeoType +from newton import GeoType
570-609: Solid test; simplify and de-ambiguate the fromto caseMJCF ignores size[1] when fromto is present; keeping it in the XML can confuse readers. Also, assert type counts to avoid depending on insertion order.
- <geom type="cylinder" size="0.3 0.8" fromto="0 0 0 1 0 0" /> + <geom type="cylinder" size="0.3 0.0" fromto="0 0 0 1 0 0" />- # Check shape types - shape_types = list(builder.shape_type) - # First two shapes should be cylinders - self.assertEqual(shape_types[0], GeoType.CYLINDER) - self.assertEqual(shape_types[1], GeoType.CYLINDER) - # Third shape should be capsule - self.assertEqual(shape_types[2], GeoType.CAPSULE) - # Fourth shape should be box - self.assertEqual(shape_types[3], GeoType.BOX) + # Check shape type counts (order-independent) + from collections import Counter + counts = Counter(builder.shape_type) + self.assertEqual(counts[GeoType.CYLINDER], 2) + self.assertEqual(counts[GeoType.CAPSULE], 1) + self.assertEqual(counts[GeoType.BOX], 1)
📜 Review details
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
newton/_src/utils/import_mjcf.py(1 hunks)newton/_src/utils/import_urdf.py(1 hunks)newton/tests/test_import_mjcf.py(2 hunks)newton/tests/test_import_urdf.py(3 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: eric-heiden
PR: newton-physics/newton#584
File: newton/_src/utils/import_urdf.py:159-167
Timestamp: 2025-08-20T03:30:16.037Z
Learning: In the URDF importer (newton/_src/utils/import_urdf.py), cylinders are intentionally created using add_shape_capsule() instead of add_shape_cylinder() because cylinder collisions are not fully supported yet. This is a deliberate workaround until proper cylinder collision support is implemented.
📚 Learning: 2025-08-20T03:30:16.037Z
Learnt from: eric-heiden
PR: newton-physics/newton#584
File: newton/_src/utils/import_urdf.py:159-167
Timestamp: 2025-08-20T03:30:16.037Z
Learning: In the URDF importer (newton/_src/utils/import_urdf.py), cylinders are intentionally created using add_shape_capsule() instead of add_shape_cylinder() because cylinder collisions are not fully supported yet. This is a deliberate workaround until proper cylinder collision support is implemented.
Applied to files:
newton/_src/utils/import_urdf.pynewton/tests/test_import_urdf.pynewton/_src/utils/import_mjcf.pynewton/tests/test_import_mjcf.py
🧬 Code graph analysis (4)
newton/_src/utils/import_urdf.py (1)
newton/_src/sim/builder.py (1)
add_shape_cylinder(2633-2673)
newton/tests/test_import_urdf.py (1)
newton/_src/geometry/types.py (1)
GeoType(25-64)
newton/_src/utils/import_mjcf.py (1)
newton/_src/sim/builder.py (1)
add_shape_cylinder(2633-2673)
newton/tests/test_import_mjcf.py (2)
newton/_src/geometry/types.py (1)
GeoType(25-64)newton/_src/sim/builder.py (1)
shape_count(508-512)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Run GPU Benchmarks (Pull Request)
- GitHub Check: Run GPU Unit Tests on AWS EC2 (Pull Request)
- GitHub Check: run-newton-tests / newton-unittests (ubuntu-latest)
- GitHub Check: run-newton-tests / newton-unittests (windows-latest)
🔇 Additional comments (3)
newton/_src/utils/import_mjcf.py (1)
410-420: LGTM: cylinder geometries now correctly call add_shape_cylinderMapping keeps radius/half_height semantics and existing axis-rotation. Tests cover both size= and fromto= paths. Nice.
newton/tests/test_import_mjcf.py (1)
610-639: LGTM: verifies radius and half_height mapping for cylindersAsserts match builder’s scale layout (radius, half_height, 0). Nice.
newton/_src/utils/import_urdf.py (1)
158-167: Ignore — mapping URDF cylinders to add_shape_cylinder is correctRepository already implements cylinder collision end-to-end and tests expect cylinders, so no blanket fallback to capsules is needed. Key evidence: newton/_src/sim/builder.py (add_shape_cylinder), newton/_src/geometry/kernels.py (cylinder SDF/grad + kernel handling), newton/_src/geometry/inertia.py (compute_cylinder_inertia), newton/_src/geometry/raycast.py (raycast support) and tests newton/tests/test_import_urdf.py, newton/tests/test_import_mjcf.py (assert GeoType.CYLINDER). Ignore the suggested capsule fallback.
Likely an incorrect or invalid review comment.
…der-colliders-to-capsules
…ton-physics#333 (newton-physics#771) Signed-off-by: Viktor Reutskyy <vreutskyy@nvidia.com> Co-authored-by: Eric Heiden <eheiden@nvidia.com>
…ton-physics#333 (newton-physics#771) Signed-off-by: Viktor Reutskyy <vreutskyy@nvidia.com> Co-authored-by: Eric Heiden <eheiden@nvidia.com>
Description
This PR fixes the MJCF and URDF importers to properly preserve cylinder collision shapes instead of incorrectly converting them to capsules. Closes #333
Changes Made:
add_shape_capsuletoadd_shape_cylinderfor cylinder geometries innewton/_src/utils/import_mjcf.pyadd_shape_capsuletoadd_shape_cylinderfor cylinder geometries innewton/_src/utils/import_urdf.pyNewton Migration Guide
Please ensure the migration guide for warp.sim users is up-to-date with the changes made in this PR.
docs/migration.rstis up-to dateBefore your PR is "Ready for review"
newton/tests/test_examples.py)test_cylinder_shapes_preservedandtest_cylinder_properties_preservedtests for MJCF importertest_cylinder_shapes_preservedtest for URDF importerpre-commit run -aSummary by CodeRabbit
Bug Fixes
Tests