From fea7c10b9568fd470e8d03b27ceb7efeb8cb4563 Mon Sep 17 00:00:00 2001 From: Yuval Tassa Date: Fri, 19 Jan 2024 02:59:06 -0800 Subject: [PATCH] Add `bvhactive` flag to `visual/global`. Fixes #1279. PiperOrigin-RevId: 599778272 Change-Id: If5530629035cc68b198447503388f59a04041b13 --- doc/XMLreference.rst | 15 ++++++++ doc/XMLschema.rst | 6 ++-- doc/changelog.rst | 21 ++++++++---- doc/includes/references.h | 3 ++ include/mujoco/mjmodel.h | 3 ++ introspect/structs.py | 21 ++++++++++++ python/mujoco/structs.cc | 3 ++ python/mujoco/structs.h | 2 ++ simulate/simulate.cc | 1 + src/engine/engine_collision_driver.c | 29 +++++++++++----- src/engine/engine_collision_sdf.c | 9 ++--- src/engine/engine_io.c | 3 ++ src/engine/engine_ray.c | 8 ++++- src/engine/engine_vis_visualize.c | 38 +++++++++++++-------- src/xml/xml_native_reader.cc | 14 +++++--- src/xml/xml_native_writer.cc | 3 ++ test/engine/testdata/ray/stanford_bunny.xml | 9 ++++- unity/Runtime/Bindings/MjBindings.cs | 3 ++ 18 files changed, 147 insertions(+), 44 deletions(-) diff --git a/doc/XMLreference.rst b/doc/XMLreference.rst index 072aaab5..c781f400 100644 --- a/doc/XMLreference.rst +++ b/doc/XMLreference.rst @@ -715,6 +715,12 @@ is effectively a miscellaneous subsection. This attribute specifies how the equivalent inertia is visualized. "false": use box, "true": use ellipsoid. +.. _visual-global-bvactive: + +:at:`bvactive`: :at-val:`[false, true], "true"` + This attribute specifies whether collision and raycasting code should mark elements of Bounding Volume Hierarchies + as intersecting, for the purpose of visualization. Setting this attribute to "false" can speed up simulation for + models with high-resolution meshes. .. _visual-quality: @@ -1122,6 +1128,15 @@ disables the rendering of the corresponding object. :at:`frustum`: :at-val:`real(4), "1 1 0 0.2"` Color used to render the camera frustum. +.. _visual-rgba-bv: + +:at:`bv`: :at-val:`real(4), "0 1 0 0.5"` + Color used to render bounding volumes. + +.. _visual-rgba-bvactive: + +:at:`bvactive`: :at-val:`real(4), "1 0 0 0.5"` + Color used to render active bounding volumes, if the :ref:`bvactive` flag is "true". .. _asset: diff --git a/doc/XMLschema.rst b/doc/XMLschema.rst index 702adeb2..b319f26b 100644 --- a/doc/XMLschema.rst +++ b/doc/XMLschema.rst @@ -65,7 +65,7 @@ | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | | | | | :ref:`linewidth` | :ref:`glow` | :ref:`offwidth` | :ref:`offheight` | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | -| | | | :ref:`realtime` | :ref:`ellipsoidinertia` | | | | +| | | | :ref:`realtime` | :ref:`ellipsoidinertia` | :ref:`bvactive` | | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | +------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | |_| visual |br| |_| |L| | | .. table:: | @@ -126,7 +126,9 @@ | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | | | | | :ref:`contacttorque` | :ref:`contactgap` | :ref:`rangefinder` | :ref:`constraint` | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | -| | | | :ref:`slidercrank` | :ref:`crankbroken` | :ref:`frustum` | | | +| | | | :ref:`slidercrank` | :ref:`crankbroken` | :ref:`frustum` | :ref:`bv` | | +| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | +| | | | :ref:`bvactive` | | | | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | +------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | mujoco |br| |L| | | *no attributes* | diff --git a/doc/changelog.rst b/doc/changelog.rst index 33230ae5..910d6048 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -12,19 +12,26 @@ General 2. Removed the :ref:`timer` for midphase colllision detection, it is now folded in with the narrowphase timer. This is because timing the two phases seperately required fine-grained timers inside the collision functions; these functions are so small and fast that the timer itself was incurring a measurable cost. +3. Added the flag :ref:`bvactive` to ``visual/global``, allowing users to turn off + visualisation of active bounding volumes (the red/green boxes in this :ref:`this changelog item`). For + models with very high-resolution meshes, the computation required for this visualization can slow down simulation + speed. Fixes :github:issue:`1279`. + + - Added color of :ref:`bounding volumes` and :ref:`active bounding volumes` + to :ref:`visual/rgba`. MJX ^^^ -3. Added :ref:`dyntype` ``filterexact``. -4. Added :at:`site` transmission. -5. Updated MJX colab tutorial with more stable quadruped environment. -6. Added ``mjx.ray`` which mirrors :ref:`mj_ray` for planes, spheres, capsules, boxes, and meshes. +4. Added :ref:`dyntype` ``filterexact``. +5. Added :at:`site` transmission. +6. Updated MJX colab tutorial with more stable quadruped environment. +7. Added ``mjx.ray`` which mirrors :ref:`mj_ray` for planes, spheres, capsules, boxes, and meshes. Bug fixes ^^^^^^^^^ -7. Fixed a bug that prevented the use of pins with plugins if flexes are not in the worldbody. Fixes +8. Fixed a bug that prevented the use of pins with plugins if flexes are not in the worldbody. Fixes :github:issue:`1270`. -7. Fixed a bug in the :ref:`muscle model` that led to non-zero values outside the lower +9. Fixed a bug in the :ref:`muscle model` that led to non-zero values outside the lower bound of the length range. Fixes :github:issue:`1342`. @@ -624,6 +631,8 @@ General :align: right :width: 350px +.. _midphase: + 2. Added a collision mid-phase for pruning geoms in body pairs, see :ref:`documentation` for more details. This is based on static AABB bounding volume hierarchy (a BVH binary tree) in the body inertial frame. The GIF on the right is cut from `this longer video `__. diff --git a/doc/includes/references.h b/doc/includes/references.h index 01504204..b94d5d71 100644 --- a/doc/includes/references.h +++ b/doc/includes/references.h @@ -753,6 +753,7 @@ struct mjVisual_ { // visualization options int offwidth; // width of offscreen buffer int offheight; // height of offscreen buffer int ellipsoidinertia; // geom for inertia visualization (0: box, 1: ellipsoid) + int bvactive; // visualize active bounding volumes (0: no, 1: yes) } global; struct { // rendering quality @@ -830,6 +831,8 @@ struct mjVisual_ { // visualization options float slidercrank[4]; // slidercrank float crankbroken[4]; // used when crank must be stretched/broken float frustum[4]; // camera frustum + float bv[4]; // bounding volume + float bvactive[4]; // active bounding volume } rgba; }; typedef struct mjVisual_ mjVisual; diff --git a/include/mujoco/mjmodel.h b/include/mujoco/mjmodel.h index d3417683..b91ee594 100644 --- a/include/mujoco/mjmodel.h +++ b/include/mujoco/mjmodel.h @@ -459,6 +459,7 @@ struct mjVisual_ { // visualization options int offwidth; // width of offscreen buffer int offheight; // height of offscreen buffer int ellipsoidinertia; // geom for inertia visualization (0: box, 1: ellipsoid) + int bvactive; // visualize active bounding volumes (0: no, 1: yes) } global; struct { // rendering quality @@ -536,6 +537,8 @@ struct mjVisual_ { // visualization options float slidercrank[4]; // slidercrank float crankbroken[4]; // used when crank must be stretched/broken float frustum[4]; // camera frustum + float bv[4]; // bounding volume + float bvactive[4]; // active bounding volume } rgba; }; typedef struct mjVisual_ mjVisual; diff --git a/introspect/structs.py b/introspect/structs.py index 02e92c7f..8f44927d 100644 --- a/introspect/structs.py +++ b/introspect/structs.py @@ -352,6 +352,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([ type=ValueType(name='int'), doc='geom for inertia visualization (0: box, 1: ellipsoid)', # pylint: disable=line-too-long ), + StructFieldDecl( + name='bvactive', + type=ValueType(name='int'), + doc='visualize active bounding volumes (0: no, 1: yes)', # pylint: disable=line-too-long + ), ), ), doc='', @@ -780,6 +785,22 @@ STRUCTS: Mapping[str, StructDecl] = dict([ ), doc='camera frustum', ), + StructFieldDecl( + name='bv', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(4,), + ), + doc='bounding volume', + ), + StructFieldDecl( + name='bvactive', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(4,), + ), + doc='active bounding volume', + ), ), ), doc='', diff --git a/python/mujoco/structs.cc b/python/mujoco/structs.cc index 1d7cc901..0de15f76 100644 --- a/python/mujoco/structs.cc +++ b/python/mujoco/structs.cc @@ -1400,6 +1400,7 @@ PYBIND11_MODULE(_structs, m) { X(offwidth); X(offheight); X(ellipsoidinertia); + X(bvactive); #undef X py::class_ mjVisualQuality(mjVisual, "Quality"); @@ -1529,6 +1530,8 @@ PYBIND11_MODULE(_structs, m) { X(slidercrank); X(crankbroken); X(frustum); + X(bv); + X(bvactive); #undef X #define X(var) \ diff --git a/python/mujoco/structs.h b/python/mujoco/structs.h index 5fe3e1b3..5aba80f5 100644 --- a/python/mujoco/structs.h +++ b/python/mujoco/structs.h @@ -233,6 +233,8 @@ class MjWrapper : public WrapperBase { X(slidercrank); X(crankbroken); X(frustum); + X(bv); + X(bvactive); #undef X }; diff --git a/simulate/simulate.cc b/simulate/simulate.cc index d9d4f99d..f9938468 100644 --- a/simulate/simulate.cc +++ b/simulate/simulate.cc @@ -896,6 +896,7 @@ void MakeVisualizationSection(mj::Simulate* sim, const mjModel* m, int oldstate) {mjITEM_EDITNUM, "Extent", 2, &(stat->extent), "1"}, {mjITEM_EDITFLOAT, "Field of view", 2, &(vis->global.fovy), "1"}, {mjITEM_RADIO, "Inertia", 5, &(vis->global.ellipsoidinertia), "Box\nEllipsoid"}, + {mjITEM_RADIO, "BVH active", 5, &(vis->global.bvactive), "False\nTrue"}, {mjITEM_SEPARATOR, "Map", 1}, {mjITEM_EDITFLOAT, "Stiffness", 2, &(vis->map.stiffness), "1"}, {mjITEM_EDITFLOAT, "Rot stiffness", 2, &(vis->map.stiffnessrot), "1"}, diff --git a/src/engine/engine_collision_driver.c b/src/engine/engine_collision_driver.c index b8a8a7d0..e3a18c2d 100644 --- a/src/engine/engine_collision_driver.c +++ b/src/engine/engine_collision_driver.c @@ -273,7 +273,9 @@ void mj_collision(const mjModel* m, mjData* d) { mj_clearEfc(d); // reset the visualization flags - memset(d->bvh_active, 0, m->nbvh); + if (m->vis.global.bvactive) { + memset(d->bvh_active, 0, m->nbvh); + } // return if disabled if (mjDISABLED(mjDSBL_CONSTRAINT) || mjDISABLED(mjDSBL_CONTACT) @@ -634,6 +636,7 @@ void mj_collideTree(const mjModel* m, mjData* d, int bf1, int bf2, mjtByte isbody2 = (bf2 < nbody); int f1 = isbody1 ? -1 : bf1 - nbody; int f2 = isbody2 ? -1 : bf2 - nbody; + int mark_active = m->vis.global.bvactive; const int bvhadr1 = isbody1 ? m->body_bvhadr[bf1] : m->flex_bvhadr[f1]; const int bvhadr2 = isbody2 ? m->body_bvhadr[bf2] : m->flex_bvhadr[f2]; const int* child1 = m->bvh_child + 2*bvhadr1; @@ -705,8 +708,10 @@ void mj_collideTree(const mjModel* m, mjData* d, int bf1, int bf2, d->geom_xpos + 3*nodeid2, d->geom_xmat + 9*nodeid2, margin, NULL, NULL, &initialize)) { mj_collideGeomPair(m, d, nodeid1, nodeid2, merged, startadr, pairadr); - d->bvh_active[node1 + bvhadr1] = 1; - d->bvh_active[node2 + bvhadr2] = 1; + if (mark_active) { + d->bvh_active[node1 + bvhadr1] = 1; + d->bvh_active[node2 + bvhadr2] = 1; + } } } continue; @@ -742,8 +747,10 @@ void mj_collideTree(const mjModel* m, mjData* d, int bf1, int bf2, if (m->geom_type[nodeid1] != mjGEOM_PLANE) { mj_collideGeomElem(m, d, nodeid1, f2, nodeid2); } - d->bvh_active[node1 + bvhadr1] = 1; - d->bvh_active[node2 + bvhadr2] = 1; + if (mark_active) { + d->bvh_active[node1 + bvhadr1] = 1; + d->bvh_active[node2 + bvhadr2] = 1; + } } } continue; @@ -771,8 +778,10 @@ void mj_collideTree(const mjModel* m, mjData* d, int bf1, int bf2, // box filter applied in mj_collideElems, bitmask filter applied earlier if (isleaf1 && isleaf2) { mj_collideElems(m, d, f1, nodeid1, f2, nodeid2); - d->bvh_active[node1 + bvhadr1] = 1; - d->bvh_active[node2 + bvhadr2] = 1; + if (mark_active) { + d->bvh_active[node1 + bvhadr1] = 1; + d->bvh_active[node2 + bvhadr2] = 1; + } continue; } @@ -784,8 +793,10 @@ void mj_collideTree(const mjModel* m, mjData* d, int bf1, int bf2, } } - d->bvh_active[node1 + bvhadr1] = 1; - d->bvh_active[node2 + bvhadr2] = 1; + if (mark_active) { + d->bvh_active[node1 + bvhadr1] = 1; + d->bvh_active[node2 + bvhadr2] = 1; + } // keep traversing the tree if (!isleaf1 && isleaf2) { diff --git a/src/engine/engine_collision_sdf.c b/src/engine/engine_collision_sdf.c index 82237438..79d97e03 100644 --- a/src/engine/engine_collision_sdf.c +++ b/src/engine/engine_collision_sdf.c @@ -496,7 +496,7 @@ static void collideBVH(const mjModel* m, mjData* d, int g, const int* faceid = m->bvh_nodeid + bvhadr; const mjtNum* bvh = m->bvh_aabb + 6*bvhadr; const int* child = m->bvh_child + 2*bvhadr; - mjtByte* visited = d->bvh_active + bvhadr; + mjtByte* bvh_active = m->vis.global.bvactive ? d->bvh_active + bvhadr : NULL; mj_markStack(d); // TODO(quaglino): Store bvh max depths to make this bound tighter. @@ -521,9 +521,6 @@ static void collideBVH(const mjModel* m, mjData* d, int g, // node1 is a leaf if (faceid[node] != -1) { - if (visited[node]) { - continue; - } if (boxIntersect(bvh+6*node, offset, rotation, m, sdf, d)) { faces[*npoints] = faceid[node]; if (++(*npoints) == MAXSDFFACE) { @@ -531,7 +528,7 @@ static void collideBVH(const mjModel* m, mjData* d, int g, mj_freeStack(d); return; } - visited[node] = 1; + if (bvh_active) bvh_active[node] = 1; } continue; } @@ -541,7 +538,7 @@ static void collideBVH(const mjModel* m, mjData* d, int g, continue; } - visited[node] = 1; + if (bvh_active) bvh_active[node] = 1; // recursive call for (int i=0; i < 2; i++) { diff --git a/src/engine/engine_io.c b/src/engine/engine_io.c index 7c44b54f..1b1975e2 100644 --- a/src/engine/engine_io.c +++ b/src/engine/engine_io.c @@ -194,6 +194,7 @@ void mj_defaultVisual(mjVisual* vis) { vis->global.offheight = 480; vis->global.realtime = 1.0; vis->global.ellipsoidinertia = 0; + vis->global.bvactive = 1; // rendering quality vis->quality.shadowsize = 4096; @@ -272,6 +273,8 @@ void mj_defaultVisual(mjVisual* vis) { setf4(vis->rgba.slidercrank, .5, .3, .8, 1.); setf4(vis->rgba.crankbroken, .9, .0, .0, 1.); setf4(vis->rgba.frustum, 1., 1., .0, .2); + setf4(vis->rgba.bv, 0., 1., .0, .5); + setf4(vis->rgba.bvactive, 1., 0., .0, .5); } diff --git a/src/engine/engine_ray.c b/src/engine/engine_ray.c index c33edd5c..b520a818 100644 --- a/src/engine/engine_ray.c +++ b/src/engine/engine_ray.c @@ -627,6 +627,7 @@ int mju_raySlab(const mjtNum aabb[6], const mjtNum xpos[3], // ray vs tree intersection mjtNum mju_rayTree(const mjModel* m, const mjData* d, int id, const mjtNum* pnt, const mjtNum* vec) { + int mark_active = m->vis.global.bvactive; const int meshid = m->geom_dataid[id]; const int bvhadr = m->mesh_bvhadr[meshid]; const int* faceid = m->bvh_nodeid + bvhadr; @@ -701,12 +702,17 @@ mjtNum mju_rayTree(const mjModel* m, const mjData* d, int id, const mjtNum* pnt, // update if (sol >= 0 && (x < 0 || sol < x)) { x = sol; + if (mark_active) { + d->bvh_active[node + bvhadr] = 1; + } } continue; } // used for rendering - d->bvh_active[node + bvhadr] = 1; + if (mark_active) { + d->bvh_active[node + bvhadr] = 1; + } // add children to the stack for (int i=0; i < 2; i++) { diff --git a/src/engine/engine_vis_visualize.c b/src/engine/engine_vis_visualize.c index d2e2de20..74bd9cd5 100644 --- a/src/engine/engine_vis_visualize.c +++ b/src/engine/engine_vis_visualize.c @@ -532,6 +532,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, mjvGeom* thisgeom; mjvPerturb localpert; float scl = m->stat.meansize; + int mark_active = m->vis.global.bvactive; // make default pert if missing if (!pert) { @@ -636,7 +637,6 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, category = mjCAT_DECOR; objtype = mjOBJ_UNKNOWN; if (vopt->flags[mjVIS_BODYBVH]) { - float rgba[] = {1, 0, 0, 1}; for (int i = 0; i < m->nbvhstatic; i++) { int isleaf = m->bvh_child[2*i] == -1 && m->bvh_child[2*i+1] == -1; if (scn->ngeom >= scn->maxgeom) break; @@ -671,8 +671,11 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, mju_rotVecMat(pos, center, xmat); mju_addTo3(pos, xpos); - rgba[0] = d->bvh_active[i] ? 1 : 0; - rgba[1] = d->bvh_active[i] ? 0 : 1; + // set box color + const float* rgba = m->vis.rgba.bv; + if (mark_active && d->bvh_active[i]) { + rgba = m->vis.rgba.bvactive; + } START mjv_initGeom(thisgeom, mjGEOM_LINEBOX, size, pos, xmat, rgba); @@ -685,10 +688,8 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, category = mjCAT_DECOR; objtype = mjOBJ_UNKNOWN; if (vopt->flags[mjVIS_FLEXBVH]) { - float rgba[] = {1, 0, 0, 0.1}; for (int f=0; f < m->nflex; f++) { - if (m->flex_bvhnum[f] && - vopt->flexgroup[mjMAX(0, mjMIN(mjNGROUP-1, m->flex_group[f]))]) { + if (m->flex_bvhnum[f] && vopt->flexgroup[mjMAX(0, mjMIN(mjNGROUP-1, m->flex_group[f]))]) { for (int i=m->flex_bvhadr[f]; i < m->flex_bvhadr[f]+m->flex_bvhnum[f]; i++) { int isleaf = m->bvh_child[2*i] == -1 && m->bvh_child[2*i+1] == -1; if (scn->ngeom >= scn->maxgeom) break; @@ -698,10 +699,14 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, } } - // set box data + // get box data mjtNum *aabb = d->bvh_aabb_dyn + 6*(i - m->nbvhstatic); - rgba[0] = d->bvh_active[i] ? 1 : 0; - rgba[1] = d->bvh_active[i] ? 0 : 1; + + // set box color + const float* rgba = m->vis.rgba.bv; + if (mark_active && d->bvh_active[i]) { + rgba = m->vis.rgba.bvactive; + } START mjv_initGeom(thisgeom, mjGEOM_LINEBOX, aabb+3, aabb, NULL, rgba); @@ -715,7 +720,6 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, category = mjCAT_DECOR; objtype = mjOBJ_UNKNOWN; if (vopt->flags[mjVIS_MESHBVH]) { - float rgba[] = {1, 0, 0, 1}; for (int geomid = 0; geomid < m->ngeom; geomid++) { int meshid = m->geom_dataid[geomid]; if (meshid == -1) { @@ -732,13 +736,17 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, } } - if (!d->bvh_active[i]) { - continue; + // box color + const float* rgba = m->vis.rgba.bv; + if (mark_active) { + if (d->bvh_active[i]) { + rgba = m->vis.rgba.bvactive; + } else { + // when marking active bvs, skip inactive volumes + continue; + } } - rgba[0] = d->bvh_active[i] ? 1 : 0; - rgba[1] = d->bvh_active[i] ? 0 : 1; - // get xpos, xmat, size const mjtNum* xpos = d->geom_xpos + 3 * geomid; const mjtNum* xmat = d->geom_xmat + 9 * geomid; diff --git a/src/xml/xml_native_reader.cc b/src/xml/xml_native_reader.cc index 48de27c2..aeb074fb 100644 --- a/src/xml/xml_native_reader.cc +++ b/src/xml/xml_native_reader.cc @@ -113,8 +113,8 @@ static const char* MJCF[nMJCF][mjXATTRNUM] = { {"visual", "*", "0"}, {"<"}, - {"global", "?", "10", "fovy", "ipd", "azimuth", "elevation", "linewidth", "glow", - "offwidth", "offheight", "realtime", "ellipsoidinertia"}, + {"global", "?", "11", "fovy", "ipd", "azimuth", "elevation", "linewidth", "glow", + "offwidth", "offheight", "realtime", "ellipsoidinertia", "bvactive"}, {"quality", "?", "5", "shadowsize", "offsamples", "numslices", "numstacks", "numquads"}, {"headlight", "?", "4", "ambient", "diffuse", "specular", "active"}, @@ -124,11 +124,11 @@ static const char* MJCF[nMJCF][mjXATTRNUM] = { {"scale", "?", "17", "forcewidth", "contactwidth", "contactheight", "connect", "com", "camera", "light", "selectpoint", "jointlength", "jointwidth", "actuatorlength", "actuatorwidth", "framelength", "framewidth", "constraint", "slidercrank", "frustum"}, - {"rgba", "?", "23", "fog", "haze", "force", "inertia", "joint", + {"rgba", "?", "25", "fog", "haze", "force", "inertia", "joint", "actuator", "actuatornegative", "actuatorpositive", "com", "camera", "light", "selectpoint", "connect", "contactpoint", "contactforce", "contactfriction", "contacttorque", "contactgap", "rangefinder", - "constraint", "slidercrank", "crankbroken", "frustum"}, + "constraint", "slidercrank", "crankbroken", "frustum", "bv", "bvactive"}, {">"}, {"statistic", "*", "5", "meaninertia", "meanmass", "meansize", "extent", "center"}, @@ -2723,6 +2723,10 @@ void mjXReader::Visual(XMLElement* section) { if (MapValue(elem, "ellipsoidinertia", &ellipsoidinertia, bool_map, 2)) { vis->global.ellipsoidinertia = (ellipsoidinertia==1); } + int bvactive; + if (MapValue(elem, "bvactive", &bvactive, bool_map, 2)) { + vis->global.bvactive = (bvactive==1); + } } // quality sub-element @@ -2808,6 +2812,8 @@ void mjXReader::Visual(XMLElement* section) { ReadAttr(elem, "slidercrank", 4, vis->rgba.slidercrank, text); ReadAttr(elem, "crankbroken", 4, vis->rgba.crankbroken, text); ReadAttr(elem, "frustum", 4, vis->rgba.frustum, text); + ReadAttr(elem, "bv", 4, vis->rgba.bv, text); + ReadAttr(elem, "bvactive", 4, vis->rgba.bvactive, text); } // advance to next element diff --git a/src/xml/xml_native_writer.cc b/src/xml/xml_native_writer.cc index 926e7089..88bc6fc6 100644 --- a/src/xml/xml_native_writer.cc +++ b/src/xml/xml_native_writer.cc @@ -1030,6 +1030,7 @@ void mjXWriter::Visual(XMLElement* root) { WriteAttrInt(elem, "offwidth", vis->global.offwidth, visdef.global.offwidth); WriteAttrInt(elem, "offheight", vis->global.offheight, visdef.global.offheight); WriteAttrKey(elem, "ellipsoidinertia", bool_map, 2, vis->global.ellipsoidinertia, visdef.global.ellipsoidinertia); + WriteAttrKey(elem, "bvactive", bool_map, 2, vis->global.bvactive, visdef.global.bvactive); if (!elem->FirstAttribute()) { section->DeleteChild(elem); } @@ -1122,6 +1123,8 @@ void mjXWriter::Visual(XMLElement* root) { WriteAttr(elem, "slidercrank", 4, vis->rgba.slidercrank, visdef.rgba.slidercrank); WriteAttr(elem, "crankbroken", 4, vis->rgba.crankbroken, visdef.rgba.crankbroken); WriteAttr(elem, "frustum", 4, vis->rgba.frustum, visdef.rgba.frustum); + WriteAttr(elem, "bv", 4, vis->rgba.bv, visdef.rgba.bv); + WriteAttr(elem, "bvactive", 4, vis->rgba.bvactive, visdef.rgba.bvactive); if (!elem->FirstAttribute()) { section->DeleteChild(elem); } diff --git a/test/engine/testdata/ray/stanford_bunny.xml b/test/engine/testdata/ray/stanford_bunny.xml index e8e5d8f3..916255d5 100644 --- a/test/engine/testdata/ray/stanford_bunny.xml +++ b/test/engine/testdata/ray/stanford_bunny.xml @@ -3,6 +3,13 @@ - + + + + + + + + diff --git a/unity/Runtime/Bindings/MjBindings.cs b/unity/Runtime/Bindings/MjBindings.cs index ad6bc8b9..19b0f4fc 100644 --- a/unity/Runtime/Bindings/MjBindings.cs +++ b/unity/Runtime/Bindings/MjBindings.cs @@ -4985,6 +4985,7 @@ public unsafe struct global { public int offwidth; public int offheight; public int ellipsoidinertia; + public int bvactive; } [StructLayout(LayoutKind.Sequential)] @@ -5067,6 +5068,8 @@ public unsafe struct rgba { public fixed float slidercrank[4]; public fixed float crankbroken[4]; public fixed float frustum[4]; + public fixed float bv[4]; + public fixed float bvactive[4]; } [StructLayout(LayoutKind.Sequential)]