Remove all visual-only assets if discardvisual is selected, not only the geoms.

PiperOrigin-RevId: 598598250
Change-Id: I12e60ce41ad436d037b4877d197decf6bdce0fc8
This commit is contained in:
Alessio Quaglino
2024-01-15 06:38:11 -08:00
committed by Copybara-Service
parent acf698e2df
commit 1e2e0b3053
11 changed files with 557 additions and 65 deletions
+14 -8
View File
@@ -306,14 +306,20 @@ any effect. The settings here are global and apply to the entire model.
.. _compiler-discardvisual: .. _compiler-discardvisual:
:at:`discardvisual`: :at-val:`[false, true], "false" for MJCF, "true" for URDF` :at:`discardvisual`: :at-val:`[false, true], "false" for MJCF, "true" for URDF`
This attribute instructs the parser to discard "visual geoms", defined as geoms whose contype and conaffinity This attribute instructs the compiler to discard all model elements which are purely visual and have no effect on the
attributes are both set to 0. This functionality is useful for models that contain two sets of geoms, one for physics (with one exception, see below). This often enables smaller :ref:`mjModel` structs and faster simulation.
collisions and the other for visualization. Note that URDF models are usually constructed in this way. It rarely
makes sense to have two sets of geoms in the model, especially since MuJoCo uses convex hulls for collisions, so we - All materials are discarded.
recommend using this feature to discard redundant geoms. Keep in mind however that geoms considered visual per the - All textures are discarded.
above definition can still participate in collisions, if they appear in the explicit list of contact - All geoms with :ref:`contype<body-geom-contype>`=:ref:`conaffinity<body-geom-conaffinity>`=0 are discarded, if they
:ref:`pairs <contact-pair>`. The parser does not check this list before discarding geoms; it relies solely on the geom are not referenced in another MJCF element. If a discarded geom was used for inferring body inertia, an explicit
attributes to make the determination. :ref:`inertial<body-inertial>` element is added to the body.
- All meshes which are not referenced by any geom (in particular those discarded above) are discarded.
The resulting compiled model will have exactly the same dynamics as the original model, with the exception of
raycasting, as used for example by :ref:`rangefinder<sensor-rangefinder>`, since raycasting reports distances to
visual geoms. When visualizing models compiled with this flag, it is important to remember that colliding geoms are
often placed in a :ref:`group<body-geom-group>` which is invisible by default.
.. _compiler-convexhull: .. _compiler-convexhull:
+11 -5
View File
@@ -5,16 +5,22 @@ Changelog
Upcoming version (not yet released) Upcoming version (not yet released)
----------------------------------- -----------------------------------
General
^^^^^^^
1. Improved the :ref:discardvisual<compiler-discardvisual> compiler flag, which now discards all visual-only assets. See
:ref:discardvisual<compiler-discardvisual> for details.
MJX MJX
^^^ ^^^
1. Added :ref:`dyntype<actuator-general-dyntype>` ``filterexact``. 2. Added :ref:`dyntype<actuator-general-dyntype>` ``filterexact``.
2. Added :at:`site` transmission. 3. Added :at:`site` transmission.
3. Updated MJX colab tutorial with more stable quadruped environment. 4. Updated MJX colab tutorial with more stable quadruped environment.
4. Added ``mjx.ray`` which mirrors :ref:`mj_ray` for planes, spheres, capsules, and boxes. 5. Added ``mjx.ray`` which mirrors :ref:`mj_ray` for planes, spheres, capsules, and boxes.
Bug fixes Bug fixes
^^^^^^^^^ ^^^^^^^^^
5. Fixed a bug that prevented the use of pins with plugins if flexes are not in the worldbody. Fixes :github:issue:`1270`. 6. Fixed a bug that prevented the use of pins with plugins if flexes are not in the worldbody. Fixes
:github:issue:`1270`.
Version 3.1.1 (December 18, 2023) Version 3.1.1 (December 18, 2023)
+17
View File
@@ -166,6 +166,7 @@ mjCMesh::mjCMesh(mjCModel* _model, mjCDef* _def) {
valideigenvalue_ = true; valideigenvalue_ = true;
validinequality_ = true; validinequality_ = true;
processed_ = false; processed_ = false;
visual_ = true;
// reset to default if given // reset to default if given
if (_def) { if (_def) {
@@ -708,6 +709,13 @@ void mjCMesh::CopyGraph(int* arr) const {
void mjCMesh::DelTexcoord() {
if (texcoord_) mju_free(texcoord_);
ntexcoord_ = 0;
}
// set geom size to match mesh // set geom size to match mesh
void mjCMesh::FitGeom(mjCGeom* geom, double* meshpos) { void mjCMesh::FitGeom(mjCGeom* geom, double* meshpos) {
// copy mesh pos into meshpos // copy mesh pos into meshpos
@@ -2229,6 +2237,15 @@ mjCFlex::mjCFlex(mjCModel* _model) {
} }
bool mjCFlex::HasTexcoord() const {
return !texcoord.empty();
}
void mjCFlex::DelTexcoord() {
texcoord.clear();
}
// compiler // compiler
void mjCFlex::Compile(const mjVFS* vfs) { void mjCFlex::Compile(const mjVFS* vfs) {
+157 -6
View File
@@ -649,9 +649,126 @@ void mjCModel::MakeLists(mjCBody* body) {
} }
// delete material with given name or all materials if the name is omitted
template <class T>
static void DeleteMaterial(std::vector<T*>& list, std::string_view name = "") {
for (T* plist : list) {
if (name.empty() || plist->material == name) {
plist->material.clear();
}
}
}
// delete texture with given name or all textures if the name is omitted
template <class T>
static void DeleteTexture(std::vector<T*>& list, std::string_view name = "") {
for (T* plist : list) {
if (name.empty() || plist->texture == name) {
plist->texture.clear();
}
}
}
// delete all texture coordinates
template <class T>
static void DeleteTexcoord(std::vector<T*>& list) {
for (T* plist : list) {
if (plist->HasTexcoord()) {
plist->DelTexcoord();
}
}
}
// returns a vector that stores the reference correction for each entry
template <class T>
static void DeleteElements(std::vector<T*>& elements,
const std::vector<bool>& discard) {
if (elements.empty()) {
return;
}
std::vector<int> ndiscard(elements.size(), 0);
int i = 0;
for (int j=0; j<elements.size(); j++) {
if (discard[j]) {
delete elements[j];
} else {
elements[i] = elements[j];
i++;
}
}
// count cumulative discarded elements
for (int i=0; i<elements.size()-1; i++) {
ndiscard[i+1] = ndiscard[i] + discard[i];
}
// erase elements from vector
if (i < elements.size()) {
elements.erase(elements.begin() + i, elements.end());
}
// update elements
for (T* element : elements) {
if (element->id > 0) {
element->id -= ndiscard[element->id];
}
}
}
template <>
void mjCModel::Delete<mjCGeom>(std::vector<mjCGeom*>& elements,
const std::vector<bool>& discard) {
// update bodies
for (mjCBody* body : bodies) {
body->geoms.erase(
std::remove_if(body->geoms.begin(), body->geoms.end(),
[&discard](mjCGeom* geom) { return discard[geom->id]; }),
body->geoms.end());
}
// remove geoms from the main vector
DeleteElements(elements, discard);
}
template <>
void mjCModel::Delete<mjCMesh>(std::vector<mjCMesh*>& elements,
const std::vector<bool>& discard) {
DeleteElements(elements, discard);
}
template <>
void mjCModel::DeleteAll<mjCMaterial>(std::vector<mjCMaterial*>& elements) {
DeleteMaterial(geoms);
DeleteMaterial(skins);
DeleteMaterial(sites);
DeleteMaterial(tendons);
for (mjCMaterial* element : elements) {
delete element;
}
elements.clear();
}
template <>
void mjCModel::DeleteAll<mjCTexture>(std::vector<mjCTexture*>& elements) {
DeleteTexture(materials);
for (mjCTexture* element : elements) {
delete element;
}
elements.clear();
}
// index assets // index assets
void mjCModel::IndexAssets(void) { void mjCModel::IndexAssets(bool discard) {
// assets referenced in geoms // assets referenced in geoms
for (int i=0; i<geoms.size(); i++) { for (int i=0; i<geoms.size(); i++) {
mjCGeom* pgeom = geoms[i]; mjCGeom* pgeom = geoms[i];
@@ -670,7 +787,19 @@ void mjCModel::IndexAssets(void) {
if (!pgeom->meshname.empty()) { if (!pgeom->meshname.empty()) {
mjCBase* m = FindObject(mjOBJ_MESH, pgeom->meshname); mjCBase* m = FindObject(mjOBJ_MESH, pgeom->meshname);
if (m) { if (m) {
pgeom->mesh = (mjCMesh*)m; if (discard && geoms[i]->visual_) {
// do not associate with a mesh
pgeom->mesh = nullptr;
} else {
// associate mesh with geom
pgeom->mesh = (mjCMesh*)m;
// mark mesh as not visual
// this is irreversible so only performed when IndexAssets is called with discard
if (discard) {
pgeom->mesh->SetNotVisual();
}
}
} else { } else {
throw mjCError(pgeom, "mesh '%s' not found in geom %d", pgeom->meshname.c_str(), i); throw mjCError(pgeom, "mesh '%s' not found in geom %d", pgeom->meshname.c_str(), i);
} }
@@ -746,6 +875,20 @@ void mjCModel::IndexAssets(void) {
} }
} }
} }
// discard visual meshes and geoms
if (discard) {
std::vector<bool> discard_mesh(meshes.size(), false);
std::vector<bool> discard_geom(geoms.size(), false);
std::transform(meshes.begin(), meshes.end(), discard_mesh.begin(),
[](const mjCMesh* mesh) { return mesh->IsVisual(); });
std::transform(geoms.begin(), geoms.end(), discard_geom.begin(),
[](const mjCGeom* geom) { return geom->IsVisual(); });
Delete(meshes, discard_mesh);
Delete(geoms, discard_geom);
}
} }
@@ -2049,8 +2192,8 @@ void mjCModel::CopyObjects(mjModel* m) {
// geom pairs to include // geom pairs to include
for (int i=0; i<npair; i++) { for (int i=0; i<npair; i++) {
m->pair_dim[i] = pairs[i]->condim; m->pair_dim[i] = pairs[i]->condim;
m->pair_geom1[i] = pairs[i]->geom1; m->pair_geom1[i] = pairs[i]->geom1->id;
m->pair_geom2[i] = pairs[i]->geom2; m->pair_geom2[i] = pairs[i]->geom2->id;
m->pair_signature[i] = pairs[i]->signature; m->pair_signature[i] = pairs[i]->signature;
copyvec(m->pair_solref+mjNREF*i, pairs[i]->solref, mjNREF); copyvec(m->pair_solref+mjNREF*i, pairs[i]->solref, mjNREF);
copyvec(m->pair_solreffriction+mjNREF*i, pairs[i]->solreffriction, mjNREF); copyvec(m->pair_solreffriction+mjNREF*i, pairs[i]->solreffriction, mjNREF);
@@ -2711,8 +2854,16 @@ void mjCModel::TryCompile(mjModel*& m, mjData*& d, const mjVFS* vfs) {
} }
} }
// delete visual assets
if (discardvisual) {
DeleteAll(materials);
DeleteTexcoord(flexes);
DeleteTexcoord(meshes);
DeleteAll(textures);
}
// convert names into indices // convert names into indices
IndexAssets(); IndexAssets(false);
// mark meshes that need convex hull // mark meshes that need convex hull
for (int i=0; i<geoms.size(); i++) { for (int i=0; i<geoms.size(); i++) {
@@ -2811,7 +2962,7 @@ void mjCModel::TryCompile(mjModel*& m, mjData*& d, const mjVFS* vfs) {
reassignid(excludes); reassignid(excludes);
// resolve asset references, compute sizes // resolve asset references, compute sizes
IndexAssets(); IndexAssets(discardvisual);
SetSizes(); SetSizes();
// fuse static if enabled // fuse static if enabled
if (fusestatic) { if (fusestatic) {
+18 -10
View File
@@ -96,6 +96,14 @@ class mjCModel {
mjCKey* AddKey(void); // keyframe mjCKey* AddKey(void); // keyframe
mjCPlugin* AddPlugin(void); // plugin instance mjCPlugin* AddPlugin(void); // plugin instance
//------------------------ API for deleting model elements
template <class T>
void Delete(std::vector<T*>& elements,
const std::vector<bool>& discard); // delete elements marked as discard=true
template <class T>
void DeleteAll(std::vector<T*>& elements); // delete all elements
//------------------------ API for access to model elements (outside tree) //------------------------ API for access to model elements (outside tree)
int NumObjects(mjtObj type); // number of objects in specified list int NumObjects(mjtObj type); // number of objects in specified list
mjCBase* GetObject(mjtObj type, int id); // pointer to specified object mjCBase* GetObject(mjtObj type, int id); // pointer to specified object
@@ -185,16 +193,16 @@ class mjCModel {
void SetDefaultNames(std::vector<T*>& assets); void SetDefaultNames(std::vector<T*>& assets);
//------------------------ compile phases //------------------------ compile phases
void MakeLists(mjCBody* body); // make lists of bodies, geoms, joints, sites void MakeLists(mjCBody* body); // make lists of bodies, geoms, joints, sites
void IndexAssets(void); // convert asset names into indices void IndexAssets(bool discard); // convert asset names into indices
void CheckEmptyNames(void); // check empty names void CheckEmptyNames(void); // check empty names
void SetSizes(void); // compute sizes void SetSizes(void); // compute sizes
void AutoSpringDamper(mjModel*);// automatic stiffness and damping computation void AutoSpringDamper(mjModel*); // automatic stiffness and damping computation
void LengthRange(mjModel*, mjData*); // compute actuator lengthrange void LengthRange(mjModel*, mjData*); // compute actuator lengthrange
void CopyNames(mjModel*); // copy names, compute name addresses void CopyNames(mjModel*); // copy names, compute name addresses
void CopyPaths(mjModel*); // copy paths, compute path addresses void CopyPaths(mjModel*); // copy paths, compute path addresses
void CopyObjects(mjModel*); // copy objects outside kinematic tree void CopyObjects(mjModel*); // copy objects outside kinematic tree
void CopyTree(mjModel*); // copy objects inside kinematic tree void CopyTree(mjModel*); // copy objects inside kinematic tree
//------------------------ sizes //------------------------ sizes
// sizes set from object list lengths // sizes set from object list lengths
+68 -27
View File
@@ -314,6 +314,7 @@ int mjCBoundingVolumeHierarchy::MakeBVH(std::vector<const mjCBoundingVolume*>& e
return -1; return -1;
} }
bool is_visual = true;
int nelements = elements.size(); int nelements = elements.size();
mjtNum AAMM[6] = {mjMAXVAL, mjMAXVAL, mjMAXVAL, -mjMAXVAL, -mjMAXVAL, -mjMAXVAL}; mjtNum AAMM[6] = {mjMAXVAL, mjMAXVAL, mjMAXVAL, -mjMAXVAL, -mjMAXVAL, -mjMAXVAL};
@@ -325,6 +326,8 @@ int mjCBoundingVolumeHierarchy::MakeBVH(std::vector<const mjCBoundingVolume*>& e
// skip visual objects // skip visual objects
if (elements[i]->conaffinity==0 && elements[i]->contype==0) { if (elements[i]->conaffinity==0 && elements[i]->contype==0) {
continue; continue;
} else {
is_visual = false;
} }
// transform element aabb to aamm format // transform element aabb to aamm format
@@ -360,6 +363,11 @@ int mjCBoundingVolumeHierarchy::MakeBVH(std::vector<const mjCBoundingVolume*>& e
} }
} }
// a body with only visual geoms does not have a bvh
if (is_visual) {
return nbvh;
}
// inflate flat AABBs // inflate flat AABBs
for (int i=0; i<3; i++) { for (int i=0; i<3; i++) {
if (mju_abs(AAMM[i]-AAMM[i+3])<mjEPS) { if (mju_abs(AAMM[i]-AAMM[i+3])<mjEPS) {
@@ -1088,6 +1096,18 @@ void mjCBody::Compile(void) {
throw mjCError(this, "plugin '%s' does not support passive forces", plugin->name); throw mjCError(this, "plugin '%s' does not support passive forces", plugin->name);
} }
} }
if (!model->discardvisual) {
return;
}
// set inertial to explicit for bodies containing visual geoms
for (int j=0; j<geoms.size(); j++) {
if (geoms[j]->IsVisual()) {
explicitinertial = true;
break;
}
}
} }
@@ -1350,6 +1370,7 @@ mjCGeom::mjCGeom(mjCModel* _model, mjCDef* _def) {
matid = -1; matid = -1;
mesh = nullptr; mesh = nullptr;
hfield = nullptr; hfield = nullptr;
visual_ = false;
// reset to default if given // reset to default if given
if (_def) { if (_def) {
@@ -1737,6 +1758,9 @@ void mjCGeom::Compile(void) {
name.c_str(), id); name.c_str(), id);
} }
// check if can collide
visual_ = !contype && !conaffinity;
// normalize quaternion // normalize quaternion
mjuu_normvec(quat, 4); mjuu_normvec(quat, 4);
@@ -3145,7 +3169,9 @@ mjCPair::mjCPair(mjCModel* _model, mjCDef* _def) {
friction[4] = 0.0001; friction[4] = 0.0001;
// clear internal variables // clear internal variables
geom1 = geom2 = signature = -1; geom1 = nullptr;
geom2 = nullptr;
signature = -1;
// reset to default if given // reset to default if given
if (_def) { if (_def) {
@@ -3167,46 +3193,48 @@ void mjCPair::Compile(void) {
} }
// find geom 1 // find geom 1
mjCGeom* pg1 = (mjCGeom*)model->FindObject(mjOBJ_GEOM, geomname1); geom1 = (mjCGeom*)model->FindObject(mjOBJ_GEOM, geomname1);
if (!pg1) { if (!geom1) {
throw mjCError(this, "geom '%s' not found in collision %d", geomname1.c_str(), id); throw mjCError(this, "geom '%s' not found in collision %d", geomname1.c_str(), id);
} }
// find geom 2 // find geom 2
mjCGeom* pg2 = (mjCGeom*)model->FindObject(mjOBJ_GEOM, geomname2); geom2 = (mjCGeom*)model->FindObject(mjOBJ_GEOM, geomname2);
if (!pg2) { if (!geom2) {
throw mjCError(this, "geom '%s' not found in collision %d", geomname2.c_str(), id); throw mjCError(this, "geom '%s' not found in collision %d", geomname2.c_str(), id);
} }
// mark geoms as not visual
geom1->SetNotVisual();
geom2->SetNotVisual();
// swap if body1 > body2 // swap if body1 > body2
if (pg1->body->id > pg2->body->id) { if (geom1->body->id > geom2->body->id) {
string nametmp = geomname1; string nametmp = geomname1;
geomname1 = geomname2; geomname1 = geomname2;
geomname2 = nametmp; geomname2 = nametmp;
mjCGeom* geomtmp = pg1; mjCGeom* geomtmp = geom1;
pg1 = pg2; geom1 = geom2;
pg2 = geomtmp; geom2 = geomtmp;
} }
// get geom ids and body signature // get geom ids and body signature
geom1 = pg1->id; signature = ((geom1->body->id)<<16) + geom2->body->id;
geom2 = pg2->id;
signature = ((pg1->body->id)<<16) + pg2->body->id;
// set undefined margin: max // set undefined margin: max
if (!mjuu_defined(margin)) { if (!mjuu_defined(margin)) {
margin = mjMAX(pg1->margin, pg2->margin); margin = mjMAX(geom1->margin, geom2->margin);
} }
// set undefined gap: max // set undefined gap: max
if (!mjuu_defined(gap)) { if (!mjuu_defined(gap)) {
gap = mjMAX(pg1->gap, pg2->gap); gap = mjMAX(geom1->gap, geom2->gap);
} }
// set undefined condim, friction, solref, solimp: different priority // set undefined condim, friction, solref, solimp: different priority
if (pg1->priority!=pg2->priority) { if (geom1->priority!=geom2->priority) {
mjCGeom* pgh = (pg1->priority>pg2->priority ? pg1 : pg2); mjCGeom* pgh = (geom1->priority>geom2->priority ? geom1 : geom2);
// condim // condim
if (condim<0) { if (condim<0) {
@@ -3239,23 +3267,23 @@ void mjCPair::Compile(void) {
else { else {
// condim: max // condim: max
if (condim<0) { if (condim<0) {
condim = mjMAX(pg1->condim, pg2->condim); condim = mjMAX(geom1->condim, geom2->condim);
} }
// friction: max // friction: max
if (!mjuu_defined(friction[0])) { if (!mjuu_defined(friction[0])) {
friction[0] = friction[1] = mju_max(pg1->friction[0], pg2->friction[0]); friction[0] = friction[1] = mju_max(geom1->friction[0], geom2->friction[0]);
friction[2] = mju_max(pg1->friction[1], pg2->friction[1]); friction[2] = mju_max(geom1->friction[1], geom2->friction[1]);
friction[3] = friction[4] = mju_max(pg1->friction[2], pg2->friction[2]); friction[3] = friction[4] = mju_max(geom1->friction[2], geom2->friction[2]);
} }
// solver mix factor // solver mix factor
double mix; double mix;
if (pg1->solmix>=mjMINVAL && pg2->solmix>=mjMINVAL) { if (geom1->solmix>=mjMINVAL && geom2->solmix>=mjMINVAL) {
mix = pg1->solmix / (pg1->solmix + pg2->solmix); mix = geom1->solmix / (geom1->solmix + geom2->solmix);
} else if (pg1->solmix<mjMINVAL && pg2->solmix<mjMINVAL) { } else if (geom1->solmix<mjMINVAL && geom2->solmix<mjMINVAL) {
mix = 0.5; mix = 0.5;
} else if (pg1->solmix<mjMINVAL) { } else if (geom1->solmix<mjMINVAL) {
mix = 0.0; mix = 0.0;
} else { } else {
mix = 1.0; mix = 1.0;
@@ -3266,14 +3294,14 @@ void mjCPair::Compile(void) {
// standard: mix // standard: mix
if (solref[0]>0) { if (solref[0]>0) {
for (int i=0; i<mjNREF; i++) { for (int i=0; i<mjNREF; i++) {
solref[i] = mix*pg1->solref[i] + (1-mix)*pg2->solref[i]; solref[i] = mix*geom1->solref[i] + (1-mix)*geom2->solref[i];
} }
} }
// direct: min // direct: min
else { else {
for (int i=0; i<mjNREF; i++) { for (int i=0; i<mjNREF; i++) {
solref[i] = mju_min(pg1->solref[i], pg2->solref[i]); solref[i] = mju_min(geom1->solref[i], geom2->solref[i]);
} }
} }
} }
@@ -3281,7 +3309,7 @@ void mjCPair::Compile(void) {
// impedance // impedance
if (!mjuu_defined(solimp[0])) { if (!mjuu_defined(solimp[0])) {
for (int i=0; i<mjNIMP; i++) { for (int i=0; i<mjNIMP; i++) {
solimp[i] = mix*pg1->solimp[i] + (1-mix)*pg2->solimp[i]; solimp[i] = mix*geom1->solimp[i] + (1-mix)*geom2->solimp[i];
} }
} }
} }
@@ -3666,6 +3694,9 @@ void mjCTendon::Compile(void) {
"tendon '%s' (id = %d): geom at pos %d not bracketed by sites", "tendon '%s' (id = %d): geom at pos %d not bracketed by sites",
name.c_str(), id, i); name.c_str(), id, i);
} }
// mark geoms as non visual
model->geoms[path[i]->obj->id]->SetNotVisual();
break; break;
case mjWRAP_JOINT: case mjWRAP_JOINT:
@@ -4107,6 +4138,11 @@ void mjCSensor::Compile(void) {
name.c_str(), id); name.c_str(), id);
} }
// if geom mark it as non visual
if (objtype == mjOBJ_GEOM) {
((mjCGeom*)obj)->SetNotVisual();
}
// get sensorized object id // get sensorized object id
} else if (type != mjSENS_CLOCK && type != mjSENS_PLUGIN && type != mjSENS_USER) { } else if (type != mjSENS_CLOCK && type != mjSENS_PLUGIN && type != mjSENS_USER) {
throw mjCError(this, "invalid type in sensor '%s' (id = %d)", name.c_str(), id); throw mjCError(this, "invalid type in sensor '%s' (id = %d)", name.c_str(), id);
@@ -4579,6 +4615,11 @@ void mjCTuple::Compile(void) {
throw mjCError(this, "unrecognized object '%s' in tuple %d", objname[i].c_str(), id); throw mjCError(this, "unrecognized object '%s' in tuple %d", objname[i].c_str(), id);
} }
// if geom mark it as non visual
if (objtype[i] == mjOBJ_GEOM) {
((mjCGeom*)res)->SetNotVisual();
}
// assign id // assign id
obj[i] = res; obj[i] = res;
} }
+14 -3
View File
@@ -381,6 +381,8 @@ class mjCGeom : public mjCBase {
public: public:
double GetVolume(void); // compute geom volume double GetVolume(void); // compute geom volume
void SetInertia(void); // compute and set geom inertia void SetInertia(void); // compute and set geom inertia
bool IsVisual(void) const { return visual_; }
void SetNotVisual(void) { visual_ = false; }
// Compute all coefs modeling the interaction with the surrounding fluid. // Compute all coefs modeling the interaction with the surrounding fluid.
void SetFluidCoefs(void); void SetFluidCoefs(void);
@@ -432,6 +434,7 @@ class mjCGeom : public mjCBase {
double GetRBound(void); // compute bounding sphere radius double GetRBound(void); // compute bounding sphere radius
void ComputeAABB(void); // compute axis-aligned bounding box void ComputeAABB(void); // compute axis-aligned bounding box
bool visual_; // true: geom does not collide and is unreferenced
int matid; // id of geom's material int matid; // id of geom's material
mjCMesh* mesh; // geom's mesh mjCMesh* mesh; // geom's mesh
mjCHField* hfield; // geom's hfield mjCHField* hfield; // geom's hfield
@@ -591,6 +594,9 @@ class mjCFlex: public mjCBase {
std::vector<int> elem; // element vertex ids std::vector<int> elem; // element vertex ids
std::vector<float> texcoord; // vertex texture coordinates std::vector<float> texcoord; // vertex texture coordinates
bool HasTexcoord() const; // texcoord not null
void DelTexcoord(); // delete texcoord
private: private:
mjCFlex(mjCModel* = 0); // constructor mjCFlex(mjCModel* = 0); // constructor
void Compile(const mjVFS* vfs); // compiler void Compile(const mjVFS* vfs); // compiler
@@ -680,6 +686,9 @@ class mjCMesh: public mjCBase {
double& GetVolumeRef(mjtMeshType type); // get volume double& GetVolumeRef(mjtMeshType type); // get volume
void FitGeom(mjCGeom* geom, double* meshpos); // approximate mesh with simple geom void FitGeom(mjCGeom* geom, double* meshpos); // approximate mesh with simple geom
bool HasTexcoord() const; // texcoord not null bool HasTexcoord() const; // texcoord not null
void DelTexcoord(); // delete texcoord
bool IsVisual(void) const { return visual_; } // is geom visual
void SetNotVisual(void) { visual_ = false; } // mark mesh as not visual
void CopyVert(float* arr) const; // copy vert data into array void CopyVert(float* arr) const; // copy vert data into array
void CopyNormal(float* arr) const; // copy normal data into array void CopyNormal(float* arr) const; // copy normal data into array
@@ -693,6 +702,7 @@ class mjCMesh: public mjCBase {
void SetBoundingVolume(int faceid); void SetBoundingVolume(int faceid);
private: private:
bool visual_; // true: the mesh is only visual
std::string content_type_; // content type of file std::string content_type_; // content type of file
std::string file_; // mesh file std::string file_; // mesh file
double refpos_[3]; // reference position (translate) double refpos_[3]; // reference position (translate)
@@ -847,6 +857,8 @@ class mjCTexture : public mjCBase {
friend class mjXWriter; friend class mjXWriter;
public: public:
~mjCTexture(); // destructor
std::string get_file() const { return file; } std::string get_file() const { return file; }
mjtTexture type; // texture type mjtTexture type; // texture type
@@ -876,7 +888,6 @@ class mjCTexture : public mjCBase {
private: private:
mjCTexture(mjCModel*); // constructor mjCTexture(mjCModel*); // constructor
~mjCTexture(); // destructior
void Compile(const mjVFS* vfs); // compiler void Compile(const mjVFS* vfs); // compiler
void Builtin2D(void); // make builtin 2D void Builtin2D(void); // make builtin 2D
@@ -959,8 +970,8 @@ class mjCPair : public mjCBase {
mjCPair(mjCModel* = 0, mjCDef* = 0); // constructor mjCPair(mjCModel* = 0, mjCDef* = 0); // constructor
void Compile(void); // compiler void Compile(void); // compiler
int geom1; // id of geom1 mjCGeom* geom1; // geom1
int geom2; // id of geom2 mjCGeom* geom2; // geom2
int signature; // body1<<16 + body2 int signature; // body1<<16 + body2
}; };
-6
View File
@@ -3019,12 +3019,6 @@ void mjXReader::Body(XMLElement* section, mjCBody* pbody, mjCFrame* frame) {
mjCGeom* pgeom = pbody->AddGeom(def); mjCGeom* pgeom = pbody->AddGeom(def);
OneGeom(elem, pgeom); OneGeom(elem, pgeom);
pgeom->SetFrame(frame); pgeom->SetFrame(frame);
// discard visual
if (!pgeom->contype && !pgeom->conaffinity && model->discardvisual) {
delete pbody->geoms.back();
pbody->geoms.pop_back();
}
} }
// site sub-element // site sub-element
+73
View File
@@ -0,0 +1,73 @@
<mujoco>
<compiler discardvisual="true"/>
<asset>
<texture name="discarded_skybox" type="skybox" builtin="gradient"
rgb1=".3 .5 .7" rgb2="0 0 0" width="32" height="512"/>
<texture name="discarded" type="2d" builtin="checker" width="512" height="512" rgb1=".1 .2 .3" rgb2=".2 .3 .4"/>
<material name="discarded" texture="discarded" texrepeat="1 1" texuniform="true" reflectance=".2"/>
<mesh name="kept" vertex="0 0 0 1 0 0 0 1 0 0 0 1"/>
<mesh name="discarded0" vertex="0 0 0 1 0 0 0 1 0 0 0 1"/>
<mesh name="discarded1" vertex="0 0 0 1 0 0 0 1 0 0 0 1"/>
</asset>
<worldbody>
<geom name="kept_floor" size="0 0 .05" type="plane" material="discarded"/>
<body pos="0 0 -.5">
<freejoint/>
<geom name="discarded0" type="mesh" mesh="discarded0" contype="0" conaffinity="0"/>
</body>
<site name="tendon0" pos=".1 0 1.5"/>
<geom name="kept_tendon" size="0.2" contype="0" conaffinity="0" pos="0 0 1"/>
<body pos=".1 0 -.5">
<site name="tendon1" pos="0 0 1"/>
<freejoint/>
<geom name="discarded1" type="mesh" mesh="kept" contype="0" conaffinity="0"/>
</body>
<body pos=".2 0 -.5">
<geom name="kept0" type="mesh" mesh="kept"/>
</body>
<body pos=".3 0 -.5">
<freejoint/>
<geom name="kept1" type="mesh" mesh="kept"/>
</body>
<body pos=".4 0 -.5">
<geom name="discarded2" type="mesh" mesh="kept" contype="0" conaffinity="0"/>
</body>
<geom name="kept_sensor" size="0.2" contype="0" conaffinity="0"/>
<geom name="kept_pair" size="0.2" contype="0" conaffinity="0" pos=".3 0 .4"/>
<geom name="kept_tuple" size="0.2" contype="0" conaffinity="0"/>
</worldbody>
<contact>
<pair geom1="kept1" geom2="kept_pair"/>
</contact>
<tendon>
<spatial range="0 1">
<site site="tendon0"/>
<geom geom="kept_tendon"/>
<site site="tendon1"/>
</spatial>
</tendon>
<sensor>
<framepos objtype="geom" objname="kept_sensor"/>
</sensor>
<custom>
<tuple name="tuple">
<element objtype="geom" objname="kept_tuple"/>
</tuple>
</custom>
</mujoco>
+73
View File
@@ -0,0 +1,73 @@
<mujoco>
<compiler discardvisual="false"/>
<asset>
<texture name="discarded_skybox" type="skybox" builtin="gradient"
rgb1=".3 .5 .7" rgb2="0 0 0" width="32" height="512"/>
<texture name="discarded" type="2d" builtin="checker" width="512" height="512" rgb1=".1 .2 .3" rgb2=".2 .3 .4"/>
<material name="discarded" texture="discarded" texrepeat="1 1" texuniform="true" reflectance=".2"/>
<mesh name="kept" vertex="0 0 0 1 0 0 0 1 0 0 0 1"/>
<mesh name="discarded0" vertex="0 0 0 1 0 0 0 1 0 0 0 1"/>
<mesh name="discarded1" vertex="0 0 0 1 0 0 0 1 0 0 0 1"/>
</asset>
<worldbody>
<geom name="kept_floor" size="0 0 .05" type="plane" material="discarded"/>
<body pos="0 0 -.5">
<freejoint/>
<geom name="discarded0" type="mesh" mesh="discarded0" contype="0" conaffinity="0"/>
</body>
<site name="tendon0" pos=".1 0 1.5"/>
<geom name="kept_tendon" size="0.2" contype="0" conaffinity="0" pos="0 0 1"/>
<body pos=".1 0 -.5">
<site name="tendon1" pos="0 0 1"/>
<freejoint/>
<geom name="discarded1" type="mesh" mesh="kept" contype="0" conaffinity="0"/>
</body>
<body pos=".2 0 -.5">
<geom name="kept0" type="mesh" mesh="kept"/>
</body>
<body pos=".3 0 -.5">
<freejoint/>
<geom name="kept1" type="mesh" mesh="kept"/>
</body>
<body pos=".4 0 -.5">
<geom name="discarded2" type="mesh" mesh="kept" contype="0" conaffinity="0"/>
</body>
<geom name="kept_sensor" size="0.2" contype="0" conaffinity="0"/>
<geom name="kept_pair" size="0.2" contype="0" conaffinity="0" pos=".3 0 .4"/>
<geom name="kept_tuple" size="0.2" contype="0" conaffinity="0"/>
</worldbody>
<contact>
<pair geom1="kept1" geom2="kept_pair"/>
</contact>
<tendon>
<spatial range="0 1">
<site site="tendon0"/>
<geom geom="kept_tendon"/>
<site site="tendon1"/>
</spatial>
</tendon>
<sensor>
<framepos objtype="geom" objname="kept_sensor"/>
</sensor>
<custom>
<tuple name="tuple">
<element objtype="geom" objname="kept_tuple"/>
</tuple>
</custom>
</mujoco>
+112
View File
@@ -15,6 +15,7 @@
// Tests for user/user_model.cc. // Tests for user/user_model.cc.
#include <array> #include <array>
#include <cstddef>
#include <string> #include <string>
#include <gmock/gmock.h> #include <gmock/gmock.h>
@@ -283,5 +284,116 @@ TEST_F(FuseStaticTest, FuseStaticEquivalent) {
mj_deleteModel(m_no_fuse); mj_deleteModel(m_no_fuse);
} }
// ------------- test discardvisual --------------------------------------------
using DiscardVisualTest = MujocoTest;
TEST_F(DiscardVisualTest, DiscardVisualKeepsInertia) {
static constexpr char xml[] = R"(
<mujoco>
<compiler discardvisual="true"/>
<asset>
<mesh name="visual_mesh"
vertex="0 0 0 1 0 0 0 1 0 0 0 1"
normal="1 0 0 0 1 0 0 0 1 0.707 0 0.707"
face="0 2 1 0 3 2" />
<mesh name="collision_mesh"
vertex="0 0 0 1 0 0 0 1 0 0 0 1"
normal="1 0 0 0 1 0 0 0 1 0.707 0 0.707"
face="0 2 1 0 3 2" />
</asset>
<worldbody>
<body>
<geom type="mesh" mesh="visual_mesh" contype="0" conaffinity="0"/>
</body>
<body>
<geom type="mesh" mesh="collision_mesh"/>
</body>
</worldbody>
</mujoco>
)";
std::array<char, 1024> error;
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
EXPECT_THAT(model, NotNull()) << error.data();
EXPECT_THAT(model->nmesh, 1);
EXPECT_THAT(model->body_inertia[3], model->body_inertia[6]);
EXPECT_THAT(model->body_inertia[4], model->body_inertia[7]);
EXPECT_THAT(model->body_inertia[5], model->body_inertia[8]);
mj_deleteModel(model);
}
TEST_F(DiscardVisualTest, DiscardVisualEquivalent) {
char error[1024];
size_t error_sz = 1024;
static const char* const kDiscardvisualPath =
"user/testdata/discardvisual.xml";
static const char* const kDiscardvisualFalsePath =
"user/testdata/discardvisual_false.xml";
const std::string xml_path1 = GetTestDataFilePath(kDiscardvisualPath);
mjModel* model1 = mj_loadXML(xml_path1.c_str(), 0, error, error_sz);
EXPECT_THAT(model1, NotNull()) << error;
const std::string xml_path2 = GetTestDataFilePath(kDiscardvisualFalsePath);
mjModel* model2 = mj_loadXML(xml_path2.c_str(), 0, error, error_sz);
EXPECT_THAT(model2, NotNull()) << error;
EXPECT_THAT(model1->nq, model2->nq);
EXPECT_THAT(model1->nmat, 0);
EXPECT_THAT(model1->ntex, 0);
EXPECT_THAT(model2->ngeom-model1->ngeom, 3);
EXPECT_THAT(model2->nmesh-model1->nmesh, 2);
EXPECT_THAT(model1->npair, model2->npair);
EXPECT_THAT(model1->nsensor, model2->nsensor);
EXPECT_THAT(model1->nwrap, model2->nwrap);
for (int i = 0; i < model1->ngeom; i++) {
std::string name = std::string(model1->names + model1->name_geomadr[i]);
EXPECT_NE(name.find("kept"), std::string::npos);
EXPECT_EQ(name.find("discard"), std::string::npos);
}
for (int i = 0; i < model1->npair; i++) {
int adr1 = model1->name_geomadr[model1->pair_geom1[i]];
int adr2 = model2->name_geomadr[model2->pair_geom1[i]];
EXPECT_STREQ(model1->names + adr1, model2->names + adr2);
adr1 = model1->name_geomadr[model1->pair_geom2[i]];
adr2 = model2->name_geomadr[model2->pair_geom2[i]];
EXPECT_STREQ(model1->names + adr1, model2->names + adr2);
}
for (int i = 0; i < model1->nsensor; i++) {
int adr1 = model1->name_geomadr[model1->sensor_objid[i]];
int adr2 = model2->name_geomadr[model2->sensor_objid[i]];
EXPECT_STREQ(model1->names + adr1, model2->names + adr2);
}
for (int i = 0; i < model1->nwrap; i++) {
int adr1 = model1->name_geomadr[model1->wrap_objid[i]];
int adr2 = model2->name_geomadr[model2->wrap_objid[i]];
EXPECT_STREQ(model1->names + adr1, model2->names + adr2);
}
mjData *d1 = mj_makeData(model1);
mjData *d2 = mj_makeData(model2);
for (int i = 0; i < 100; i++) {
mj_step(model1, d1);
mj_step(model2, d2);
}
for (int i = 0; i < model1->nq; i++) {
EXPECT_THAT(d1->qpos[i], d2->qpos[i]);
}
mj_deleteModel(model1);
mj_deleteModel(model2);
mj_deleteData(d1);
mj_deleteData(d2);
}
} // namespace } // namespace
} // namespace mujoco } // namespace mujoco