Added support for GMSH22 surface loading

PiperOrigin-RevId: 629734063
Change-Id: I6a250b81de4e7c8f7520795bfe74a81d719dfb50
This commit is contained in:
Mohammad Hamid
2024-05-01 08:42:53 -07:00
committed by Copybara-Service
parent 57b1dbac65
commit 131b1745db
14 changed files with 403 additions and 42 deletions
+4 -3
View File
@@ -3452,9 +3452,10 @@ saving the XML:
.. _body-flexcomp-file: .. _body-flexcomp-file:
:at:`file`: :at-val:`string, optional` :at:`file`: :at-val:`string, optional`
The name of the file from which a **mesh** or a **gmsh** is loaded. For mesh, the file extentsion is used to The name of the file from which a **surface** (triangular) or **volumetric** (tetrahedral) mesh is loaded. For
determine the file format. Supported formats are the same as in :ref:`mesh assets<asset-mesh>`. For gmsh, the file is surface meshes, the file extension is used to determine the file format. Supported formats are the same as in
expected to be in GMSH format 4.1 or 2.2, ascii or binary, see :ref:`here<gmsh-file-docs>`. :ref:`mesh assets<asset-mesh>` and also including GMSH. Volumetric meshes are supported only in GMSH format.
See :ref:`here<gmsh-file-docs>` for more information on GMSH files.
.. _body-flexcomp-rigid: .. _body-flexcomp-rigid:
+18 -17
View File
@@ -24,39 +24,40 @@ General
field. field.
4. The type of the ``size`` argument of :ref:`mj_stackAllocNum` and :ref:`mj_stackAllocInt` was changed from ``int`` 4. The type of the ``size`` argument of :ref:`mj_stackAllocNum` and :ref:`mj_stackAllocInt` was changed from ``int``
to ``size_t``. to ``size_t``.
5. Added support for gmsh format version 2.2 surface meshes in :ref:`flexcomp<body-flexcomp-file>`.
MJX MJX
^^^ ^^^
.. admonition:: Breaking API changes .. admonition:: Breaking API changes
:class: attention :class: attention
5. Removed deprecated ``mjx.device_get_into`` and ``mjx.device_put`` functions as they lack critical new 6. Removed deprecated ``mjx.device_get_into`` and ``mjx.device_put`` functions as they lack critical new
functionality. functionality.
**Migration:** Use ``mjx.get_data_into`` instead of ``mjx.device_get_into``, and ``mjx.put_data`` instead of **Migration:** Use ``mjx.get_data_into`` instead of ``mjx.device_get_into``, and ``mjx.put_data`` instead of
``mjx.device_put``. ``mjx.device_put``.
6. Added cylinder plane collisions. 7. Added cylinder plane collisions.
7. Added ``efc_type`` to ``mjx.Data`` and ``dim``, ``efc_address`` to ``mjx.Contact``. 8. Added ``efc_type`` to ``mjx.Data`` and ``dim``, ``efc_address`` to ``mjx.Contact``.
8. Added ``geom`` to ``mjx.Contact`` and marked ``geom1``, ``geom2`` deprecated. 9. Added ``geom`` to ``mjx.Contact`` and marked ``geom1``, ``geom2`` deprecated.
9. Added ``ne``, ``nf``, ``nl``, ``nefc``, and ``ncon`` to ``mjx.Data`` to match ``mujoco.MjData``. 10. Added ``ne``, ``nf``, ``nl``, ``nefc``, and ``ncon`` to ``mjx.Data`` to match ``mujoco.MjData``.
10. Given the above added fields, removed ``mjx.get_params``, ``mjx.ncon``, and ``mjx.count_constraints``. 11. Given the above added fields, removed ``mjx.get_params``, ``mjx.ncon``, and ``mjx.count_constraints``.
11. Changed the way meshes are organized on device to speed up collision detection when a mesh is replicated for many 12. Changed the way meshes are organized on device to speed up collision detection when a mesh is replicated for many
geoms. geoms.
12. Fixed a bug where capsules might be ignored in broadphase colliision checking. 13. Fixed a bug where capsules might be ignored in broadphase colliision checking.
13. Added cylinder collisions using SDFs. 14. Added cylinder collisions using SDFs.
14. Added support for all :ref:`condim <coContact>`: 1, 3, 4, 6. 15. Added support for all :ref:`condim <coContact>`: 1, 3, 4, 6.
15. Add support functions for ``id2name`` and ``name2id``, MJX versions of :ref:`mj_id2name` and :ref:`mj_name2id`. 16. Add support functions for ``id2name`` and ``name2id``, MJX versions of :ref:`mj_id2name` and :ref:`mj_name2id`.
16. Added support for :ref:`gravcomp<body-gravcomp>` and :ref:`actuatorgravcomp<body-joint-actuatorgravcomp>`. 17. Added support for :ref:`gravcomp<body-gravcomp>` and :ref:`actuatorgravcomp<body-joint-actuatorgravcomp>`.
17. Fixed a bug in ``mjx.ray`` for sometimes allowed negative distances for ray-mesh tests. 18. Fixed a bug in ``mjx.ray`` for sometimes allowed negative distances for ray-mesh tests.
Bug fixes Bug fixes
^^^^^^^^^ ^^^^^^^^^
18. Defaults of lights were not being saved, now fixed. 19. Defaults of lights were not being saved, now fixed.
19. Prevent overwriting of frame names by body names when saving an XML. Bug introduced in 3.1.4. 20. Prevent overwriting of frame names by body names when saving an XML. Bug introduced in 3.1.4.
20. Fixed bug in Python binding of :ref:`mj_saveModel`: ``buffer`` argument was documented as optional but was actually 21. Fixed bug in Python binding of :ref:`mj_saveModel`: ``buffer`` argument was documented as optional but was actually
not optional. not optional.
21. Fixed bug that prevented memory allocations larger than 2.15 GB. 22. Fixed bug that prevented memory allocations larger than 2.15 GB.
Version 3.1.4 (April 10th, 2024) Version 3.1.4 (April 10th, 2024)
+54 -17
View File
@@ -62,7 +62,7 @@ static void ReadStrFromBuffer(char* dest, const char* src, int maxlen) {
std::strncpy(dest, src, maxlen); std::strncpy(dest, src, maxlen);
} }
bool IsValidElementHeader22(const std::string& line) { bool IsValidElementOrNodeHeader22(const std::string& line) {
// making sure characters are numbers // making sure characters are numbers
for (char c : line) { for (char c : line) {
if (!std::isdigit(c)) { if (!std::isdigit(c)) {
@@ -1296,10 +1296,21 @@ void mjCFlexcomp::LoadGMSH41(char* buffer, int binary, int nodeend,
// load GMSH format 2.2 // load GMSH format 2.2
void mjCFlexcomp::LoadGMSH22(char* buffer, int binary, int nodeend, void mjCFlexcomp::LoadGMSH22(char* buffer, int binary, int nodeend,
int nodebegin, int elemend, int elembegin) { int nodebegin, int elemend, int elembegin) {
// number of nodes
size_t numNodes = 0;
// ascii nodes // ascii nodes
if (binary == 0) { if (binary == 0) {
// convert node char buffer to stringstream // convert node char buffer to stringstream
stringstream ss(std::string(buffer + nodebegin, nodeend - nodebegin)); stringstream ss(std::string(buffer + nodebegin, nodeend - nodebegin));
std::string line;
// checking header template
std::getline(ss, line);
if (!IsValidElementOrNodeHeader22(line)) {
throw mjCError(NULL, "Invalid node header");
}
ss.seekg(-(line.size()+1), std::ios::cur);
// read header // read header
size_t maxNodeTag = 0; size_t maxNodeTag = 0;
@@ -1307,7 +1318,7 @@ void mjCFlexcomp::LoadGMSH22(char* buffer, int binary, int nodeend,
if (!ss.good()) { if (!ss.good()) {
throw mjCError(NULL, "Error reading Nodes header"); throw mjCError(NULL, "Error reading Nodes header");
} }
size_t numNodes = maxNodeTag; numNodes = maxNodeTag;
if (numNodes < 0 || numNodes >= INT_MAX / 3) { if (numNodes < 0 || numNodes >= INT_MAX / 3) {
throw mjCError(NULL, "Invalid number of nodes."); throw mjCError(NULL, "Invalid number of nodes.");
@@ -1349,7 +1360,7 @@ void mjCFlexcomp::LoadGMSH22(char* buffer, int binary, int nodeend,
ReadStrFromBuffer(maxNodeTagChar, buffer + nodebegin, std::min(10, nodeend - nodebegin)); ReadStrFromBuffer(maxNodeTagChar, buffer + nodebegin, std::min(10, nodeend - nodebegin));
size_t measuredHeaderSize = strnlen(maxNodeTagChar, 10) - 1; size_t measuredHeaderSize = strnlen(maxNodeTagChar, 10) - 1;
size_t maxNodeTag = std::stoi(maxNodeTagChar); size_t maxNodeTag = std::stoi(maxNodeTagChar);
size_t numNodes = maxNodeTag; numNodes = maxNodeTag;
// check number of nodes is a positive number // check number of nodes is a positive number
if (numNodes < 0) { if (numNodes < 0) {
@@ -1396,7 +1407,7 @@ void mjCFlexcomp::LoadGMSH22(char* buffer, int binary, int nodeend,
// checking header template // checking header template
std::getline(ss, line); std::getline(ss, line);
if (!IsValidElementHeader22(line)) { if (!IsValidElementOrNodeHeader22(line)) {
throw mjCError(NULL, "Invalid elements header"); throw mjCError(NULL, "Invalid elements header");
} }
ss.seekg(-(line.size()+1), std::ios::cur); ss.seekg(-(line.size()+1), std::ios::cur);
@@ -1404,7 +1415,7 @@ void mjCFlexcomp::LoadGMSH22(char* buffer, int binary, int nodeend,
size_t maxElementTag = 0; size_t maxElementTag = 0;
ss >> maxElementTag; ss >> maxElementTag;
if (!ss.good()) { if (!ss.good()) {
throw mjCError(NULL, "GetMaxElementTag::Error reading Elements header"); throw mjCError(NULL, "Error reading Elements header");
} }
size_t numElements = maxElementTag; size_t numElements = maxElementTag;
@@ -1419,18 +1430,30 @@ void mjCFlexcomp::LoadGMSH22(char* buffer, int binary, int nodeend,
} }
// reading first element's type // reading first element's type
int tag = 0, type = 0, numTags = 0; int tag = 0, elementType = 0, numTags = 0;
ss >> tag >> type >> numTags; ss >> tag >> elementType >> numTags;
if (!ss.good()) { if (!ss.good()) {
throw mjCError(NULL, "Error reading Elements"); throw mjCError(NULL, "Error reading Elements");
} }
int numNodeTags = type;
size_t entityDim = 0;
int numNodeTags = 0;
// surface
if (elementType == 2) {
entityDim = 2;
numNodeTags = 3;
}
// tetrahedral
else if (elementType == 4) {
entityDim = 3;
numNodeTags = 4;
}
if (numNodeTags < 1 || numNodeTags >4) { if (numNodeTags < 1 || numNodeTags >4) {
throw mjCError(NULL, "Invalid number of node tags"); throw mjCError(NULL, "Invalid number of node tags");
} }
// setting entityDim // setting entityDim
size_t entityDim = numNodeTags - 1;
def.spec.flex->dim = entityDim; def.spec.flex->dim = entityDim;
// read elements, discard all tags // read elements, discard all tags
@@ -1438,7 +1461,7 @@ void mjCFlexcomp::LoadGMSH22(char* buffer, int binary, int nodeend,
for (size_t i=0; i<numElements; i++) { for (size_t i=0; i<numElements; i++) {
int nodeTag = 0, physicalEntityTag = 0, elmentModelEntityTag = 0; int nodeTag = 0, physicalEntityTag = 0, elmentModelEntityTag = 0;
if (i != 0) { if (i != 0) {
ss >> tag >> type >> numTags; ss >> tag >> elementType >> numTags;
if (!ss.good()) { if (!ss.good()) {
throw mjCError(NULL, "Error reading Elements"); throw mjCError(NULL, "Error reading Elements");
} }
@@ -1454,7 +1477,7 @@ void mjCFlexcomp::LoadGMSH22(char* buffer, int binary, int nodeend,
if (!ss.good()) { if (!ss.good()) {
throw mjCError(NULL, "Error reading Elements"); throw mjCError(NULL, "Error reading Elements");
} }
if (nodeTag > numElements || nodeTag < 1) { if (nodeTag > numNodes || nodeTag < 1) {
throw mjCError(NULL, "Invalid node tag"); throw mjCError(NULL, "Invalid node tag");
} }
element.push_back((int)(nodeTag-1)); element.push_back((int)(nodeTag-1));
@@ -1464,7 +1487,7 @@ void mjCFlexcomp::LoadGMSH22(char* buffer, int binary, int nodeend,
// binary elements // binary elements
else { else {
// header size for gmshApp // header size for gmshApp
constexpr int elementHeaderSizeGmshApp = 6; constexpr int elementHeaderSizeGmshApp = 4;
// header size for Ftetwild // header size for Ftetwild
constexpr int elementHeaderSizeFtetwild = 17; constexpr int elementHeaderSizeFtetwild = 17;
// check header size // check header size
@@ -1480,7 +1503,7 @@ void mjCFlexcomp::LoadGMSH22(char* buffer, int binary, int nodeend,
int numElements = maxElementTag; int numElements = maxElementTag;
int tag, numTags; int tag, numTags;
int nodeTag; int nodeTag;
int numNodeTags; int elementType;
// check number of elements is a positive number // check number of elements is a positive number
if (numElements < 0) { if (numElements < 0) {
@@ -1491,14 +1514,28 @@ void mjCFlexcomp::LoadGMSH22(char* buffer, int binary, int nodeend,
int componentSize = sizeof(int); int componentSize = sizeof(int);
// element buffer // element buffer
const char* elementsBuffer = buffer + elembegin + measuredHeaderSize; const char* elementsBuffer = buffer + elembegin + measuredHeaderSize;
ReadFromBuffer(&numNodeTags, elementsBuffer); ReadFromBuffer(&elementType, elementsBuffer);
ReadFromBuffer(&numTags, elementsBuffer + componentSize*2); ReadFromBuffer(&numTags, elementsBuffer + componentSize*2);
ReadFromBuffer(&tag, elementsBuffer + componentSize*3); ReadFromBuffer(&tag, elementsBuffer + componentSize*3);
// tetrahedral has 4 node tags and surface has 3
int numNodeTags = 0;
size_t entityDim = 0;
// surface
if (elementType == 2) {
entityDim = 2;
numNodeTags = 3;
}
// tetrahedral
else if (elementType == 4) {
entityDim = 3;
numNodeTags = 4;
}
if (numNodeTags < 1 || numNodeTags >4) { if (numNodeTags < 1 || numNodeTags >4) {
throw mjCError(NULL, "Invalid number of node tags"); throw mjCError(NULL, "Invalid number of node tags");
} }
size_t entityDim = numNodeTags - 1;
def.spec.flex->dim = entityDim; def.spec.flex->dim = entityDim;
// element data(Ftetwild): tag and 4 nodeTag // element data(Ftetwild): tag and 4 nodeTag
@@ -1534,7 +1571,7 @@ void mjCFlexcomp::LoadGMSH22(char* buffer, int binary, int nodeend,
// read first element // read first element
for (int k =0; k<numNodeTags; k++) { for (int k =0; k<numNodeTags; k++) {
ReadFromBuffer(&nodeTag, elementsBuffer + componentSize*(6+k)); ReadFromBuffer(&nodeTag, elementsBuffer + componentSize*(6+k));
if (nodeTag > numElements || nodeTag < 1) { if (nodeTag > numNodes || nodeTag < 1) {
throw mjCError(NULL, "Invalid node tag"); throw mjCError(NULL, "Invalid node tag");
} }
element.push_back(nodeTag-1); element.push_back(nodeTag-1);
@@ -1564,7 +1601,7 @@ void mjCFlexcomp::LoadGMSH22(char* buffer, int binary, int nodeend,
for (int k = 0; k < numNodeTags; k++) { for (int k = 0; k < numNodeTags; k++) {
const char* nodeTagBuffer = elementsBuffer + componentSize*(4+k); const char* nodeTagBuffer = elementsBuffer + componentSize*(4+k);
ReadFromBuffer(&nodeTag, nodeTagBuffer); ReadFromBuffer(&nodeTag, nodeTagBuffer);
if (nodeTag > numElements || nodeTag < 1) { if (nodeTag > numNodes || nodeTag < 1) {
throw mjCError(NULL, "Invalid node tag"); throw mjCError(NULL, "Invalid node tag");
} }
element.push_back(nodeTag-1); element.push_back(nodeTag-1);
+47
View File
@@ -0,0 +1,47 @@
$MeshFormat
2.2 0 8
$EndMeshFormat
$Nodes
14
1 -0.5 -0.5 0
2 0.5 -0.5 0
3 0.5 0.5 0
4 -0.5 0.5 0
5 -0.5 -0.5 1
6 0.5 -0.5 1
7 0.5 0.5 1
8 -0.5 0.5 1
9 0 0 0
10 0 -0.5 0.5
11 0.5 0 0.5
12 0 0.5 0.5
13 -0.5 0 0.5
14 0 0 1
$EndNodes
$Elements
24
1 2 2 0 1 9 4 3
2 2 2 0 1 9 3 2
3 2 2 0 1 9 1 4
4 2 2 0 1 9 2 1
5 2 2 0 1 2 10 1
6 2 2 0 1 1 10 5
7 2 2 0 1 6 10 2
8 2 2 0 1 5 10 6
9 2 2 0 1 3 11 2
10 2 2 0 1 2 11 6
11 2 2 0 1 7 11 3
12 2 2 0 1 6 11 7
13 2 2 0 1 4 12 3
14 2 2 0 1 3 12 7
15 2 2 0 1 8 12 4
16 2 2 0 1 7 12 8
17 2 2 0 1 1 13 4
18 2 2 0 1 5 13 1
19 2 2 0 1 4 13 8
20 2 2 0 1 8 13 5
21 2 2 0 1 5 6 14
22 2 2 0 1 8 5 14
23 2 2 0 1 6 7 14
24 2 2 0 1 7 8 14
$EndElements
+26
View File
@@ -0,0 +1,26 @@
<mujoco model="gmsh">
<option timestep="0.01" integrator="implicitfast" solver="CG"/>
<asset>
<texture type="skybox" builtin="gradient" rgb1=".3 .5 .7" rgb2="0 0 0" width="512" height="512"/>
<texture name="grid" type="2d" builtin="checker" width="512" height="512" rgb1=".1 .2 .3" rgb2=".2 .3 .4"/>
<material name="grid" texture="grid" texrepeat="1 1" texuniform="true" reflectance=".2"/>
</asset>
<visual>
<headlight diffuse="0.9 0.9 0.9" ambient="0.3 0.3 0.3" specular="0 0 0"/>
<map force="0.1" zfar="30"/>
<rgba haze="0.15 0.25 0.35 1"/>
<quality shadowsize="4096"/>
<global offwidth="800" offheight="800"/>
</visual>
<worldbody>
<geom name="floor" size="1 1 .05" type="plane" material="grid" condim="3"/>
<light directional="false" diffuse=".2 .2 .2" specular="0 0 0" pos="0 0 5" dir="0 0 -1"/>
<flexcomp name="cube" type="gmsh" dim="2" rgba=".8 .2 .2 1" radius="0.001" pos="0 0 0"
mass="10" scale="1 1 1" file="cube_22_ascii_surf_gmshApp.msh">
<edge equality="true"/>
</flexcomp>
</worldbody>
</mujoco>
Binary file not shown.
+26
View File
@@ -0,0 +1,26 @@
<mujoco model="gmsh">
<option timestep="0.01" integrator="implicitfast" solver="CG"/>
<asset>
<texture type="skybox" builtin="gradient" rgb1=".3 .5 .7" rgb2="0 0 0" width="512" height="512"/>
<texture name="grid" type="2d" builtin="checker" width="512" height="512" rgb1=".1 .2 .3" rgb2=".2 .3 .4"/>
<material name="grid" texture="grid" texrepeat="1 1" texuniform="true" reflectance=".2"/>
</asset>
<visual>
<headlight diffuse="0.9 0.9 0.9" ambient="0.3 0.3 0.3" specular="0 0 0"/>
<map force="0.1" zfar="30"/>
<rgba haze="0.15 0.25 0.35 1"/>
<quality shadowsize="4096"/>
<global offwidth="800" offheight="800"/>
</visual>
<worldbody>
<geom name="floor" size="1 1 .05" type="plane" material="grid" condim="3"/>
<light directional="false" diffuse=".2 .2 .2" specular="0 0 0" pos="0 0 5" dir="0 0 -1"/>
<flexcomp name="cube" type="gmsh" dim="2" rgba=".8 .2 .2 1" radius="0.001" pos="0 0 0"
mass="10" scale="1 1 1" file="cube_22_binary_surf_gmshApp.msh">
<edge equality="true"/>
</flexcomp>
</worldbody>
</mujoco>
+67
View File
@@ -0,0 +1,67 @@
$MeshFormat
4.1 0 8
$EndMeshFormat
$Entities
0 0 1 0
1 -0.5 -0.5 0 0.5 0.5 1 0 0
$EndEntities
$Nodes
1 14 1 14
2 1 0 14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
-0.5 -0.5 0
0.5 -0.5 0
0.5 0.5 0
-0.5 0.5 0
-0.5 -0.5 1
0.5 -0.5 1
0.5 0.5 1
-0.5 0.5 1
0 0 0
0 -0.5 0.5
0.5 0 0.5
0 0.5 0.5
-0.5 0 0.5
0 0 1
$EndNodes
$Elements
1 24 1 24
2 1 2 24
1 9 4 3
2 9 3 2
3 9 1 4
4 9 2 1
5 2 10 1
6 1 10 5
7 6 10 2
8 5 10 6
9 3 11 2
10 2 11 6
11 7 11 3
12 6 11 7
13 4 12 3
14 3 12 7
15 8 12 4
16 7 12 8
17 1 13 4
18 5 13 1
19 4 13 8
20 8 13 5
21 5 6 14
22 8 5 14
23 6 7 14
24 7 8 14
$EndElements
+26
View File
@@ -0,0 +1,26 @@
<mujoco model="gmsh">
<option timestep="0.01" integrator="implicitfast" solver="CG"/>
<asset>
<texture type="skybox" builtin="gradient" rgb1=".3 .5 .7" rgb2="0 0 0" width="512" height="512"/>
<texture name="grid" type="2d" builtin="checker" width="512" height="512" rgb1=".1 .2 .3" rgb2=".2 .3 .4"/>
<material name="grid" texture="grid" texrepeat="1 1" texuniform="true" reflectance=".2"/>
</asset>
<visual>
<headlight diffuse="0.9 0.9 0.9" ambient="0.3 0.3 0.3" specular="0 0 0"/>
<map force="0.1" zfar="30"/>
<rgba haze="0.15 0.25 0.35 1"/>
<quality shadowsize="4096"/>
<global offwidth="800" offheight="800"/>
</visual>
<worldbody>
<geom name="floor" size="1 1 .05" type="plane" material="grid" condim="3"/>
<light directional="false" diffuse=".2 .2 .2" specular="0 0 0" pos="0 0 5" dir="0 0 -1"/>
<flexcomp name="cube" type="gmsh" dim="2" rgba=".8 .2 .2 1" radius="0.001" pos="0 0 0"
mass="10" scale="1 1 1" file="cube_41_ascii_surf_gmshApp.msh">
<edge equality="true"/>
</flexcomp>
</worldbody>
</mujoco>
Binary file not shown.
+26
View File
@@ -0,0 +1,26 @@
<mujoco model="gmsh">
<option timestep="0.01" integrator="implicitfast" solver="CG"/>
<asset>
<texture type="skybox" builtin="gradient" rgb1=".3 .5 .7" rgb2="0 0 0" width="512" height="512"/>
<texture name="grid" type="2d" builtin="checker" width="512" height="512" rgb1=".1 .2 .3" rgb2=".2 .3 .4"/>
<material name="grid" texture="grid" texrepeat="1 1" texuniform="true" reflectance=".2"/>
</asset>
<visual>
<headlight diffuse="0.9 0.9 0.9" ambient="0.3 0.3 0.3" specular="0 0 0"/>
<map force="0.1" zfar="30"/>
<rgba haze="0.15 0.25 0.35 1"/>
<quality shadowsize="4096"/>
<global offwidth="800" offheight="800"/>
</visual>
<worldbody>
<geom name="floor" size="1 1 .05" type="plane" material="grid" condim="3"/>
<light directional="false" diffuse=".2 .2 .2" specular="0 0 0" pos="0 0 5" dir="0 0 -1"/>
<flexcomp name="cube" type="gmsh" dim="2" rgba=".8 .2 .2 1" radius="0.001" pos="0 0 0"
mass="10" scale="1 1 1" file="cube_41_binary_surf_gmshApp.msh">
<edge equality="true"/>
</flexcomp>
</worldbody>
</mujoco>
@@ -19,7 +19,7 @@
<worldbody> <worldbody>
<geom name="floor" size="20 20 .05" type="plane" material="grid" condim="3" /> <geom name="floor" size="20 20 .05" type="plane" material="grid" condim="3" />
<light directional="false" diffuse=".2 .2 .2" specular="0 0 0" pos="0 0 5" dir="0 0 -1" /> <light directional="false" diffuse=".2 .2 .2" specular="0 0 0" pos="0 0 5" dir="0 0 -1" />
<flexcomp name="shark binary 2.2" type="gmsh" rgba=".8 .2 .2 1" radius="0.001" pos="0 0 4" <flexcomp name="shark binary 2.2" type="gmsh" dim="3" rgba=".8 .2 .2 1" radius="0.001" pos="0 0 4"
mass="10" scale="1 1 1" file="shark_41_ascii_missing_node_index.msh"> mass="10" scale="1 1 1" file="shark_41_ascii_missing_node_index.msh">
<edge equality="true" /> <edge equality="true" />
</flexcomp> </flexcomp>
@@ -8,7 +8,6 @@ $EndEntities
$Nodes $Nodes
1 652 1 652 1 652 1 652
3 0 0 652 3 0 0 652
1
2 2
3 3
4 4
+108 -3
View File
@@ -23,6 +23,8 @@
#include <mujoco/mujoco.h> #include <mujoco/mujoco.h>
#include "test/fixture.h" #include "test/fixture.h"
namespace mujoco { namespace mujoco {
namespace { namespace {
@@ -32,6 +34,8 @@ using ::testing::HasSubstr;
using UserFlexTest = MujocoTest; using UserFlexTest = MujocoTest;
TEST_F(UserFlexTest, ParentMustHaveName) { TEST_F(UserFlexTest, ParentMustHaveName) {
static constexpr char xml[] = R"( static constexpr char xml[] = R"(
<mujoco> <mujoco>
@@ -255,6 +259,56 @@ TEST_F(UserFlexTest, LoadMSHBinaryGMSH_22_Success) {
mj_deleteData(d); mj_deleteData(d);
} }
TEST_F(UserFlexTest, LoadMSHSurfaceBinaryGMSH_41_Success) {
const std::string xml_path =
GetTestDataFilePath("user/testdata/cube_41_binary_surf_gmshApp.xml");
std::array<char, 1024> error;
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
ASSERT_THAT(m, NotNull()) << error.data();
mjData* d = mj_makeData(m);
EXPECT_EQ(m->nflexvert, 14);
EXPECT_EQ(m->nflexelem, 24);
// first node x y z
EXPECT_EQ(m->flex_xvert0[0], -0.5 );
EXPECT_EQ(m->flex_xvert0[1], -0.5 );
EXPECT_EQ(m->flex_xvert0[2], 0 );
// first element
EXPECT_EQ(m->flex_elem[0], 9-1 );
EXPECT_EQ(m->flex_elem[1], 4-1 );
EXPECT_EQ(m->flex_elem[2], 3-1 );
mj_step(m, d);
mj_deleteModel(m);
mj_deleteData(d);
}
TEST_F(UserFlexTest, LoadMSHSurfaceBinaryGMSH_22_Success) {
const std::string xml_path =
GetTestDataFilePath("user/testdata/cube_22_binary_surf_gmshApp.xml");
std::array<char, 1024> error;
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
ASSERT_THAT(m, NotNull()) << error.data();
mjData* d = mj_makeData(m);
EXPECT_EQ(m->nflexvert, 14);
EXPECT_EQ(m->nflexelem, 24);
// first node x y z
EXPECT_EQ(m->flex_xvert0[0], -0.5 );
EXPECT_EQ(m->flex_xvert0[1], -0.5 );
EXPECT_EQ(m->flex_xvert0[2], 0 );
// first element
EXPECT_EQ(m->flex_elem[0], 9-1 );
EXPECT_EQ(m->flex_elem[1], 4-1 );
EXPECT_EQ(m->flex_elem[2], 3-1 );
mj_step(m, d);
mj_deleteModel(m);
mj_deleteData(d);
}
TEST_F(UserFlexTest, LoadMSHBinaryFTETWILD_22_Success) { TEST_F(UserFlexTest, LoadMSHBinaryFTETWILD_22_Success) {
const std::string xml_path = const std::string xml_path =
GetTestDataFilePath("user/testdata/shark_22_binary_fTetWild.xml"); GetTestDataFilePath("user/testdata/shark_22_binary_fTetWild.xml");
@@ -297,6 +351,56 @@ TEST_F(UserFlexTest, LoadMSHASCIIGMSH_22_Success) {
mj_deleteData(d); mj_deleteData(d);
} }
TEST_F(UserFlexTest, LoadMSHSurfaceASCIIGMSH_41_Success) {
const std::string xml_path =
GetTestDataFilePath("user/testdata/cube_41_ascii_surf_gmshApp.xml");
std::array<char, 1024> error;
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
ASSERT_THAT(m, NotNull()) << error.data();
mjData* d = mj_makeData(m);
EXPECT_EQ(m->nflexvert, 14);
EXPECT_EQ(m->nflexelem, 24);
// first node x y z
EXPECT_EQ(m->flex_xvert0[0], -0.5 );
EXPECT_EQ(m->flex_xvert0[1], -0.5 );
EXPECT_EQ(m->flex_xvert0[2], 0 );
// first element
EXPECT_EQ(m->flex_elem[0], 9-1 );
EXPECT_EQ(m->flex_elem[1], 4-1 );
EXPECT_EQ(m->flex_elem[2], 3-1 );
mj_step(m, d);
mj_deleteModel(m);
mj_deleteData(d);
}
TEST_F(UserFlexTest, LoadMSHSurfaceASCIIGMSH_22_Success) {
const std::string xml_path =
GetTestDataFilePath("user/testdata/cube_22_ascii_surf_gmshApp.xml");
std::array<char, 1024> error;
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
ASSERT_THAT(m, NotNull()) << error.data();
mjData* d = mj_makeData(m);
EXPECT_EQ(m->nflexvert, 14);
EXPECT_EQ(m->nflexelem, 24);
// first node x y z
EXPECT_EQ(m->flex_xvert0[0], -0.5 );
EXPECT_EQ(m->flex_xvert0[1], -0.5 );
EXPECT_EQ(m->flex_xvert0[2], 0 );
// first element
EXPECT_EQ(m->flex_elem[0], 9-1 );
EXPECT_EQ(m->flex_elem[1], 4-1 );
EXPECT_EQ(m->flex_elem[2], 3-1 );
mj_step(m, d);
mj_deleteModel(m);
mj_deleteData(d);
}
TEST_F(UserFlexTest, LoadMSHASCIIFTETWILD_22_Success) { TEST_F(UserFlexTest, LoadMSHASCIIFTETWILD_22_Success) {
const std::string xml_path = const std::string xml_path =
GetTestDataFilePath("user/testdata/shark_22_ascii_fTetWild.xml"); GetTestDataFilePath("user/testdata/shark_22_ascii_fTetWild.xml");
@@ -329,7 +433,7 @@ TEST_F(UserFlexTest, LoadMSHASCII_41_MissingNodeIndex_Fail) {
std::array<char, 1024> error; std::array<char, 1024> error;
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size()); mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
EXPECT_THAT(error.data(), HasSubstr( EXPECT_THAT(error.data(), HasSubstr(
"XML Error: Element size must be a multiple of dim+1")); "XML Error: Error: Node tags must be sequential"));
mj_deleteModel(m); mj_deleteModel(m);
} }
@@ -361,8 +465,9 @@ TEST_F(UserFlexTest, LoadMSHASCII_22_MissingNumNodes_Fail) {
"user/testdata/malformed_shark_22_ascii_missing_num_nodes.xml"); "user/testdata/malformed_shark_22_ascii_missing_num_nodes.xml");
std::array<char, 1024> error; std::array<char, 1024> error;
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size()); mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
EXPECT_THAT(error.data(), HasSubstr( // TODO(mohammadhamid): Replace with an assertion about the error message. For
"XML Error: Element size must be a multiple of dim+1")); // some reason, on Windows the error message is different on GH Actions
EXPECT_THAT(m, IsNull());
mj_deleteModel(m); mj_deleteModel(m);
} }