diff --git a/src/user/user_objects.cc b/src/user/user_objects.cc index 0e8c0044..4c5fc228 100644 --- a/src/user/user_objects.cc +++ b/src/user/user_objects.cc @@ -5413,9 +5413,13 @@ const mjCWrap* mjCTendon::GetWrap(int i) const { void mjCTendon::ResolveReferences(const mjCModel* m) { int nfailure = 0; + int npulley = 0; for (int i=0; iname; std::string psidesite = path[i]->sidesite; + if (path[i]->type == mjWRAP_PULLEY) { + npulley++; + } try { // look for wrapped element with namespace path[i]->name = prefix + pname + suffix; @@ -5429,7 +5433,7 @@ void mjCTendon::ResolveReferences(const mjCModel* m) { nfailure++; } } - if (nfailure==path.size()) { + if (nfailure==path.size()-npulley) { throw mjCError(this, "tendon '%s' (id = %d): no attached reference found", name.c_str(), id); } prefix.clear(); diff --git a/test/xml/xml_native_reader_test.cc b/test/xml/xml_native_reader_test.cc index 873d9294..2747af2a 100644 --- a/test/xml/xml_native_reader_test.cc +++ b/test/xml/xml_native_reader_test.cc @@ -1441,6 +1441,70 @@ TEST_F(XMLReaderTest, ParseReplicateRepeatedName) { EXPECT_THAT(error.data(), HasSubstr("Element 'replicate'")); } +TEST_F(XMLReaderTest, ParseReplicateTendon) { + static constexpr char xml[] = R"( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + )"; + std::array error; + mjSpec* spec = mj_parseXMLString(xml, 0, error.data(), error.size()); + EXPECT_THAT(spec, NotNull()) << error.data(); + mjModel* m = mj_compile(spec, 0); + EXPECT_THAT(m, NotNull()); + EXPECT_THAT(m->nbody, 67); + EXPECT_THAT(m->ngeom, 66); + EXPECT_THAT(m->nsite, 6); + EXPECT_THAT(m->nu, 1); + EXPECT_THAT(m->ntendon, 1); + mj_deleteModel(m); + mj_deleteSpec(spec); +} + // ---------------------- test spec assets parsing ----------------------------- TEST_F(XMLReaderTest, ParseSpecAssets) {