mjcPhysics MjcTendon path now has an indices attribute to allow repeated sites in tendon path.

Since relationships in USD can only contain a single instance of a path, it was previously impossible to have a tendon path that visits the same wrap target more than once. This is a fairly common occurence when dealing with tendons that have pulleys.

With this change, if mjc:path:indices is not defined then the behaviour is as it was and the mjc:path is considered to be ordered. If it is authored then the parser will use the indices into mjc:path as the order. This mirrors other similar concepts in USD such as face vertex indices.

PiperOrigin-RevId: 856339887
Change-Id: I6b9503973de377acdb0689be168c16e4aa19b70b
This commit is contained in:
Sam Haves
2026-01-14 13:23:41 -08:00
committed by Copybara-Service
parent 42c565d860
commit d816b38f4c
7 changed files with 69 additions and 12 deletions
@@ -593,7 +593,7 @@ class MjcTendon "MjcTendon" (
doc = "The limit constraint becomes active when the absolute value of the difference between the tendon length and either limit of the specified range falls below this margin. Similar to contacts, the margin parameter is subtracted from the difference between the range limit and the tendon length. The resulting constraint distance is always negative when the constraint is active. This quantity is used to compute constraint impedance as a function of distance."
)
rel mjc:path (
doc = "For spatial tendons, this describes a list of sites and geoms the tendon wraps. For fixed tendons, this is instead a list of joints."
doc = "For spatial tendons, this describes a list of unique of sites and geoms the tendon wraps. For fixed tendons, this is instead a list of joints."
)
uniform double[] mjc:path:coef = [] (
doc = "For fixed tendons passing through joints this represents a multiplicative factor on the position or angle of the targeted joint."
@@ -601,6 +601,9 @@ class MjcTendon "MjcTendon" (
uniform double[] mjc:path:divisors = [] (
doc = "For spatial tendons, this represents an indexed array of divisors. A tendon path segments' length contribution to the overall tendon length is divided by its divisor."
)
uniform int[] mjc:path:indices = [] (
doc = "This list represents the order in which the tendon wraps the sites in mjc:path."
)
uniform int[] mjc:path:segments = [] (
doc = "For spatial tendons, this holds the index of the segment each tendon path wrap point belongs to."
)
+5 -1
View File
@@ -1077,13 +1077,17 @@ class MjcTendon "MjcTendon"
)
rel mjc:path (
doc = "For spatial tendons, this describes a list of sites and geoms the tendon wraps. For fixed tendons, this is instead a list of joints."
doc = "For spatial tendons, this describes a list of unique of sites and geoms the tendon wraps. For fixed tendons, this is instead a list of joints."
)
rel mjc:sideSites (
doc = "For spatial tendons, a geom wrapped by the tendon may specify which side of the geom the tendon wraps around via a site prim. This is a list of sites that are used as side sites in mjc:path."
)
uniform int[] mjc:path:indices = [] (
doc = "This list represents the order in which the tendon wraps the sites in mjc:path."
)
uniform int[] mjc:sideSites:indices = [] (
doc = "For spatial tendons, if mjc:sideSites has targets then index 'i' in this list represents the position in the relationship targets of mjc:sideSites that the geom at index 'i' in mjc:path uses as a side site. It is considered an authoring error to assign a side site to something other than a geom. Geoms that do not use a side site should use index value '-1'."
)
@@ -90,6 +90,17 @@ UsdAttribute MjcPhysicsTendon::CreateTypeAttr(VtValue const& defaultValue,
/* custom = */ false, SdfVariabilityUniform, defaultValue, writeSparsely);
}
UsdAttribute MjcPhysicsTendon::GetMjcPathIndicesAttr() const {
return GetPrim().GetAttribute(MjcPhysicsTokens->mjcPathIndices);
}
UsdAttribute MjcPhysicsTendon::CreateMjcPathIndicesAttr(
VtValue const& defaultValue, bool writeSparsely) const {
return UsdSchemaBase::_CreateAttr(
MjcPhysicsTokens->mjcPathIndices, SdfValueTypeNames->IntArray,
/* custom = */ false, SdfVariabilityUniform, defaultValue, writeSparsely);
}
UsdAttribute MjcPhysicsTendon::GetMjcSideSitesIndicesAttr() const {
return GetPrim().GetAttribute(MjcPhysicsTokens->mjcSideSitesIndices);
}
@@ -377,6 +388,7 @@ const TfTokenVector& MjcPhysicsTendon::GetSchemaAttributeNames(
bool includeInherited) {
static TfTokenVector localNames = {
MjcPhysicsTokens->mjcType,
MjcPhysicsTokens->mjcPathIndices,
MjcPhysicsTokens->mjcSideSitesIndices,
MjcPhysicsTokens->mjcPathSegments,
MjcPhysicsTokens->mjcPathDivisors,
@@ -148,6 +148,7 @@ MjcPhysicsTokensType::MjcPhysicsTokensType()
mjcPath("mjc:path", TfToken::Immortal),
mjcPathCoef("mjc:path:coef", TfToken::Immortal),
mjcPathDivisors("mjc:path:divisors", TfToken::Immortal),
mjcPathIndices("mjc:path:indices", TfToken::Immortal),
mjcPathSegments("mjc:path:segments", TfToken::Immortal),
mjcPriority("mjc:priority", TfToken::Immortal),
mjcQpos("mjc:qpos", TfToken::Immortal),
@@ -323,6 +324,7 @@ MjcPhysicsTokensType::MjcPhysicsTokensType()
mjcPath,
mjcPathCoef,
mjcPathDivisors,
mjcPathIndices,
mjcPathSegments,
mjcPriority,
mjcQpos,