Add visualisation groups to skins.
PiperOrigin-RevId: 458441645 Change-Id: I14324e8efda5142b2718a5cc9215228c7556dbf7
This commit is contained in:
committed by
Copybara-Service
parent
040deb161f
commit
2d0995b47c
@@ -2222,16 +2222,17 @@ mjvOption
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
struct _mjvOption // abstract visualization options
|
||||
struct _mjvOption // abstract visualization options
|
||||
{
|
||||
int label; // what objects to label (mjtLabel)
|
||||
int frame; // which frame to show (mjtFrame)
|
||||
mjtByte geomgroup[mjNGROUP]; // geom visualization by group
|
||||
mjtByte sitegroup[mjNGROUP]; // site visualization by group
|
||||
mjtByte jointgroup[mjNGROUP]; // joint visualization by group
|
||||
int label; // what objects to label (mjtLabel)
|
||||
int frame; // which frame to show (mjtFrame)
|
||||
mjtByte geomgroup[mjNGROUP]; // geom visualization by group
|
||||
mjtByte sitegroup[mjNGROUP]; // site visualization by group
|
||||
mjtByte jointgroup[mjNGROUP]; // joint visualization by group
|
||||
mjtByte tendongroup[mjNGROUP]; // tendon visualization by group
|
||||
mjtByte actuatorgroup[mjNGROUP]; // actuator visualization by group
|
||||
mjtByte flags[mjNVISFLAG]; // visualization flags (indexed by mjtVisFlag)
|
||||
mjtByte skingroup[mjNGROUP]; // skin visualization by group
|
||||
mjtByte flags[mjNVISFLAG]; // visualization flags (indexed by mjtVisFlag)
|
||||
};
|
||||
typedef struct _mjvOption mjvOption;
|
||||
|
||||
|
||||
@@ -732,6 +732,7 @@ struct mjModel_ {
|
||||
|
||||
// skins
|
||||
int* skin_matid; // skin material id; -1: none (nskin x 1)
|
||||
int* skin_group; // group for visibility (nskin x 1)
|
||||
float* skin_rgba; // skin rgba (nskin x 4)
|
||||
float* skin_inflate; // inflate skin in normal direction (nskin x 1)
|
||||
int* skin_vertadr; // first vertex address (nskin x 1)
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#include <mujoco/mjtnum.h>
|
||||
#include <mujoco/mjmodel.h>
|
||||
|
||||
#define mjNGROUP 6 // number of geom, site, joint groups with visflags
|
||||
#define mjNGROUP 6 // number of geom, site, joint, skin groups with visflags
|
||||
#define mjMAXLIGHT 100 // maximum number of lights in a scene
|
||||
#define mjMAXOVERLAY 500 // maximum number of characters in overlay text
|
||||
#define mjMAXLINE 100 // maximum number of lines per plot
|
||||
@@ -250,15 +250,16 @@ typedef struct mjvLight_ mjvLight;
|
||||
|
||||
//---------------------------------- mjvOption -----------------------------------------------------
|
||||
|
||||
struct mjvOption_ { // abstract visualization options
|
||||
int label; // what objects to label (mjtLabel)
|
||||
int frame; // which frame to show (mjtFrame)
|
||||
mjtByte geomgroup[mjNGROUP]; // geom visualization by group
|
||||
mjtByte sitegroup[mjNGROUP]; // site visualization by group
|
||||
mjtByte jointgroup[mjNGROUP]; // joint visualization by group
|
||||
mjtByte tendongroup[mjNGROUP]; // tendon visualization by group
|
||||
struct mjvOption_ { // abstract visualization options
|
||||
int label; // what objects to label (mjtLabel)
|
||||
int frame; // which frame to show (mjtFrame)
|
||||
mjtByte geomgroup[mjNGROUP]; // geom visualization by group
|
||||
mjtByte sitegroup[mjNGROUP]; // site visualization by group
|
||||
mjtByte jointgroup[mjNGROUP]; // joint visualization by group
|
||||
mjtByte tendongroup[mjNGROUP]; // tendon visualization by group
|
||||
mjtByte actuatorgroup[mjNGROUP]; // actuator visualization by group
|
||||
mjtByte flags[mjNVISFLAG]; // visualization flags (indexed by mjtVisFlag)
|
||||
mjtByte skingroup[mjNGROUP]; // skin visualization by group
|
||||
mjtByte flags[mjNVISFLAG]; // visualization flags (indexed by mjtVisFlag)
|
||||
};
|
||||
typedef struct mjvOption_ mjvOption;
|
||||
|
||||
|
||||
@@ -267,6 +267,7 @@
|
||||
X( int, mesh_face, nmeshface, 3 ) \
|
||||
X( int, mesh_graph, nmeshgraph, 1 ) \
|
||||
X( int, skin_matid, nskin, 1 ) \
|
||||
X( int, skin_group, nskin, 1 ) \
|
||||
X( float, skin_rgba, nskin, 4 ) \
|
||||
X( float, skin_inflate, nskin, 1 ) \
|
||||
X( int, skin_vertadr, nskin, 1 ) \
|
||||
|
||||
@@ -1053,6 +1053,7 @@ MjvOptionWrapper::MjWrapper()
|
||||
X(jointgroup),
|
||||
X(tendongroup),
|
||||
X(actuatorgroup),
|
||||
X(skingroup),
|
||||
X(flags) {}
|
||||
#undef X
|
||||
|
||||
@@ -2037,6 +2038,7 @@ This is useful for example when the MJB is not available as a file on disk.)"));
|
||||
X(jointgroup);
|
||||
X(tendongroup);
|
||||
X(actuatorgroup);
|
||||
X(skingroup);
|
||||
X(flags);
|
||||
#undef X
|
||||
|
||||
|
||||
@@ -700,6 +700,7 @@ class MjWrapper<raw::MjvOption> : public WrapperBase<raw::MjvOption> {
|
||||
X(jointgroup);
|
||||
X(tendongroup);
|
||||
X(actuatorgroup);
|
||||
X(skingroup);
|
||||
X(flags);
|
||||
#undef X
|
||||
};
|
||||
|
||||
@@ -877,6 +877,13 @@ void makegroup(int oldstate) {
|
||||
{mjITEM_CHECKBYTE, "Actuator 3", 2, vopt.actuatorgroup+3, ""},
|
||||
{mjITEM_CHECKBYTE, "Actuator 4", 2, vopt.actuatorgroup+4, ""},
|
||||
{mjITEM_CHECKBYTE, "Actuator 5", 2, vopt.actuatorgroup+5, ""},
|
||||
{mjITEM_SEPARATOR, "Skin groups", 1},
|
||||
{mjITEM_CHECKBYTE, "Skin 0", 2, vopt.skingroup, ""},
|
||||
{mjITEM_CHECKBYTE, "Skin 1", 2, vopt.skingroup+1, ""},
|
||||
{mjITEM_CHECKBYTE, "Skin 2", 2, vopt.skingroup+2, ""},
|
||||
{mjITEM_CHECKBYTE, "Skin 3", 2, vopt.skingroup+3, ""},
|
||||
{mjITEM_CHECKBYTE, "Skin 4", 2, vopt.skingroup+4, ""},
|
||||
{mjITEM_CHECKBYTE, "Skin 5", 2, vopt.skingroup+5, ""},
|
||||
{mjITEM_END}
|
||||
};
|
||||
|
||||
|
||||
@@ -217,6 +217,7 @@ void mjv_defaultOption(mjvOption* vopt) {
|
||||
vopt->jointgroup[i] = state;
|
||||
vopt->tendongroup[i] = state;
|
||||
vopt->actuatorgroup[i] = state;
|
||||
vopt->skingroup[i] = state;
|
||||
}
|
||||
|
||||
for (int i=0; i<mjNVISFLAG; i++) {
|
||||
|
||||
@@ -1767,9 +1767,18 @@ void mjv_updateCamera(const mjModel* m, mjData* d, mjvCamera* cam, mjvScene* scn
|
||||
}
|
||||
|
||||
|
||||
|
||||
// update skins only
|
||||
// update all skins, here for backward API compatibility
|
||||
void mjv_updateSkin(const mjModel* m, mjData* d, mjvScene* scn) {
|
||||
mjvOption opt;
|
||||
mjv_defaultOption(&opt);
|
||||
mjv_updateActiveSkin(m, d, scn, &opt);
|
||||
mju_warning("mjv_updateSkin is deprecated, please use mjv_updateActiveSkin.");
|
||||
}
|
||||
|
||||
|
||||
// update visible skins only
|
||||
void mjv_updateActiveSkin(const mjModel* m, mjData* d, mjvScene* scn, const mjvOption* opt) {
|
||||
|
||||
// process skins
|
||||
for (int i=0; i<m->nskin; i++) {
|
||||
// get info
|
||||
@@ -1782,111 +1791,113 @@ void mjv_updateSkin(const mjModel* m, mjData* d, mjvScene* scn) {
|
||||
memset(scn->skinvert + 3*vertadr, 0, 3*vertnum*sizeof(float));
|
||||
memset(scn->skinnormal + 3*vertadr, 0, 3*vertnum*sizeof(float));
|
||||
|
||||
// accumulate positions from all bones
|
||||
for (int j=m->skin_boneadr[i];
|
||||
j<m->skin_boneadr[i]+m->skin_bonenum[i];
|
||||
j++) {
|
||||
// get bind pose
|
||||
mjtNum bindpos[3] = {
|
||||
(mjtNum) m->skin_bonebindpos[3*j],
|
||||
(mjtNum) m->skin_bonebindpos[3*j+1],
|
||||
(mjtNum) m->skin_bonebindpos[3*j+2]
|
||||
};
|
||||
mjtNum bindquat[4] = {
|
||||
(mjtNum) m->skin_bonebindquat[4*j],
|
||||
(mjtNum) m->skin_bonebindquat[4*j+1],
|
||||
(mjtNum) m->skin_bonebindquat[4*j+2],
|
||||
(mjtNum) m->skin_bonebindquat[4*j+3]
|
||||
};
|
||||
|
||||
// compute rotation
|
||||
int bodyid = m->skin_bonebodyid[j];
|
||||
mjtNum quat[4], quatneg[4], rotate[9];
|
||||
mju_negQuat(quatneg, bindquat);
|
||||
mju_mulQuat(quat, d->xquat+4*bodyid, quatneg);
|
||||
mju_quat2Mat(rotate, quat);
|
||||
|
||||
// compute translation
|
||||
mjtNum translate[3];
|
||||
mju_rotVecMat(translate, bindpos, rotate);
|
||||
mju_sub3(translate, d->xpos+3*bodyid, translate);
|
||||
|
||||
// process all bone vertices
|
||||
for (int k=m->skin_bonevertadr[j];
|
||||
k<m->skin_bonevertadr[j]+m->skin_bonevertnum[j];
|
||||
k++) {
|
||||
// vertex id and weight
|
||||
int vid = m->skin_bonevertid[k];
|
||||
float vweight = m->skin_bonevertweight[k];
|
||||
|
||||
// get original position
|
||||
mjtNum pos[3] = {
|
||||
(mjtNum) m->skin_vert[3*(vertadr+vid)],
|
||||
(mjtNum) m->skin_vert[3*(vertadr+vid)+1],
|
||||
(mjtNum) m->skin_vert[3*(vertadr+vid)+2],
|
||||
if (opt->skingroup[m->skin_group[i]]) {
|
||||
// accumulate positions from all bones
|
||||
for (int j=m->skin_boneadr[i];
|
||||
j<m->skin_boneadr[i]+m->skin_bonenum[i];
|
||||
j++) {
|
||||
// get bind pose
|
||||
mjtNum bindpos[3] = {
|
||||
(mjtNum) m->skin_bonebindpos[3*j],
|
||||
(mjtNum) m->skin_bonebindpos[3*j+1],
|
||||
(mjtNum) m->skin_bonebindpos[3*j+2]
|
||||
};
|
||||
mjtNum bindquat[4] = {
|
||||
(mjtNum) m->skin_bonebindquat[4*j],
|
||||
(mjtNum) m->skin_bonebindquat[4*j+1],
|
||||
(mjtNum) m->skin_bonebindquat[4*j+2],
|
||||
(mjtNum) m->skin_bonebindquat[4*j+3]
|
||||
};
|
||||
|
||||
// transform
|
||||
mjtNum pos1[3];
|
||||
mju_rotVecMat(pos1, pos, rotate);
|
||||
mju_addTo3(pos1, translate);
|
||||
// compute rotation
|
||||
int bodyid = m->skin_bonebodyid[j];
|
||||
mjtNum quat[4], quatneg[4], rotate[9];
|
||||
mju_negQuat(quatneg, bindquat);
|
||||
mju_mulQuat(quat, d->xquat+4*bodyid, quatneg);
|
||||
mju_quat2Mat(rotate, quat);
|
||||
|
||||
// accumulate position
|
||||
scn->skinvert[3*(vertadr+vid)] += vweight*(float)pos1[0];
|
||||
scn->skinvert[3*(vertadr+vid)+1] += vweight*(float)pos1[1];
|
||||
scn->skinvert[3*(vertadr+vid)+2] += vweight*(float)pos1[2];
|
||||
}
|
||||
}
|
||||
// compute translation
|
||||
mjtNum translate[3];
|
||||
mju_rotVecMat(translate, bindpos, rotate);
|
||||
mju_sub3(translate, d->xpos+3*bodyid, translate);
|
||||
|
||||
// compute vertex normals from face normals
|
||||
for (int k=faceadr; k<faceadr+facenum; k++) {
|
||||
// get face vertex indices
|
||||
int vid[3] = {
|
||||
m->skin_face[3*k],
|
||||
m->skin_face[3*k+1],
|
||||
m->skin_face[3*k+2]
|
||||
};
|
||||
// process all bone vertices
|
||||
for (int k=m->skin_bonevertadr[j];
|
||||
k<m->skin_bonevertadr[j]+m->skin_bonevertnum[j];
|
||||
k++) {
|
||||
// vertex id and weight
|
||||
int vid = m->skin_bonevertid[k];
|
||||
float vweight = m->skin_bonevertweight[k];
|
||||
|
||||
// get triangle edges
|
||||
mjtNum vec01[3], vec02[3];
|
||||
for (int r=0; r<3; r++) {
|
||||
vec01[r] = scn->skinvert[3*(vertadr+vid[1])+r] - scn->skinvert[3*(vertadr+vid[0])+r];
|
||||
vec02[r] = scn->skinvert[3*(vertadr+vid[2])+r] - scn->skinvert[3*(vertadr+vid[0])+r];
|
||||
}
|
||||
// get original position
|
||||
mjtNum pos[3] = {
|
||||
(mjtNum) m->skin_vert[3*(vertadr+vid)],
|
||||
(mjtNum) m->skin_vert[3*(vertadr+vid)+1],
|
||||
(mjtNum) m->skin_vert[3*(vertadr+vid)+2],
|
||||
};
|
||||
|
||||
// compute face normal
|
||||
mjtNum nrm[3];
|
||||
mju_cross(nrm, vec01, vec02);
|
||||
// transform
|
||||
mjtNum pos1[3];
|
||||
mju_rotVecMat(pos1, pos, rotate);
|
||||
mju_addTo3(pos1, translate);
|
||||
|
||||
// add normal to each vertex with weight = area
|
||||
for (int r=0; r<3; r++) {
|
||||
for (int t=0; t<3; t++) {
|
||||
scn->skinnormal[3*(vertadr+vid[r])+t] += nrm[t];
|
||||
// accumulate position
|
||||
scn->skinvert[3*(vertadr+vid)] += vweight*(float)pos1[0];
|
||||
scn->skinvert[3*(vertadr+vid)+1] += vweight*(float)pos1[1];
|
||||
scn->skinvert[3*(vertadr+vid)+2] += vweight*(float)pos1[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// normalize normals
|
||||
for (int k=vertadr; k<vertadr+vertnum; k++) {
|
||||
float s = sqrtf(
|
||||
scn->skinnormal[3*k]*scn->skinnormal[3*k] +
|
||||
scn->skinnormal[3*k+1]*scn->skinnormal[3*k+1] +
|
||||
scn->skinnormal[3*k+2]*scn->skinnormal[3*k+2]
|
||||
);
|
||||
float scl = 1/mjMAX(mjMINVAL, s);
|
||||
// compute vertex normals from face normals
|
||||
for (int k=faceadr; k<faceadr+facenum; k++) {
|
||||
// get face vertex indices
|
||||
int vid[3] = {
|
||||
m->skin_face[3*k],
|
||||
m->skin_face[3*k+1],
|
||||
m->skin_face[3*k+2]
|
||||
};
|
||||
|
||||
scn->skinnormal[3*k] *= scl;
|
||||
scn->skinnormal[3*k+1] *= scl;
|
||||
scn->skinnormal[3*k+2] *= scl;
|
||||
}
|
||||
// get triangle edges
|
||||
mjtNum vec01[3], vec02[3];
|
||||
for (int r=0; r<3; r++) {
|
||||
vec01[r] = scn->skinvert[3*(vertadr+vid[1])+r] - scn->skinvert[3*(vertadr+vid[0])+r];
|
||||
vec02[r] = scn->skinvert[3*(vertadr+vid[2])+r] - scn->skinvert[3*(vertadr+vid[0])+r];
|
||||
}
|
||||
|
||||
// inflate
|
||||
if (m->skin_inflate[i]) {
|
||||
float inflate = m->skin_inflate[i];
|
||||
// compute face normal
|
||||
mjtNum nrm[3];
|
||||
mju_cross(nrm, vec01, vec02);
|
||||
|
||||
// add normal to each vertex with weight = area
|
||||
for (int r=0; r<3; r++) {
|
||||
for (int t=0; t<3; t++) {
|
||||
scn->skinnormal[3*(vertadr+vid[r])+t] += nrm[t];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// normalize normals
|
||||
for (int k=vertadr; k<vertadr+vertnum; k++) {
|
||||
scn->skinvert[3*k] += inflate*scn->skinnormal[3*k];
|
||||
scn->skinvert[3*k+1] += inflate*scn->skinnormal[3*k+1];
|
||||
scn->skinvert[3*k+2] += inflate*scn->skinnormal[3*k+2];
|
||||
float s = sqrtf(
|
||||
scn->skinnormal[3*k]*scn->skinnormal[3*k] +
|
||||
scn->skinnormal[3*k+1]*scn->skinnormal[3*k+1] +
|
||||
scn->skinnormal[3*k+2]*scn->skinnormal[3*k+2]
|
||||
);
|
||||
float scl = 1/mjMAX(mjMINVAL, s);
|
||||
|
||||
scn->skinnormal[3*k] *= scl;
|
||||
scn->skinnormal[3*k+1] *= scl;
|
||||
scn->skinnormal[3*k+2] *= scl;
|
||||
}
|
||||
|
||||
// inflate
|
||||
if (m->skin_inflate[i]) {
|
||||
float inflate = m->skin_inflate[i];
|
||||
for (int k=vertadr; k<vertadr+vertnum; k++) {
|
||||
scn->skinvert[3*k] += inflate*scn->skinnormal[3*k];
|
||||
scn->skinvert[3*k+1] += inflate*scn->skinnormal[3*k+1];
|
||||
scn->skinvert[3*k+2] += inflate*scn->skinnormal[3*k+2];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1909,6 +1920,6 @@ void mjv_updateScene(const mjModel* m, mjData* d, const mjvOption* opt,
|
||||
|
||||
// update skins
|
||||
if (opt->flags[mjVIS_SKIN]) {
|
||||
mjv_updateSkin(m, d, scn);
|
||||
mjv_updateActiveSkin(m, d, scn, opt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,9 @@ MJAPI void mjv_updateCamera(const mjModel* m, mjData* d, mjvCamera* cam, mjvScen
|
||||
// update skins only
|
||||
MJAPI void mjv_updateSkin(const mjModel* m, mjData* d, mjvScene* scn);
|
||||
|
||||
// update visible skins only
|
||||
MJAPI void mjv_updateActiveSkin(const mjModel* m, mjData* d, mjvScene* scn, const mjvOption* opt);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -67,6 +67,7 @@ mjCComposite::mjCComposite(void) {
|
||||
mjuu_setvec(skinrgba, 1, 1, 1, 1);
|
||||
skininflate = 0;
|
||||
skinsubgrid = 0;
|
||||
skingroup = 0;
|
||||
|
||||
// clear add flags
|
||||
for (int i=0; i<mjNCOMPKINDS; i++) {
|
||||
@@ -1053,6 +1054,7 @@ void mjCComposite::MakeSkin2(mjCModel* model) {
|
||||
skin->material = skinmaterial;
|
||||
mjuu_copyvec(skin->rgba, skinrgba, 4);
|
||||
skin->inflate = skininflate;
|
||||
skin->group = skingroup;
|
||||
|
||||
// populate mesh: two sides
|
||||
for (int i=0; i<2; i++) {
|
||||
@@ -1473,6 +1475,7 @@ void mjCComposite::MakeSkin2Subgrid(mjCModel* model) {
|
||||
skin->material = skinmaterial;
|
||||
mjuu_copyvec(skin->rgba, skinrgba, 4);
|
||||
skin->inflate = skininflate;
|
||||
skin->group = skingroup;
|
||||
|
||||
// populate mesh: two sides
|
||||
mjtNum S = spacing/(1+skinsubgrid);
|
||||
@@ -1643,6 +1646,7 @@ void mjCComposite::MakeSkin3(mjCModel* model) {
|
||||
skin->material = skinmaterial;
|
||||
mjuu_copyvec(skin->rgba, skinrgba, 4);
|
||||
skin->inflate = skininflate;
|
||||
skin->group = skingroup;
|
||||
|
||||
// box
|
||||
if (type==mjCOMPTYPE_BOX) {
|
||||
|
||||
@@ -94,6 +94,7 @@ class mjCComposite {
|
||||
float skinrgba[4]; // skin rgba
|
||||
float skininflate; // inflate skin
|
||||
int skinsubgrid; // number of skin subgrid points; 0: none (2D only)
|
||||
int skingroup; // skin group of the composite object
|
||||
|
||||
// element options
|
||||
bool add[mjNCOMPKINDS]; // add element
|
||||
|
||||
@@ -1408,6 +1408,7 @@ mjCSkin::mjCSkin(mjCModel* _model) {
|
||||
rgba[0] = rgba[1] = rgba[2] = 0.5f;
|
||||
rgba[3] = 1.0f;
|
||||
inflate = 0;
|
||||
group = 0;
|
||||
|
||||
vert.clear();
|
||||
texcoord.clear();
|
||||
@@ -1530,7 +1531,6 @@ void mjCSkin::Compile(const mjVFS* vfs) {
|
||||
throw mjCError(this, "unkown material '%s' in skin", material.c_str());
|
||||
}
|
||||
|
||||
|
||||
// set total vertex weights to 0
|
||||
vector<float> vw;
|
||||
size_t nvert = vert.size()/3;
|
||||
|
||||
@@ -1628,6 +1628,7 @@ void mjCModel::CopyObjects(mjModel* m) {
|
||||
// set fields
|
||||
m->skin_matid[i] = psk->matid;
|
||||
copyvec(m->skin_rgba+4*i, psk->rgba, 4);
|
||||
m->skin_group[i] = psk->group;
|
||||
m->skin_inflate[i] = psk->inflate;
|
||||
m->skin_vertadr[i] = vert_adr;
|
||||
m->skin_vertnum[i] = psk->vert.size()/3;
|
||||
|
||||
@@ -504,6 +504,7 @@ class mjCSkin: public mjCBase {
|
||||
std::string material; // name of material used for rendering
|
||||
float rgba[4]; // rgba when material is omitted
|
||||
float inflate; // inflate in normal direction
|
||||
int group; // group for visualization
|
||||
|
||||
// mesh
|
||||
std::vector<float> vert; // vertex positions
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include <mujoco/mjmodel.h>
|
||||
#include <mujoco/mjvisualize.h>
|
||||
#include "engine/engine_macro.h"
|
||||
#include "engine/engine_util_errmem.h"
|
||||
#include "engine/engine_util_misc.h"
|
||||
@@ -165,8 +166,8 @@ static const char* MJCF[nMJCF][mjXATTRNUM] = {
|
||||
{"hfield", "*", "5", "name", "file", "nrow", "ncol", "size"},
|
||||
{"mesh", "*", "11", "name", "class", "file", "vertex", "normal",
|
||||
"texcoord", "face", "refpos", "refquat", "scale", "smoothnormal"},
|
||||
{"skin", "*", "8", "name", "file", "material", "rgba", "inflate",
|
||||
"vertex", "texcoord", "face"},
|
||||
{"skin", "*", "9", "name", "file", "material", "rgba", "inflate",
|
||||
"vertex", "texcoord", "face", "group"},
|
||||
{"<"},
|
||||
{"bone", "*", "5", "body", "bindpos", "bindquat", "vertid", "vertweight"},
|
||||
{">"},
|
||||
@@ -210,7 +211,7 @@ static const char* MJCF[nMJCF][mjXATTRNUM] = {
|
||||
"limited", "range", "margin", "solreflimit", "solimplimit",
|
||||
"frictionloss", "solreffriction", "solimpfriction",
|
||||
"material", "rgba", "width"},
|
||||
{"skin", "?", "5", "texcoord", "material", "rgba", "inflate", "subgrid"},
|
||||
{"skin", "?", "6", "texcoord", "material", "group", "rgba", "inflate", "subgrid"},
|
||||
{"geom", "?", "17", "type", "contype", "conaffinity", "condim",
|
||||
"group", "priority", "size", "material", "rgba", "friction", "mass",
|
||||
"density", "solmix", "solref", "solimp", "margin", "gap"},
|
||||
@@ -983,6 +984,10 @@ void mjXReader::OneSkin(XMLElement* elem, mjCSkin* pskin) {
|
||||
ReadAttrTxt(elem, "name", pskin->name);
|
||||
ReadAttrTxt(elem, "file", pskin->file);
|
||||
ReadAttrTxt(elem, "material", pskin->material);
|
||||
ReadAttrInt(elem, "group", &pskin->group);
|
||||
if (pskin->group<0 || pskin->group>=mjNGROUP) {
|
||||
throw mjXError(elem, "skin group must be between 0 and 5");
|
||||
}
|
||||
ReadAttr(elem, "rgba", 4, pskin->rgba, text);
|
||||
ReadAttr(elem, "inflate", 1, &pskin->inflate, text);
|
||||
|
||||
@@ -1617,6 +1622,10 @@ void mjXReader::OneComposite(XMLElement* elem, mjCBody* pbody, mjCDef* def) {
|
||||
ReadAttr(eskin, "rgba", 4, comp.skinrgba, text);
|
||||
ReadAttr(eskin, "inflate", 1, &comp.skininflate, text);
|
||||
ReadAttrInt(eskin, "subgrid", &comp.skinsubgrid);
|
||||
ReadAttrInt(eskin, "group", &comp.skingroup, 0);
|
||||
if (comp.skingroup<0 || comp.skingroup>=mjNGROUP) {
|
||||
throw mjXError(eskin, "skin group must be between 0 and 5");
|
||||
}
|
||||
}
|
||||
|
||||
// set type-specific defaults
|
||||
|
||||
@@ -125,6 +125,7 @@ void mjXWriter::OneSkin(XMLElement* elem, mjCSkin* pskin) {
|
||||
WriteAttrTxt(elem, "name", pskin->name);
|
||||
WriteAttrTxt(elem, "file", pskin->file);
|
||||
WriteAttrTxt(elem, "material", pskin->material);
|
||||
WriteAttrInt(elem, "group", pskin->group, 0);
|
||||
WriteAttr(elem, "rgba", 4, pskin->rgba, mydef.geom.rgba);
|
||||
WriteAttr(elem, "inflate", 1, &pskin->inflate, &zero);
|
||||
|
||||
|
||||
Vendored
+44
@@ -0,0 +1,44 @@
|
||||
<mujoco>
|
||||
<asset>
|
||||
<skin name="skin" rgba="1 0 1 1" inflate="0.01"
|
||||
vertex="
|
||||
-.5 -.5 -.5
|
||||
.5 -.5 -.5
|
||||
-.5 .5 -.5
|
||||
.5 .5 -.5
|
||||
-.5 -.5 .5
|
||||
.5 -.5 .5
|
||||
-.5 .5 .5
|
||||
.5 .5 .5"
|
||||
texcoord=".1 .1 .1 .1 .1 .1 .1 .1 .1 .1 .1 .1 .1 .1 .1 .1"
|
||||
face="0 1 4 4 1 5 5 1 3 5 3 7 7 3 2 7 2 6 6 2 0 6 0 4 4 5 7 4 7 6">
|
||||
<bone body="box" bindpos="0 0 .5" bindquat="1 0 0 0" vertid="4 5 6 7" vertweight="1 1 1 1"/>
|
||||
<bone body="world" bindpos="0 0 -.5" bindquat="1 0 0 0" vertid="0 1 2 3" vertweight="1 1 1 1"/>
|
||||
</skin>
|
||||
</asset>
|
||||
|
||||
|
||||
<worldbody>
|
||||
<geom type="plane" size="2 2 .01"/>
|
||||
<light pos="0 1 3" dir="0 -1 -2"/>
|
||||
<light pos="0 -1 3" dir="0 1 -2"/>
|
||||
<body name="box" pos="0 0 .9">
|
||||
<joint limited="true" range="-45 45"/>
|
||||
<geom type="box" size=".5 .5 .1"/>
|
||||
</body>
|
||||
<body pos="1 -1 .6">
|
||||
<freejoint/>
|
||||
<composite prefix="B0" type="box" count="4 4 4" spacing=".2">
|
||||
<geom group="4" size=".1"/>
|
||||
<skin rgba="1 1 0 1" group="2"/>
|
||||
</composite>
|
||||
</body>
|
||||
<body pos="-1 1 .6">
|
||||
<freejoint/>
|
||||
<composite prefix="B1" type="box" count="4 4 4" spacing=".2">
|
||||
<geom group="2" size=".1"/>
|
||||
<skin rgba="0 1 1 1" group="4"/>
|
||||
</composite>
|
||||
</body>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
@@ -379,6 +379,57 @@ TEST_F(UserDataTest, PositiveControlRange) {
|
||||
EXPECT_THAT(error.data(), HasSubstr("control range cannot be negative"));
|
||||
}
|
||||
|
||||
TEST_F(UserDataTest, ReadsSkinGroups) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
<body>
|
||||
<composite prefix="B0" type="box" count="4 4 4" spacing=".2">
|
||||
<geom size=".1" group="2"/>
|
||||
<skin group="4"/>
|
||||
</composite>
|
||||
</body>
|
||||
<body>
|
||||
<composite prefix="B1" type="box" count="4 4 4" spacing=".2">
|
||||
<geom size=".1" group="4"/>
|
||||
<skin group="2"/>
|
||||
</composite>
|
||||
</body>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
)";
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
ASSERT_THAT(model, NotNull());
|
||||
int geomid1 = mj_name2id(model, mjOBJ_GEOM, "B0G0_0_0");
|
||||
int geomid2 = mj_name2id(model, mjOBJ_GEOM, "B1G0_0_0");
|
||||
EXPECT_THAT(model->geom_group[geomid1], 2);
|
||||
EXPECT_THAT(model->skin_group[0], 4);
|
||||
EXPECT_THAT(model->geom_group[geomid2], 4);
|
||||
EXPECT_THAT(model->skin_group[1], 2);
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(UserDataTest, InvalidSkinGroup) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
<body>
|
||||
<composite prefix="B0" type="box" count="6 6 6" spacing=".2">
|
||||
<geom size=".1"/>
|
||||
<skin group="6"/>
|
||||
</composite>
|
||||
</body>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
)";
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
ASSERT_THAT(model, IsNull());
|
||||
EXPECT_THAT(error.data(), HasSubstr("skin group must be between 0 and 5\nElement 'skin', line 7"));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
// ------------- test relative frame sensor parsing ----------------------------
|
||||
|
||||
using RelativeFrameSensorParsingTest = MujocoTest;
|
||||
|
||||
Reference in New Issue
Block a user