From e441868dad2b6088fa6654acbb26c8f303cefcb0 Mon Sep 17 00:00:00 2001 From: Sam Haves Date: Mon, 14 Jul 2025 12:44:54 -0700 Subject: [PATCH] Add missing attributes to MjcCollisionAPI USD schema. PiperOrigin-RevId: 783006362 Change-Id: I02d51190d62bb9460863025cd16360427afc70c4 --- .../usd/mjcPhysics/collisionAPI.h | 179 ++++++++++++++++++ .../experimental/usd/mjcPhysics/tokens.h | 26 ++- .../usd/mjcPhysics/collisionAPI.cpp | 84 +++++++- .../usd/mjcPhysics/generatedSchema.usda | 28 +++ src/experimental/usd/mjcPhysics/schema.usda | 57 ++++++ src/experimental/usd/mjcPhysics/tokens.cpp | 12 ++ .../usd/plugins/mjcf/mujoco_to_usd.cc | 52 +++-- src/experimental/usd/usd_to_mjspec.cc | 57 ++++++ .../usd/plugins/mjcf/mjcf_file_format_test.cc | 25 ++- 9 files changed, 502 insertions(+), 18 deletions(-) diff --git a/include/mujoco/experimental/usd/mjcPhysics/collisionAPI.h b/include/mujoco/experimental/usd/mjcPhysics/collisionAPI.h index 8c4de794..6903e633 100644 --- a/include/mujoco/experimental/usd/mjcPhysics/collisionAPI.h +++ b/include/mujoco/experimental/usd/mjcPhysics/collisionAPI.h @@ -191,6 +191,185 @@ class MjcPhysicsCollisionAPI : public UsdAPISchemaBase { UsdAttribute CreateShellInertiaAttr(VtValue const &defaultValue = VtValue(), bool writeSparsely = false) const; + public: + // --------------------------------------------------------------------- // + // PRIORITY + // --------------------------------------------------------------------- // + /// Priority determining how the properties of two colliders are combined to + /// form the properties of the contact. + /// + /// | || + /// | -- | -- | + /// | Declaration | `uniform int mjc:priority = 0` | + /// | C++ Type | int | + /// | \ref Usd_Datatypes "Usd Type" | SdfValueTypeNames->Int | + /// | \ref SdfVariability "Variability" | SdfVariabilityUniform | + MJCPHYSICS_API + UsdAttribute GetPriorityAttr() const; + + /// See GetPriorityAttr(), and also + /// \ref Usd_Create_Or_Get_Property for when to use Get vs Create. + /// If specified, author \p defaultValue as the attribute's default, + /// sparsely (when it makes sense to do so) if \p writeSparsely is \c true - + /// the default for \p writeSparsely is \c false. + MJCPHYSICS_API + UsdAttribute CreatePriorityAttr(VtValue const &defaultValue = VtValue(), + bool writeSparsely = false) const; + + public: + // --------------------------------------------------------------------- // + // CONDIM + // --------------------------------------------------------------------- // + /// The dimensionality of the contact space for a dynamically generated + /// contact pair is set to the maximum of the condim values of the two + /// participating geoms. + /// + /// | || + /// | -- | -- | + /// | Declaration | `uniform int mjc:condim = 3` | + /// | C++ Type | int | + /// | \ref Usd_Datatypes "Usd Type" | SdfValueTypeNames->Int | + /// | \ref SdfVariability "Variability" | SdfVariabilityUniform | + MJCPHYSICS_API + UsdAttribute GetConDimAttr() const; + + /// See GetConDimAttr(), and also + /// \ref Usd_Create_Or_Get_Property for when to use Get vs Create. + /// If specified, author \p defaultValue as the attribute's default, + /// sparsely (when it makes sense to do so) if \p writeSparsely is \c true - + /// the default for \p writeSparsely is \c false. + MJCPHYSICS_API + UsdAttribute CreateConDimAttr(VtValue const &defaultValue = VtValue(), + bool writeSparsely = false) const; + + public: + // --------------------------------------------------------------------- // + // SOLMIX + // --------------------------------------------------------------------- // + /// Specifies the weight used for averaging of contact parameters, and + /// interacts with the priority attribute. + /// + /// | || + /// | -- | -- | + /// | Declaration | `uniform double mjc:solmix = 1` | + /// | C++ Type | double | + /// | \ref Usd_Datatypes "Usd Type" | SdfValueTypeNames->Double | + /// | \ref SdfVariability "Variability" | SdfVariabilityUniform | + MJCPHYSICS_API + UsdAttribute GetSolMixAttr() const; + + /// See GetSolMixAttr(), and also + /// \ref Usd_Create_Or_Get_Property for when to use Get vs Create. + /// If specified, author \p defaultValue as the attribute's default, + /// sparsely (when it makes sense to do so) if \p writeSparsely is \c true - + /// the default for \p writeSparsely is \c false. + MJCPHYSICS_API + UsdAttribute CreateSolMixAttr(VtValue const &defaultValue = VtValue(), + bool writeSparsely = false) const; + + public: + // --------------------------------------------------------------------- // + // SOLREF + // --------------------------------------------------------------------- // + /// Specifies the weight used for averaging of contact parameters, and + /// interacts with the priority attribute. + /// + /// | || + /// | -- | -- | + /// | Declaration | `uniform double[] mjc:solref = [0.02, 1]` | + /// | C++ Type | VtArray | + /// | \ref Usd_Datatypes "Usd Type" | SdfValueTypeNames->DoubleArray | + /// | \ref SdfVariability "Variability" | SdfVariabilityUniform | + MJCPHYSICS_API + UsdAttribute GetSolRefAttr() const; + + /// See GetSolRefAttr(), and also + /// \ref Usd_Create_Or_Get_Property for when to use Get vs Create. + /// If specified, author \p defaultValue as the attribute's default, + /// sparsely (when it makes sense to do so) if \p writeSparsely is \c true - + /// the default for \p writeSparsely is \c false. + MJCPHYSICS_API + UsdAttribute CreateSolRefAttr(VtValue const &defaultValue = VtValue(), + bool writeSparsely = false) const; + + public: + // --------------------------------------------------------------------- // + // SOLIMP + // --------------------------------------------------------------------- // + /// Specifies the weight used for averaging of contact parameters, and + /// interacts with the priority attribute. + /// + /// | || + /// | -- | -- | + /// | Declaration | `uniform double[] mjc:solimp = [0.9, 0.95, 0.001, 0.5, 2]` + /// | | C++ Type | VtArray | | \ref Usd_Datatypes "Usd Type" | + /// SdfValueTypeNames->DoubleArray | | \ref SdfVariability "Variability" | + /// SdfVariabilityUniform | + MJCPHYSICS_API + UsdAttribute GetSolImpAttr() const; + + /// See GetSolImpAttr(), and also + /// \ref Usd_Create_Or_Get_Property for when to use Get vs Create. + /// If specified, author \p defaultValue as the attribute's default, + /// sparsely (when it makes sense to do so) if \p writeSparsely is \c true - + /// the default for \p writeSparsely is \c false. + MJCPHYSICS_API + UsdAttribute CreateSolImpAttr(VtValue const &defaultValue = VtValue(), + bool writeSparsely = false) const; + + public: + // --------------------------------------------------------------------- // + // MARGIN + // --------------------------------------------------------------------- // + /// Distance threshold below which contacts are detected and included in the + /// global array mjData.contact. + /// + /// | || + /// | -- | -- | + /// | Declaration | `uniform double mjc:margin = 0` | + /// | C++ Type | double | + /// | \ref Usd_Datatypes "Usd Type" | SdfValueTypeNames->Double | + /// | \ref SdfVariability "Variability" | SdfVariabilityUniform | + MJCPHYSICS_API + UsdAttribute GetMarginAttr() const; + + /// See GetMarginAttr(), and also + /// \ref Usd_Create_Or_Get_Property for when to use Get vs Create. + /// If specified, author \p defaultValue as the attribute's default, + /// sparsely (when it makes sense to do so) if \p writeSparsely is \c true - + /// the default for \p writeSparsely is \c false. + MJCPHYSICS_API + UsdAttribute CreateMarginAttr(VtValue const &defaultValue = VtValue(), + bool writeSparsely = false) const; + + public: + // --------------------------------------------------------------------- // + // GAP + // --------------------------------------------------------------------- // + /// This attribute is used to enable the generation of inactive contacts, + /// i.e., contacts that are ignored by the constraint solver but are included + /// in mjData.contact for the purpose of custom computations. When this value + /// is positive, geom distances between margin and margin-gap correspond to + /// such inactive contacts. + /// + /// | || + /// | -- | -- | + /// | Declaration | `uniform double mjc:gap = 0` | + /// | C++ Type | double | + /// | \ref Usd_Datatypes "Usd Type" | SdfValueTypeNames->Double | + /// | \ref SdfVariability "Variability" | SdfVariabilityUniform | + MJCPHYSICS_API + UsdAttribute GetGapAttr() const; + + /// See GetGapAttr(), and also + /// \ref Usd_Create_Or_Get_Property for when to use Get vs Create. + /// If specified, author \p defaultValue as the attribute's default, + /// sparsely (when it makes sense to do so) if \p writeSparsely is \c true - + /// the default for \p writeSparsely is \c false. + MJCPHYSICS_API + UsdAttribute CreateGapAttr(VtValue const &defaultValue = VtValue(), + bool writeSparsely = false) const; + public: // ===================================================================== // // Feel free to add custom code below this line, it will be preserved by diff --git a/include/mujoco/experimental/usd/mjcPhysics/tokens.h b/include/mujoco/experimental/usd/mjcPhysics/tokens.h index 10ab4555..8ad5004e 100644 --- a/include/mujoco/experimental/usd/mjcPhysics/tokens.h +++ b/include/mujoco/experimental/usd/mjcPhysics/tokens.h @@ -184,6 +184,10 @@ struct MjcPhysicsTokensType { /// /// MjcPhysicsActuator const TfToken mjcBiasType; + /// \brief "mjc:condim" + /// + /// MjcPhysicsCollisionAPI + const TfToken mjcCondim; /// \brief "mjc:crankLength" /// /// MjcPhysicsActuator @@ -332,6 +336,10 @@ struct MjcPhysicsTokensType { /// /// MjcPhysicsActuator const TfToken mjcGainType; + /// \brief "mjc:gap" + /// + /// MjcPhysicsCollisionAPI + const TfToken mjcGap; /// \brief "mjc:gear" /// /// MjcPhysicsActuator @@ -359,7 +367,7 @@ struct MjcPhysicsTokensType { const TfToken mjcLengthRangeMin; /// \brief "mjc:margin" /// - /// MjcPhysicsJointAPI + /// MjcPhysicsCollisionAPI, MjcPhysicsJointAPI const TfToken mjcMargin; /// \brief "mjc:maxhullvert" /// @@ -477,6 +485,10 @@ struct MjcPhysicsTokensType { /// /// MjcPhysicsSceneAPI const TfToken mjcOptionWind; + /// \brief "mjc:priority" + /// + /// MjcPhysicsCollisionAPI + const TfToken mjcPriority; /// \brief "mjc:qpos" /// /// MjcPhysicsKeyframe @@ -505,6 +517,10 @@ struct MjcPhysicsTokensType { /// /// MjcPhysicsActuator const TfToken mjcSliderSite; + /// \brief "mjc:solimp" + /// + /// MjcPhysicsCollisionAPI + const TfToken mjcSolimp; /// \brief "mjc:solimpfriction" /// /// MjcPhysicsJointAPI @@ -513,6 +529,14 @@ struct MjcPhysicsTokensType { /// /// MjcPhysicsJointAPI const TfToken mjcSolimplimit; + /// \brief "mjc:solmix" + /// + /// MjcPhysicsCollisionAPI + const TfToken mjcSolmix; + /// \brief "mjc:solref" + /// + /// MjcPhysicsCollisionAPI + const TfToken mjcSolref; /// \brief "mjc:solreffriction" /// /// MjcPhysicsJointAPI diff --git a/src/experimental/usd/mjcPhysics/collisionAPI.cpp b/src/experimental/usd/mjcPhysics/collisionAPI.cpp index 7499cdb9..077eb034 100644 --- a/src/experimental/usd/mjcPhysics/collisionAPI.cpp +++ b/src/experimental/usd/mjcPhysics/collisionAPI.cpp @@ -97,6 +97,83 @@ UsdAttribute MjcPhysicsCollisionAPI::CreateShellInertiaAttr( /* custom = */ false, SdfVariabilityUniform, defaultValue, writeSparsely); } +UsdAttribute MjcPhysicsCollisionAPI::GetPriorityAttr() const { + return GetPrim().GetAttribute(MjcPhysicsTokens->mjcPriority); +} + +UsdAttribute MjcPhysicsCollisionAPI::CreatePriorityAttr( + VtValue const &defaultValue, bool writeSparsely) const { + return UsdSchemaBase::_CreateAttr( + MjcPhysicsTokens->mjcPriority, SdfValueTypeNames->Int, + /* custom = */ false, SdfVariabilityUniform, defaultValue, writeSparsely); +} + +UsdAttribute MjcPhysicsCollisionAPI::GetConDimAttr() const { + return GetPrim().GetAttribute(MjcPhysicsTokens->mjcCondim); +} + +UsdAttribute MjcPhysicsCollisionAPI::CreateConDimAttr( + VtValue const &defaultValue, bool writeSparsely) const { + return UsdSchemaBase::_CreateAttr( + MjcPhysicsTokens->mjcCondim, SdfValueTypeNames->Int, + /* custom = */ false, SdfVariabilityUniform, defaultValue, writeSparsely); +} + +UsdAttribute MjcPhysicsCollisionAPI::GetSolMixAttr() const { + return GetPrim().GetAttribute(MjcPhysicsTokens->mjcSolmix); +} + +UsdAttribute MjcPhysicsCollisionAPI::CreateSolMixAttr( + VtValue const &defaultValue, bool writeSparsely) const { + return UsdSchemaBase::_CreateAttr( + MjcPhysicsTokens->mjcSolmix, SdfValueTypeNames->Double, + /* custom = */ false, SdfVariabilityUniform, defaultValue, writeSparsely); +} + +UsdAttribute MjcPhysicsCollisionAPI::GetSolRefAttr() const { + return GetPrim().GetAttribute(MjcPhysicsTokens->mjcSolref); +} + +UsdAttribute MjcPhysicsCollisionAPI::CreateSolRefAttr( + VtValue const &defaultValue, bool writeSparsely) const { + return UsdSchemaBase::_CreateAttr( + MjcPhysicsTokens->mjcSolref, SdfValueTypeNames->DoubleArray, + /* custom = */ false, SdfVariabilityUniform, defaultValue, writeSparsely); +} + +UsdAttribute MjcPhysicsCollisionAPI::GetSolImpAttr() const { + return GetPrim().GetAttribute(MjcPhysicsTokens->mjcSolimp); +} + +UsdAttribute MjcPhysicsCollisionAPI::CreateSolImpAttr( + VtValue const &defaultValue, bool writeSparsely) const { + return UsdSchemaBase::_CreateAttr( + MjcPhysicsTokens->mjcSolimp, SdfValueTypeNames->DoubleArray, + /* custom = */ false, SdfVariabilityUniform, defaultValue, writeSparsely); +} + +UsdAttribute MjcPhysicsCollisionAPI::GetMarginAttr() const { + return GetPrim().GetAttribute(MjcPhysicsTokens->mjcMargin); +} + +UsdAttribute MjcPhysicsCollisionAPI::CreateMarginAttr( + VtValue const &defaultValue, bool writeSparsely) const { + return UsdSchemaBase::_CreateAttr( + MjcPhysicsTokens->mjcMargin, SdfValueTypeNames->Double, + /* custom = */ false, SdfVariabilityUniform, defaultValue, writeSparsely); +} + +UsdAttribute MjcPhysicsCollisionAPI::GetGapAttr() const { + return GetPrim().GetAttribute(MjcPhysicsTokens->mjcGap); +} + +UsdAttribute MjcPhysicsCollisionAPI::CreateGapAttr(VtValue const &defaultValue, + bool writeSparsely) const { + return UsdSchemaBase::_CreateAttr( + MjcPhysicsTokens->mjcGap, SdfValueTypeNames->Double, + /* custom = */ false, SdfVariabilityUniform, defaultValue, writeSparsely); +} + namespace { static inline TfTokenVector _ConcatenateAttributeNames( const TfTokenVector &left, const TfTokenVector &right) { @@ -112,8 +189,11 @@ static inline TfTokenVector _ConcatenateAttributeNames( const TfTokenVector &MjcPhysicsCollisionAPI::GetSchemaAttributeNames( bool includeInherited) { static TfTokenVector localNames = { - MjcPhysicsTokens->mjcGroup, - MjcPhysicsTokens->mjcShellinertia, + MjcPhysicsTokens->mjcGroup, MjcPhysicsTokens->mjcShellinertia, + MjcPhysicsTokens->mjcPriority, MjcPhysicsTokens->mjcCondim, + MjcPhysicsTokens->mjcSolmix, MjcPhysicsTokens->mjcSolref, + MjcPhysicsTokens->mjcSolimp, MjcPhysicsTokens->mjcMargin, + MjcPhysicsTokens->mjcGap, }; static TfTokenVector allNames = _ConcatenateAttributeNames( UsdAPISchemaBase::GetSchemaAttributeNames(true), localNames); diff --git a/src/experimental/usd/mjcPhysics/generatedSchema.usda b/src/experimental/usd/mjcPhysics/generatedSchema.usda index e7e8166b..344f9bc5 100644 --- a/src/experimental/usd/mjcPhysics/generatedSchema.usda +++ b/src/experimental/usd/mjcPhysics/generatedSchema.usda @@ -234,14 +234,42 @@ class "MjcCollisionAPI" ( doc = "API describing a MuJoCo collider." ) { + uniform int mjc:condim = 3 ( + displayName = "ConDim" + doc = "The dimensionality of the contact space for a dynamically generated contact pair is set to the maximum of the condim values of the two participating geoms." + ) + uniform double mjc:gap = 0 ( + displayName = "Gap" + doc = "This attribute is used to enable the generation of inactive contacts, i.e., contacts that are ignored by the constraint solver but are included in mjData.contact for the purpose of custom computations. When this value is positive, geom distances between margin and margin-gap correspond to such inactive contacts." + ) uniform int mjc:group = 0 ( displayName = "Group" doc = "Integer MuJoCo group to which the collider belongs." ) + uniform double mjc:margin = 0 ( + displayName = "Margin" + doc = "Distance threshold below which contacts are detected and included in the global array mjData.contact." + ) + uniform int mjc:priority = 0 ( + displayName = "Priority" + doc = "Priority determining how the properties of two colliders are combined to form the properties of the contact." + ) uniform bool mjc:shellinertia = 0 ( displayName = "Shell Inertia" doc = "Enables handling of the inertia assuming mass is concentrated on the surface." ) + uniform double[] mjc:solimp = [0.9, 0.95, 0.001, 0.5, 2] ( + displayName = "SolImp" + doc = "Specifies the weight used for averaging of contact parameters, and interacts with the priority attribute." + ) + uniform double mjc:solmix = 1 ( + displayName = "SolMix" + doc = "Specifies the weight used for averaging of contact parameters, and interacts with the priority attribute." + ) + uniform double[] mjc:solref = [0.02, 1] ( + displayName = "SolMix" + doc = "Specifies the weight used for averaging of contact parameters, and interacts with the priority attribute." + ) } class "MjcMeshCollisionAPI" ( diff --git a/src/experimental/usd/mjcPhysics/schema.usda b/src/experimental/usd/mjcPhysics/schema.usda index 1d43cd15..a26a172d 100644 --- a/src/experimental/usd/mjcPhysics/schema.usda +++ b/src/experimental/usd/mjcPhysics/schema.usda @@ -547,6 +547,7 @@ class "MjcCollisionAPI" displayName = "Group" doc = """Integer MuJoCo group to which the collider belongs.""" ) + uniform bool mjc:shellinertia = False ( customData = { string apiName = "ShellInertia" @@ -554,6 +555,62 @@ class "MjcCollisionAPI" displayName = "Shell Inertia" doc = """Enables handling of the inertia assuming mass is concentrated on the surface.""" ) + + uniform int mjc:priority = 0 ( + customData = { + string apiName = "Priority" + } + displayName = "Priority" + doc = """Priority determining how the properties of two colliders are combined to form the properties of the contact.""" + ) + + uniform int mjc:condim = 3 ( + customData = { + string apiName = "ConDim" + } + displayName = "ConDim" + doc = """The dimensionality of the contact space for a dynamically generated contact pair is set to the maximum of the condim values of the two participating geoms.""" + ) + + uniform double mjc:solmix = 1.0 ( + customData = { + string apiName = "SolMix" + } + displayName = "SolMix" + doc = """Specifies the weight used for averaging of contact parameters, and interacts with the priority attribute.""" + ) + + uniform double[] mjc:solref = [0.02, 1.0] ( + customData = { + string apiName = "SolRef" + } + displayName = "SolMix" + doc = """Specifies the weight used for averaging of contact parameters, and interacts with the priority attribute.""" + ) + + uniform double[] mjc:solimp = [0.9, 0.95, 0.001, 0.5, 2.0] ( + customData = { + string apiName = "SolImp" + } + displayName = "SolImp" + doc = """Specifies the weight used for averaging of contact parameters, and interacts with the priority attribute.""" + ) + + uniform double mjc:margin = 0.0 ( + customData = { + string apiName = "Margin" + } + displayName = "Margin" + doc = """Distance threshold below which contacts are detected and included in the global array mjData.contact.""" + ) + + uniform double mjc:gap = 0.0 ( + customData = { + string apiName = "Gap" + } + displayName = "Gap" + doc = """This attribute is used to enable the generation of inactive contacts, i.e., contacts that are ignored by the constraint solver but are included in mjData.contact for the purpose of custom computations. When this value is positive, geom distances between margin and margin-gap correspond to such inactive contacts.""" + ) } class "MjcMeshCollisionAPI" diff --git a/src/experimental/usd/mjcPhysics/tokens.cpp b/src/experimental/usd/mjcPhysics/tokens.cpp index b5794aee..a5fa8ef3 100644 --- a/src/experimental/usd/mjcPhysics/tokens.cpp +++ b/src/experimental/usd/mjcPhysics/tokens.cpp @@ -46,6 +46,7 @@ MjcPhysicsTokensType::MjcPhysicsTokensType() mjcArmature("mjc:armature", TfToken::Immortal), mjcBiasPrm("mjc:biasPrm", TfToken::Immortal), mjcBiasType("mjc:biasType", TfToken::Immortal), + mjcCondim("mjc:condim", TfToken::Immortal), mjcCrankLength("mjc:crankLength", TfToken::Immortal), mjcCtrl("mjc:ctrl", TfToken::Immortal), mjcCtrlLimited("mjc:ctrlLimited", TfToken::Immortal), @@ -83,6 +84,7 @@ MjcPhysicsTokensType::MjcPhysicsTokensType() mjcFrictionloss("mjc:frictionloss", TfToken::Immortal), mjcGainPrm("mjc:gainPrm", TfToken::Immortal), mjcGainType("mjc:gainType", TfToken::Immortal), + mjcGap("mjc:gap", TfToken::Immortal), mjcGear("mjc:gear", TfToken::Immortal), mjcGroup("mjc:group", TfToken::Immortal), mjcInertia("mjc:inertia", TfToken::Immortal), @@ -122,6 +124,7 @@ MjcPhysicsTokensType::MjcPhysicsTokensType() mjcOptionTolerance("mjc:option:tolerance", TfToken::Immortal), mjcOptionViscosity("mjc:option:viscosity", TfToken::Immortal), mjcOptionWind("mjc:option:wind", TfToken::Immortal), + mjcPriority("mjc:priority", TfToken::Immortal), mjcQpos("mjc:qpos", TfToken::Immortal), mjcQvel("mjc:qvel", TfToken::Immortal), mjcRef("mjc:ref", TfToken::Immortal), @@ -129,8 +132,11 @@ MjcPhysicsTokensType::MjcPhysicsTokensType() mjcRollingfriction("mjc:rollingfriction", TfToken::Immortal), mjcShellinertia("mjc:shellinertia", TfToken::Immortal), mjcSliderSite("mjc:sliderSite", TfToken::Immortal), + mjcSolimp("mjc:solimp", TfToken::Immortal), mjcSolimpfriction("mjc:solimpfriction", TfToken::Immortal), mjcSolimplimit("mjc:solimplimit", TfToken::Immortal), + mjcSolmix("mjc:solmix", TfToken::Immortal), + mjcSolref("mjc:solref", TfToken::Immortal), mjcSolreffriction("mjc:solreffriction", TfToken::Immortal), mjcSolreflimit("mjc:solreflimit", TfToken::Immortal), mjcSpringdamper("mjc:springdamper", TfToken::Immortal), @@ -185,6 +191,7 @@ MjcPhysicsTokensType::MjcPhysicsTokensType() mjcArmature, mjcBiasPrm, mjcBiasType, + mjcCondim, mjcCrankLength, mjcCtrl, mjcCtrlLimited, @@ -222,6 +229,7 @@ MjcPhysicsTokensType::MjcPhysicsTokensType() mjcFrictionloss, mjcGainPrm, mjcGainType, + mjcGap, mjcGear, mjcGroup, mjcInertia, @@ -258,6 +266,7 @@ MjcPhysicsTokensType::MjcPhysicsTokensType() mjcOptionTolerance, mjcOptionViscosity, mjcOptionWind, + mjcPriority, mjcQpos, mjcQvel, mjcRef, @@ -265,8 +274,11 @@ MjcPhysicsTokensType::MjcPhysicsTokensType() mjcRollingfriction, mjcShellinertia, mjcSliderSite, + mjcSolimp, mjcSolimpfriction, mjcSolimplimit, + mjcSolmix, + mjcSolref, mjcSolreffriction, mjcSolreflimit, mjcSpringdamper, diff --git a/src/experimental/usd/plugins/mjcf/mujoco_to_usd.cc b/src/experimental/usd/plugins/mjcf/mujoco_to_usd.cc index 1c0b66e5..8941e196 100644 --- a/src/experimental/usd/plugins/mjcf/mujoco_to_usd.cc +++ b/src/experimental/usd/plugins/mjcf/mujoco_to_usd.cc @@ -761,8 +761,7 @@ class ModelWriter { ApplyApiSchema(data_, material_path, pxr::UsdPhysicsTokens->PhysicsMaterialAPI); - ApplyApiSchema(data_, material_path, - MjcPhysicsTokens->MjcMaterialAPI); + ApplyApiSchema(data_, material_path, MjcPhysicsTokens->MjcMaterialAPI); mjsGeom *geom_default = mjs_getDefault(geom->element)->geom; if (geom->friction[0] != geom_default->friction[0]) { @@ -1094,8 +1093,7 @@ class ModelWriter { } } - void WriteActuator(mjsActuator *actuator, - const pxr::SdfPath &parent_path) { + void WriteActuator(mjsActuator *actuator, const pxr::SdfPath &parent_path) { pxr::TfToken valid_name = GetValidPrimName(*mjs_getName(actuator->element)); pxr::SdfPath actuator_path = parent_path.AppendChild(valid_name); if (!data_->HasSpec(actuator_path)) { @@ -1121,9 +1119,8 @@ class ModelWriter { return; } - CreateRelationshipSpec(data_, actuator_path, - MjcPhysicsTokens->mjcTarget, target_path, - pxr::SdfVariabilityUniform); + CreateRelationshipSpec(data_, actuator_path, MjcPhysicsTokens->mjcTarget, + target_path, pxr::SdfVariabilityUniform); WriteUniformAttribute(actuator_path, pxr::SdfValueTypeNames->Int, MjcPhysicsTokens->mjcGroup, actuator->group); @@ -1132,9 +1129,8 @@ class ModelWriter { int refsite_id = mj_name2id(model_, mjOBJ_SITE, actuator->refsite->c_str()); pxr::SdfPath refsite_path = site_paths_[refsite_id]; - CreateRelationshipSpec(data_, actuator_path, - MjcPhysicsTokens->mjcRefSite, refsite_path, - pxr::SdfVariabilityUniform); + CreateRelationshipSpec(data_, actuator_path, MjcPhysicsTokens->mjcRefSite, + refsite_path, pxr::SdfVariabilityUniform); } if (!actuator->slidersite->empty()) { @@ -1158,8 +1154,8 @@ class ModelWriter { } else if (value == mjLIMITED_FALSE) { limited_token = pxr::MjcPhysicsTokens->false_; } - WriteUniformAttribute(actuator_path, pxr::SdfValueTypeNames->Token, - token, limited_token); + WriteUniformAttribute(actuator_path, pxr::SdfValueTypeNames->Token, token, + limited_token); } const std::vector> @@ -1248,8 +1244,8 @@ class ModelWriter { void WriteActuators() { pxr::SdfPath scope_path = - CreatePrimSpec(data_, body_paths_[kWorldIndex], - kTokens->actuatorsScope, pxr::UsdGeomTokens->Scope); + CreatePrimSpec(data_, body_paths_[kWorldIndex], kTokens->actuatorsScope, + pxr::UsdGeomTokens->Scope); mjsActuator *actuator = mjs_asActuator(mjs_firstElement(spec_, mjOBJ_ACTUATOR)); while (actuator) { @@ -1570,6 +1566,34 @@ class ModelWriter { MjcPhysicsTokens->mjcShellinertia, geom->typeinertia == mjtGeomInertia::mjINERTIA_SHELL); + WriteUniformAttribute(geom_path, pxr::SdfValueTypeNames->Int, + MjcPhysicsTokens->mjcPriority, geom->priority); + + WriteUniformAttribute(geom_path, pxr::SdfValueTypeNames->Int, + MjcPhysicsTokens->mjcCondim, geom->condim); + + WriteUniformAttribute(geom_path, pxr::SdfValueTypeNames->Double, + MjcPhysicsTokens->mjcSolmix, geom->solmix); + + WriteUniformAttribute(geom_path, pxr::SdfValueTypeNames->Double, + MjcPhysicsTokens->mjcSolmix, geom->solmix); + + WriteUniformAttribute( + geom_path, pxr::SdfValueTypeNames->DoubleArray, + MjcPhysicsTokens->mjcSolref, + pxr::VtArray(geom->solref, geom->solref + mjNREF)); + + WriteUniformAttribute( + geom_path, pxr::SdfValueTypeNames->DoubleArray, + MjcPhysicsTokens->mjcSolimp, + pxr::VtArray(geom->solimp, geom->solimp + mjNIMP)); + + WriteUniformAttribute(geom_path, pxr::SdfValueTypeNames->Double, + MjcPhysicsTokens->mjcMargin, geom->margin); + + WriteUniformAttribute(geom_path, pxr::SdfValueTypeNames->Double, + MjcPhysicsTokens->mjcGap, geom->gap); + if (geom->mass >= mjMINVAL || geom->density >= mjMINVAL) { ApplyApiSchema(data_, geom_path, pxr::UsdPhysicsTokens->PhysicsMassAPI); } diff --git a/src/experimental/usd/usd_to_mjspec.cc b/src/experimental/usd/usd_to_mjspec.cc index 2b07dedb..3fe22e04 100644 --- a/src/experimental/usd/usd_to_mjspec.cc +++ b/src/experimental/usd/usd_to_mjspec.cc @@ -584,6 +584,63 @@ void ParseMjcPhysicsCollisionAPI( if (group_attr.HasAuthoredValue()) { group_attr.Get(&geom->group); } + + auto priority_attr = collision_api.GetPriorityAttr(); + if (priority_attr.HasAuthoredValue()) { + priority_attr.Get(&geom->priority); + } + + auto condim_attr = collision_api.GetConDimAttr(); + if (condim_attr.HasAuthoredValue()) { + condim_attr.Get(&geom->condim); + } + + auto solmix_attr = collision_api.GetSolMixAttr(); + if (solmix_attr.HasAuthoredValue()) { + solmix_attr.Get(&geom->solmix); + } + + auto solref_attr = collision_api.GetSolRefAttr(); + if (solref_attr.HasAuthoredValue()) { + pxr::VtDoubleArray solref; + solref_attr.Get(&solref); + if (solref.size() == mjNREF) { + for (int i = 0; i < mjNREF; ++i) { + geom->solref[i] = solref[i]; + } + } else { + mju_warning( + "solref attribute for geom %s has incorrect size %zu, " + "expected %d.", + mjs_getName(geom->element)->c_str(), solref.size(), mjNREF); + } + } + + auto solimp_attr = collision_api.GetSolImpAttr(); + if (solimp_attr.HasAuthoredValue()) { + pxr::VtDoubleArray solimp; + solimp_attr.Get(&solimp); + if (solimp.size() == mjNIMP) { + for (int i = 0; i < mjNIMP; ++i) { + geom->solimp[i] = solimp[i]; + } + } else { + mju_warning( + "solimp attribute for geom %s has incorrect size %zu, " + "expected %d.", + mjs_getName(geom->element)->c_str(), solimp.size(), mjNIMP); + } + } + + auto margin_attr = collision_api.GetMarginAttr(); + if (margin_attr.HasAuthoredValue()) { + margin_attr.Get(&geom->margin); + } + + auto gap_attr = collision_api.GetGapAttr(); + if (gap_attr.HasAuthoredValue()) { + gap_attr.Get(&geom->gap); + } } void ParseMjcPhysicsMeshCollisionAPI( 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 0107301b..63c018a4 100644 --- a/test/experimental/usd/plugins/mjcf/mjcf_file_format_test.cc +++ b/test/experimental/usd/plugins/mjcf/mjcf_file_format_test.cc @@ -1463,13 +1463,36 @@ TEST_F(MjcfSdfFileFormatPluginTest, TestMjcPhysicsCollisionAPI) { - + )"; auto stage = OpenStageWithPhysics(xml); + ExpectAttributeEqual(stage, "/test/body/box.mjc:group", 4); + ExpectAttributeEqual(stage, "/test/body/box.mjc:priority", 2); + ExpectAttributeEqual(stage, "/test/body/box.mjc:condim", 4); + ExpectAttributeEqual(stage, "/test/body/box.mjc:solmix", 0.5); + ExpectAttributeEqual(stage, "/test/body/box.mjc:solref", + pxr::VtArray({0.1, 0.2})); + ExpectAttributeEqual(stage, "/test/body/box.mjc:solimp", + pxr::VtArray({0.3, 0.4, 0.5, 0.6, 0.7})); + ExpectAttributeEqual(stage, "/test/body/box.mjc:margin", 0.8); + ExpectAttributeEqual(stage, "/test/body/box.mjc:gap", 0.9); ExpectAttributeEqual(stage, "/test/body/box.mjc:shellinertia", true); }