Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@
- Add `joint_dof_mask` to `newton.ik.IKSolver` to keep selected joint DOFs fixed during LM optimization. (#3488)
### Changed

- Decide collider visibility from USD `purpose` and visibility rather than from a bound render material. A collider whose `purpose` resolves to `default` is viewport geometry and is drawn; mark it `guide` to state that it is collision-only. Previously an unrelated visual elsewhere in the scene could make a collider vanish. `force_show_colliders` and `hide_collision_shapes` are unchanged.
- Disable the implicit positive Dahl-friction defaults in `SolverVBD.register_custom_attributes()` (deprecated in 1.3.0): `vbd:dahl_eps_max` and `vbd:dahl_tau` now default to zero, and Dahl cable friction is enabled only where both are authored positive. Pass `dahl_defaults_enabled=True` to temporarily restore the old defaults; the compatibility mode will be removed in a future release.
- Keep the authored render mesh when `ModelBuilder.add_usd()` approximates a collider. `physics:approximation` is scoped to collision, so a Mesh that is both render geometry and a collider now imports as an approximated collision shape plus a visual shape carrying the original topology, instead of replacing the render mesh with the approximation. This raises `Model.shape_count` for such prims: iterate on `ShapeFlags.COLLIDE_SHAPES` rather than assuming one shape per collider prim. The visual shape adds no mass and no collision, appends after the originals so existing shape indices and `path_shape_map` entries are unchanged, and is skipped when `load_visual_shapes=False`.
- Compile tiled camera render kernels with CUDA fast math by default for faster rendering; set `SensorTiledCamera.render_config.enable_fast_math = False` for bit-exact, IEEE-precise output.
- Make `CollisionPipeline` the sole owner of rigid-contact geometry for `SolverVBD`: `"latest"` supplies fresh geometry and `"sticky"` supplies replayed geometry. `SolverVBD(rigid_contact_history=True)` uses either mode's match indices only to warm-start its numeric lambda/penalty state.
- Optimize raycast/raytrace queries by restructuring ray-shape intersection into local-space primitives and compile specialized depth/shadow variants that skip unused surface-normal work (mesh shadows also use any-hit queries).
Expand Down Expand Up @@ -87,6 +89,7 @@

- Complete Kamino RCM traversal for large and disconnected systems and reuse the resulting permutation by default; set `reuse_permutation=False` to recompute it for changing matrix topology.
- Fix panel-parallel RCM-blocked LLT factorization hanging when a matrix ends in a partial tile.
- Fix `ModelBuilder.add_usd()` marking a `guide`-purpose collider visible when it has a bound render material. Such a collider is not viewport geometry, and the extra `VISIBLE` flag left it drawn by the viewer's visual toggle instead of its collision toggle. `force_show_colliders` still reveals it.
- Fix USD capsule, cylinder, and cone visual and site scaling to follow the authored primitive axis.
- Fix USD plane visual width and length to scale along the axes defined by the `UsdGeomPlane` schema, and orient X- and Y-axis plane visuals along the authored axis.
- Validate `ArticulationView` mask shapes and devices before launching selection kernels. (#3448)
Expand Down
71 changes: 47 additions & 24 deletions newton/_src/utils/import_usd.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@
_scout_deformable_prims,
)
from .import_usd_deformable_volume import _deformable_import_volume
from .import_utils import should_show_collider

logger = logging.getLogger("newton")

Expand Down Expand Up @@ -495,6 +494,9 @@ class PhysicsMaterial:
}
# mapping from remeshing method to a list of shape indices
remeshing_queue = {}
# Approximated colliders whose prim is viewport geometry, and which therefore keep
# their authored topology as a visual shape. See the approximation pass below.
approximated_viewport_shapes: set[int] = set()

if ignore_paths is None:
ignore_paths = []
Expand Down Expand Up @@ -1207,11 +1209,10 @@ def _is_viewport_drawn(prim: Usd.Prim) -> bool:

