diff --git a/doc/changelog.rst b/doc/changelog.rst index 1bd56f67..c14b133b 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -44,6 +44,8 @@ General - The ``vertcollide`` field in :ref:`mjsFlex` has been removed. It is no longer required since :doc:`MuJoCo Warp ` supports native flex collisions. + - The :ref:`mjtWarning` enum value ``mjWARN_VGEOMFULL`` is removed. Exhaustion of visual geoms is now handled + internally by the :ref:`mjvScene`. Bug fixes ^^^^^^^^^ diff --git a/doc/includes/references.h b/doc/includes/references.h index fd855a1c..89b2e81f 100644 --- a/doc/includes/references.h +++ b/doc/includes/references.h @@ -63,7 +63,6 @@ typedef enum mjtWarning_ { // warning types mjWARN_INERTIA = 0, // (near) singular inertia matrix mjWARN_CONTACTFULL, // too many contacts in contact list mjWARN_CNSTRFULL, // too many constraints - mjWARN_VGEOMFULL, // too many visual geoms mjWARN_BADQPOS, // bad number in qpos mjWARN_BADQVEL, // bad number in qvel mjWARN_BADQACC, // bad number in qacc @@ -3096,7 +3095,9 @@ struct mjvScene_ { // abstract scene passed to OpenGL renderer // framing int framewidth; // frame pixel width; 0: disable framing float framergb[3]; // frame color - int status; // status; 0: ok, 1: geoms exhausted + + // geom buffer status + int status; // 0: ok, 1: geoms exhausted, warning issued }; typedef struct mjvScene_ mjvScene; struct mjvFigure_ { // abstract 2D figure passed to OpenGL renderer diff --git a/include/mujoco/mjdata.h b/include/mujoco/mjdata.h index 03585e6c..f98de218 100644 --- a/include/mujoco/mjdata.h +++ b/include/mujoco/mjdata.h @@ -77,7 +77,6 @@ typedef enum mjtWarning_ { // warning types mjWARN_INERTIA = 0, // (near) singular inertia matrix mjWARN_CONTACTFULL, // too many contacts in contact list mjWARN_CNSTRFULL, // too many constraints - mjWARN_VGEOMFULL, // too many visual geoms mjWARN_BADQPOS, // bad number in qpos mjWARN_BADQVEL, // bad number in qvel mjWARN_BADQACC, // bad number in qacc diff --git a/include/mujoco/mjvisualize.h b/include/mujoco/mjvisualize.h index 10127c7c..68e22d21 100644 --- a/include/mujoco/mjvisualize.h +++ b/include/mujoco/mjvisualize.h @@ -355,7 +355,9 @@ struct mjvScene_ { // abstract scene passed to OpenGL renderer // framing int framewidth; // frame pixel width; 0: disable framing float framergb[3]; // frame color - int status; // status; 0: ok, 1: geoms exhausted + + // geom buffer status + int status; // 0: ok, 1: geoms exhausted, warning issued }; typedef struct mjvScene_ mjvScene; diff --git a/plugin/sdf/sdf.cc b/plugin/sdf/sdf.cc index c881c2c6..d3e77d0a 100644 --- a/plugin/sdf/sdf.cc +++ b/plugin/sdf/sdf.cc @@ -91,7 +91,11 @@ void SdfVisualizer::Visualize(const mjModel* m, const mjData* d, for (int k = 0; k < 2; k++) { for (int j = 0; j < (k == 0 ? 2 : n-1); j++) { if (scn->ngeom >= scn->maxgeom) { - mj_warning((mjData*)d, mjWARN_VGEOMFULL, scn->maxgeom); + if (!scn->status) { + mju_warning("Pre-allocated visual geom buffer is full. " + "Increase maxgeom above %d.", scn->maxgeom); + scn->status = 1; + } return; } mjvGeom* thisgeom = scn->geoms + scn->ngeom; diff --git a/plugin/sensor/touch_grid.cc b/plugin/sensor/touch_grid.cc index 1dfdfd02..98ba90f5 100644 --- a/plugin/sensor/touch_grid.cc +++ b/plugin/sensor/touch_grid.cc @@ -427,7 +427,11 @@ void TouchGrid::Visualize(const mjModel* m, mjData* d, const mjvOption* opt, continue; } if (scn->ngeom >= scn->maxgeom) { - mj_warning(d, mjWARN_VGEOMFULL, scn->maxgeom); + if (!scn->status) { + mju_warning("Pre-allocated visual geom buffer is full. " + "Increase maxgeom above %d.", scn->maxgeom); + scn->status = 1; + } mj_freeStack(d); return; } else { diff --git a/python/mujoco/introspect/enums.py b/python/mujoco/introspect/enums.py index bc28f78d..d686e861 100644 --- a/python/mujoco/introspect/enums.py +++ b/python/mujoco/introspect/enums.py @@ -578,12 +578,11 @@ ENUMS: Mapping[str, EnumDecl] = dict([ ('mjWARN_INERTIA', 0), ('mjWARN_CONTACTFULL', 1), ('mjWARN_CNSTRFULL', 2), - ('mjWARN_VGEOMFULL', 3), - ('mjWARN_BADQPOS', 4), - ('mjWARN_BADQVEL', 5), - ('mjWARN_BADQACC', 6), - ('mjWARN_BADCTRL', 7), - ('mjNWARNING', 8), + ('mjWARN_BADQPOS', 3), + ('mjWARN_BADQVEL', 4), + ('mjWARN_BADQACC', 5), + ('mjWARN_BADCTRL', 6), + ('mjNWARNING', 7), ]), )), ('mjtTimer', diff --git a/python/mujoco/introspect/structs.py b/python/mujoco/introspect/structs.py index c1838efa..57a0476e 100644 --- a/python/mujoco/introspect/structs.py +++ b/python/mujoco/introspect/structs.py @@ -5416,7 +5416,7 @@ STRUCTS: Mapping[str, StructDecl] = dict([ name='warning', type=ArrayType( inner_type=ValueType(name='mjWarningStat'), - extents=(8,), + extents=(7,), ), doc='warning statistics (mutable)', ), @@ -10524,7 +10524,7 @@ STRUCTS: Mapping[str, StructDecl] = dict([ StructFieldDecl( name='status', type=ValueType(name='int'), - doc='status; 0: ok, 1: geoms exhausted', + doc='0: ok, 1: geoms exhausted, warning issued', ), ), )), diff --git a/python/mujoco/introspect/structs_test.py b/python/mujoco/introspect/structs_test.py index 0bec9a8b..e43e3a4c 100644 --- a/python/mujoco/introspect/structs_test.py +++ b/python/mujoco/introspect/structs_test.py @@ -14,8 +14,6 @@ # ============================================================================== """Tests for structs.py.""" -import re - from absl.testing import absltest from . import ast_nodes @@ -36,7 +34,7 @@ class StructsTest(absltest.TestCase): field_names.add(field.name) if field.name == 'warning': self.assertEqual(field.type, - type_parsing.parse_type('mjWarningStat[8]')) + type_parsing.parse_type('mjWarningStat[7]')) self.assertEqual(field.doc, 'warning statistics (mutable)') elif field.name == 'qpos': self.assertEqual(field.type, type_parsing.parse_type('mjtNum*')) diff --git a/simulate/simulate.cc b/simulate/simulate.cc index 8144ad8f..522404b9 100644 --- a/simulate/simulate.cc +++ b/simulate/simulate.cc @@ -2142,11 +2142,7 @@ void Simulate::Sync(bool state_only) { m_->stat = m_passive_->stat; } - // synchronize number of mjWARN_VGEOMFULL warnings - if (d_passive_->warning[mjWARN_VGEOMFULL].number > warn_vgeomfull_prev_) { - d_->warning[mjWARN_VGEOMFULL].number += - d_passive_->warning[mjWARN_VGEOMFULL].number - warn_vgeomfull_prev_; - } + } if (pending_.save_xml) { @@ -2334,7 +2330,7 @@ void Simulate::Sync(bool state_only) { mjopt_prev_ = m_passive_->opt; mjvis_prev_ = m_passive_->vis; mjstat_prev_ = m_passive_->stat; - warn_vgeomfull_prev_ = d_passive_->warning[mjWARN_VGEOMFULL].number; + } // update settings @@ -2550,7 +2546,7 @@ void Simulate::LoadOnRenderThread() { mjopt_prev_ = m_->opt; opt_prev_ = opt; cam_prev_ = cam; - warn_vgeomfull_prev_ = d_->warning[mjWARN_VGEOMFULL].number; + // full copy on init m_passive_ = mj_copyModel(nullptr, m_); @@ -3022,7 +3018,11 @@ void Simulate::RenderLoop() { int nusergeom = user_scn_geoms_.size(); int ngeom = std::min(nusergeom, this->scn.maxgeom - this->scn.ngeom); if (ngeom < nusergeom) { - mj_warning(d_passive_, mjWARN_VGEOMFULL, this->scn.maxgeom); + if (!this->scn.status) { + mju_warning("Pre-allocated visual geom buffer is full. " + "Increase maxgeom above %d.", this->scn.maxgeom); + this->scn.status = 1; + } } std::memcpy(this->scn.geoms + this->scn.ngeom, user_scn_geoms_.data(), ngeom * sizeof(mjvGeom)); diff --git a/simulate/simulate.h b/simulate/simulate.h index 4fad3961..27727ee7 100644 --- a/simulate/simulate.h +++ b/simulate/simulate.h @@ -147,8 +147,6 @@ class Simulate { mjvOption opt_prev_; mjvCamera cam_prev_; - int warn_vgeomfull_prev_; - // pending GUI-driven actions, to be applied at the next call to Sync struct { std::optional save_xml; diff --git a/src/engine/engine_util_misc.c b/src/engine/engine_util_misc.c index 65057a51..8a867b1d 100644 --- a/src/engine/engine_util_misc.c +++ b/src/engine/engine_util_misc.c @@ -1595,10 +1595,6 @@ const char* mju_warningText(int warning, size_t info) { "Increase arena memory allocation above %s bytes.", mju_writeNumBytes(info)); break; - case mjWARN_VGEOMFULL: - mjSNPRINTF(str, "Pre-allocated visual geom buffer is full. Increase maxgeom above %zu.", info); - break; - case mjWARN_BADQPOS: mjSNPRINTF(str, "Nan, Inf or huge value in QPOS at DOF %zu. The simulation is unstable.", info); break; diff --git a/src/engine/engine_vis_visualize.c b/src/engine/engine_vis_visualize.c index 3e8f7cf1..b1b74d21 100644 --- a/src/engine/engine_vis_visualize.c +++ b/src/engine/engine_vis_visualize.c @@ -169,7 +169,11 @@ static int bodycategory(const mjModel* m, int bodyid) { mjvGeom* acquireGeom(mjvScene* scn, int objid, int category, int objtype) { // check for overflow, SHOULD NOT OCCUR if (scn->ngeom >= scn->maxgeom) { - scn->status = 1; + if (!scn->status) { + mju_warning("Pre-allocated visual geom buffer is full. " + "Increase maxgeom above %d.", scn->maxgeom); + scn->status = 1; + } return NULL; } @@ -3379,7 +3383,6 @@ void mjv_updateScene(const mjModel* m, mjData* d, const mjvOption* opt, const mjvPerturb* pert, mjvCamera* cam, int catmask, mjvScene* scn) { // clear geoms scn->ngeom = 0; - scn->status = 0; // trigger plugin visualization hooks if (m->nplugin) { @@ -3416,10 +3419,6 @@ void mjv_updateScene(const mjModel* m, mjData* d, const mjvOption* opt, if (opt->flags[mjVIS_SKIN]) { mjv_updateActiveSkin(m, d, scn, opt); } - - if (scn->status) { - mj_warning(d, mjWARN_VGEOMFULL, scn->maxgeom); - } } diff --git a/test/engine/engine_vis_visualize_test.cc b/test/engine/engine_vis_visualize_test.cc index 90ec776e..31462697 100644 --- a/test/engine/engine_vis_visualize_test.cc +++ b/test/engine/engine_vis_visualize_test.cc @@ -101,7 +101,6 @@ TEST_F(MjvSceneTest, UpdateSceneGeomsExhausted) { mjv_updateScene(model, data, &opt_, &pert_, &cam_, mjCAT_ALL, &scn_); EXPECT_EQ(scn_.status, 1); EXPECT_EQ(scn_.ngeom, maxgeoms); - EXPECT_EQ(data->warning[mjWARN_VGEOMFULL].number, 1); mj_deleteData(data); FreeSceneObjects(); diff --git a/unity/Runtime/Bindings/MjBindings.cs b/unity/Runtime/Bindings/MjBindings.cs index 1e6168a9..ca287bd6 100644 --- a/unity/Runtime/Bindings/MjBindings.cs +++ b/unity/Runtime/Bindings/MjBindings.cs @@ -489,12 +489,11 @@ public enum mjtWarning : int{ mjWARN_INERTIA = 0, mjWARN_CONTACTFULL = 1, mjWARN_CNSTRFULL = 2, - mjWARN_VGEOMFULL = 3, - mjWARN_BADQPOS = 4, - mjWARN_BADQVEL = 5, - mjWARN_BADQACC = 6, - mjWARN_BADCTRL = 7, - mjNWARNING = 8, + mjWARN_BADQPOS = 3, + mjWARN_BADQVEL = 4, + mjWARN_BADQACC = 5, + mjWARN_BADCTRL = 6, + mjNWARNING = 7, } public enum mjtTimer : int{ mjTIMER_STEP = 0, @@ -5699,7 +5698,6 @@ public unsafe struct mjData_ { public mjWarningStat_ warning4; public mjWarningStat_ warning5; public mjWarningStat_ warning6; - public mjWarningStat_ warning7; public mjTimerStat_ timer0; public mjTimerStat_ timer1; public mjTimerStat_ timer2; diff --git a/unity/Runtime/Components/MjScene.cs b/unity/Runtime/Components/MjScene.cs index c9219d8e..13111a6b 100644 --- a/unity/Runtime/Components/MjScene.cs +++ b/unity/Runtime/Components/MjScene.cs @@ -359,22 +359,18 @@ public class MjScene : MonoBehaviour { } if (Data->warning3.number > 0) { Data->warning3.number = 0; - throw new PhysicsRuntimeException("VGEOMFULL: who constructed a mjvScene?!"); + throw new PhysicsRuntimeException("BADQPOS: NaN/inf in qpos."); } if (Data->warning4.number > 0) { Data->warning4.number = 0; - throw new PhysicsRuntimeException("BADQPOS: NaN/inf in qpos."); + throw new PhysicsRuntimeException("BADQVEL: NaN/inf in qvel."); } if (Data->warning5.number > 0) { Data->warning5.number = 0; - throw new PhysicsRuntimeException("BADQVEL: NaN/inf in qvel."); + throw new PhysicsRuntimeException("BADQACC: NaN/inf in qacc."); } if (Data->warning6.number > 0) { Data->warning6.number = 0; - throw new PhysicsRuntimeException("BADQACC: NaN/inf in qacc."); - } - if (Data->warning7.number > 0) { - Data->warning7.number = 0; throw new PhysicsRuntimeException("BADCTRL: NaN/inf in ctrl."); } } diff --git a/wasm/codegen/generated/bindings.cc b/wasm/codegen/generated/bindings.cc index dc2f2fc9..410cef5a 100644 --- a/wasm/codegen/generated/bindings.cc +++ b/wasm/codegen/generated/bindings.cc @@ -11380,7 +11380,6 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) { .value("mjWARN_INERTIA", mjWARN_INERTIA) .value("mjWARN_CONTACTFULL", mjWARN_CONTACTFULL) .value("mjWARN_CNSTRFULL", mjWARN_CNSTRFULL) - .value("mjWARN_VGEOMFULL", mjWARN_VGEOMFULL) .value("mjWARN_BADQPOS", mjWARN_BADQPOS) .value("mjWARN_BADQVEL", mjWARN_BADQVEL) .value("mjWARN_BADQACC", mjWARN_BADQACC)