diff --git a/src/user/user_objects.cc b/src/user/user_objects.cc
index 461fb4ce..b7cc39ba 100644
--- a/src/user/user_objects.cc
+++ b/src/user/user_objects.cc
@@ -120,16 +120,6 @@ mjCAlternative::mjCAlternative() {
// used for geom, site, body and camera frames
const char* mjCAlternative::Set(double* quat, double* inertia,
bool degree, const char* sequence) {
- // check for multiple defitions
- int ndef = (int)mjuu_defined(axisangle[0]) +
- (int)mjuu_defined(xyaxes[0]) +
- (int)mjuu_defined(zaxis[0]) +
- (int)mjuu_defined(euler[0]) +
- (int)mjuu_defined(fullinertia[0]);
- if (ndef>1) {
- return "too many alternative definitions";
- }
-
// set quat using axisangle
if (mjuu_defined(axisangle[0])) {
// convert to radians if necessary, normalize axis
diff --git a/src/xml/xml_base.cc b/src/xml/xml_base.cc
index dae4d611..d95b1f8d 100644
--- a/src/xml/xml_base.cc
+++ b/src/xml/xml_base.cc
@@ -52,9 +52,12 @@ void mjXBase::SetModel(mjCModel* _model) {
// read alternative orientation specification
void mjXBase::ReadAlternative(XMLElement* elem, mjCAlternative& alt) {
string text;
- ReadAttr(elem, "axisangle", 4, alt.axisangle, text);
- ReadAttr(elem, "xyaxes", 6, alt.xyaxes, text);
- ReadAttr(elem, "zaxis", 3, alt.zaxis, text);
- ReadAttr(elem, "euler", 3, alt.euler, text);
- ReadAttr(elem, "fullinertia", 6, alt.fullinertia, text);
+ int read = (int)(elem->Attribute("quat") != 0) +
+ (ReadAttr(elem, "axisangle", 4, alt.axisangle, text) ? 1 : 0) +
+ (ReadAttr(elem, "xyaxes", 6, alt.xyaxes, text) ? 1 : 0) +
+ (ReadAttr(elem, "zaxis", 3, alt.zaxis, text) ? 1 : 0) +
+ (ReadAttr(elem, "euler", 3, alt.euler, text) ? 1 : 0) +
+ (ReadAttr(elem, "fullinertia", 6, alt.fullinertia, text) ? 1 : 0);
+ if (read > 1)
+ throw mjXError(elem, "multiple orientation specifiers for the same field are not allowed");
}
diff --git a/src/xml/xml_native_reader.cc b/src/xml/xml_native_reader.cc
index 71116e3f..943c7b46 100644
--- a/src/xml/xml_native_reader.cc
+++ b/src/xml/xml_native_reader.cc
@@ -1120,9 +1120,9 @@ void mjXReader::OneGeom(XMLElement* elem, mjCGeom* pgeom) {
ReadAttr(elem, "mass", 1, &pgeom->_mass, text);
ReadAttr(elem, "density", 1, &pgeom->density, text);
ReadAttr(elem, "fromto", 6, pgeom->fromto, text);
- ReadAlternative(elem, pgeom->alt);
ReadAttr(elem, "pos", 3, pgeom->pos, text);
ReadAttr(elem, "quat", 4, pgeom->quat, text);
+ ReadAlternative(elem, pgeom->alt);
GetXMLPos(elem, pgeom);
}
diff --git a/test/xml/xml_native_reader_test.cc b/test/xml/xml_native_reader_test.cc
index e7689ad6..bd5c216f 100644
--- a/test/xml/xml_native_reader_test.cc
+++ b/test/xml/xml_native_reader_test.cc
@@ -192,6 +192,49 @@ TEST_F(UserDataTest, AllowsSpaces) {
mj_deleteModel(model);
}
+TEST_F(UserDataTest, InvalidDoubleOrientation) {
+ std::string prefix = "<";
+ std::string suffix = "/>";
+ std::vector orientations = {
+ R"( quat="0 1 0 0" )",
+ R"( euler="1.7 2.9 0.1" )",
+ R"( zaxis="1.7 2.9 0.1" )",
+ R"( axisangle="1.7 2.9 0.1 0" )",
+ R"( xyaxes="1.7 2.9 0.1 0.4 1.4 0.6" )",
+ };
+ std::vector fields = {
+ "geom", "body", "camera", "site"
+ };
+ for (auto const& field : fields) {
+ for (auto const& orient1 : orientations) {
+ for (auto const& orient2 : orientations) {
+ if (orient1 == orient2) continue;
+ std::string xml = prefix + field + orient1 + orient2 + suffix;
+ std::array error;
+ mjModel* model = LoadModelFromString(xml.c_str(), error.data(), error.size());
+ ASSERT_THAT(model, IsNull());
+ EXPECT_THAT(error.data(), HasSubstr("multiple orientation specifiers for the same field"));
+ }
+ }
+ }
+}
+
+TEST_F(UserDataTest, InvalidInertialOrientation) {
+ static constexpr char xml[] = R"(
+
+
+
+
+
+
+
+ )";
+ std::array error;
+ mjModel* model = LoadModelFromString(xml, error.data(), error.size());
+ ASSERT_THAT(model, IsNull());
+ EXPECT_THAT(error.data(), HasSubstr("multiple orientation specifiers for the same field"));
+}
+
// ------------- test relative frame sensor parsing ----------------------------
using RelativeFrameSensorParsingTest = MujocoTest;