From 02656532c2ef7ebb1f7e45158fc6a01a856fd61e Mon Sep 17 00:00:00 2001 From: Robin Alazard Date: Fri, 2 May 2025 03:46:34 -0700 Subject: [PATCH] Fix Mesh PhysicsCollisionAPI by accounting for how it's referenced/instanced. Because we introduce an additional parent scope when referencing a `Mesh` prim (required for instancing), we need to create an `over` prim that allows to actually operate on the to-be-referenced Mesh. PiperOrigin-RevId: 753941355 Change-Id: I829be9e82fe650802afcbc46f4b2f4c81eb53bd1 --- .../usd/plugins/mjcf/mujoco_to_usd.cc | 16 ++++++--- .../usd/plugins/mjcf/mjcf_file_format_test.cc | 34 ++++++++++++++++++- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/src/experimental/usd/plugins/mjcf/mujoco_to_usd.cc b/src/experimental/usd/plugins/mjcf/mujoco_to_usd.cc index d4da714a..b89abc3c 100644 --- a/src/experimental/usd/plugins/mjcf/mujoco_to_usd.cc +++ b/src/experimental/usd/plugins/mjcf/mujoco_to_usd.cc @@ -66,7 +66,6 @@ using TfStaticData = pxr::TfStaticData; // clang-format off TF_DEFINE_PRIVATE_TOKENS(kTokens, - // Xform ops ((body, "Body")) ((body_name, "mujoco:body_name")) ((geom, "Geom")) @@ -89,7 +88,8 @@ TF_DEFINE_PRIVATE_TOKENS(kTokens, ((outputsRgb, "outputs:rgb")) ((inputsMetallic, "inputs:metallic")) (repeat) - ); + ((sourceMesh, pxr::UsdGeomTokens->Mesh)) + ); // Using to satisfy TF_REGISTRY_FUNCTION macro below and avoid operating in PXR_NS. using pxr::TfEnum; @@ -330,7 +330,7 @@ class ModelWriter { pxr::SdfPath subcomponent_path = CreatePrimSpec(data_, parent_path, name, pxr::UsdGeomTokens->Xform); pxr::SdfPath mesh_path = - CreatePrimSpec(data_, subcomponent_path, pxr::UsdGeomTokens->Mesh, + CreatePrimSpec(data_, subcomponent_path, kTokens->sourceMesh, pxr::UsdGeomTokens->Mesh); mesh_paths_[*mesh->name] = subcomponent_path; @@ -600,7 +600,15 @@ class ModelWriter { // Reference the mesh asset written in WriteMeshes. AddPrimReference(data_, subcomponent_path, mesh_paths_[*geom->meshname]); - return subcomponent_path; + // We want to use instancing with meshes, and it requires creating a parent + // scope to be referenced, with the Mesh prim as a child. + // To be able to actually manipulate the Mesh prim, we need to create and + // return the corresponding `over` prim as a child of the referencing prim. + pxr::SdfPath over_mesh_path = + CreatePrimSpec(data_, subcomponent_path, kTokens->sourceMesh, + pxr::UsdGeomTokens->Mesh, pxr::SdfSpecifierOver); + + return over_mesh_path; } pxr::SdfPath WriteSiteGeom(const mjsSite *site, diff --git a/test/experimental/usd/plugins/mjcf/mjcf_file_format_test.cc b/test/experimental/usd/plugins/mjcf/mjcf_file_format_test.cc index fb2a1a65..abdf6a23 100644 --- a/test/experimental/usd/plugins/mjcf/mjcf_file_format_test.cc +++ b/test/experimental/usd/plugins/mjcf/mjcf_file_format_test.cc @@ -612,6 +612,9 @@ TEST_F(MjcfSdfFileFormatPluginTest, TestPhysicsRigidBody) { TEST_F(MjcfSdfFileFormatPluginTest, TestPhysicsColliders) { static constexpr char kXml[] = R"( + + + @@ -636,6 +639,11 @@ TEST_F(MjcfSdfFileFormatPluginTest, TestPhysicsColliders) { + + + + )"; @@ -655,7 +663,7 @@ TEST_F(MjcfSdfFileFormatPluginTest, TestPhysicsColliders) { // body_0/body_0 [rigidbody] // body_0/body_0/body_0_col [collider] // - // body_0/body_0_0 [rigidbody] <-- Note: USD reparents nested rigid bodies + // body_0/body_0_0 [rigidbody] <-- USD reparents nested rigid bodies // body_0/body_0/body_0_0/body_0_0_col [collider] // // body_1/body_1 [rigidbody] @@ -664,6 +672,10 @@ TEST_F(MjcfSdfFileFormatPluginTest, TestPhysicsColliders) { // // body_2/body_2 [rigidbody] // body_2/body_2/body_2_nocol [] + // + // body_3/body_3 [rigidbody] + // body_3/body_3/body_3_col [] <-- Intermediate prim for mesh instancing + // body_3/body_3/body_3_col/Mesh [collider] // ground [collider] (Static collider) EXPECT_PRIM_VALID(stage, "/test/ground"); @@ -728,6 +740,26 @@ TEST_F(MjcfSdfFileFormatPluginTest, TestPhysicsColliders) { pxr::UsdPhysicsRigidBodyAPI); EXPECT_PRIM_API_NOT_APPLIED(stage, "/test/body_2/body_2/body_2_nocol", pxr::UsdPhysicsCollisionAPI); + + // body_3/body_3 [rigidbody] + EXPECT_PRIM_VALID(stage, "/test/body_3/body_3"); + EXPECT_PRIM_API_APPLIED(stage, "/test/body_3/body_3", + pxr::UsdPhysicsRigidBodyAPI); + EXPECT_PRIM_API_NOT_APPLIED(stage, "/test/body_3/body_3", + pxr::UsdPhysicsCollisionAPI); + // body_3/body_3/body_3_col [] (Intermediate prim for mesh instancing) + EXPECT_PRIM_VALID(stage, "/test/body_3/body_3/body_3_col"); + EXPECT_PRIM_API_NOT_APPLIED(stage, "/test/body_3/body_3/body_3_col", + pxr::UsdPhysicsRigidBodyAPI); + EXPECT_PRIM_API_NOT_APPLIED(stage, "/test/body_3/body_3/body_3_col", + pxr::UsdPhysicsCollisionAPI); + // body_3/body_3/body_3_col/Mesh [collider] + EXPECT_PRIM_VALID(stage, "/test/body_3/body_3/body_3_col/Mesh"); + EXPECT_PRIM_API_NOT_APPLIED( + stage, "/test/body_3/body_3/body_3_col/Mesh", + pxr::UsdPhysicsRigidBodyAPI); + EXPECT_PRIM_API_APPLIED(stage, "/test/body_3/body_3/body_3_col/Mesh", + pxr::UsdPhysicsCollisionAPI); } } // namespace