diff --git a/doc/XMLreference.rst b/doc/XMLreference.rst index 51904145..207f55a9 100644 --- a/doc/XMLreference.rst +++ b/doc/XMLreference.rst @@ -2086,7 +2086,7 @@ miscellaneous. This and the next attribute determine the clipping planes of the OpenGL projection. The near clipping plane is particularly important: setting it too close causes (often severe) loss of resolution in the depth buffer, while setting it too far causes objects of interest to be clipped, making it impossible to zoom in. The distance to the - near clipping plane is the model extent multiplied by the value of this attribute. + near clipping plane is the model extent multiplied by the value of this attribute. Must be strictly positive. :at:`zfar`: :at-val:`real, "50"` The distance to the far clipping plane is the model extent multiplied by the value of this attribute. :at:`haze`: :at-val:`real, "0.3"` @@ -2243,7 +2243,7 @@ parameters. If this attribute is specified, it replaces the value of mjModel.stat.extent computed by the compiler. The computed value is half the side of the bounding box of the model in the initial configuration. At runtime this value is multiplied by some of the attributes of the :ref:`map ` element above. When the model is first loaded, the free - camera's initial distance from the :at:`center` (see below) is 1.5 times the :at:`extent`. + camera's initial distance from the :at:`center` (see below) is 1.5 times the :at:`extent`. Must be strictly positive. :at:`center`: :at-val:`real(3), optional` If this attribute is specified, it replaces the value of mjModel.stat.center computed by the compiler. The computed value is the center of the bounding box of the entire model in the initial configuration. This 3D vector is used to diff --git a/src/xml/xml_native_reader.cc b/src/xml/xml_native_reader.cc index 4ec15cbe..a0167cfb 100644 --- a/src/xml/xml_native_reader.cc +++ b/src/xml/xml_native_reader.cc @@ -1016,6 +1016,9 @@ void mjXReader::Statistic(XMLElement* section) { ReadAttr(section, "meanmass", 1, &model->meanmass, text); ReadAttr(section, "meansize", 1, &model->meansize, text); ReadAttr(section, "extent", 1, &model->extent, text); + if (mjuu_defined(model->extent) && model->extent<=0) { + throw mjXError(section, "extent must be strictly positive"); + } ReadAttr(section, "center", 3, model->center, text); } @@ -2192,6 +2195,9 @@ void mjXReader::Visual(XMLElement* section) { ReadAttr(elem, "fogstart", 1, &vis->map.fogstart, text); ReadAttr(elem, "fogend", 1, &vis->map.fogend, text); ReadAttr(elem, "znear", 1, &vis->map.znear, text); + if (vis->map.znear<=0) { + throw mjXError(elem, "znear must be strictly positive"); + } ReadAttr(elem, "zfar", 1, &vis->map.zfar, text); ReadAttr(elem, "haze", 1, &vis->map.haze, text); ReadAttr(elem, "shadowclip", 1, &vis->map.shadowclip, text); diff --git a/test/xml/xml_native_reader_test.cc b/test/xml/xml_native_reader_test.cc index b28fceee..f88e37bb 100644 --- a/test/xml/xml_native_reader_test.cc +++ b/test/xml/xml_native_reader_test.cc @@ -762,6 +762,58 @@ TEST_F(ActuatorParseTest, DampersDontRequireRange) { mj_deleteModel(model); } +TEST_F(UserDataTest, ZnearZeroNotAllowed) { + 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("znear must be strictly positive")); +} + +TEST_F(UserDataTest, ZnearNegativeNotAllowed) { + 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("znear must be strictly positive")); +} + +TEST_F(UserDataTest, ExtentZeroNotAllowed) { + 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("extent must be strictly positive")); +} + +TEST_F(UserDataTest, ExtentNegativeNotAllowed) { + 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("extent must be strictly positive")); +} + } // namespace } // namespace mujoco