USD viewports draw the ``default`` and ``proxy`` purposes and hide ``guide`` and
``render``; the allowlist also keeps any future purpose hidden until explicitly
handled. Colliders deliberately do not use this check: ``guide`` is the conventional
purpose for authored collision geometry (e.g. the MuJoCo USD exporter), and the
collider display policy (``force_show_colliders`` / ``hide_collision_shapes``) is the
explicit mechanism for revealing colliders — gating them on purpose would make
``force_show_colliders`` a no-op on such assets.
handled. This is what decides whether a collider is drawn: ``guide`` is the
conventional purpose for authored collision geometry (e.g. the MuJoCo USD
exporter), and such a prim is not viewport geometry. ``force_show_colliders``
is the explicit override for inspecting it anyway.
"""
if not _is_effectively_visible(prim):
return False
Expand Down Expand Up @@ -3476,24 +3477,18 @@ def _record_fallback_collider_mass_information(
margin_val = newton_margin

has_body_visual_shapes = load_visual_shapes and body_id in bodies_with_visual_shapes
model_has_visual_shapes = load_visual_shapes and bool(bodies_with_visual_shapes)
material_props = _get_material_props_cached(prim)
collider_has_visual_material = (
key == UsdPhysics.ObjectType.MeshShape and _has_visual_material_properties(material_props)
)

# Explicit hide_collision_shapes overrides material-based visibility:
# Explicit hide_collision_shapes overrides drawability:
# if the body already has visual shapes, hide its colliders unconditionally.
hide_collider_for_body = hide_collision_shapes and has_body_visual_shapes
show_collider_by_policy = should_show_collider(
force_show_colliders,
model_has_visual_shapes=model_has_visual_shapes,
)
collider_is_visible = (
show_collider_by_policy or collider_has_visual_material
) and not hide_collider_for_body
# visibility only — see _is_viewport_drawn for why purpose does not gate colliders
collider_is_visible = collider_is_visible and _is_effectively_visible(prim)
# A collider is drawn when USD says it is drawn: ``purpose`` resolving to
# ``default``/``proxy`` and the prim not being invisible. Not because a
# render material happens to be bound, and not because nothing else in the
# scene is visible -- an asset whose geometry is all ``guide`` has no render
# geometry, and an empty viewport is the honest result of that. Reach for
# ``force_show_colliders`` to inspect such a scene.
collider_is_visible = (force_show_colliders or _is_viewport_drawn(prim)) and not hide_collider_for_body

# Contact response precedence:
# per-shape mjc:solref (non-legacy) > material > legacy per-shape > default
Expand Down Expand Up @@ -3802,8 +3797,13 @@ def _record_fallback_collider_mass_information(
)
elif key == UsdPhysics.ObjectType.MeshShape:
# Resolve mesh hull vertex limit from schema with fallback to parameter
if collider_is_visible:
# Visible colliders should render with the same visual material metadata
# The mesh needs its render material when anything will draw it: either
# the collider itself is visible, or it is viewport geometry whose
# authored topology is about to be split off as a visual shape. The
# latter is not covered by collider_is_visible, which hide_collision_shapes
# can clear while the visual copy is still produced.
if collider_is_visible or (load_visual_shapes and _is_viewport_drawn(prim)):
# Drawn colliders should render with the same visual material metadata
# as visual-only mesh imports.
mesh = _get_mesh_with_visual_material(prim, path_name=path)
else:
Expand Down Expand Up @@ -3863,6 +3863,8 @@ def _record_fallback_collider_mass_information(
if remeshing_method not in remeshing_queue:
remeshing_queue[remeshing_method] = []
remeshing_queue[remeshing_method].append(shape_id)
if _is_viewport_drawn(prim):
approximated_viewport_shapes.add(shape_id)
Comment thread
coderabbitai[bot] marked this conversation as resolved.

elif key == UsdPhysics.ObjectType.PlaneShape:
# Warp uses +Z convention for planes
Expand Down Expand Up @@ -3903,9 +3905,30 @@ def _record_fallback_collider_mass_information(
no_collision_shapes.add(shape_id)
builder.shape_flags[shape_id] &= ~ShapeFlags.COLLIDE_SHAPES

# approximate meshes
# Approximate meshes. ``physics:approximation`` belongs to
# UsdPhysicsMeshCollisionAPI and is scoped to collision: it says which shape to
# collide against, not which to draw. Approximating a prim that is viewport
# geometry therefore splits it in two -- an approximated collider and a visual
# carrying the authored topology -- rather than replacing what is drawn.
#
# Viewport geometry is decided by USD purpose and visibility alone. A prim whose
# purpose resolves to ``default`` is drawable whether that value was authored or
# inherited from the fallback, and whether or not a material is bound; the
# collider display policy that governs pure colliders does not apply to a prim
# that is also render geometry. ``approximate_meshes`` copies shapes carrying
# VISIBLE, so mark these before handing them over.
for remeshing_method, shape_ids in remeshing_queue.items():
builder.approximate_meshes(method=remeshing_method, shape_indices=shape_ids)
drawn = [s for s in shape_ids if s in approximated_viewport_shapes] if load_visual_shapes else []
for shape_id in drawn:
builder.shape_flags[shape_id] |= int(ShapeFlags.VISIBLE)
if drawn:
builder.approximate_meshes(method=remeshing_method, shape_indices=drawn, keep_visual_shapes=True)
# Colliders that are not render geometry keep no visual: there is nothing
# authored to preserve. If one is on screen it is because the collider
# display policy put it there, and what it should show is the collider.
rest = [s for s in shape_ids if s not in set(drawn)]
if rest:
builder.approximate_meshes(method=remeshing_method, shape_indices=rest, keep_visual_shapes=False)
Comment thread
andrewkaufman marked this conversation as resolved.

# Filtered pairs are applied after the deformable passes below, once every endpoint's
# Newton shapes exist.
Expand Down
3 changes: 0 additions & 3 deletions newton/examples/assets/cartpole_single_pendulum.usda
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ over "slider"
)
{
float3[] extent = [(-0.015, -4, -0.015), (0.015, 4, 0.015)]
uniform token purpose = "guide"
double size = 1
quatd xformOp:orient = (1, 0, 0, 0)
double3 xformOp:scale = (0.029999999329447746, 8, 0.029999999329447746)
Expand All @@ -35,7 +34,6 @@ over "pole"
)
{
float3[] extent = [(-0.02, -0.03, -0.5), (0.02, 0.03, 0.5)]
uniform token purpose = "guide"
double size = 1
quatd xformOp:orient = (1, 0, 0, 0)
double3 xformOp:scale = (0.03999999910593033, 0.05999999865889549, 1)
Expand All @@ -51,7 +49,6 @@ over "cart"
)
{
float3[] extent = [(-0.1, -0.125, -0.1), (0.1, 0.125, 0.1)]
uniform token purpose = "guide"
double size = 1
quatd xformOp:orient = (1, 0, 0, 0)
double3 xformOp:scale = (0.20000000298023224, 0.25, 0.20000000298023224)
Expand Down
Loading
Loading