Added support for msh2.2

Now we can load flex with format 2.2 binary and ASCII.
There are two different parsing strategies. One for gmsh app and the other is for ftetwild.

Used the shark model from Scanned Objects by Google Research:
https://app.gazebosim.org/GoogleResearch/fuel/models/Weisshai_Great_White_Shark

PiperOrigin-RevId: 619229499
Change-Id: I6eb876afcbc8a00631914b093c45bde10c0caaf6
This commit is contained in:
Google DeepMind
2024-03-26 10:11:07 -07:00
committed by Copybara-Service
parent 4d2aa46e56
commit 4f0293c6cb
35 changed files with 38103 additions and 219 deletions
+8 -4
View File
@@ -3808,8 +3808,12 @@ saving the XML:
in MuJoCo can be used as a rigid geom attached to a single body. In contrast, the flex generated here corresponds to in MuJoCo can be used as a rigid geom attached to a single body. In contrast, the flex generated here corresponds to
a soft mesh with the same initial shape, where each vertex is a separate moving body (unless pinned). a soft mesh with the same initial shape, where each vertex is a separate moving body (unless pinned).
**gmsh** is similar to mesh, but it loads a `GMSH file <https://gmsh.info//doc/texinfo/gmsh.html#MSH-file-format>`__ .. _gmsh-file-docs:
in format 4.1 (ascii or binary). The file extension can be anything; the parser recognizes the format by examining
**gmsh** is similar to mesh, but it loads a GMSH file in
`format 4.1 <https://gmsh.info//doc/texinfo/gmsh.html#MSH-file-format>`__
and `format 2.2 <https://gmsh.info//doc/texinfo/gmsh.html#MSH-file-format-version-2-_0028Legacy_0029>`__
(ascii or binary). The file extension can be anything; the parser recognizes the format by examining
the file header. This is a very rich file format, allowing all kinds of elements with different dimensionality and the file header. This is a very rich file format, allowing all kinds of elements with different dimensionality and
topology. MuJoCo only supports GMSH element types 1, 2, 4 which happen to correspond to our 1D, 2D and 3D flexes and topology. MuJoCo only supports GMSH element types 1, 2, 4 which happen to correspond to our 1D, 2D and 3D flexes and
assumes that the nodes are specified in a single block. Only the Nodes and Elements sections of the GMHS file are assumes that the nodes are specified in a single block. Only the Nodes and Elements sections of the GMHS file are
@@ -3817,7 +3821,7 @@ saving the XML:
GMSH file contains meshes that are not supported by MuJoCo. :at:`dim` is automatically set to the dimensionality GMSH file contains meshes that are not supported by MuJoCo. :at:`dim` is automatically set to the dimensionality
specified in the GMSH file. Presently this is the only mechanism to load a large tetrahedral mesh in MuJoCo and specified in the GMSH file. Presently this is the only mechanism to load a large tetrahedral mesh in MuJoCo and
generate a corresponding soft entity. If such a mesh is available in a different file format, use the freely generate a corresponding soft entity. If such a mesh is available in a different file format, use the freely
available `GMSH software <https://gmsh.info/>`__ to convert it to GMSH 4.1. available `GMSH software <https://gmsh.info/>`__ to convert it to GMSH in one of the supported versions.
**direct** allows the user to specify the point and element data of the flexcomp directly in the XML. Note that **direct** allows the user to specify the point and element data of the flexcomp directly in the XML. Note that
flexcomp will still generate moving bodies automatically, as well as automate other settings; so it still provides flexcomp will still generate moving bodies automatically, as well as automate other settings; so it still provides
@@ -3873,7 +3877,7 @@ saving the XML:
: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 **mesh** or a **gmsh** is loaded. For mesh, the file extentsion is used to
determine the file format. Supported formats are the same as in :ref:`mesh assets<asset-mesh>`. For gmsh, the file is determine the file format. Supported formats are the same as in :ref:`mesh assets<asset-mesh>`. For gmsh, the file is
expected to be in GMSH format 4.1, ascii or binary. expected to be in GMSH format 4.1 or 2.2, ascii or binary, see :ref:`here<gmsh-file-docs>`.
.. _body-flexcomp-rigid: .. _body-flexcomp-rigid:
+9 -5
View File
@@ -5,16 +5,20 @@ Changelog
Upcoming version (not yet released) Upcoming version (not yet released)
----------------------------------- -----------------------------------
General
^^^^^^^
1. Added support for gmsh format 2.2, as generated by e.g. `fTetwild <https://github.com/wildmeshing/fTetWild>`__.
MJX MJX
^^^ ^^^
1. Improved performance of SAT for convex collisions. 2. Improved performance of SAT for convex collisions.
2. Fixed bug for sphere/capsule-convex deep penetration. 3. Fixed bug for sphere/capsule-convex deep penetration.
3. Fixed bug where ``mjx.Data`` produced by ``mjx.put_data`` had different treedef than ``mjx.make_data``. 4. Fixed bug where ``mjx.Data`` produced by ``mjx.put_data`` had different treedef than ``mjx.make_data``.
4. Throw an error for margin/gap for convex mesh collisions, since they are not supported. 5. Throw an error for margin/gap for convex mesh collisions, since they are not supported.
Version 3.1.3 (March 5th, 2024) Version 3.1.3 (March 5th, 2024)
----------------------------------- -------------------------------
General General
^^^^^^^ ^^^^^^^
+331 -71
View File
@@ -12,16 +12,14 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "user/user_api.h" #include <algorithm>
#include "user/user_flexcomp.h"
#include <stdio.h>
#include <cstddef> #include <cstddef>
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#include <sstream>
#include <stdio.h>
#include <string> #include <string>
#include <vector> #include <vector>
#include <sstream>
#include <mujoco/mjmacro.h> #include <mujoco/mjmacro.h>
#include <mujoco/mjmodel.h> #include <mujoco/mjmodel.h>
@@ -34,6 +32,7 @@
#include "engine/engine_util_errmem.h" #include "engine/engine_util_errmem.h"
#include "engine/engine_util_misc.h" #include "engine/engine_util_misc.h"
#include "engine/engine_util_spatial.h" #include "engine/engine_util_spatial.h"
#include "user/user_flexcomp.h"
#include "user/user_model.h" #include "user/user_model.h"
#include "user/user_objects.h" #include "user/user_objects.h"
#include "user/user_util.h" #include "user/user_util.h"
@@ -58,6 +57,12 @@ static void ReadFromBuffer(T* dst, const char* src) {
} }
static void ReadStrFromBuffer(char* dest, const char* src, int maxlen) {
std::strncpy(dest, src, maxlen);
}
// constructor: set defaults outside mjCDef // constructor: set defaults outside mjCDef
mjCFlexcomp::mjCFlexcomp(void) { mjCFlexcomp::mjCFlexcomp(void) {
type = mjFCOMPTYPE_GRID; type = mjFCOMPTYPE_GRID;
@@ -958,7 +963,6 @@ static int findstring(const char* buffer, int buffer_sz, const char* str) {
// scan buffer // scan buffer
for (int i=0; i < buffer_sz-len; i++) { for (int i=0; i < buffer_sz-len; i++) {
// check for string at position i // check for string at position i
bool found = true; bool found = true;
for (int k=0; k < len; k++) { for (int k=0; k < len; k++) {
@@ -1020,55 +1024,11 @@ bool mjCFlexcomp::MakeGMSH(mjCModel* model, char* error, int error_sz) {
// load GMSH file from resource // load GMSH format 4.1
void mjCFlexcomp::LoadGMSH(mjCModel* model, mjResource* resource) { void mjCFlexcomp::LoadGMSH41(char* buffer, int binary, int nodeend,
// get buffer from resource int nodebegin, int elemend, int elembegin){
char* buffer = 0; // header size
int buffer_sz = mju_readResource(resource, (const void**) &buffer); constexpr int kGmsh41HeaderSize = 52;
// check buffer
if (buffer_sz<0) {
throw mjCError(NULL, "Could not read GMSH file");
} else if (buffer_sz==0) {
throw mjCError(NULL, "Empty GMSH file");
} else if (buffer_sz<11 || strncmp(buffer, "$MeshFormat", 11)) {
throw mjCError(NULL, "GMSH file must begin with $MeshFormat");
}
// check version, determine ascii or binary
double version;
int binary;
if (sscanf(buffer+11, "%lf %d", &version, &binary) != 2) {
throw mjCError(NULL, "Could not read GMSH file header");
}
if (mju_round(100*version)!=410) {
throw mjCError(NULL, "Only GMSH file format 4.1 supported");
}
// find section begin/end
int nodebegin = findstring(buffer, buffer_sz, "$Nodes");
int nodeend = findstring(buffer, buffer_sz, "$EndNodes");
int elembegin = findstring(buffer, buffer_sz, "$Elements");
int elemend = findstring(buffer, buffer_sz, "$EndElements");
// check sections
if (nodebegin<0) {
throw mjCError(NULL, "GMSH file missing $Nodes");
}
if (nodeend<nodebegin) {
throw mjCError(NULL, "GMSH file missing $EndNodes after $Nodes");
}
if (elembegin<0) {
throw mjCError(NULL, "GMSH file missing $Elements");
}
if (elemend<elembegin) {
throw mjCError(NULL, "GMSH file missing $EndElements after $Elements");
}
// correct begin for string size, +1 for LF in binary (CRLF in Win ascii works)
nodebegin += (int)strlen("$Nodes") + 1;
elembegin += (int)strlen("$Elements") + 1;
// base for node tags, to be subtracted from element data // base for node tags, to be subtracted from element data
size_t minNodeTag, numEntityBlocks, numNodes, maxNodeTag, numNodesInBlock, tag; size_t minNodeTag, numEntityBlocks, numNodes, maxNodeTag, numNodesInBlock, tag;
int entityDim, entityTag, parametric; int entityDim, entityTag, parametric;
@@ -1076,8 +1036,7 @@ void mjCFlexcomp::LoadGMSH(mjCModel* model, mjResource* resource) {
// ascii nodes // ascii nodes
if (binary == 0) { if (binary == 0) {
// convert node char buffer to stringstream // convert node char buffer to stringstream
buffer[nodeend] = 0; stringstream ss(string(buffer + nodebegin, nodeend - nodebegin));
stringstream ss(buffer+nodebegin);
// read header // read header
ss >> numEntityBlocks >> numNodes >> minNodeTag >> maxNodeTag; ss >> numEntityBlocks >> numNodes >> minNodeTag >> maxNodeTag;
@@ -1120,11 +1079,10 @@ void mjCFlexcomp::LoadGMSH(mjCModel* model, mjResource* resource) {
point.push_back(x); point.push_back(x);
} }
} }
// binary nodes // binary nodes
else { else {
// check header size: 5 size_t, 3 int // check header size
if (nodeend-nodebegin < 52) { if (nodeend-nodebegin < kGmsh41HeaderSize) {
throw mjCError(NULL, "Invalid nodes header"); throw mjCError(NULL, "Invalid nodes header");
} }
@@ -1149,15 +1107,20 @@ void mjCFlexcomp::LoadGMSH(mjCModel* model, mjResource* resource) {
} }
def.spec.flex->dim = entityDim; def.spec.flex->dim = entityDim;
// nodeData: node tag and 3 nodes
constexpr int numNodeComponents = 4;
constexpr int componentSize = 8;
int nodeDataSize = numNodeComponents*componentSize;
// check section byte size // check section byte size
if (nodeend-nodebegin < 52+numNodes*4*8) { if (nodeend-nodebegin < kGmsh41HeaderSize + numNodes*nodeDataSize) {
throw mjCError(NULL, "Insufficient byte size of Nodes"); throw mjCError(NULL, "Insufficient byte size of Nodes");
} }
// check node tags: must range from minNodeTag to maxNodeTag // check node tags: must range from minNodeTag to maxNodeTag
const char* tagbuffer = buffer + nodebegin + 52; const char* tagbuffer = buffer + nodebegin + kGmsh41HeaderSize;
for (size_t i=0; i < numNodes; i++) { for (size_t i=0; i < numNodes; i++) {
ReadFromBuffer(&tag, tagbuffer+i*8); ReadFromBuffer(&tag, tagbuffer + i*componentSize);
if (tag != i+minNodeTag) { if (tag != i+minNodeTag) {
throw mjCError(NULL, "Node tags must be sequential"); throw mjCError(NULL, "Node tags must be sequential");
} }
@@ -1166,9 +1129,9 @@ void mjCFlexcomp::LoadGMSH(mjCModel* model, mjResource* resource) {
// read points // read points
double x; double x;
point.reserve(3*numNodes); point.reserve(3*numNodes);
const char* pointbuffer = buffer + nodebegin + 52 + 8*numNodes; const char* pointbuffer = buffer + nodebegin + kGmsh41HeaderSize + componentSize*numNodes;
for (size_t i=0; i < 3*numNodes; i++) { for (size_t i=0; i < 3*numNodes; i++) {
ReadFromBuffer(&x, pointbuffer+i*8); ReadFromBuffer(&x, pointbuffer + i*componentSize);
point.push_back(x); point.push_back(x);
} }
} }
@@ -1223,8 +1186,8 @@ void mjCFlexcomp::LoadGMSH(mjCModel* model, mjResource* resource) {
// binary elements // binary elements
else { else {
// check header size: 5 size_t, 3 int // check header size
if (elemend-elembegin < 52) { if (elemend-elembegin < kGmsh41HeaderSize) {
throw mjCError(NULL, "Invalid elements header"); throw mjCError(NULL, "Invalid elements header");
} }
@@ -1255,17 +1218,22 @@ void mjCFlexcomp::LoadGMSH(mjCModel* model, mjResource* resource) {
throw mjCError(NULL, "Element type inconsistent with dimensionality"); throw mjCError(NULL, "Element type inconsistent with dimensionality");
} }
// elementData: element tag and n node tags
int numElementComponents = (entityDim+2);
constexpr int componentSize = 8;
int elementDataSize = numElementComponents*componentSize;
// check section byte size // check section byte size
if (elemend-elembegin < 52+numElements*(entityDim+2)*8) { if (elemend - elembegin < kGmsh41HeaderSize + numElements*elementDataSize) {
throw mjCError(NULL, "Insufficient byte size of Elements"); throw mjCError(NULL, "Insufficient byte size of Elements");
} }
// read elements, discard tags // read elements, discard tags
element.reserve((entityDim+1)*numElements); element.reserve((entityDim+1)*numElements);
const char* elembuffer = buffer + elembegin + 52; const char* elembuffer = buffer + elembegin + kGmsh41HeaderSize;
for (size_t i=0; i < numElements; i++) { for (size_t i=0; i < numElements; i++) {
// skip element tag // skip element tag
elembuffer += 8; elembuffer += componentSize;
// read vertex ids // read vertex ids
size_t elemid; size_t elemid;
@@ -1273,8 +1241,300 @@ void mjCFlexcomp::LoadGMSH(mjCModel* model, mjResource* resource) {
ReadFromBuffer(&elemid, elembuffer); ReadFromBuffer(&elemid, elembuffer);
int elementid = elemid - minNodeTag; int elementid = elemid - minNodeTag;
element.push_back(elementid); element.push_back(elementid);
elembuffer += 8; elembuffer += componentSize;
} }
} }
} }
} }
// load GMSH format 2.2
void mjCFlexcomp::LoadGMSH22(char* buffer, int binary, int nodeend,
int nodebegin, int elemend, int elembegin) {
// ascii nodes
if (binary == 0) {
// convert node char buffer to stringstream
stringstream ss(string(buffer + nodebegin, nodeend - nodebegin));
// read header
size_t maxNodeTag = 0;
ss >> maxNodeTag;
if (!ss.good()) {
throw mjCError(NULL, "Error reading Nodes header");
}
size_t numNodes = maxNodeTag;
// read points, discard tag
point.reserve(3*numNodes);
for (size_t i=0; i < numNodes; i++) {
size_t tag;
double x;
ss >> tag;
if (!ss.good()) {
throw mjCError(NULL, "Error reading node tags");
}
// reading nodes
for (int k=0; k < 3; k++) {
ss >> x;
if (!ss.good()) {
throw mjCError(NULL, "Error reading node coordinates");
}
point.push_back(x);
}
}
}
// binary nodes
else {
// header size for gmshApp
constexpr int nodeHeaderSizeGmshApp = 5;
// header size compatible with both gmshApp and Ftetwild
constexpr int nodeHeaderSize = nodeHeaderSizeGmshApp - 1;
// check header size
if (nodeend-nodebegin < nodeHeaderSize) {
throw mjCError(NULL, "Invalid nodes header");
}
// parse maxNodeTag and then cast it to int
char maxNodeTagChar[20];
ReadStrFromBuffer(maxNodeTagChar, buffer + nodebegin, std::min(10, nodeend - nodebegin));
size_t measuredHeaderSize = strnlen(maxNodeTagChar, 20) - 1;
size_t maxNodeTag = std::stoi(maxNodeTagChar);
size_t numNodes = maxNodeTag;
// node data: node tag and 3 nodes
int nodeSize = sizeof(double);
int indexSize = sizeof(int);
int nodeDataSize = indexSize + 3*nodeSize;
// check section byte size
if (nodeend - nodebegin < nodeHeaderSize + numNodes*nodeDataSize) {
throw mjCError(NULL, "Insufficient byte size of Nodes");
}
// read point, discard tag
point.reserve(3*numNodes);
// beginning of buffer containing node info
const char* tagBuffer = buffer + nodebegin + measuredHeaderSize;
for (int i=0; i < numNodes; i++) {
int tag;
int offset = i*(sizeof(int) + sizeof(double)*3);
ReadFromBuffer(&tag, tagBuffer + offset);
for (int k=0; k < 3; k++) {
double x;
const char* nodeBuffer = tagBuffer + sizeof(int) + sizeof(double)*k;
ReadFromBuffer(&x, nodeBuffer + offset);
point.push_back(x);
}
}
}
size_t entityDim = 3;
def.spec.flex->dim = entityDim;
// ascii elements
if (binary == 0) {
// convert element char buffer to stringstream
buffer[elemend] = 0;
stringstream ss(buffer + elembegin);
// read header
size_t maxElementTag = 0;
ss >> maxElementTag;
if (!ss.good()) {
throw mjCError(NULL, "Error reading Elements header");
}
size_t numElements = maxElementTag;
// read elements, discard all tags
element.reserve((entityDim+1)*numElements);
for (size_t i=0; i < numElements; i++) {
int tag = 0, type = 0, numTags = 0, nodeTag = 0, physicalEntityTag = 0,
elmentModelEntityTag = 0;
ss >> tag >> type >> numTags;
if (!ss.good()) {
throw mjCError(NULL, "Error reading Elements");
}
if (numTags > 0) {
ss >> physicalEntityTag >> elmentModelEntityTag;
if (!ss.good()) {
throw mjCError(NULL, "Error reading Elements");
}
}
for (int k=0; k <= entityDim; k++) {
ss >> nodeTag;
if (!ss.good()) {
throw mjCError(NULL, "Error reading Elements");
}
element.push_back((int)(nodeTag-1));
}
}
}
// binary elements
else {
// header size for gmshApp
constexpr int elementHeaderSizeGmshApp = 6;
// header size for Ftetwild
constexpr int elementHeaderSizeFtetwild = 17;
// check header size
if (elemend - elembegin < elementHeaderSizeGmshApp) {
throw mjCError(NULL, "Invalid elements header");
}
// reading elements
char maxElementTagChar[20];
ReadStrFromBuffer(maxElementTagChar, buffer + elembegin, std::min(10, elemend - elembegin));
int measuredHeaderSize = strnlen(maxElementTagChar, 20) - 1;
int maxElementTag = std::stoi(maxElementTagChar);
int numElements = maxElementTag;
int tag, numTags;
int nodeTag;
// size of single component in element data
int componentSize = sizeof(int);
// element buffer
const char* elementsBuffer = buffer + elembegin + measuredHeaderSize;
ReadFromBuffer(&numTags, elementsBuffer + componentSize*2);
ReadFromBuffer(&tag, elementsBuffer + componentSize*3);
// element data(Ftetwild): tag and 4 nodeTag
constexpr int numComponentsFtetwild = 5;
// element data(gmshApp): 4 Info components, 2 entity tag and 4 nodeTags
constexpr int numInfoComponents = 4;
constexpr int numEntityTagComponents = 2;
constexpr int numNodeTags = 4;
constexpr int numComponentsGmshApp = numInfoComponents +
numEntityTagComponents + numNodeTags;
// single element data size
int elementDataSizeFtetwild = numComponentsFtetwild*componentSize;
int elementDataSizeGmshApp = numComponentsGmshApp*componentSize;
// elements section buffer size
int elementsBufferSizeFtetwild = elementHeaderSizeFtetwild +
numElements*elementDataSizeFtetwild;
int elementsBufferSizeGmshApp = elementHeaderSizeGmshApp +
numElements*elementDataSizeGmshApp;
// check section byte size for ftetwild
if (elemend - elembegin < elementsBufferSizeFtetwild) {
throw mjCError(NULL, "Insufficient byte size of Elements");
}
// Handling elements produced by gmsh
if (numTags > 0) {
// check section byte size for gmsh
if (elemend - elembegin < elementsBufferSizeGmshApp) {
throw mjCError(NULL, "Insufficient byte size of Elements");
}
// read first element
for (int k =0; k<numNodeTags; k++) {
ReadFromBuffer(&nodeTag, elementsBuffer + componentSize*(6+k));
element.push_back(nodeTag-1);
}
// read every other element
for (int i=1; i < numElements; i++) {
const char* numTagsBuffer = elementsBuffer + componentSize*2;
const char* tagBuffer = elementsBuffer + componentSize*3;
int offset = i*elementDataSizeGmshApp;
ReadFromBuffer(&numTags, numTagsBuffer + offset);
ReadFromBuffer(&tag, tagBuffer+offset);
for (int k =0; k < numNodeTags; k++) {
const char* nodeTagBuffer = elementsBuffer + componentSize*(6+k);
ReadFromBuffer(&nodeTag, nodeTagBuffer + offset);
element.push_back(nodeTag-1);
}
}
}
// Handling elements produced by ftetwild
else {
// read first element
for (int k =0; k < numNodeTags; k++) {
const char* nodeTagBuffer = elementsBuffer + componentSize*(4+k);
ReadFromBuffer(&nodeTag, nodeTagBuffer);
element.push_back(nodeTag-1);
}
// read every other element
for (int i=0; i<numElements-1; i++) {
int offset = componentSize*(4+2) + i*elementDataSizeFtetwild;
const char* tagBuffer = elementsBuffer + componentSize*2;
ReadFromBuffer(&tag, tagBuffer + offset);
for (int k=0; k < numNodeTags; k++) {
const char* nodeTagBuffer = elementsBuffer + componentSize*(3+k);
ReadFromBuffer(&nodeTag, nodeTagBuffer + offset);
element.push_back(nodeTag-1);
}
}
}
}
}
// load GMSH file from resource
void mjCFlexcomp::LoadGMSH(mjCModel* model, mjResource* resource) {
// get buffer from resource
char* buffer = 0;
int buffer_sz = mju_readResource(resource, (const void**) &buffer);
// check buffer
if (buffer_sz < 0) {
throw mjCError(NULL, "Could not read GMSH file");
} else if (buffer_sz == 0) {
throw mjCError(NULL, "Empty GMSH file");
} else if (buffer_sz < 11 || strncmp(buffer, "$MeshFormat", 11)) {
throw mjCError(NULL, "GMSH file must begin with $MeshFormat");
}
// check version, determine ascii or binary
double version;
int binary;
if (sscanf(buffer + 11, "%lf %d", &version, &binary) != 2) {
throw mjCError(NULL, "Could not read GMSH file header");
}
if (mju_round(100*version) != 220 && mju_round(100*version) != 410) {
throw mjCError(NULL, "Only GMSH file format versions 4.1 and 2.2 are supported");
}
// find section begin/end
int nodebegin = findstring(buffer, buffer_sz, "$Nodes");
int nodeend = findstring(buffer, buffer_sz, "$EndNodes");
int elembegin = findstring(buffer, buffer_sz, "$Elements");
int elemend = findstring(buffer, buffer_sz, "$EndElements");
// check sections
if (nodebegin < 0) {
throw mjCError(NULL, "GMSH file missing $Nodes");
}
if (nodeend < nodebegin) {
throw mjCError(NULL, "GMSH file missing $EndNodes after $Nodes");
}
if (elembegin < 0) {
throw mjCError(NULL, "GMSH file missing $Elements");
}
if (elemend < elembegin) {
throw mjCError(NULL, "GMSH file missing $EndElements after $Elements");
}
// correct begin for string size, +1 for LF in binary (CRLF in Win ascii works)
nodebegin += (int)strlen("$Nodes") + 1;
elembegin += (int)strlen("$Elements") + 1;
// Support for 4.1
if (mju_round(100*version) == 410) {
LoadGMSH41(buffer, binary, nodeend, nodebegin, elemend, elembegin);
}
// Support for 2.2
else if (mju_round(100*version) == 220) {
LoadGMSH22(buffer, binary, nodeend, nodebegin, elemend, elembegin);
}
}
+5
View File
@@ -50,6 +50,11 @@ class mjCFlexcomp {
bool MakeMesh(mjCModel* model, char* error, int error_sz); bool MakeMesh(mjCModel* model, char* error, int error_sz);
bool MakeGMSH(mjCModel* model, char* error, int error_sz); bool MakeGMSH(mjCModel* model, char* error, int error_sz);
void LoadGMSH(mjCModel* model, mjResource* resource); void LoadGMSH(mjCModel* model, mjResource* resource);
void LoadGMSH41(char* buffer, int binary, int nodeend, int nodebegin,
int elemend, int elembegin);
void LoadGMSH22(char* buffer, int binary, int nodeend, int nodebegin,
int elemend, int elembegin);
int GridID(int ix, int iy); int GridID(int ix, int iy);
int GridID(int ix, int iy, int iz); int GridID(int ix, int iy, int iz);
+8 -2
View File
@@ -32,8 +32,14 @@ test_model() {
# this test can take several minutes under ASAN # this test can take several minutes under ASAN
return 0 return 0
fi fi
if [[ "$model" == */benchmark/testdata/humanoid200.xml if [[ "$model" == */benchmark/testdata/humanoid200.xml ||
|| "$model" == */engine/testdata/collision_convex/stacked_boxes.xml "$model" == */engine/testdata/collision_convex/stacked_boxes.xml ||
"$model" == */user/testdata/shark_41_ascii_gmshApp.xml ||
"$model" == */user/testdata/shark_22_ascii_fTetWild.xml ||
"$model" == */user/testdata/shark_22_ascii_gmshApp.xml ||
"$model" == */user/testdata/shark_22_binary_fTetWild.xml ||
"$model" == */user/testdata/shark_41_binary_gmshApp.xml ||
"$model" == */user/testdata/shark_22_binary_gmshApp.xml
]]; then ]]; then
iterations=2 iterations=2
fi fi
@@ -0,0 +1,25 @@
<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="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"/>
<flexcomp name="shark" type="gmsh" rgba=".8 .2 .2 1" radius="0.001" pos="0 0 4" mass="10" scale="1 1 1" file="shark_22_ascii_missing_element.msh">
<edge equality="true"/>
</flexcomp>
</worldbody>
</mujoco>
@@ -0,0 +1,27 @@
<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="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" />
<flexcomp name="shark" type="gmsh" rgba=".8 .2 .2 1" radius="0.001" pos="0 0 4" mass="10"
scale="1 1 1" file="shark_22_ascii_missing_node.msh">
<edge equality="true" />
</flexcomp>
</worldbody>
</mujoco>
@@ -0,0 +1,27 @@
<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="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" />
<flexcomp name="shark" type="gmsh" rgba=".8 .2 .2 1" radius="0.001" pos="0 0 4" mass="10"
scale="1 1 1" file="shark_22_ascii_missing_num_elements.msh">
<edge equality="true" />
</flexcomp>
</worldbody>
</mujoco>
@@ -0,0 +1,27 @@
<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="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" />
<flexcomp name="shark" type="gmsh" rgba=".8 .2 .2 1" radius="0.001" pos="0 0 4" mass="10"
scale="1 1 1" file="shark_22_ascii_missing_num_nodes.msh">
<edge equality="true" />
</flexcomp>
</worldbody>
</mujoco>
@@ -0,0 +1,27 @@
<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="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" />
<flexcomp name="shark binary 2.2" type="gmsh" rgba=".8 .2 .2 1" radius="0.001" pos="0 0 4"
mass="10" scale="1 1 1" file="shark_41_ascii_missing_element.msh">
<edge equality="true" />
</flexcomp>
</worldbody>
</mujoco>
@@ -0,0 +1,27 @@
<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="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" />
<flexcomp name="shark binary 2.2" type="gmsh" rgba=".8 .2 .2 1" radius="0.001" pos="0 0 4"
mass="10" scale="1 1 1" file="shark_41_ascii_missing_element_header.msh">
<edge equality="true" />
</flexcomp>
</worldbody>
</mujoco>
@@ -0,0 +1,27 @@
<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="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" />
<flexcomp name="shark binary 2.2" type="gmsh" rgba=".8 .2 .2 1" radius="0.001" pos="0 0 4"
mass="10" scale="1 1 1" file="shark_41_ascii_missing_node_header.msh">
<edge equality="true" />
</flexcomp>
</worldbody>
</mujoco>
@@ -0,0 +1,27 @@
<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="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" />
<flexcomp name="shark binary 2.2" type="gmsh" 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">
<edge equality="true" />
</flexcomp>
</worldbody>
</mujoco>
File diff suppressed because it is too large Load Diff
+25
View File
@@ -0,0 +1,25 @@
<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="shark" type="gmsh" dim="3" rgba=".8 .2 .2 1" radius="0.001" pos="0 0 .3" mass="10" scale="1 1 1" file="shark_22_ascii_fTetWild.msh">
<edge equality="true"/>
</flexcomp>
</worldbody>
</mujoco>
File diff suppressed because it is too large Load Diff
+25
View File
@@ -0,0 +1,25 @@
<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="shark" type="gmsh" dim="3" rgba=".8 .2 .2 1" radius="0.001" pos="0 0 0.3" mass="10" scale="1 1 1" file="shark_22_ascii_gmshApp.msh">
<edge equality="true"/>
</flexcomp>
</worldbody>
</mujoco>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
Binary file not shown.
+25
View File
@@ -0,0 +1,25 @@
<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="shark" type="gmsh" dim="3" rgba=".8 .2 .2 1" radius="0.001" pos="0 0 0.3" mass="10" scale="1 1 1" file="shark_22_binary_fTetWild.msh">
<edge equality="true"/>
</flexcomp>
</worldbody>
</mujoco>
Binary file not shown.
+25
View File
@@ -0,0 +1,25 @@
<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="shark" type="gmsh" dim="3" rgba=".8 .2 .2 1" radius="0.001" pos="0 0 0.3" mass="10" scale="1 1 1" file="shark_22_binary_gmshApp.msh">
<edge equality="true"/>
</flexcomp>
</worldbody>
</mujoco>
File diff suppressed because it is too large Load Diff
+25
View File
@@ -0,0 +1,25 @@
<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="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"/>
<flexcomp name="shark" 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_gmshApp.msh">
<edge equality="true"/>
</flexcomp>
</worldbody>
</mujoco>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
Binary file not shown.
+25
View File
@@ -0,0 +1,25 @@
<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="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"/>
<flexcomp name="shark" 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_binary_gmshApp.msh">
<edge equality="true"/>
</flexcomp>
</worldbody>
</mujoco>
+172
View File
@@ -227,5 +227,177 @@ TEST_F(UserFlexTest, CreateBVHSuccess) {
mj_deleteData(d); mj_deleteData(d);
} }
TEST_F(UserFlexTest, LoadMSHBinaryGMSH_41_Success) {
const std::string xml_path =
GetTestDataFilePath("user/testdata/shark_41_binary_gmshApp.xml");
std::array<char, 1024> error;
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
mjData* d = mj_makeData(m);
EXPECT_THAT(m, NotNull()) << error.data();
EXPECT_EQ(m->nflexvert, 652);
EXPECT_EQ(m->nflexelem, 1654);
mj_step(m, d);
mj_deleteModel(m);
mj_deleteData(d);
}
TEST_F(UserFlexTest, LoadMSHBinaryGMSH_22_Success) {
const std::string xml_path =
GetTestDataFilePath("user/testdata/shark_22_binary_gmshApp.xml");
std::array<char, 1024> error;
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
mjData* d = mj_makeData(m);
EXPECT_THAT(m, NotNull()) << error.data();
EXPECT_EQ(m->nflexvert, 644);
EXPECT_EQ(m->nflexelem, 1635);
mj_step(m, d);
mj_deleteModel(m);
mj_deleteData(d);
}
TEST_F(UserFlexTest, LoadMSHBinaryFTETWILD_22_Success) {
const std::string xml_path =
GetTestDataFilePath("user/testdata/shark_22_binary_fTetWild.xml");
std::array<char, 1024> error;
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
mjData* d = mj_makeData(m);
EXPECT_THAT(m, NotNull()) << error.data();
EXPECT_EQ(m->nflexvert, 644);
EXPECT_EQ(m->nflexelem, 1635);
mj_step(m, d);
mj_deleteModel(m);
mj_deleteData(d);
}
TEST_F(UserFlexTest, LoadMSHASCIIGMSH_41_Success) {
const std::string xml_path =
GetTestDataFilePath("user/testdata/shark_41_ascii_gmshApp.xml");
std::array<char, 1024> error;
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
mjData* d = mj_makeData(m);
EXPECT_THAT(m, NotNull()) << error.data();
EXPECT_EQ(m->nflexvert, 652);
EXPECT_EQ(m->nflexelem, 1654);
mj_step(m, d);
mj_deleteModel(m);
mj_deleteData(d);
}
TEST_F(UserFlexTest, LoadMSHASCIIGMSH_22_Success) {
const std::string xml_path =
GetTestDataFilePath("user/testdata/shark_22_ascii_gmshApp.xml");
std::array<char, 1024> error;
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
mjData* d = mj_makeData(m);
EXPECT_THAT(m, NotNull()) << error.data();
EXPECT_EQ(m->nflexvert, 652);
EXPECT_EQ(m->nflexelem, 1654);
mj_step(m, d);
mj_deleteModel(m);
mj_deleteData(d);
}
TEST_F(UserFlexTest, LoadMSHASCIIFTETWILD_22_Success) {
const std::string xml_path =
GetTestDataFilePath("user/testdata/shark_22_ascii_fTetWild.xml");
std::array<char, 1024> error;
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
mjData* d = mj_makeData(m);
EXPECT_THAT(m, NotNull()) << error.data();
EXPECT_EQ(m->nflexvert, 652);
EXPECT_EQ(m->nflexelem, 1654);
mj_step(m, d);
mj_deleteModel(m);
mj_deleteData(d);
}
TEST_F(UserFlexTest, LoadMSHASCII_41_MissingNodeHeader_Fail) {
const std::string xml_path =
GetTestDataFilePath(
"user/testdata/malformed_shark_41_ascii_missing_node_header.xml");
std::array<char, 1024> error;
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
EXPECT_THAT(error.data(), HasSubstr(
"XML Error: Error: All nodes must be in single block"));
mj_deleteModel(m);
}
TEST_F(UserFlexTest, LoadMSHASCII_41_MissingNodeIndex_Fail) {
const std::string xml_path =
GetTestDataFilePath(
"user/testdata/malformed_shark_41_ascii_missing_node_index.xml");
std::array<char, 1024> error;
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
EXPECT_THAT(error.data(), HasSubstr(
"XML Error: Element size must be a multiple of dim+1"));
mj_deleteModel(m);
}
TEST_F(UserFlexTest, LoadMSHASCII_41_MissingElementHeader_Fail) {
const std::string xml_path =
GetTestDataFilePath(
"user/testdata/malformed_shark_41_ascii_missing_element_header.xml");
std::array<char, 1024> error;
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
EXPECT_THAT(error.data(), HasSubstr(
"XML Error: Error: All elements must be in single block"));
mj_deleteModel(m);
}
TEST_F(UserFlexTest, LoadMSHASCII_41_MissingElement_Fail) {
const std::string xml_path =
GetTestDataFilePath(
"user/testdata/malformed_shark_41_ascii_missing_element.xml");
std::array<char, 1024> error;
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
EXPECT_THAT(error.data(), HasSubstr(
"XML Error: Error: Error reading Elements"));
mj_deleteModel(m);
}
TEST_F(UserFlexTest, LoadMSHASCII_22_MissingNumNodes_Fail) {
const std::string xml_path =
GetTestDataFilePath(
"user/testdata/malformed_shark_22_ascii_missing_num_nodes.xml");
std::array<char, 1024> error;
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
EXPECT_THAT(error.data(), HasSubstr(
"XML Error: Element size must be a multiple of dim+1"));
mj_deleteModel(m);
}
TEST_F(UserFlexTest, LoadMSHASCII_22_MissingNode_Fail) {
const std::string xml_path =
GetTestDataFilePath(
"user/testdata/malformed_shark_22_ascii_missing_node.xml");
std::array<char, 1024> error;
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
EXPECT_THAT(error.data(), HasSubstr(
"XML Error: Error: Error reading node tags"));
mj_deleteModel(m);
}
TEST_F(UserFlexTest, LoadMSHASCII_22_MissingNumElements_Fail) {
const std::string xml_path =
GetTestDataFilePath(
"user/testdata/malformed_shark_22_ascii_missing_num_elements.xml");
std::array<char, 1024> error;
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
EXPECT_THAT(error.data(), HasSubstr(
"XML Error: Element size must be a multiple of dim+1"));
mj_deleteModel(m);
}
TEST_F(UserFlexTest, LoadMSHASCII_22_MissingElement_Fail) {
const std::string xml_path =
GetTestDataFilePath(
"user/testdata/malformed_shark_22_ascii_missing_element.xml");
std::array<char, 1024> error;
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
EXPECT_THAT(error.data(), HasSubstr(
"XML Error: Error: Error reading Elements"));
mj_deleteModel(m);
}
} // namespace } // namespace
} // namespace mujoco } // namespace mujoco
+1
View File
@@ -1204,6 +1204,7 @@ TEST_F(XMLWriterTest, WriteReadCompare) {
if (absl::StrContains(p.path().string(), "malformed_") || if (absl::StrContains(p.path().string(), "malformed_") ||
absl::StrContains(p.path().string(), "touch_grid") || absl::StrContains(p.path().string(), "touch_grid") ||
absl::StrContains(p.path().string(), "gmsh_") || absl::StrContains(p.path().string(), "gmsh_") ||
absl::StrContains(p.path().string(), "shark_") ||
absl::StrContains(p.path().string(), "cow")) { absl::StrContains(p.path().string(), "cow")) {
continue; continue;
} }