From 70d03adc71445bbcf17f3dbe98f804f47f532d55 Mon Sep 17 00:00:00 2001 From: Haroon Qureshi Date: Tue, 30 Sep 2025 00:05:06 -0700 Subject: [PATCH] Store the status of the scene in the scene itself. PiperOrigin-RevId: 813119377 Change-Id: Iebff9e7796ca9809ea22902522982bfd96eeba06 --- doc/includes/references.h | 1 + include/mujoco/mjvisualize.h | 1 + python/mujoco/introspect/structs.py | 5 + python/mujoco/structs.cc | 1 + src/engine/engine_vis_visualize.c | 111 ++++++++++++----------- test/engine/engine_vis_visualize_test.cc | 2 + unity/Runtime/Bindings/MjBindings.cs | 1 + 7 files changed, 69 insertions(+), 53 deletions(-) diff --git a/doc/includes/references.h b/doc/includes/references.h index 25cc0b4a..b494a351 100644 --- a/doc/includes/references.h +++ b/doc/includes/references.h @@ -2948,6 +2948,7 @@ 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 }; typedef struct mjvScene_ mjvScene; struct mjvFigure_ { // abstract 2D figure passed to OpenGL renderer diff --git a/include/mujoco/mjvisualize.h b/include/mujoco/mjvisualize.h index 07c67bac..25a28618 100644 --- a/include/mujoco/mjvisualize.h +++ b/include/mujoco/mjvisualize.h @@ -354,6 +354,7 @@ 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 }; typedef struct mjvScene_ mjvScene; diff --git a/python/mujoco/introspect/structs.py b/python/mujoco/introspect/structs.py index 599669f8..988a54ef 100644 --- a/python/mujoco/introspect/structs.py +++ b/python/mujoco/introspect/structs.py @@ -7139,6 +7139,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([ ), doc='frame color', ), + StructFieldDecl( + name='status', + type=ValueType(name='int'), + doc='status; 0: ok, 1: geoms exhausted', + ), ), )), ('mjvFigure', diff --git a/python/mujoco/structs.cc b/python/mujoco/structs.cc index 62ac789c..8c036e7d 100644 --- a/python/mujoco/structs.cc +++ b/python/mujoco/structs.cc @@ -1196,6 +1196,7 @@ This is useful for example when the MJB is not available as a file on disk.)")); X(scale); X(stereo); X(framewidth); + X(status); #undef X #define X(var) DefinePyArray(mjvScene, #var, &MjvSceneWrapper::var) diff --git a/src/engine/engine_vis_visualize.c b/src/engine/engine_vis_visualize.c index ad5a23da..9247b3e8 100644 --- a/src/engine/engine_vis_visualize.c +++ b/src/engine/engine_vis_visualize.c @@ -73,9 +73,9 @@ static void makeLabel(const mjModel* m, mjtObj type, int id, char* label) { // returns 1 if there are no more geoms available in the scene, 0 otherwise -static inline int geomsExhausted(mjData* d, mjvScene* scn) { +static inline int geomsExhausted(mjvScene* scn) { if ( scn->ngeom>=scn->maxgeom ) { - mj_warning(d, mjWARN_VGEOMFULL, scn->maxgeom); + scn->status = 1; return 1; } return 0; @@ -203,7 +203,7 @@ static void addContactGeom(const mjModel* m, mjData* d, const mjtByte* flags, // contact point if (flags[mjVIS_CONTACTPOINT]) { - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -288,7 +288,7 @@ static void addContactGeom(const mjModel* m, mjData* d, const mjtByte* flags, // draw the three axes (separate geoms) for (int j=0; j < 3; j++) { - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -369,7 +369,7 @@ static void addContactGeom(const mjModel* m, mjData* d, const mjtByte* flags, } // one-directional arrow for friction and world, symmetric otherwise - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -759,7 +759,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, (category & catmask)) { for (int i=0; i < m->nflex; i++) { if (vopt->flexgroup[mjMAX(0, mjMIN(mjNGROUP-1, m->flex_group[i]))]) { - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -808,7 +808,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, if (vopt->flags[mjVIS_SKIN] && (category & catmask)) { for (int i=0; i < m->nskin; i++) { if (vopt->skingroup[mjMAX(0, mjMIN(mjNGROUP-1, m->skin_group[i]))]) { - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -890,7 +890,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, rgba = m->vis.rgba.bvactive; } - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -925,7 +925,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, rgba = m->vis.rgba.bvactive; } - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -958,7 +958,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, for (int k=0; k < 2; k++) { if (scn->ngeom >= scn->maxgeom) break; if (i == 0) { - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -967,7 +967,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, releaseGeom(&thisgeom, scn); } if (j == 0) { - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -976,7 +976,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, releaseGeom(&thisgeom, scn); } if (k == 0) { - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -1033,7 +1033,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, mju_mulMatVec3(pos, xmat, center); mju_addTo3(pos, xpos); - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -1057,7 +1057,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, for (int b = 0; b < m->mesh_octnum[meshid]; b++) { int i = b + m->mesh_octadr[meshid]; - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -1118,7 +1118,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, float* mesh_vert = m->mesh_vert + 3*m->mesh_vertadr[mesh_id]; int* face = m->mesh_face + 3*m->mesh_faceadr[mesh_id]; for (int i=0; i < m->mesh_facenum[mesh_id]; i++) { - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } else { // triangle in global frame @@ -1169,7 +1169,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, for (int i=1; i < m->nbody; i++) { // skip if mass too small or if this body is static and static bodies are masked if (m->body_mass[i] > mjMINVAL && (bodycategory(m, i) & catmask)) { - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -1228,7 +1228,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, int i = pert->select; if ((pert->active | pert->active2) & mjPERT_TRANSLATE) { - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -1253,7 +1253,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, releaseGeom(&thisgeom, scn); // add small sphere at end-effector - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -1269,7 +1269,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, } if ((pert->active | pert->active2) & mjPERT_ROTATE) { - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -1327,7 +1327,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, // draw the three axes (separate geoms) for (int j=0; j < 3; j++) { - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -1366,7 +1366,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, mju_mulMatVec3(selpos, d->xmat+9*pert->select, pert->localpos); mju_addTo3(selpos, d->xpos+3*pert->select); - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -1396,7 +1396,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, if (bodycategory(m, i) & ~catmask) { continue; } - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -1425,7 +1425,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, sz[1] = m->vis.scale.jointlength * scl; sz[0] = m->vis.scale.jointwidth * scl; - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -1568,7 +1568,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, if (m->actuator_trntype[i] == mjTRN_JOINT || m->actuator_trntype[i] == mjTRN_JOINTINPARENT || m->actuator_trntype[i] == mjTRN_SITE) { - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -1631,7 +1631,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, // add inflated geom if it is a regular primitive if (geomtype != mjGEOM_PLANE && geomtype != mjGEOM_HFIELD && geomtype != mjGEOM_MESH && geomtype != mjGEOM_SDF) { - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -1658,7 +1658,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, else if (m->actuator_trntype[i] == mjTRN_TENDON && d->ten_wrapnum[j]) { for (int k=d->ten_wrapadr[j]; k < d->ten_wrapadr[j]+d->ten_wrapnum[j]-1; k++) { if (d->wrap_obj[k] != -2 && d->wrap_obj[k+1] != -2) { - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -1705,7 +1705,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, if (m->body_dofnum[weld_id]) { int islandid = d->dof_island[m->body_dofadr[weld_id]]; if (islandid > -1) { - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -1743,7 +1743,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, int geomgroup = mjMAX(0, mjMIN(mjNGROUP-1, m->geom_group[i])); if (vopt->geomgroup[geomgroup]) { - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -1869,7 +1869,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, sz[0] = m->vis.scale.framewidth * scl; sz[1] = m->vis.scale.framelength * scl; for (int j=0; j < 3; j++) { - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -1911,7 +1911,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, // show if group enabled if (vopt->sitegroup[mjMAX(0, mjMIN(mjNGROUP-1, m->site_group[i]))]) { - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -1952,7 +1952,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, sz[0] = m->vis.scale.framewidth * scl; sz[1] = m->vis.scale.framelength * scl; for (int j=0; j < 3; j++) { - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -2041,21 +2041,21 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, // triangulation and wireframe of the frustum for (int e=0; e < 4; e++) { - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } thisgeom = acquireGeom(scn, i, category, objtype); makeTriangle(thisgeom, vnear[e], vfar[e], vnear[(e+1)%4], rgba); releaseGeom(&thisgeom, scn); - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } thisgeom = acquireGeom(scn, i, category, objtype); makeTriangle(thisgeom, vfar[e], vfar[(e+1)%4], vnear[(e+1)%4], rgba); releaseGeom(&thisgeom, scn); - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -2063,7 +2063,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, mjv_connector(thisgeom, mjGEOM_LINE, 3, vnear[e], vnear[(e+1)%4]); f2f(thisgeom->rgba, rgba, 4); releaseGeom(&thisgeom, scn); - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -2071,7 +2071,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, mjv_connector(thisgeom, mjGEOM_LINE, 3, vfar[e], vfar[(e+1)%4]); f2f(thisgeom->rgba, rgba, 4); releaseGeom(&thisgeom, scn); - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -2082,7 +2082,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, } } - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -2104,7 +2104,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, releaseGeom(&thisgeom, scn); - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -2140,7 +2140,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, sz[0] = m->vis.scale.framewidth * scl; sz[1] = m->vis.scale.framelength * scl; for (int j=0; j < 3; j++) { - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -2182,7 +2182,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, // make light position: offset backward, to avoid casting shadow mju_addScl3(vec, d->light_xpos+3*i, d->light_xdir+3*i, -scl * m->vis.scale.light -0.0001); - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -2215,7 +2215,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, sz[0] = m->vis.scale.framewidth * scl; sz[1] = m->vis.scale.framelength * scl; for (int j=0; j < 3; j++) { - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -2290,7 +2290,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, if (!draw_catenary) { for (int j=d->ten_wrapadr[i]; j < d->ten_wrapadr[i]+d->ten_wrapnum[i]-1; j++) { if (d->wrap_obj[j] != -2 && d->wrap_obj[j+1] != -2) { - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -2378,7 +2378,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, // draw npoints-1 segments for (int j=0; j < npoints-1; j++) { - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -2434,7 +2434,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, mju_addTo3(end, d->site_xpos+3*k); // render slider - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -2448,7 +2448,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, releaseGeom(&thisgeom, scn); // render crank - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -2471,7 +2471,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, if (vopt->flags[mjVIS_COM] && (category & catmask)) { for (int i=1; i < m->nbody; i++) { if (m->body_rootid[i] == i) { - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -2500,7 +2500,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, cur = d->xipos+3*i; if (m->body_jntnum[i]) { for (int j=m->body_jntadr[i]+m->body_jntnum[i]-1; j >= m->body_jntadr[i]; j--) { - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -2517,7 +2517,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, } // connect first joint (or com) to parent com - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -2545,7 +2545,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, } // make ray - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -2567,7 +2567,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, } // make ray - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -2593,7 +2593,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, // map force to spatial vector in world frame mju_scl3(vec, xfrc, m->vis.map.force/m->stat.meanmass); - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -2637,7 +2637,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, // construct geom sz[0] = scl * m->vis.scale.constraint; - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -2648,7 +2648,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, } releaseGeom(&thisgeom, scn); - if (geomsExhausted(d, scn)) { + if (geomsExhausted(scn)) { return; } @@ -3282,6 +3282,7 @@ 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) { @@ -3318,6 +3319,10 @@ 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 7e51c806..ce2e4022 100644 --- a/test/engine/engine_vis_visualize_test.cc +++ b/test/engine/engine_vis_visualize_test.cc @@ -67,6 +67,7 @@ TEST_F(MjvSceneTest, UpdateScene) { } mjv_updateScene(model, data, &opt_, &pert_, &cam_, mjCAT_ALL, &scn_); + EXPECT_EQ(scn_.status, 0); EXPECT_GT(scn_.ngeom, 0); EXPECT_GT(scn_.nlight, 0); if (model->nskin) EXPECT_GT(scn_.nskin, 0); @@ -98,6 +99,7 @@ TEST_F(MjvSceneTest, UpdateSceneGeomsExhausted) { // clear handlers to avoid test failure; we are explicitly expecting a warning mju_clearHandlers(); 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); diff --git a/unity/Runtime/Bindings/MjBindings.cs b/unity/Runtime/Bindings/MjBindings.cs index 62837b05..dc791081 100644 --- a/unity/Runtime/Bindings/MjBindings.cs +++ b/unity/Runtime/Bindings/MjBindings.cs @@ -6286,6 +6286,7 @@ public unsafe struct mjvScene_ { public fixed byte flags[10]; public int framewidth; public fixed float framergb[3]; + public int status; } [StructLayout(LayoutKind.Sequential)]