Remove box, cylinder and ellipsoid composite types.
Also, fix a bug with Flex textures that was causing an incorrect allocation of the textures in mjModel. Fixes #2013. PiperOrigin-RevId: 692510858 Change-Id: If781716216974e085244da27bc3aa57d91c3458c
This commit is contained in:
committed by
Copybara-Service
parent
ebe60b9ad5
commit
831d9881d3
@@ -3566,9 +3566,10 @@ saving the XML:
|
|||||||
|
|
||||||
:at:`texcoord`: :at-val:`real(2*npoint), optional`
|
:at:`texcoord`: :at-val:`real(2*npoint), optional`
|
||||||
Texture coordinates of each point, passed through to the automatically-generated flex. Note that flexcomp does not
|
Texture coordinates of each point, passed through to the automatically-generated flex. Note that flexcomp does not
|
||||||
generate texture coordinates automatically, except for 2D grids. For all other types, the user can specify explicit
|
generate texture coordinates automatically, except for 2D grids, box, cylinder and ellipsoid. For all other types,
|
||||||
texture coordinates here, even if the points themselves were generated automatically. This requires understanding of
|
the user can specify explicit texture coordinates here, even if the points themselves were generated automatically.
|
||||||
the layout of the automatically-generated points and how they correspond to the texture referenced by the material.
|
This requires understanding of the layout of the automatically-generated points and how they correspond to the
|
||||||
|
texture referenced by the material.
|
||||||
|
|
||||||
.. _body-flexcomp-mass:
|
.. _body-flexcomp-mass:
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ General
|
|||||||
- Sorting now uses a faster, native sort function (fixes :github:issue:`1638`).
|
- Sorting now uses a faster, native sort function (fixes :github:issue:`1638`).
|
||||||
- The PBR texture layers introduced in 3.2.1 were refactored from seperate sub-elements to a single
|
- The PBR texture layers introduced in 3.2.1 were refactored from seperate sub-elements to a single
|
||||||
:ref:`layer<material-layer>` sub-element.
|
:ref:`layer<material-layer>` sub-element.
|
||||||
|
- The composite types box, cylinder, and sphere have been removed. Users should instead use the equivalent types
|
||||||
|
available in :ref:`flexcomp<body-flexcomp>`.
|
||||||
|
|
||||||
MJX
|
MJX
|
||||||
^^^
|
^^^
|
||||||
@@ -33,6 +35,7 @@ Bug fixes
|
|||||||
several places leading to incorrect computations of constraint inertia, readings of affected force/torque sensors and
|
several places leading to incorrect computations of constraint inertia, readings of affected force/torque sensors and
|
||||||
runtime enabling/disabling of such constraints.
|
runtime enabling/disabling of such constraints.
|
||||||
- Fixed a bug in slider-crank :ref:`transmission<geTransmission>`. The bug was introduced in 3.0.0.
|
- Fixed a bug in slider-crank :ref:`transmission<geTransmission>`. The bug was introduced in 3.0.0.
|
||||||
|
- Fixed a bug in flex texture coordinates that prevented the correct allocation of textures in mjModel.
|
||||||
|
|
||||||
|
|
||||||
Documentation
|
Documentation
|
||||||
|
|||||||
+3
-54
@@ -1280,62 +1280,11 @@ scenario (e.g. a stretched rubber band).
|
|||||||
The cloth is deprecated. It is recommended to use 2D flex :ref:`deformable objects <CDeformable>` for simulating thin
|
The cloth is deprecated. It is recommended to use 2D flex :ref:`deformable objects <CDeformable>` for simulating thin
|
||||||
elastic structures.
|
elastic structures.
|
||||||
|
|
||||||
**Box**.
|
**Box, cylinder and ellipsoid**.
|
||||||
|
|
||||||
|image14| |image15|
|
|
||||||
|
|
||||||
.. code-block:: xml
|
The box type, as well as the cylinder and ellipsoid types, are now deprecated in favor of 3D flex :ref:`deformable
|
||||||
|
objects <CDeformable>``. element.
|
||||||
<body pos="0 0 1">
|
|
||||||
<freejoint/>
|
|
||||||
<composite type="box" count="7 7 7" spacing="0.04">
|
|
||||||
<skin texcoord="true" material="matsponge" rgba=".7 .7 .7 1"/>
|
|
||||||
<geom type="capsule" size=".015 0.05" rgba=".8 .2 .1 1"/>
|
|
||||||
</composite>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
The box type, as well as the cylinder and ellipsoid types below, are used to model soft 3D objects. The element bodies
|
|
||||||
form a grid along the outer shell, thus the number of element bodies scales with the square of the linear dimension.
|
|
||||||
This is much more efficient than simulating a 3D grid. The parent body within which :el:`composite` appears is at the
|
|
||||||
center of the soft object. All element bodies are children of the parent. Each element body has a single sliding joint
|
|
||||||
pointing away from the parent. These joints allow the surface of the soft object to compress and expand at any point.
|
|
||||||
The joints are equality-constrained to their initial position, so as to maintain the shape. In addition each joint is
|
|
||||||
equality-constrained to its neighbor joints, so that when the soft objects deforms, the deformation is smooth.
|
|
||||||
Finally, there is a tendon equality constraint specifying that the sum of all joints should remain constant. This
|
|
||||||
attempts to preserve the volume of the soft object approximately. If the object is squeezed from all sides it will
|
|
||||||
compress and the volume will decrease, but otherwise some element bodies will stick out to compensate for squeezing
|
|
||||||
elsewhere. The plot on the left shows this effect; we are using the capsule probe to compress one corner, and the
|
|
||||||
opposite sides of the cube expand a bit, while the deformations remain smooth. The :at:`count` attribute determines
|
|
||||||
the number of element bodies in each dimension, so if the counts are different the resulting object will be a
|
|
||||||
rectangular box and not a cube. The geoms attached to the element bodies can be spheres, capsules or ellipsoids.
|
|
||||||
Spheres are faster for collision detection, but they result in a thin shell, allowing other bodies to "get under the
|
|
||||||
skin" of the soft object. When capsules or ellipsoids are used, they are automatically oriented so that the long axis
|
|
||||||
points to the outside, thus creating a thicker shell which is harder to penetrate.
|
|
||||||
|
|
||||||
**Cylinder and ellipsoid**.
|
|
||||||
|
|
||||||
|image16| |image17|
|
|
||||||
|
|
||||||
.. code-block:: xml
|
|
||||||
|
|
||||||
<body pos="0 0 1">
|
|
||||||
<freejoint/>
|
|
||||||
<composite type="ellipsoid" count="5 7 9" spacing="0.05">
|
|
||||||
<skin texcoord="true" material="matsponge" rgba=".7 .7 .7 1"/>
|
|
||||||
<geom type="capsule" size=".015 0.05" rgba=".8 .2 .1 1"/>
|
|
||||||
</composite>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
Cylinders and ellipsoids are created in the same way as boxes. The only difference is that the reference positions of
|
|
||||||
the element bodies (relative to the parent) are projected on a cylinder or ellipsoid, with size implied by the
|
|
||||||
:at:`count` attribute. The automatic skin generator is aware of the smooth surfaces, and adjusts the skin normals
|
|
||||||
accordingly. In the plots we have used the capsule probe to press on each body, then paused the simulation and moved the
|
|
||||||
probe away (which is possible because the probe is a mocap body which can move independent of the physics). In this way
|
|
||||||
we can see the indentation made by the probe, and the resulting deformation in the rest of the body. By changing the
|
|
||||||
solref and solimp attributes of the equality constraints that hold the soft object together, one can adjust the behavior
|
|
||||||
of the system making it softer or harder, damped or springy, etc. Note that box, cylinder and ellipsoid objects do not
|
|
||||||
involve long kinematic chains, and can be simulated at large timesteps -- similar to particle and grid, and unlike rope
|
|
||||||
and cloth.
|
|
||||||
|
|
||||||
.. _CDeformable:
|
.. _CDeformable:
|
||||||
|
|
||||||
|
|||||||
@@ -14,10 +14,6 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<mujoco model="Soft box">
|
<mujoco model="Soft box">
|
||||||
<!-- Degree of Freedom: 224
|
|
||||||
Actuators: 0
|
|
||||||
Equality constraints: 651
|
|
||||||
-->
|
|
||||||
<include file="scene.xml"/>
|
<include file="scene.xml"/>
|
||||||
|
|
||||||
<option solver="CG" tolerance="1e-6"/>
|
<option solver="CG" tolerance="1e-6"/>
|
||||||
@@ -25,13 +21,14 @@
|
|||||||
<size memory="30M"/>
|
<size memory="30M"/>
|
||||||
|
|
||||||
<worldbody>
|
<worldbody>
|
||||||
<body pos="0 0 1">
|
<body pos="0 0 1" name="body">
|
||||||
<freejoint/>
|
<freejoint/>
|
||||||
<composite type="box" count="7 7 7" spacing="0.04">
|
<geom size=".1" contype="0" conaffinity="0" group="4"/>
|
||||||
<skin texcoord="true" material="matsponge" rgba=".7 .7 .7 1"/>
|
<flexcomp name="softbox" type="box" count="7 7 7" spacing=".04 .04 .04"
|
||||||
<geom type="capsule" size=".015 0.05" rgba=".8 .2 .1 1"/>
|
radius="0.01" dim="3" material="matsponge">
|
||||||
<joint kind="main" solreffix="0.03 1" solimpfix="0 .1 .01"/>
|
<contact internal="false" selfcollide="none"/>
|
||||||
</composite>
|
<edge equality="true"/>
|
||||||
|
</flexcomp>
|
||||||
</body>
|
</body>
|
||||||
</worldbody>
|
</worldbody>
|
||||||
</mujoco>
|
</mujoco>
|
||||||
|
|||||||
@@ -192,25 +192,6 @@ void mjCComposite::SetDefault(void) {
|
|||||||
case mjCOMPTYPE_CLOTH: // cloth
|
case mjCOMPTYPE_CLOTH: // cloth
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case mjCOMPTYPE_BOX: // 3D
|
|
||||||
case mjCOMPTYPE_CYLINDER:
|
|
||||||
case mjCOMPTYPE_ELLIPSOID:
|
|
||||||
|
|
||||||
// no self-collisions
|
|
||||||
def[0].spec.geom->contype = 0;
|
|
||||||
|
|
||||||
// soft smoothing
|
|
||||||
AdjustSoft(solrefsmooth, solimpsmooth, 1);
|
|
||||||
|
|
||||||
// soft fix everywhere
|
|
||||||
for (int i=0; i<mjNCOMPKINDS; i++) {
|
|
||||||
AdjustSoft(def[i].spec.equality->solref, def[i].spec.equality->solimp, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// hard main tendon fix
|
|
||||||
AdjustSoft(def[mjCOMPKIND_TENDON].spec.equality->solref,
|
|
||||||
def[mjCOMPKIND_TENDON].spec.equality->solimp, 0);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
// SHOULD NOT OCCUR
|
// SHOULD NOT OCCUR
|
||||||
mju_error("Invalid composite type: %d", type);
|
mju_error("Invalid composite type: %d", type);
|
||||||
@@ -347,11 +328,6 @@ bool mjCComposite::Make(mjSpec* spec, mjsBody* body, char* error, int error_sz)
|
|||||||
"\"shell\" instead.",
|
"\"shell\" instead.",
|
||||||
error_sz);
|
error_sz);
|
||||||
|
|
||||||
case mjCOMPTYPE_BOX:
|
|
||||||
case mjCOMPTYPE_CYLINDER:
|
|
||||||
case mjCOMPTYPE_ELLIPSOID:
|
|
||||||
return MakeBox(model, body, error, error_sz);
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return comperr(error, "Unknown shape in composite", error_sz);
|
return comperr(error, "Unknown shape in composite", error_sz);
|
||||||
}
|
}
|
||||||
@@ -919,166 +895,6 @@ mjsBody* mjCComposite::AddRopeBody(mjCModel* model, mjsBody* body, int ix, int i
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// project from box to other shape
|
|
||||||
void mjCComposite::BoxProject(double* pos) {
|
|
||||||
// determine sizes
|
|
||||||
double size[3] = {
|
|
||||||
0.5*spacing*(count[0]-1),
|
|
||||||
0.5*spacing*(count[1]-1),
|
|
||||||
0.5*spacing*(count[2]-1)
|
|
||||||
};
|
|
||||||
|
|
||||||
// box
|
|
||||||
if (type==mjCOMPTYPE_BOX) {
|
|
||||||
pos[0] *= size[0];
|
|
||||||
pos[1] *= size[1];
|
|
||||||
pos[2] *= size[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
// cylinder
|
|
||||||
else if (type==mjCOMPTYPE_CYLINDER) {
|
|
||||||
double L0 = std::max(std::abs(pos[0]), std::abs(pos[1]));
|
|
||||||
mjuu_normvec(pos, 2);
|
|
||||||
pos[0] *= size[0]*L0;
|
|
||||||
pos[1] *= size[1]*L0;
|
|
||||||
pos[2] *= size[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
// ellipsoid
|
|
||||||
else if (type==mjCOMPTYPE_ELLIPSOID) {
|
|
||||||
mjuu_normvec(pos, 3);
|
|
||||||
pos[0] *= size[0];
|
|
||||||
pos[1] *= size[1];
|
|
||||||
pos[2] *= size[2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// make 3d box, ellipsoid or cylinder
|
|
||||||
bool mjCComposite::MakeBox(mjCModel* model, mjsBody* body, char* error, int error_sz) {
|
|
||||||
char txt[100];
|
|
||||||
|
|
||||||
// check dim
|
|
||||||
if (dim!=3) {
|
|
||||||
return comperr(error, "Box and ellipsoid must be three-dimensional", error_sz);
|
|
||||||
}
|
|
||||||
|
|
||||||
// center geom: two times bigger
|
|
||||||
mjsGeom* geom = mjs_addGeom(body, &def[0].spec);
|
|
||||||
mjs_setDefault(geom->element, mjs_getDefault(body->element));
|
|
||||||
geom->type = mjGEOM_SPHERE;
|
|
||||||
mju::sprintf_arr(txt, "%sGcenter", prefix.c_str());
|
|
||||||
mjs_setString(geom->name, txt);
|
|
||||||
mjuu_setvec(geom->pos, 0, 0, 0);
|
|
||||||
geom->size[0] *= 2;
|
|
||||||
geom->size[1] = 0;
|
|
||||||
geom->size[2] = 0;
|
|
||||||
|
|
||||||
// fixed tendon for all joints
|
|
||||||
mjCTendon* ten = model->AddTendon(def + mjCOMPKIND_TENDON);
|
|
||||||
ten->classname = model->Default()->name;
|
|
||||||
mju::sprintf_arr(txt, "%sT", prefix.c_str());
|
|
||||||
ten->name = txt;
|
|
||||||
|
|
||||||
// create bodies, geoms and joints: outside shell only
|
|
||||||
for (int ix=0; ix<count[0]; ix++) {
|
|
||||||
for (int iy=0; iy<count[1]; iy++) {
|
|
||||||
for (int iz=0; iz<count[2]; iz++) {
|
|
||||||
if (ix==0 || ix==count[0]-1 ||
|
|
||||||
iy==0 || iy==count[1]-1 ||
|
|
||||||
iz==0 || iz==count[2]-1) {
|
|
||||||
// create body
|
|
||||||
mjsBody* b = mjs_addBody(body, NULL);
|
|
||||||
mju::sprintf_arr(txt, "%sB%d_%d_%d", prefix.c_str(), ix, iy, iz);
|
|
||||||
mjs_setString(b->name, txt);
|
|
||||||
|
|
||||||
// set body position (+/- 1)
|
|
||||||
b->pos[0] = 2.0*ix/(count[0]-1) - 1;
|
|
||||||
b->pos[1] = 2.0*iy/(count[1]-1) - 1;
|
|
||||||
b->pos[2] = 2.0*iz/(count[2]-1) - 1;
|
|
||||||
|
|
||||||
// reshape
|
|
||||||
BoxProject(b->pos);
|
|
||||||
|
|
||||||
// reorient body
|
|
||||||
b->alt.type = mjORIENTATION_ZAXIS;
|
|
||||||
mjuu_copyvec(b->alt.zaxis, b->pos, 3);
|
|
||||||
mjuu_normvec(b->alt.zaxis, 3);
|
|
||||||
|
|
||||||
// add geom
|
|
||||||
mjsGeom* g = mjs_addGeom(b, &def[0].spec);
|
|
||||||
mjs_setDefault(g->element, mjs_getDefault(body->element));
|
|
||||||
mju::sprintf_arr(txt, "%sG%d_%d_%d", prefix.c_str(), ix, iy, iz);
|
|
||||||
mjs_setString(g->name, txt);
|
|
||||||
|
|
||||||
// offset inwards, enforce sphere or capsule
|
|
||||||
if (g->type==mjGEOM_CAPSULE) {
|
|
||||||
g->pos[2] = -(g->size[0] + g->size[1]);
|
|
||||||
} else {
|
|
||||||
g->type = mjGEOM_SPHERE;
|
|
||||||
g->pos[2] = -g->size[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
// add slider joint
|
|
||||||
mjsJoint* jnt = mjs_addJoint(b, &defjoint[mjCOMPKIND_JOINT][0].spec);
|
|
||||||
mjs_setDefault(jnt->element, mjs_getDefault(body->element));
|
|
||||||
mju::sprintf_arr(txt, "%sJ%d_%d_%d", prefix.c_str(), ix, iy, iz);
|
|
||||||
mjs_setString(jnt->name, txt);
|
|
||||||
jnt->type = mjJNT_SLIDE;
|
|
||||||
mjuu_setvec(jnt->pos, 0, 0, 0);
|
|
||||||
mjuu_setvec(jnt->axis, 0, 0, 1);
|
|
||||||
|
|
||||||
// add fix constraint
|
|
||||||
mjsEquality* eq = mjs_addEquality(&model->spec, &def[mjCOMPKIND_JOINT].spec);
|
|
||||||
mjs_setDefault(eq->element, &model->Default()->spec);
|
|
||||||
eq->type = mjEQ_JOINT;
|
|
||||||
mjs_setString(eq->name1, mjs_getString(jnt->name));
|
|
||||||
|
|
||||||
// add joint to tendon
|
|
||||||
ten->WrapJoint(std::string(mjs_getString(jnt->name)), 1);
|
|
||||||
|
|
||||||
// add neighbor constraints
|
|
||||||
for (int i=0; i<3; i++) {
|
|
||||||
int ix1 = mjMIN(ix+(i==0), count[0]-1);
|
|
||||||
int iy1 = mjMIN(iy+(i==1), count[1]-1);
|
|
||||||
int iz1 = mjMIN(iz+(i==2), count[2]-1);
|
|
||||||
if ((ix1==0 || ix1==count[0]-1 ||
|
|
||||||
iy1==0 || iy1==count[1]-1 ||
|
|
||||||
iz1==0 || iz1==count[2]-1) &&
|
|
||||||
(ix!=ix1 || iy!=iy1 || iz!=iz1)) {
|
|
||||||
char txt2[200];
|
|
||||||
mju::sprintf_arr(txt2,
|
|
||||||
"%sJ%d_%d_%d", prefix.c_str(), ix1, iy1, iz1);
|
|
||||||
mjsEquality* eqn = mjs_addEquality(&model->spec, 0);
|
|
||||||
mju_copy(eqn->solref, solrefsmooth, mjNREF);
|
|
||||||
mju_copy(eqn->solimp, solimpsmooth, mjNIMP);
|
|
||||||
eqn->type = mjEQ_JOINT;
|
|
||||||
mjs_setString(eqn->name1, txt);
|
|
||||||
mjs_setString(eqn->name2, txt2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// finalize fixed tendon
|
|
||||||
mjsEquality* eqt = mjs_addEquality(&model->spec, &def[mjCOMPKIND_TENDON].spec);
|
|
||||||
mjs_setDefault(eqt->element, &model->Default()->spec);
|
|
||||||
eqt->type = mjEQ_TENDON;
|
|
||||||
mjs_setString(eqt->name1, ten->name.c_str());
|
|
||||||
|
|
||||||
// skin
|
|
||||||
if (skin) {
|
|
||||||
MakeSkin3(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// add shear tendons to 2D
|
// add shear tendons to 2D
|
||||||
void mjCComposite::MakeShear(mjCModel* model) {
|
void mjCComposite::MakeShear(mjCModel* model) {
|
||||||
char txt[100], txt1[100], txt2[100];
|
char txt[100], txt1[100], txt2[100];
|
||||||
@@ -1838,272 +1654,3 @@ void mjCComposite::MakeSkin2Subgrid(mjCModel* model, mjtNum inflate) {
|
|||||||
mju_free(D);
|
mju_free(D);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// add skin to 3D
|
|
||||||
void mjCComposite::MakeSkin3(mjCModel* model) {
|
|
||||||
int vcnt = 0;
|
|
||||||
std::map<std::string, int> vmap;
|
|
||||||
char txt[100], cnt0[10], cnt1[10], cnt2[10];
|
|
||||||
std::string fmt;
|
|
||||||
|
|
||||||
// string counts
|
|
||||||
mju::sprintf_arr(cnt0, "%d", count[0]-1);
|
|
||||||
mju::sprintf_arr(cnt1, "%d", count[1]-1);
|
|
||||||
mju::sprintf_arr(cnt2, "%d", count[2]-1);
|
|
||||||
|
|
||||||
// add skin, set name and material
|
|
||||||
mjsSkin* skin = mjs_addSkin(&model->spec);
|
|
||||||
mju::sprintf_arr(txt, "%sSkin", prefix.c_str());
|
|
||||||
mjs_setString(skin->name, txt);
|
|
||||||
mjs_setString(skin->material, skinmaterial.c_str());
|
|
||||||
mjuu_copyvec(skin->rgba, skinrgba, 4);
|
|
||||||
skin->inflate = skininflate;
|
|
||||||
skin->group = skingroup;
|
|
||||||
|
|
||||||
// box
|
|
||||||
if (type==mjCOMPTYPE_BOX || type==mjCOMPTYPE_PARTICLE) {
|
|
||||||
// z-faces
|
|
||||||
MakeSkin3Box(skin, count[0], count[1], 1, vcnt, "%sB%d_%d_0");
|
|
||||||
fmt = "%sB%d_%d_" + std::string(cnt2);
|
|
||||||
MakeSkin3Box(skin, count[0], count[1], 0, vcnt, fmt.c_str());
|
|
||||||
|
|
||||||
// y-faces
|
|
||||||
MakeSkin3Box(skin, count[0], count[2], 0, vcnt, "%sB%d_0_%d");
|
|
||||||
fmt = "%sB%d_" + std::string(cnt1) + "_%d";
|
|
||||||
MakeSkin3Box(skin, count[0], count[2], 1, vcnt, fmt.c_str());
|
|
||||||
|
|
||||||
// x-faces
|
|
||||||
MakeSkin3Box(skin, count[1], count[2], 1, vcnt, "%sB0_%d_%d");
|
|
||||||
fmt = "%sB" + std::string(cnt0) + "_%d_%d";
|
|
||||||
MakeSkin3Box(skin, count[1], count[2], 0, vcnt, fmt.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// cylinder
|
|
||||||
else if (type==mjCOMPTYPE_CYLINDER) {
|
|
||||||
// generate vertices in map
|
|
||||||
for (int ix=0; ix<count[0]; ix++) {
|
|
||||||
for (int iy=0; iy<count[1]; iy++) {
|
|
||||||
for (int iz=0; iz<count[2]; iz++) {
|
|
||||||
bool xedge = (ix==0 || ix==count[0]-1);
|
|
||||||
bool yedge = (iy==0 || iy==count[1]-1);
|
|
||||||
if (xedge || yedge) {
|
|
||||||
// body name
|
|
||||||
mju::sprintf_arr(txt, "%sB%d_%d_%d", prefix.c_str(), ix, iy, iz);
|
|
||||||
|
|
||||||
// add vertex
|
|
||||||
vert.push_back(0);
|
|
||||||
vert.push_back(0);
|
|
||||||
vert.push_back(0);
|
|
||||||
|
|
||||||
// texture coordinate
|
|
||||||
if (skintexcoord) {
|
|
||||||
float X = 0, Y = 0;
|
|
||||||
if (xedge) {
|
|
||||||
X = iy/(float)(count[1]-1);
|
|
||||||
Y = iz/(float)(count[2]-1);
|
|
||||||
} else {
|
|
||||||
X = ix/(float)(count[0]-1);
|
|
||||||
Y = iz/(float)(count[2]-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
texcoord.push_back(X);
|
|
||||||
texcoord.push_back(Y);
|
|
||||||
}
|
|
||||||
|
|
||||||
// save vertex id in map
|
|
||||||
vmap[txt] = vcnt;
|
|
||||||
vcnt++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// y-faces
|
|
||||||
MakeSkin3Smooth(skin, count[0], count[2], 0, vmap, "%sB%d_0_%d");
|
|
||||||
fmt = "%sB%d_" + std::string(cnt1) + "_%d";
|
|
||||||
MakeSkin3Smooth(skin, count[0], count[2], 1, vmap, fmt.c_str());
|
|
||||||
|
|
||||||
// x-faces
|
|
||||||
MakeSkin3Smooth(skin, count[1], count[2], 1, vmap, "%sB0_%d_%d");
|
|
||||||
fmt = "%sB" + std::string(cnt0) + "_%d_%d";
|
|
||||||
MakeSkin3Smooth(skin, count[1], count[2], 0, vmap, fmt.c_str());
|
|
||||||
|
|
||||||
// z-faces, boxy-type
|
|
||||||
MakeSkin3Box(skin, count[0], count[1], 1, vcnt, "%sB%d_%d_0");
|
|
||||||
fmt = "%sB%d_%d_" + std::string(cnt2);
|
|
||||||
MakeSkin3Box(skin, count[0], count[1], 0, vcnt, fmt.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// smooth
|
|
||||||
else {
|
|
||||||
// generate vertices in map
|
|
||||||
for (int ix=0; ix<count[0]; ix++) {
|
|
||||||
for (int iy=0; iy<count[1]; iy++) {
|
|
||||||
for (int iz=0; iz<count[2]; iz++) {
|
|
||||||
bool xedge = (ix==0 || ix==count[0]-1);
|
|
||||||
bool yedge = (iy==0 || iy==count[1]-1);
|
|
||||||
bool zedge = (iz==0 || iz==count[2]-1);
|
|
||||||
if (xedge || yedge || zedge) {
|
|
||||||
// body name
|
|
||||||
mju::sprintf_arr(txt, "%sB%d_%d_%d", prefix.c_str(), ix, iy, iz);
|
|
||||||
|
|
||||||
// add vertex
|
|
||||||
vert.push_back(0);
|
|
||||||
vert.push_back(0);
|
|
||||||
vert.push_back(0);
|
|
||||||
|
|
||||||
// texture coordinate
|
|
||||||
if (skintexcoord) {
|
|
||||||
float X = 0, Y = 0;
|
|
||||||
if (xedge) {
|
|
||||||
X = iy/(float)(count[1]-1);
|
|
||||||
Y = iz/(float)(count[2]-1);
|
|
||||||
} else if (yedge) {
|
|
||||||
X = ix/(float)(count[0]-1);
|
|
||||||
Y = iz/(float)(count[2]-1);
|
|
||||||
} else {
|
|
||||||
X = ix/(float)(count[0]-1);
|
|
||||||
Y = iy/(float)(count[1]-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
texcoord.push_back(X);
|
|
||||||
texcoord.push_back(Y);
|
|
||||||
}
|
|
||||||
|
|
||||||
// save vertex id in map
|
|
||||||
vmap[txt] = vcnt;
|
|
||||||
vcnt++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// z-faces
|
|
||||||
MakeSkin3Smooth(skin, count[0], count[1], 1, vmap, "%sB%d_%d_0");
|
|
||||||
fmt = "%sB%d_%d_" + std::string(cnt2);
|
|
||||||
MakeSkin3Smooth(skin, count[0], count[1], 0, vmap, fmt.c_str());
|
|
||||||
|
|
||||||
// y-faces
|
|
||||||
MakeSkin3Smooth(skin, count[0], count[2], 0, vmap, "%sB%d_0_%d");
|
|
||||||
fmt = "%sB%d_" + std::string(cnt1) + "_%d";
|
|
||||||
MakeSkin3Smooth(skin, count[0], count[2], 1, vmap, fmt.c_str());
|
|
||||||
|
|
||||||
// x-faces
|
|
||||||
MakeSkin3Smooth(skin, count[1], count[2], 1, vmap, "%sB0_%d_%d");
|
|
||||||
fmt = "%sB" + std::string(cnt0) + "_%d_%d";
|
|
||||||
MakeSkin3Smooth(skin, count[1], count[2], 0, vmap, fmt.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
CopyIntoSkin(skin);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// make one face of 3D skin, box
|
|
||||||
void mjCComposite::MakeSkin3Box(mjsSkin* skin, int c0, int c1, int side,
|
|
||||||
int& vcnt, const char* format) {
|
|
||||||
char txt[100];
|
|
||||||
|
|
||||||
// loop over bodies/vertices of specified face
|
|
||||||
for (int i0=0; i0<c0; i0++) {
|
|
||||||
for (int i1=0; i1<c1; i1++) {
|
|
||||||
// vertex
|
|
||||||
vert.push_back(0);
|
|
||||||
vert.push_back(0);
|
|
||||||
vert.push_back(0);
|
|
||||||
|
|
||||||
// texture coordinate
|
|
||||||
if (skintexcoord) {
|
|
||||||
texcoord.push_back(i0/(float)(c0-1));
|
|
||||||
texcoord.push_back(i1/(float)(c1-1));
|
|
||||||
}
|
|
||||||
|
|
||||||
// face
|
|
||||||
if (i0<c0-1 && i1<c1-1) {
|
|
||||||
face.push_back(vcnt + i0*c1+i1);
|
|
||||||
face.push_back(vcnt + (i0+1)*c1+i1+(side==1));
|
|
||||||
face.push_back(vcnt + (i0+1)*c1+i1+(side==0));
|
|
||||||
|
|
||||||
face.push_back(vcnt + i0*c1+i1);
|
|
||||||
face.push_back(vcnt + (i0+(side==0))*c1+i1+1);
|
|
||||||
face.push_back(vcnt + (i0+(side==1))*c1+i1+1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// body name
|
|
||||||
mju::sprintf_arr(txt, format, prefix.c_str(), i0, i1);
|
|
||||||
|
|
||||||
// bind pose: origin
|
|
||||||
mjs_appendString(skin->bodyname, txt);
|
|
||||||
bindpos.push_back(0);
|
|
||||||
bindpos.push_back(0);
|
|
||||||
bindpos.push_back(0);
|
|
||||||
bindquat.push_back(1);
|
|
||||||
bindquat.push_back(0);
|
|
||||||
bindquat.push_back(0);
|
|
||||||
bindquat.push_back(0);
|
|
||||||
|
|
||||||
// vertid and vertweight
|
|
||||||
vertid.push_back({vcnt + i0*c1+i1});
|
|
||||||
vertweight.push_back({1});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// update vertex count
|
|
||||||
vcnt += c0*c1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// make one face of 3D skin, smooth
|
|
||||||
void mjCComposite::MakeSkin3Smooth(mjsSkin* skin, int c0, int c1, int side,
|
|
||||||
const std::map<std::string, int>& vmap,
|
|
||||||
const char* format) {
|
|
||||||
char txt00[100], txt01[100], txt10[100], txt11[100];
|
|
||||||
|
|
||||||
// loop over bodies/vertices of specified face
|
|
||||||
for (int i0=0; i0<c0; i0++) {
|
|
||||||
for (int i1=0; i1<c1; i1++) {
|
|
||||||
// body names
|
|
||||||
mju::sprintf_arr(txt00, format, prefix.c_str(), i0, i1);
|
|
||||||
mju::sprintf_arr(txt01, format, prefix.c_str(), i0, i1+1);
|
|
||||||
mju::sprintf_arr(txt10, format, prefix.c_str(), i0+1, i1);
|
|
||||||
mju::sprintf_arr(txt11, format, prefix.c_str(), i0+1, i1+1);
|
|
||||||
|
|
||||||
// face
|
|
||||||
if (i0<c0-1 && i1<c1-1) {
|
|
||||||
if (side==0) {
|
|
||||||
face.push_back(vmap.find(txt00)->second);
|
|
||||||
face.push_back(vmap.find(txt10)->second);
|
|
||||||
face.push_back(vmap.find(txt11)->second);
|
|
||||||
|
|
||||||
face.push_back(vmap.find(txt00)->second);
|
|
||||||
face.push_back(vmap.find(txt11)->second);
|
|
||||||
face.push_back(vmap.find(txt01)->second);
|
|
||||||
} else {
|
|
||||||
face.push_back(vmap.find(txt00)->second);
|
|
||||||
face.push_back(vmap.find(txt01)->second);
|
|
||||||
face.push_back(vmap.find(txt11)->second);
|
|
||||||
|
|
||||||
face.push_back(vmap.find(txt00)->second);
|
|
||||||
face.push_back(vmap.find(txt11)->second);
|
|
||||||
face.push_back(vmap.find(txt10)->second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// bind pose: origin
|
|
||||||
mjs_appendString(skin->bodyname, txt00);
|
|
||||||
bindpos.push_back(0);
|
|
||||||
bindpos.push_back(0);
|
|
||||||
bindpos.push_back(0);
|
|
||||||
bindquat.push_back(1);
|
|
||||||
bindquat.push_back(0);
|
|
||||||
bindquat.push_back(0);
|
|
||||||
bindquat.push_back(0);
|
|
||||||
|
|
||||||
// vertid and vertweight
|
|
||||||
vertid.push_back({vmap.find(txt00)->second});
|
|
||||||
vertweight.push_back({1});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -31,9 +31,6 @@ typedef enum _mjtCompType {
|
|||||||
mjCOMPTYPE_ROPE,
|
mjCOMPTYPE_ROPE,
|
||||||
mjCOMPTYPE_LOOP,
|
mjCOMPTYPE_LOOP,
|
||||||
mjCOMPTYPE_CLOTH,
|
mjCOMPTYPE_CLOTH,
|
||||||
mjCOMPTYPE_BOX,
|
|
||||||
mjCOMPTYPE_CYLINDER,
|
|
||||||
mjCOMPTYPE_ELLIPSOID,
|
|
||||||
|
|
||||||
mjNCOMPTYPES
|
mjNCOMPTYPES
|
||||||
} mjtCompType;
|
} mjtCompType;
|
||||||
@@ -75,7 +72,6 @@ class mjCComposite {
|
|||||||
bool MakeGrid(mjCModel* model, mjsBody* body, char* error, int error_sz);
|
bool MakeGrid(mjCModel* model, mjsBody* body, char* error, int error_sz);
|
||||||
bool MakeRope(mjCModel* model, mjsBody* body, char* error, int error_sz);
|
bool MakeRope(mjCModel* model, mjsBody* body, char* error, int error_sz);
|
||||||
bool MakeCable(mjCModel* model, mjsBody* body, char* error, int error_sz);
|
bool MakeCable(mjCModel* model, mjsBody* body, char* error, int error_sz);
|
||||||
bool MakeBox(mjCModel* model, mjsBody* body, char* error, int error_sz);
|
|
||||||
void MakeShear(mjCModel* model);
|
void MakeShear(mjCModel* model);
|
||||||
|
|
||||||
void MakeSkin2(mjCModel* model, mjtNum inflate);
|
void MakeSkin2(mjCModel* model, mjtNum inflate);
|
||||||
@@ -85,13 +81,6 @@ class mjCComposite {
|
|||||||
void MakeCableBones(mjCModel* model, mjsSkin* skin);
|
void MakeCableBones(mjCModel* model, mjsSkin* skin);
|
||||||
void MakeCableBonesSubgrid(mjCModel* model, mjsSkin* skin);
|
void MakeCableBonesSubgrid(mjCModel* model, mjsSkin* skin);
|
||||||
|
|
||||||
void MakeSkin3(mjCModel* model);
|
|
||||||
void MakeSkin3Box(mjsSkin* skin, int c0, int c1, int side, int& vcnt, const char* format);
|
|
||||||
void MakeSkin3Smooth(mjsSkin* skin, int c0, int c1, int side,
|
|
||||||
const std::map<std::string, int>& vmap, const char* format);
|
|
||||||
|
|
||||||
void BoxProject(double* pos);
|
|
||||||
|
|
||||||
// common properties
|
// common properties
|
||||||
std::string prefix; // name prefix
|
std::string prefix; // name prefix
|
||||||
mjtCompType type; // composite type
|
mjtCompType type; // composite type
|
||||||
|
|||||||
@@ -763,6 +763,7 @@ bool mjCFlexcomp::MakeSquare(char* error, int error_sz) {
|
|||||||
// make 3d box, ellipsoid or cylinder
|
// make 3d box, ellipsoid or cylinder
|
||||||
bool mjCFlexcomp::MakeBox(char* error, int error_sz) {
|
bool mjCFlexcomp::MakeBox(char* error, int error_sz) {
|
||||||
double pos[3];
|
double pos[3];
|
||||||
|
bool needtex = texcoord.empty() && !std::string(mjs_getString(def.spec.flex->material)).empty();
|
||||||
|
|
||||||
// set 3D
|
// set 3D
|
||||||
def.spec.flex->dim = 3;
|
def.spec.flex->dim = 3;
|
||||||
@@ -772,6 +773,12 @@ bool mjCFlexcomp::MakeBox(char* error, int error_sz) {
|
|||||||
point.push_back(0);
|
point.push_back(0);
|
||||||
point.push_back(0);
|
point.push_back(0);
|
||||||
|
|
||||||
|
// add texture coordinates, if not specified explicitly
|
||||||
|
if (needtex) {
|
||||||
|
texcoord.push_back(0);
|
||||||
|
texcoord.push_back(0);
|
||||||
|
}
|
||||||
|
|
||||||
// iz=0/max
|
// iz=0/max
|
||||||
for (int iz=0; iz < count[2]; iz+=count[2]-1) {
|
for (int iz=0; iz < count[2]; iz+=count[2]-1) {
|
||||||
for (int ix=0; ix < count[0]; ix++) {
|
for (int ix=0; ix < count[0]; ix++) {
|
||||||
@@ -782,6 +789,12 @@ bool mjCFlexcomp::MakeBox(char* error, int error_sz) {
|
|||||||
point.push_back(pos[1]);
|
point.push_back(pos[1]);
|
||||||
point.push_back(pos[2]);
|
point.push_back(pos[2]);
|
||||||
|
|
||||||
|
// add texture coordinates, if not specified explicitly
|
||||||
|
if (needtex) {
|
||||||
|
texcoord.push_back(ix/(float)std::max(count[0]-1, 1));
|
||||||
|
texcoord.push_back(iy/(float)std::max(count[1]-1, 1));
|
||||||
|
}
|
||||||
|
|
||||||
// add elements
|
// add elements
|
||||||
if (ix < count[0]-1 && iy < count[1]-1) {
|
if (ix < count[0]-1 && iy < count[1]-1) {
|
||||||
element.push_back(0);
|
element.push_back(0);
|
||||||
@@ -808,6 +821,12 @@ bool mjCFlexcomp::MakeBox(char* error, int error_sz) {
|
|||||||
point.push_back(pos[0]);
|
point.push_back(pos[0]);
|
||||||
point.push_back(pos[1]);
|
point.push_back(pos[1]);
|
||||||
point.push_back(pos[2]);
|
point.push_back(pos[2]);
|
||||||
|
|
||||||
|
// add texture coordinates
|
||||||
|
if (needtex) {
|
||||||
|
texcoord.push_back(ix/(float)std::max(count[0]-1, 1));
|
||||||
|
texcoord.push_back(iz/(float)std::max(count[2]-1, 1));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// add elements
|
// add elements
|
||||||
@@ -836,6 +855,12 @@ bool mjCFlexcomp::MakeBox(char* error, int error_sz) {
|
|||||||
point.push_back(pos[0]);
|
point.push_back(pos[0]);
|
||||||
point.push_back(pos[1]);
|
point.push_back(pos[1]);
|
||||||
point.push_back(pos[2]);
|
point.push_back(pos[2]);
|
||||||
|
|
||||||
|
// add texture coordinates
|
||||||
|
if (needtex) {
|
||||||
|
texcoord.push_back(iy/(float)std::max(count[1]-1, 1));
|
||||||
|
texcoord.push_back(iz/(float)std::max(count[2]-1, 1));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// add elements
|
// add elements
|
||||||
|
|||||||
@@ -1613,6 +1613,7 @@ void mjCModel::SetSizes() {
|
|||||||
nflexelemedge += flexes_[i]->nelem * mjCFlex::kNumEdges[flexes_[i]->dim - 1];
|
nflexelemedge += flexes_[i]->nelem * mjCFlex::kNumEdges[flexes_[i]->dim - 1];
|
||||||
nflexshelldata += (int)flexes_[i]->shell.size();
|
nflexshelldata += (int)flexes_[i]->shell.size();
|
||||||
nflexevpair += (int)flexes_[i]->evpair.size()/2;
|
nflexevpair += (int)flexes_[i]->evpair.size()/2;
|
||||||
|
nflextexcoord += (flexes_[i]->HasTexcoord() ? flexes_[i]->get_texcoord().size()/2 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// mesh counts
|
// mesh counts
|
||||||
|
|||||||
@@ -742,10 +742,7 @@ const mjMap comp_map[mjNCOMPTYPES] = {
|
|||||||
{"rope", mjCOMPTYPE_ROPE},
|
{"rope", mjCOMPTYPE_ROPE},
|
||||||
{"loop", mjCOMPTYPE_LOOP},
|
{"loop", mjCOMPTYPE_LOOP},
|
||||||
{"cable", mjCOMPTYPE_CABLE},
|
{"cable", mjCOMPTYPE_CABLE},
|
||||||
{"cloth", mjCOMPTYPE_CLOTH},
|
{"cloth", mjCOMPTYPE_CLOTH}
|
||||||
{"box", mjCOMPTYPE_BOX},
|
|
||||||
{"cylinder", mjCOMPTYPE_CYLINDER},
|
|
||||||
{"ellipsoid", mjCOMPTYPE_ELLIPSOID}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Vendored
+4
-6
@@ -27,17 +27,15 @@
|
|||||||
<geom type="box" size=".5 .5 .1"/>
|
<geom type="box" size=".5 .5 .1"/>
|
||||||
</body>
|
</body>
|
||||||
<body pos="1 -1 .6">
|
<body pos="1 -1 .6">
|
||||||
<freejoint/>
|
<composite prefix="B0" type="grid" count="4 4 1" spacing=".2">
|
||||||
<composite prefix="B0" type="box" count="4 4 4" spacing=".2">
|
|
||||||
<geom group="4" size=".1"/>
|
<geom group="4" size=".1"/>
|
||||||
<skin rgba="1 1 0 1" group="2"/>
|
<skin rgba="1 1 0 1" group="2" inflate=".1"/>
|
||||||
</composite>
|
</composite>
|
||||||
</body>
|
</body>
|
||||||
<body pos="-1 1 .6">
|
<body pos="-1 1 .6">
|
||||||
<freejoint/>
|
<composite prefix="B1" type="grid" count="4 4 1" spacing=".2">
|
||||||
<composite prefix="B1" type="box" count="4 4 4" spacing=".2">
|
|
||||||
<geom group="2" size=".1"/>
|
<geom group="2" size=".1"/>
|
||||||
<skin rgba="0 1 1 1" group="4"/>
|
<skin rgba="0 1 1 1" group="4" inflate=".1"/>
|
||||||
</composite>
|
</composite>
|
||||||
</body>
|
</body>
|
||||||
</worldbody>
|
</worldbody>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ TEST_F(UserCompositeTest, MultipleJointsNotAllowedUnlessParticle) {
|
|||||||
<worldbody>
|
<worldbody>
|
||||||
<body>
|
<body>
|
||||||
<freejoint/>
|
<freejoint/>
|
||||||
<composite type="box" count="7 7 7" spacing="0.04">
|
<composite type="grid" count="7 7 1" spacing="0.04">
|
||||||
<joint kind="main" solreffix="0.03 1" solimpfix="0 .1 .01"/>
|
<joint kind="main" solreffix="0.03 1" solimpfix="0 .1 .01"/>
|
||||||
<joint kind="main" solreffix="0.03 1" solimpfix="0 .1 .01"/>
|
<joint kind="main" solreffix="0.03 1" solimpfix="0 .1 .01"/>
|
||||||
</composite>
|
</composite>
|
||||||
|
|||||||
@@ -1681,13 +1681,13 @@ TEST_F(XMLReaderTest, ReadsSkinGroups) {
|
|||||||
<mujoco>
|
<mujoco>
|
||||||
<worldbody>
|
<worldbody>
|
||||||
<body>
|
<body>
|
||||||
<composite prefix="B0" type="box" count="4 4 4" spacing=".2">
|
<composite prefix="B0" type="grid" count="4 4 1" spacing=".2">
|
||||||
<geom size=".1" group="2"/>
|
<geom size=".1" group="2"/>
|
||||||
<skin group="4"/>
|
<skin group="4"/>
|
||||||
</composite>
|
</composite>
|
||||||
</body>
|
</body>
|
||||||
<body>
|
<body>
|
||||||
<composite prefix="B1" type="box" count="4 4 4" spacing=".2">
|
<composite prefix="B1" type="grid" count="4 4 1" spacing=".2">
|
||||||
<geom size=".1" group="4"/>
|
<geom size=".1" group="4"/>
|
||||||
<skin group="2"/>
|
<skin group="2"/>
|
||||||
</composite>
|
</composite>
|
||||||
@@ -1698,8 +1698,8 @@ TEST_F(XMLReaderTest, ReadsSkinGroups) {
|
|||||||
std::array<char, 1024> error;
|
std::array<char, 1024> error;
|
||||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||||
ASSERT_THAT(model, NotNull());
|
ASSERT_THAT(model, NotNull());
|
||||||
int geomid1 = mj_name2id(model, mjOBJ_GEOM, "B0G0_0_0");
|
int geomid1 = mj_name2id(model, mjOBJ_GEOM, "B0G0_0");
|
||||||
int geomid2 = mj_name2id(model, mjOBJ_GEOM, "B1G0_0_0");
|
int geomid2 = mj_name2id(model, mjOBJ_GEOM, "B1G0_0");
|
||||||
EXPECT_THAT(model->geom_group[geomid1], 2);
|
EXPECT_THAT(model->geom_group[geomid1], 2);
|
||||||
EXPECT_THAT(model->skin_group[0], 4);
|
EXPECT_THAT(model->skin_group[0], 4);
|
||||||
EXPECT_THAT(model->geom_group[geomid2], 4);
|
EXPECT_THAT(model->geom_group[geomid2], 4);
|
||||||
@@ -1712,7 +1712,7 @@ TEST_F(XMLReaderTest, InvalidSkinGroup) {
|
|||||||
<mujoco>
|
<mujoco>
|
||||||
<worldbody>
|
<worldbody>
|
||||||
<body>
|
<body>
|
||||||
<composite prefix="B0" type="box" count="6 6 6" spacing=".2">
|
<composite prefix="B0" type="grid" count="6 6 1" spacing=".2">
|
||||||
<geom size=".1"/>
|
<geom size=".1"/>
|
||||||
<skin group="6"/>
|
<skin group="6"/>
|
||||||
</composite>
|
</composite>
|
||||||
|
|||||||
Reference in New Issue
Block a user