Read normals and textures from OBJ, do not duplicate vertices.

PiperOrigin-RevId: 510461549
Change-Id: I19245d691e722a8371e61153383f275f599353d6
This commit is contained in:
Alessio Quaglino
2023-02-17 10:14:25 -08:00
committed by Copybara-Service
parent 35967a30d9
commit 9756ed0d80
16 changed files with 279 additions and 168 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ nlight 1
nmesh 0 nmesh 0
nmeshvert 0 nmeshvert 0
nmeshface 0 nmeshface 0
nmeshtexvert 0 nmeshtexcoord 0
nmeshgraph 0 nmeshgraph 0
nskin 0 nskin 0
nskinvert 0 nskinvert 0
+2
View File
@@ -10,6 +10,8 @@ General
- The ``mjd_transitionFD`` function no longer triggers sensor calculation unless explicitly requested. - The ``mjd_transitionFD`` function no longer triggers sensor calculation unless explicitly requested.
- Corrected the spelling of the ``inteval`` attribute to ``interval`` in the ``mjLROpt`` struct. - Corrected the spelling of the ``inteval`` attribute to ``interval`` in the ``mjLROpt`` struct.
- Mesh texture and normal mappings are now 3-per-triangle rather than 1-per-vertex. Mesh vertices are no longer
duplicated in order to circumvent this limitation as they previously were.
Python bindings Python bindings
^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
+11 -5
View File
@@ -746,7 +746,8 @@ struct mjModel_ {
int nlight; // number of lights int nlight; // number of lights
int nmesh; // number of meshes int nmesh; // number of meshes
int nmeshvert; // number of vertices in all meshes int nmeshvert; // number of vertices in all meshes
int nmeshtexvert; // number of vertices with texcoords in all meshes int nmeshnormal; // number of normals in all meshes
int nmeshtexcoord; // number of texcoords in all meshes
int nmeshface; // number of triangular faces in all meshes int nmeshface; // number of triangular faces in all meshes
int nmeshgraph; // number of ints in mesh auxiliary data int nmeshgraph; // number of ints in mesh auxiliary data
int nskin; // number of skins int nskin; // number of skins
@@ -942,14 +943,19 @@ struct mjModel_ {
// meshes // meshes
int* mesh_vertadr; // first vertex address (nmesh x 1) int* mesh_vertadr; // first vertex address (nmesh x 1)
int* mesh_vertnum; // number of vertices (nmesh x 1) int* mesh_vertnum; // number of vertices (nmesh x 1)
int* mesh_texcoordadr; // texcoord data address; -1: no texcoord (nmesh x 1)
int* mesh_faceadr; // first face address (nmesh x 1) int* mesh_faceadr; // first face address (nmesh x 1)
int* mesh_facenum; // number of faces (nmesh x 1) int* mesh_facenum; // number of faces (nmesh x 1)
int* mesh_normaladr; // first normal address (nmesh x 1)
int* mesh_normalnum; // number of normals (nmesh x 1)
int* mesh_texcoordadr; // texcoord data address; -1: no texcoord (nmesh x 1)
int* mesh_texcoordnum; // number of texcoord (nmesh x 1)
int* mesh_graphadr; // graph data address; -1: no graph (nmesh x 1) int* mesh_graphadr; // graph data address; -1: no graph (nmesh x 1)
float* mesh_vert; // vertex positions for all meshes (nmeshvert x 3) float* mesh_vert; // vertex positions for all meshes (nmeshvert x 3)
float* mesh_normal; // vertex normals for all meshes (nmeshvert x 3) float* mesh_normal; // normals for all meshes (nmeshnormal x 3)
float* mesh_texcoord; // vertex texcoords for all meshes (nmeshtexvert x 2) float* mesh_texcoord; // vertex texcoords for all meshes (nmeshtexcoord x 2)
int* mesh_face; // triangle face data (nmeshface x 3) int* mesh_face; // vertex face data (nmeshface x 3)
int* mesh_facenormal; // normal face data (nmeshface x 3)
int* mesh_facetexcoord; // texture face data (nmeshface x 3)
int* mesh_graph; // convex graph data (nmeshgraph x 1) int* mesh_graph; // convex graph data (nmeshgraph x 1)
// skins // skins
+11 -5
View File
@@ -542,7 +542,8 @@ struct mjModel_ {
int nlight; // number of lights int nlight; // number of lights
int nmesh; // number of meshes int nmesh; // number of meshes
int nmeshvert; // number of vertices in all meshes int nmeshvert; // number of vertices in all meshes
int nmeshtexvert; // number of vertices with texcoords in all meshes int nmeshnormal; // number of normals in all meshes
int nmeshtexcoord; // number of texcoords in all meshes
int nmeshface; // number of triangular faces in all meshes int nmeshface; // number of triangular faces in all meshes
int nmeshgraph; // number of ints in mesh auxiliary data int nmeshgraph; // number of ints in mesh auxiliary data
int nskin; // number of skins int nskin; // number of skins
@@ -738,14 +739,19 @@ struct mjModel_ {
// meshes // meshes
int* mesh_vertadr; // first vertex address (nmesh x 1) int* mesh_vertadr; // first vertex address (nmesh x 1)
int* mesh_vertnum; // number of vertices (nmesh x 1) int* mesh_vertnum; // number of vertices (nmesh x 1)
int* mesh_texcoordadr; // texcoord data address; -1: no texcoord (nmesh x 1)
int* mesh_faceadr; // first face address (nmesh x 1) int* mesh_faceadr; // first face address (nmesh x 1)
int* mesh_facenum; // number of faces (nmesh x 1) int* mesh_facenum; // number of faces (nmesh x 1)
int* mesh_normaladr; // first normal address (nmesh x 1)
int* mesh_normalnum; // number of normals (nmesh x 1)
int* mesh_texcoordadr; // texcoord data address; -1: no texcoord (nmesh x 1)
int* mesh_texcoordnum; // number of texcoord (nmesh x 1)
int* mesh_graphadr; // graph data address; -1: no graph (nmesh x 1) int* mesh_graphadr; // graph data address; -1: no graph (nmesh x 1)
float* mesh_vert; // vertex positions for all meshes (nmeshvert x 3) float* mesh_vert; // vertex positions for all meshes (nmeshvert x 3)
float* mesh_normal; // vertex normals for all meshes (nmeshvert x 3) float* mesh_normal; // normals for all meshes (nmeshnormal x 3)
float* mesh_texcoord; // vertex texcoords for all meshes (nmeshtexvert x 2) float* mesh_texcoord; // vertex texcoords for all meshes (nmeshtexcoord x 2)
int* mesh_face; // triangle face data (nmeshface x 3) int* mesh_face; // vertex face data (nmeshface x 3)
int* mesh_facenormal; // normal face data (nmeshface x 3)
int* mesh_facetexcoord; // texture face data (nmeshface x 3)
int* mesh_graph; // convex graph data (nmeshgraph x 1) int* mesh_graph; // convex graph data (nmeshgraph x 1)
// skins // skins
+9 -3
View File
@@ -74,7 +74,8 @@
X( nlight ) \ X( nlight ) \
X( nmesh ) \ X( nmesh ) \
X( nmeshvert ) \ X( nmeshvert ) \
X( nmeshtexvert ) \ X( nmeshnormal ) \
X( nmeshtexcoord ) \
X( nmeshface ) \ X( nmeshface ) \
X( nmeshgraph ) \ X( nmeshgraph ) \
X( nskin ) \ X( nskin ) \
@@ -263,14 +264,19 @@
X( float, light_specular, nlight, 3 ) \ X( float, light_specular, nlight, 3 ) \
X( int, mesh_vertadr, nmesh, 1 ) \ X( int, mesh_vertadr, nmesh, 1 ) \
X( int, mesh_vertnum, nmesh, 1 ) \ X( int, mesh_vertnum, nmesh, 1 ) \
X( int, mesh_normaladr, nmesh, 1 ) \
X( int, mesh_normalnum, nmesh, 1 ) \
X( int, mesh_texcoordadr, nmesh, 1 ) \ X( int, mesh_texcoordadr, nmesh, 1 ) \
X( int, mesh_texcoordnum, nmesh, 1 ) \
X( int, mesh_faceadr, nmesh, 1 ) \ X( int, mesh_faceadr, nmesh, 1 ) \
X( int, mesh_facenum, nmesh, 1 ) \ X( int, mesh_facenum, nmesh, 1 ) \
X( int, mesh_graphadr, nmesh, 1 ) \ X( int, mesh_graphadr, nmesh, 1 ) \
X( float, mesh_vert, nmeshvert, 3 ) \ X( float, mesh_vert, nmeshvert, 3 ) \
X( float, mesh_normal, nmeshvert, 3 ) \ X( float, mesh_normal, nmeshnormal, 3 ) \
X( float, mesh_texcoord, nmeshtexvert, 2 ) \ X( float, mesh_texcoord, nmeshtexcoord, 2 ) \
X( int, mesh_face, nmeshface, 3 ) \ X( int, mesh_face, nmeshface, 3 ) \
X( int, mesh_facenormal, nmeshface, 3 ) \
X( int, mesh_facetexcoord, nmeshface, 3 ) \
X( int, mesh_graph, nmeshgraph, 1 ) \ X( int, mesh_graph, nmeshgraph, 1 ) \
X( int, skin_matid, nskin, 1 ) \ X( int, skin_matid, nskin, 1 ) \
X( int, skin_group, nskin, 1 ) \ X( int, skin_group, nskin, 1 ) \
+8 -6
View File
@@ -367,8 +367,8 @@ static int safeAddToBufferSize(intptr_t* offset, int* nbuffer, size_t type_size,
// allocate and initialize mjModel structure // allocate and initialize mjModel structure
mjModel* mj_makeModel(int nq, int nv, int nu, int na, int nbody, int njnt, mjModel* mj_makeModel(int nq, int nv, int nu, int na, int nbody, int njnt,
int ngeom, int nsite, int ncam, int nlight, int ngeom, int nsite, int ncam, int nlight,
int nmesh, int nmeshvert, int nmeshtexvert, int nmeshface, int nmeshgraph, int nmesh, int nmeshvert, int nmeshnormal, int nmeshtexcoord, int nmeshface,
int nskin, int nskinvert, int nskintexvert, int nskinface, int nmeshgraph, int nskin, int nskinvert, int nskintexvert, int nskinface,
int nskinbone, int nskinbonevert, int nhfield, int nhfielddata, int nskinbone, int nskinbonevert, int nhfield, int nhfielddata,
int ntex, int ntexdata, int nmat, int npair, int nexclude, int ntex, int ntexdata, int nmat, int npair, int nexclude,
int neq, int ntendon, int nwrap, int nsensor, int neq, int ntendon, int nwrap, int nsensor,
@@ -399,7 +399,8 @@ mjModel* mj_makeModel(int nq, int nv, int nu, int na, int nbody, int njnt,
m->nlight = nlight; m->nlight = nlight;
m->nmesh = nmesh; m->nmesh = nmesh;
m->nmeshvert = nmeshvert; m->nmeshvert = nmeshvert;
m->nmeshtexvert = nmeshtexvert; m->nmeshnormal = nmeshnormal;
m->nmeshtexcoord = nmeshtexcoord;
m->nmeshface = nmeshface; m->nmeshface = nmeshface;
m->nmeshgraph = nmeshgraph; m->nmeshgraph = nmeshgraph;
m->nskin = nskin; m->nskin = nskin;
@@ -512,7 +513,7 @@ mjModel* mj_copyModel(mjModel* dest, const mjModel* src) {
if (!dest) { if (!dest) {
dest = mj_makeModel(src->nq, src->nv, src->nu, src->na, src->nbody, src->njnt, dest = mj_makeModel(src->nq, src->nv, src->nu, src->na, src->nbody, src->njnt,
src->ngeom, src->nsite, src->ncam, src->nlight, src->nmesh, src->nmeshvert, src->ngeom, src->nsite, src->ncam, src->nlight, src->nmesh, src->nmeshvert,
src->nmeshtexvert, src->nmeshface, src->nmeshgraph, src->nmeshnormal, src->nmeshtexcoord, src->nmeshface, src->nmeshgraph,
src->nskin, src->nskinvert, src->nskintexvert, src->nskinface, src->nskin, src->nskinvert, src->nskintexvert, src->nskinface,
src->nskinbone, src->nskinbonevert, src->nhfield, src->nhfielddata, src->nskinbone, src->nskinbonevert, src->nhfield, src->nhfielddata,
src->ntex, src->ntexdata, src->nmat, src->npair, src->nexclude, src->ntex, src->ntexdata, src->nmat, src->npair, src->nexclude,
@@ -691,7 +692,7 @@ mjModel* mj_loadModel(const char* filename, const mjVFS* vfs) {
info[28], info[29], info[30], info[31], info[32], info[33], info[34], info[28], info[29], info[30], info[31], info[32], info[33], info[34],
info[35], info[36], info[37], info[38], info[39], info[40], info[41], info[35], info[36], info[37], info[38], info[39], info[40], info[41],
info[42], info[43], info[44], info[45], info[46], info[47], info[48], info[42], info[43], info[44], info[45], info[46], info[47], info[48],
info[49], info[50]); info[49], info[50], info[51]);
if (!m || m->nbuffer!=info[getnint()-1]) { if (!m || m->nbuffer!=info[getnint()-1]) {
if (fp) { if (fp) {
fclose(fp); fclose(fp);
@@ -1361,7 +1362,8 @@ const char* mj_validateReferences(const mjModel* m) {
X(light_bodyid, nlight, nbody , 0 ) \ X(light_bodyid, nlight, nbody , 0 ) \
X(light_targetbodyid, nlight, nbody , 0 ) \ X(light_targetbodyid, nlight, nbody , 0 ) \
X(mesh_vertadr, nmesh, nmeshvert , m->mesh_vertnum ) \ X(mesh_vertadr, nmesh, nmeshvert , m->mesh_vertnum ) \
X(mesh_texcoordadr, nmesh, nmeshtexvert , 0 ) \ X(mesh_normaladr, nmesh, nmeshnormal , m->mesh_normalnum ) \
X(mesh_texcoordadr, nmesh, nmeshtexcoord, m->mesh_texcoordnum ) \
X(mesh_faceadr, nmesh, nmeshface , m->mesh_facenum ) \ X(mesh_faceadr, nmesh, nmeshface , m->mesh_facenum ) \
X(mesh_graphadr, nmesh, nmeshgraph , 0 ) \ X(mesh_graphadr, nmesh, nmeshgraph , 0 ) \
X(skin_matid, nskin, nmat , 0 ) \ X(skin_matid, nskin, nmat , 0 ) \
+2 -2
View File
@@ -49,8 +49,8 @@ void mj_defaultStatistic(mjStatistic* stat);
// allocate mjModel // allocate mjModel
mjModel* mj_makeModel(int nq, int nv, int nu, int na, int nbody, int njnt, mjModel* mj_makeModel(int nq, int nv, int nu, int na, int nbody, int njnt,
int ngeom, int nsite, int ncam, int nlight, int ngeom, int nsite, int ncam, int nlight,
int nmesh, int nmeshvert, int nmeshtexvert, int nmeshface, int nmeshgraph, int nmesh, int nmeshvert, int nmeshnormal, int nmeshtexcoord, int nmeshface,
int nskin, int nskinvert, int nskintexvert, int nskinface, int nmeshgraph, int nskin, int nskinvert, int nskintexvert, int nskinface,
int nskinbone, int nskinbonevert, int nhfield, int nhfielddata, int nskinbone, int nskinbonevert, int nhfield, int nhfielddata,
int ntex, int ntexdata, int nmat, int npair, int nexclude, int ntex, int ntexdata, int nmat, int npair, int nexclude,
int neq, int ntendon, int nwrap, int nsensor, int neq, int ntendon, int nwrap, int nsensor,
+8 -7
View File
@@ -207,7 +207,7 @@ static void makeMesh(const mjModel* m, mjrContext* con) {
// (re) upload mesh to GPU // (re) upload mesh to GPU
void mjr_uploadMesh(const mjModel* m, const mjrContext* con, int meshid) { void mjr_uploadMesh(const mjModel* m, const mjrContext* con, int meshid) {
int vertadr, numvert, numface, texcoordadr; int vertadr, numvert, normaladr, numface, texcoordadr;
float normal[3], *v1, *v2, *v3, *n1, *n2, *n3, *t1, *t2, *t3; float normal[3], *v1, *v2, *v3, *n1, *n2, *n3, *t1, *t2, *t3;
// check index // check index
@@ -220,6 +220,7 @@ void mjr_uploadMesh(const mjModel* m, const mjrContext* con, int meshid) {
// get vertex and texcoord address for this mesh // get vertex and texcoord address for this mesh
vertadr = m->mesh_vertadr[meshid]; vertadr = m->mesh_vertadr[meshid];
normaladr = m->mesh_normaladr[meshid];
texcoordadr = m->mesh_texcoordadr[meshid]; texcoordadr = m->mesh_texcoordadr[meshid];
// render original mesh // render original mesh
@@ -234,15 +235,15 @@ void mjr_uploadMesh(const mjModel* m, const mjrContext* con, int meshid) {
v3 = m->mesh_vert + 3*(m->mesh_face[3*face+2] + vertadr); v3 = m->mesh_vert + 3*(m->mesh_face[3*face+2] + vertadr);
// compute normal addresses // compute normal addresses
n1 = m->mesh_normal + 3*(m->mesh_face[3*face] + vertadr); n1 = m->mesh_normal + 3*(m->mesh_facenormal[3*face] + normaladr);
n2 = m->mesh_normal + 3*(m->mesh_face[3*face+1] + vertadr); n2 = m->mesh_normal + 3*(m->mesh_facenormal[3*face+1] + normaladr);
n3 = m->mesh_normal + 3*(m->mesh_face[3*face+2] + vertadr); n3 = m->mesh_normal + 3*(m->mesh_facenormal[3*face+2] + normaladr);
// compute texcoord addresses // compute texcoord addresses
if (texcoordadr>=0) { if (texcoordadr>=0) {
t1 = m->mesh_texcoord + 2*(m->mesh_face[3*face] + texcoordadr); t1 = m->mesh_texcoord + 2*(m->mesh_facetexcoord[3*face] + texcoordadr);
t2 = m->mesh_texcoord + 2*(m->mesh_face[3*face+1] + texcoordadr); t2 = m->mesh_texcoord + 2*(m->mesh_facetexcoord[3*face+1] + texcoordadr);
t3 = m->mesh_texcoord + 2*(m->mesh_face[3*face+2] + texcoordadr); t3 = m->mesh_texcoord + 2*(m->mesh_facetexcoord[3*face+2] + texcoordadr);
} else { } else {
t1 = t2 = t3 = NULL; t1 = t2 = t3 = NULL;
} }
+146 -124
View File
@@ -95,6 +95,8 @@ mjCMesh::mjCMesh(mjCModel* _model, mjCDef* _def) {
usernormal.clear(); usernormal.clear();
usertexcoord.clear(); usertexcoord.clear();
userface.clear(); userface.clear();
userfacenormal.clear();
userfacetexcoord.clear();
useredge.clear(); useredge.clear();
// clear internal variables // clear internal variables
@@ -106,12 +108,16 @@ mjCMesh::mjCMesh(mjCModel* _model, mjCDef* _def) {
mjuu_setvec(boxsz_volume, 0, 0, 0); mjuu_setvec(boxsz_volume, 0, 0, 0);
mjuu_setvec(aabb, 0, 0, 0); mjuu_setvec(aabb, 0, 0, 0);
nvert = 0; nvert = 0;
nnormal = 0;
ntexcoord = 0;
nface = 0; nface = 0;
szgraph = 0; szgraph = 0;
vert = NULL; vert = NULL;
normal = NULL; normal = NULL;
texcoord = NULL; texcoord = NULL;
face = NULL; face = NULL;
facenormal = NULL;
facetexcoord = NULL;
graph = NULL; graph = NULL;
needhull = false; needhull = false;
invalidorientation.first = -1; invalidorientation.first = -1;
@@ -141,17 +147,37 @@ mjCMesh::~mjCMesh() {
usernormal.clear(); usernormal.clear();
usertexcoord.clear(); usertexcoord.clear();
userface.clear(); userface.clear();
userfacenormal.clear();
userfacetexcoord.clear();
useredge.clear(); useredge.clear();
if (vert) mju_free(vert); if (vert) mju_free(vert);
if (normal) mju_free(normal); if (normal) mju_free(normal);
if (texcoord) mju_free(texcoord); if (texcoord) mju_free(texcoord);
if (face) mju_free(face); if (face) mju_free(face);
if (facenormal) mju_free(facenormal);
if (facetexcoord) mju_free(facetexcoord);
if (graph) mju_free(graph); if (graph) mju_free(graph);
} }
template <typename T> static T* VecToArray(std::vector<T>& vector, bool clear = true){
if (vector.empty())
return nullptr;
else {
int n = (int)vector.size();
T* cvec = (T*) mju_malloc(n*sizeof(T));
memcpy(cvec, vector.data(), n*sizeof(T));
if (clear) {
vector.clear();
}
return cvec;
}
}
// compiler // compiler
void mjCMesh::Compile(const mjVFS* vfs) { void mjCMesh::Compile(const mjVFS* vfs) {
// load file // load file
@@ -191,8 +217,7 @@ void mjCMesh::Compile(const mjVFS* vfs) {
// copy from user // copy from user
nvert = (int)uservert.size()/3; nvert = (int)uservert.size()/3;
vert = (float*) mju_malloc(3*nvert*sizeof(float)); vert = VecToArray(uservert, !file.empty());
memcpy(vert, uservert.data(), 3*nvert*sizeof(float));
} }
// copy user normal // copy user normal
@@ -203,13 +228,13 @@ void mjCMesh::Compile(const mjVFS* vfs) {
} }
// check size // check size
if (usernormal.size()!=3*nvert) { if (usernormal.size()%3) {
throw mjCError(this, "vertex and normal data incompatible size"); throw mjCError(this, "normal data must be a multiple of 3");
} }
// copy from user // copy from user
normal = (float*) mju_malloc(3*nvert*sizeof(float)); nnormal = (int)usernormal.size()/3;
memcpy(normal, usernormal.data(), 3*nvert*sizeof(float)); normal = VecToArray(usernormal, !file.empty());
} }
// copy user texcoord // copy user texcoord
@@ -220,13 +245,13 @@ void mjCMesh::Compile(const mjVFS* vfs) {
} }
// check size // check size
if (usertexcoord.size()!=2*nvert) { if (usertexcoord.size()%2) {
throw mjCError(this, "vertex and texcoord data incompatible size"); throw mjCError(this, "texcoord must be a multiple of 2");
} }
// copy from user // copy from user
texcoord = (float*) mju_malloc(2*nvert*sizeof(float)); ntexcoord = (int)usertexcoord.size()/2;
memcpy(texcoord, usertexcoord.data(), 2*nvert*sizeof(float)); texcoord = VecToArray(usertexcoord, !file.empty());
} }
// copy user face // copy user face
@@ -243,8 +268,7 @@ void mjCMesh::Compile(const mjVFS* vfs) {
// copy from user // copy from user
nface = (int)userface.size()/3; nface = (int)userface.size()/3;
face = (int*) mju_malloc(3*nface*sizeof(int)); face = VecToArray(userface, !file.empty());
memcpy(face, userface.data(), 3*nface*sizeof(int));
// check vertices exist // check vertices exist
for (auto vertex_index : userface) { for (auto vertex_index : userface) {
@@ -301,6 +325,36 @@ void mjCMesh::Compile(const mjVFS* vfs) {
MakeNormal(); MakeNormal();
} }
// copy user normal indices
if (!userfacenormal.empty()) {
// check repeated
if (facenormal) {
throw mjCError(this, "repeated facenormal specification");
}
if (userfacenormal.size()!=3*nface) {
throw mjCError(this, "face data must have the same size as face normal data");
}
facenormal = VecToArray(userfacenormal, !file.empty());
}
// copy user texcoord
if (!userfacetexcoord.empty()) {
// check repeated
if (facetexcoord) {
throw mjCError(this, "repeated facetexcoord specification");
}
facetexcoord = VecToArray(userfacetexcoord, !file.empty());
}
// facenormal might not exist if usernormal was specified
if (!facenormal) {
facenormal = (int*) mju_malloc(3*nface*sizeof(int));
memcpy(facenormal, face, 3*nface*sizeof(int));
}
// scale, center, orient, compute mass and inertia // scale, center, orient, compute mass and inertia
Process(); Process();
processed = true; processed = true;
@@ -543,19 +597,6 @@ void mjCMesh::RemoveRepeated() {
} }
template <typename T> static T* VecToArray(std::vector<T>& vector){
if (vector.empty())
return nullptr;
else {
int n = (int)vector.size();
T* cvec = (T*) mju_malloc(n*sizeof(T));
memcpy(cvec, vector.data(), n*sizeof(T));
vector.clear();
return cvec;
}
}
// load OBJ mesh // load OBJ mesh
void mjCMesh::LoadOBJ(const mjVFS* vfs) { void mjCMesh::LoadOBJ(const mjVFS* vfs) {
@@ -587,105 +628,73 @@ void mjCMesh::LoadOBJ(const mjVFS* vfs) {
throw mjCError(this, "%s", msg.str().c_str()); throw mjCError(this, "%s", msg.str().c_str());
} }
auto attrib = objReader.GetAttrib(); const auto& attrib = objReader.GetAttrib();
uservert = attrib.vertices; // copy from one std::vector to another
usernormal = attrib.normals;
usertexcoord = attrib.texcoords;
if (objReader.GetShapes().empty()) { if (!objReader.GetShapes().empty()) {
uservert = attrib.vertices; // copy from one std::vector to another const auto& mesh = objReader.GetShapes()[0].mesh;
usernormal = attrib.normals;
usertexcoord = attrib.texcoords;
} else {
auto mesh = objReader.GetShapes()[0].mesh;
bool has_normals = !attrib.normals.empty();
bool has_texcoords = !attrib.texcoords.empty();
bool righthand = (scale[0]*scale[1]*scale[2] > 0); bool righthand = (scale[0]*scale[1]*scale[2] > 0);
// iterate over mesh faces // iterate over mesh faces
int index_in_mesh_indices = 0; std::vector<tinyobj::index_t> face_indices;
for (int face = 0; face < mesh.num_face_vertices.size(); face++) { for (int face = 0, idx = 0; idx < mesh.indices.size();) {
if (mesh.num_face_vertices[face] > 4) { int nfacevert = mesh.num_face_vertices[face];
if (nfacevert < 3 || nfacevert > 4) {
throw mjCError( throw mjCError(
this, "only tri or quad meshes are supported for OBJ (file '%s')", this, "only tri or quad meshes are supported for OBJ (file '%s')",
filename.c_str()); filename.c_str());
} }
// add face face_indices.push_back(mesh.indices[idx]);
std::vector<std::array<tinyobj::index_t, 3>> faces; face_indices.push_back(mesh.indices[idx + (righthand==1 ? 1 : 2)]);
tinyobj::index_t v0 = mesh.indices[index_in_mesh_indices]; face_indices.push_back(mesh.indices[idx + (righthand==1 ? 2 : 1)]);
tinyobj::index_t v1 = mesh.indices[index_in_mesh_indices+1];
tinyobj::index_t v2 = mesh.indices[index_in_mesh_indices+2];
std::array<tinyobj::index_t, 3> face1 = {v0, v1, v2};
faces.push_back(face1);
// handle quad: add second triangle with 4th vertex if (nfacevert == 4) {
if (mesh.num_face_vertices[face] == 4) { face_indices.push_back(mesh.indices[idx]);
tinyobj::index_t v3 = mesh.indices[index_in_mesh_indices+3]; face_indices.push_back(mesh.indices[idx + (righthand==1 ? 2 : 3)]);
std::array<tinyobj::index_t, 3> face2 = {v0, v2, v3}; face_indices.push_back(mesh.indices[idx + (righthand==1 ? 3 : 2)]);
faces.push_back(face2);
} }
for (const auto& face_indices : faces) { idx += nfacevert;
int index_of_first_vertex = uservert.size()/3; ++face;
for (auto tinyobj_index : face_indices) { }
// add vertices to uservert
uservert.insert(
uservert.end(),
attrib.vertices.begin() + 3*tinyobj_index.vertex_index,
attrib.vertices.begin() + 3*tinyobj_index.vertex_index + 3);
// for each vertex, add its normal // for each vertex, store index, normal, and texcoord
if (has_normals) { for (const auto& mesh_index : face_indices) {
usernormal.insert( userface.push_back(mesh_index.vertex_index);
usernormal.end(),
attrib.normals.begin() + 3*tinyobj_index.normal_index,
attrib.normals.begin() + 3*tinyobj_index.normal_index + 3);
}
// for each vertex, add two entries to usertexcoord if (!usernormal.empty()) {
if (has_texcoords) { userfacenormal.push_back(mesh_index.normal_index);
usertexcoord.push_back( }
attrib.texcoords[2*tinyobj_index.texcoord_index]);
usertexcoord.push_back( // flip the v coordinate if (!usertexcoord.empty()) {
1-attrib.texcoords[2*tinyobj_index.texcoord_index + 1]); userfacetexcoord.push_back(mesh_index.texcoord_index);
} }
} }
int i0 = index_of_first_vertex;
int i1 = index_of_first_vertex+1; for (int i = 0; i < face_indices.size(); i += 3) {
int i2 = index_of_first_vertex+2; // add edges
const float *v0 = uservert.data() + 3*face_indices[i+0].vertex_index;
// add edges const float *v1 = uservert.data() + 3*face_indices[i+1].vertex_index;
const float *v0 = uservert.data() + 3*i0; const float *v2 = uservert.data() + 3*face_indices[i+2].vertex_index;
const float *v1 = uservert.data() + 3*i1;
const float *v2 = uservert.data() + 3*i2; // only consider edges if the face contribution is significant
mjtNum normal[3]; mjtNum normal[3];
if (_triangle(normal, nullptr, v0, v1, v2)>sqrt(mjMINVAL)) {
// only consider edges if the face contribution is significant useredge.push_back(std::pair(face_indices[i+0].vertex_index, face_indices[i+1].vertex_index));
if (_triangle(normal, nullptr, v0, v1, v2)>sqrt(mjMINVAL)) { useredge.push_back(std::pair(face_indices[i+1].vertex_index, face_indices[i+2].vertex_index));
useredge.push_back(std::pair(face_indices[0].vertex_index, face_indices[1].vertex_index)); useredge.push_back(std::pair(face_indices[i+2].vertex_index, face_indices[i+0].vertex_index));
useredge.push_back(std::pair(face_indices[1].vertex_index, face_indices[2].vertex_index)); } else {
useredge.push_back(std::pair(face_indices[2].vertex_index, face_indices[0].vertex_index)); // TODO(b/255525326)
} else {
// TODO(b/255525326)
}
// add vertex indices (in uservert) to userface
userface.push_back(i0);
if (righthand) {
userface.push_back(i1);
userface.push_back(i2);
} else {
userface.push_back(i2);
userface.push_back(i1);
}
} }
index_in_mesh_indices += mesh.num_face_vertices[face];
} }
} }
nvert = (int)uservert.size()/3; // flip the second texcoord
nface = (int)userface.size()/3; for (int i=1; i<usertexcoord.size()/2; i++) {
vert = VecToArray(uservert); usertexcoord[2*i+1] = 1-usertexcoord[2*i+1];
face = VecToArray(userface); }
normal = VecToArray(usernormal);
texcoord = VecToArray(usertexcoord);
} }
@@ -857,8 +866,8 @@ void mjCMesh::LoadMSH(const mjVFS* vfs) {
// get sizes from header // get sizes from header
nvert = ((int*)buffer)[0]; nvert = ((int*)buffer)[0];
int nnormal = ((int*)buffer)[1]; nnormal = ((int*)buffer)[1];
int ntexcoord = ((int*)buffer)[2]; ntexcoord = ((int*)buffer)[2];
nface = ((int*)buffer)[3]; nface = ((int*)buffer)[3];
// check sizes // check sizes
@@ -899,7 +908,13 @@ void mjCMesh::LoadMSH(const mjVFS* vfs) {
} }
if (nface) { if (nface) {
face = (int*) mju_malloc(3*nface*sizeof(int)); face = (int*) mju_malloc(3*nface*sizeof(int));
facenormal = (int*) mju_malloc(3*nface*sizeof(int));
memcpy(face, fdata, 3*nface*sizeof(int)); memcpy(face, fdata, 3*nface*sizeof(int));
memcpy(facenormal, fdata, 3*nface*sizeof(int));
}
if (nface && texcoord) {
facetexcoord = (int*) mju_malloc(3*nface*sizeof(int));
memcpy(facetexcoord, fdata, 3*nface*sizeof(int));
} }
// rearange face data if left-handed scaling // rearange face data if left-handed scaling
@@ -940,12 +955,9 @@ void mjCMesh::Process() {
// process vertices // process vertices
for (i=0; i<nvert; i++) { for (i=0; i<nvert; i++) {
// positions
vert[3*i] -= rp[0]; vert[3*i] -= rp[0];
vert[3*i+1] -= rp[1]; vert[3*i+1] -= rp[1];
vert[3*i+2] -= rp[2]; vert[3*i+2] -= rp[2];
// normals not affected by translation
} }
} }
@@ -959,14 +971,15 @@ void mjCMesh::Process() {
// process vertices // process vertices
for (i=0; i<nvert; i++) { for (i=0; i<nvert; i++) {
// positions
mjtNum p1[3], p0[3] = {vert[3*i], vert[3*i+1], vert[3*i+2]}; mjtNum p1[3], p0[3] = {vert[3*i], vert[3*i+1], vert[3*i+2]};
mju_rotVecMatT(p1, p0, mat); mju_rotVecMatT(p1, p0, mat);
vert[3*i] = (float) p1[0]; vert[3*i] = (float) p1[0];
vert[3*i+1] = (float) p1[1]; vert[3*i+1] = (float) p1[1];
vert[3*i+2] = (float) p1[2]; vert[3*i+2] = (float) p1[2];
}
// normals // process normals
for (i=0; i<nnormal; i++) {
mjtNum n1[3], n0[3] = {normal[3*i], normal[3*i+1], normal[3*i+2]}; mjtNum n1[3], n0[3] = {normal[3*i], normal[3*i+1], normal[3*i+2]};
mju_rotVecMatT(n1, n0, mat); mju_rotVecMatT(n1, n0, mat);
normal[3*i] = (float) n1[0]; normal[3*i] = (float) n1[0];
@@ -978,12 +991,12 @@ void mjCMesh::Process() {
// scale // scale
if (scale[0]!=1 || scale[1]!=1 || scale[2]!=1) { if (scale[0]!=1 || scale[1]!=1 || scale[2]!=1) {
for (i=0; i<nvert; i++) { for (i=0; i<nvert; i++) {
// positions
vert[3*i] *= scale[0]; vert[3*i] *= scale[0];
vert[3*i+1] *= scale[1]; vert[3*i+1] *= scale[1];
vert[3*i+2] *= scale[2]; vert[3*i+2] *= scale[2];
}
// normals for (i=0; i<nnormal; i++) {
normal[3*i] *= scale[0]; normal[3*i] *= scale[0];
normal[3*i+1] *= scale[1]; normal[3*i+1] *= scale[1];
normal[3*i+2] *= scale[2]; normal[3*i+2] *= scale[2];
@@ -991,7 +1004,7 @@ void mjCMesh::Process() {
} }
// normalize normals // normalize normals
for (i=0; i<nvert; i++) { for (i=0; i<nnormal; i++) {
// compute length // compute length
float len = normal[3*i]*normal[3*i] + normal[3*i+1]*normal[3*i+1] + normal[3*i+2]*normal[3*i+2]; float len = normal[3*i]*normal[3*i] + normal[3*i+1]*normal[3*i+1] + normal[3*i+2]*normal[3*i+2];
@@ -1168,9 +1181,11 @@ void mjCMesh::Process() {
for (j=0; j<3; j++) { for (j=0; j<3; j++) {
vert[3*i+j] = (float) res[j]; vert[3*i+j] = (float) res[j];
} }
}
for (i=0; i<nnormal; i++) {
// normals // normals
const double nrm[3] = {normal[3*i], normal[3*i+1], normal[3*i+2]}; const double nrm[3] = {normal[3*i], normal[3*i+1], normal[3*i+2]};
double res[3];
mjuu_mulvecmat(res, nrm, mat); mjuu_mulvecmat(res, nrm, mat);
for (j=0; j<3; j++) { for (j=0; j<3; j++) {
normal[3*i+j] = (float) res[j]; normal[3*i+j] = (float) res[j];
@@ -1452,8 +1467,14 @@ void mjCMesh::MakeNormal(void) {
} }
// allocate and clear normals // allocate and clear normals
normal = (float*) mju_malloc(3*nvert*sizeof(float)); nnormal = nvert;
memset(normal, 0, 3*nvert*sizeof(float)); normal = (float*) mju_malloc(3*nnormal*sizeof(float));
memset(normal, 0, 3*nnormal*sizeof(float));
if (!facenormal) {
facenormal = (int*) mju_malloc(3*nface*sizeof(int));
memset(facenormal, 0, 3*nface*sizeof(int));
}
// loop over faces, accumulate vertex normals // loop over faces, accumulate vertex normals
for (i=0; i<nface; i++) { for (i=0; i<nface; i++) {
@@ -1480,14 +1501,15 @@ void mjCMesh::MakeNormal(void) {
for (k=0; k<3; k++) { for (k=0; k<3; k++) {
normal[3*vertid[j]+k] += nrm[k]*area; normal[3*vertid[j]+k] += nrm[k]*area;
} }
facenormal[3*i+j] = vertid[j];
} }
} }
// remove large-angle faces // remove large-angle faces
if (!smoothnormal) { if (!smoothnormal) {
// allocate removal and clear // allocate removal and clear
float* nremove = (float*) mju_malloc(3*nvert*sizeof(float)); float* nremove = (float*) mju_malloc(3*nnormal*sizeof(float));
memset(nremove, 0, 3*nvert*sizeof(float)); memset(nremove, 0, 3*nnormal*sizeof(float));
// remove contributions from faces at large angles with vertex normal // remove contributions from faces at large angles with vertex normal
for (i=0; i<nface; i++) { for (i=0; i<nface; i++) {
@@ -1525,14 +1547,14 @@ void mjCMesh::MakeNormal(void) {
} }
// apply removal, free nremove // apply removal, free nremove
for (i=0; i<3*nvert; i++) { for (i=0; i<3*nnormal; i++) {
normal[i] -= nremove[i]; normal[i] -= nremove[i];
} }
mju_free(nremove); mju_free(nremove);
} }
// normalize normals // normalize normals
for (i=0; i<nvert; i++) { for (i=0; i<nnormal; i++) {
// compute length // compute length
float len = sqrtf(normal[3*i]*normal[3*i] + float len = sqrtf(normal[3*i]*normal[3*i] +
normal[3*i+1]*normal[3*i+1] + normal[3*i+1]*normal[3*i+1] +
+19 -8
View File
@@ -258,7 +258,8 @@ void mjCModel::Clear(void) {
nu = 0; nu = 0;
na = 0; na = 0;
nmeshvert = 0; nmeshvert = 0;
nmeshtexvert = 0; nmeshnormal = 0;
nmeshtexcoord = 0;
nmeshface = 0; nmeshface = 0;
nmeshgraph = 0; nmeshgraph = 0;
nskinvert = 0; nskinvert = 0;
@@ -933,11 +934,12 @@ void mjCModel::SetSizes(void) {
} }
} }
// nmeshvert, nmeshface, nmeshtexvert, nmeshgraph // nmeshvert, nmeshface, nmeshtexcoord, nmeshgraph
for (i=0; i<nmesh; i++) { for (i=0; i<nmesh; i++) {
nmeshvert += meshes[i]->nvert; nmeshvert += meshes[i]->nvert;
nmeshnormal += meshes[i]->nnormal;
nmeshface += meshes[i]->nface; nmeshface += meshes[i]->nface;
nmeshtexvert += (meshes[i]->texcoord ? meshes[i]->nvert : 0); nmeshtexcoord += (meshes[i]->texcoord ? meshes[i]->ntexcoord : 0);
nmeshgraph += meshes[i]->szgraph; nmeshgraph += meshes[i]->szgraph;
} }
@@ -1645,7 +1647,7 @@ void mjCModel::CopyTree(mjModel* m) {
// copy objects outside kinematic tree // copy objects outside kinematic tree
void mjCModel::CopyObjects(mjModel* m) { void mjCModel::CopyObjects(mjModel* m) {
int i, j, adr, bone_adr, vert_adr, face_adr, texcoord_adr; int i, j, adr, bone_adr, vert_adr, normal_adr, face_adr, texcoord_adr;
int bonevert_adr, graph_adr, data_adr; int bonevert_adr, graph_adr, data_adr;
// sizes outside call to mj_makeModel // sizes outside call to mj_makeModel
@@ -1657,6 +1659,7 @@ void mjCModel::CopyObjects(mjModel* m) {
// meshes // meshes
vert_adr = 0; vert_adr = 0;
normal_adr = 0;
texcoord_adr = 0; texcoord_adr = 0;
face_adr = 0; face_adr = 0;
graph_adr = 0; graph_adr = 0;
@@ -1667,17 +1670,24 @@ void mjCModel::CopyObjects(mjModel* m) {
// set fields // set fields
m->mesh_vertadr[i] = vert_adr; m->mesh_vertadr[i] = vert_adr;
m->mesh_vertnum[i] = pme->nvert; m->mesh_vertnum[i] = pme->nvert;
m->mesh_normaladr[i] = normal_adr;
m->mesh_normalnum[i] = pme->nnormal;
m->mesh_texcoordadr[i] = (pme->texcoord ? texcoord_adr : -1); m->mesh_texcoordadr[i] = (pme->texcoord ? texcoord_adr : -1);
m->mesh_texcoordnum[i] = pme->ntexcoord;
m->mesh_faceadr[i] = face_adr; m->mesh_faceadr[i] = face_adr;
m->mesh_facenum[i] = pme->nface; m->mesh_facenum[i] = pme->nface;
m->mesh_graphadr[i] = (pme->szgraph ? graph_adr : -1); m->mesh_graphadr[i] = (pme->szgraph ? graph_adr : -1);
// copy vertices, normals, faces, texcoords, aux data // copy vertices, normals, faces, texcoords, aux data
memcpy(m->mesh_vert + 3*vert_adr, pme->vert, 3*pme->nvert*sizeof(float)); memcpy(m->mesh_vert + 3*vert_adr, pme->vert, 3*pme->nvert*sizeof(float));
memcpy(m->mesh_normal + 3*vert_adr, pme->normal, 3*pme->nvert*sizeof(float)); memcpy(m->mesh_normal + 3*normal_adr, pme->normal, 3*pme->nnormal*sizeof(float));
memcpy(m->mesh_face + 3*face_adr, pme->face, 3*pme->nface*sizeof(float)); memcpy(m->mesh_face + 3*face_adr, pme->face, 3*pme->nface*sizeof(float));
memcpy(m->mesh_facenormal + 3*face_adr, pme->facenormal, 3*pme->nface*sizeof(int));
if (pme->texcoord) { if (pme->texcoord) {
memcpy(m->mesh_texcoord + 2*texcoord_adr, pme->texcoord, 2*pme->nvert*sizeof(float)); memcpy(m->mesh_texcoord + 2*texcoord_adr, pme->texcoord, 2*pme->ntexcoord*sizeof(float));
memcpy(m->mesh_facetexcoord + 3*face_adr, pme->facetexcoord, 3*pme->nface*sizeof(int));
} else {
memset(m->mesh_facetexcoord + 3*face_adr, 0, 3*pme->nface*sizeof(int));
} }
if (pme->szgraph) { if (pme->szgraph) {
memcpy(m->mesh_graph + graph_adr, pme->graph, pme->szgraph*sizeof(int)); memcpy(m->mesh_graph + graph_adr, pme->graph, pme->szgraph*sizeof(int));
@@ -1685,7 +1695,8 @@ void mjCModel::CopyObjects(mjModel* m) {
// advance counters // advance counters
vert_adr += pme->nvert; vert_adr += pme->nvert;
texcoord_adr += (pme->texcoord ? pme->nvert : 0); normal_adr += pme->nnormal;
texcoord_adr += (pme->texcoord ? pme->ntexcoord : 0);
face_adr += pme->nface; face_adr += pme->nface;
graph_adr += pme->szgraph; graph_adr += pme->szgraph;
} }
@@ -2619,7 +2630,7 @@ void mjCModel::TryCompile(mjModel*& m, mjData*& d, const mjVFS* vfs) {
// create low-level model // create low-level model
m = mj_makeModel(nq, nv, nu, na, nbody, njnt, ngeom, nsite, ncam, nlight, m = mj_makeModel(nq, nv, nu, na, nbody, njnt, ngeom, nsite, ncam, nlight,
nmesh, nmeshvert, nmeshtexvert, nmeshface, nmeshgraph, nmesh, nmeshvert, nmeshnormal, nmeshtexcoord, nmeshface, nmeshgraph,
nskin, nskinvert, nskintexvert, nskinface, nskinbone, nskinbonevert, nskin, nskinvert, nskintexvert, nskinface, nskinbone, nskinbonevert,
nhfield, nhfielddata, ntex, ntexdata, nmat, npair, nexclude, nhfield, nhfielddata, ntex, ntexdata, nmat, npair, nexclude,
neq, ntendon, nwrap, nsensor, neq, ntendon, nwrap, nsensor,
+2 -1
View File
@@ -216,7 +216,8 @@ class mjCModel {
int nu; // number of actuators/controls int nu; // number of actuators/controls
int na; // number of activation variables int na; // number of activation variables
int nmeshvert; // number of vertices in all meshes int nmeshvert; // number of vertices in all meshes
int nmeshtexvert; // number of texture coordinates in all meshes int nmeshnormal; // number of normals in all meshes
int nmeshtexcoord; // number of texture coordinates in all meshes
int nmeshface; // number of triangular faces in all meshes int nmeshface; // number of triangular faces in all meshes
int nmeshgraph; // number of shorts in mesh auxiliary data int nmeshgraph; // number of shorts in mesh auxiliary data
int nskinvert; // number of vertices in all skins int nskinvert; // number of vertices in all skins
+9 -3
View File
@@ -476,7 +476,9 @@ class mjCMesh: public mjCBase {
std::vector<float> uservert; // user vertex data std::vector<float> uservert; // user vertex data
std::vector<float> usernormal; // user normal data std::vector<float> usernormal; // user normal data
std::vector<float> usertexcoord; // user texcoord data std::vector<float> usertexcoord; // user texcoord data
std::vector<int> userface; // user face data std::vector<int> userface; // user vertex indices
std::vector<int> userfacenormal; // user normal indices
std::vector<int> userfacetexcoord; // user texcoord indices
std::vector< std::pair<int, int> > useredge; // user half-edge data std::vector< std::pair<int, int> > useredge; // user half-edge data
private: private:
@@ -514,12 +516,16 @@ class mjCMesh: public mjCBase {
// mesh data to be copied into mjModel // mesh data to be copied into mjModel
int nvert; // number of vertices int nvert; // number of vertices
int nnormal; // number of normals
int ntexcoord; // number of texcoords
int nface; // number of faces int nface; // number of faces
int szgraph; // size of graph data in ints int szgraph; // size of graph data in ints
float* vert; // vertex data (3*nvert), relative to (pos, quat) float* vert; // vertex data (3*nvert), relative to (pos, quat)
float* normal; // vertex normal data (3*nvert) float* normal; // vertex normal data (3*nnormal)
float* texcoord; // vertex texcoord data (2*nvert, or NULL) float* texcoord; // vertex texcoord data (2*ntexcoord or NULL)
int* face; // face vertex indices (3*nface) int* face; // face vertex indices (3*nface)
int* facenormal; // face normal indices (3*nface)
int* facetexcoord; // face texcoord indices (3*nface)
int* graph; // convex graph data int* graph; // convex graph data
bool needhull; // needs convex hull for collisions bool needhull; // needs convex hull for collisions
BIN
View File
Binary file not shown.
+8
View File
@@ -0,0 +1,8 @@
<mujoco>
<asset>
<mesh file="cube.msh"/>
</asset>
<worldbody>
<geom type="mesh" mesh="cube"/>
</worldbody>
</mujoco>
+35 -1
View File
@@ -32,6 +32,8 @@ namespace {
using MjCMeshTest = MujocoTest; using MjCMeshTest = MujocoTest;
static const char* const kMeshPath =
"user/testdata/mesh.xml";
static const char* const kDuplicateVerticesPath = static const char* const kDuplicateVerticesPath =
"user/testdata/duplicate_vertices.xml"; "user/testdata/duplicate_vertices.xml";
static const char* const kCubePath = static const char* const kCubePath =
@@ -100,6 +102,17 @@ TEST_F(MjCMeshTest, DeDuplicateSTLVertices) {
mj_deleteModel(model); mj_deleteModel(model);
} }
// ------------- test Mesh loading (MSH) ------------------------------
TEST_F(MjCMeshTest, LoadMSH) {
const std::string xml_path = GetTestDataFilePath(kMeshPath);
char error[1024];
size_t error_sz = 1024;
mjModel* model = mj_loadXML(xml_path.c_str(), 0, error, error_sz);
ASSERT_THAT(model, testing::NotNull()) << error;
ASSERT_EQ(model->nmeshvert, 36);
mj_deleteModel(model);
}
// ------------- test OBJ loading ---------------------------------------------- // ------------- test OBJ loading ----------------------------------------------
using MjCMeshTest = MujocoTest; using MjCMeshTest = MujocoTest;
@@ -109,6 +122,7 @@ TEST_F(MjCMeshTest, LoadCube) {
mjModel* model = mj_loadXML(xml_path.c_str(), 0, nullptr, 0); mjModel* model = mj_loadXML(xml_path.c_str(), 0, nullptr, 0);
ASSERT_GT(model->ngeom, 0); ASSERT_GT(model->ngeom, 0);
ASSERT_EQ(model->nmeshvert, 8); ASSERT_EQ(model->nmeshvert, 8);
ASSERT_EQ(model->nmeshface, 12);
mj_deleteModel(model); mj_deleteModel(model);
} }
@@ -146,7 +160,7 @@ TEST_F(MjCMeshTest, KeepDuplicateOBJVertices) {
char error[1024]; char error[1024];
size_t error_sz = 1024; size_t error_sz = 1024;
mjModel* model = mj_loadXML(xml_path.c_str(), 0, error, error_sz); mjModel* model = mj_loadXML(xml_path.c_str(), 0, error, error_sz);
ASSERT_EQ(model->nmeshvert, 12); ASSERT_EQ(model->nmeshvert, 16);
mj_deleteModel(model); mj_deleteModel(model);
} }
@@ -175,6 +189,26 @@ TEST_F(MjCMeshTest, TinyMeshLoads) {
mj_deleteModel(model); mj_deleteModel(model);
} }
// ------------- test inline loading ------------------------------------------
TEST_F(MjCMeshTest, FaceNormalAutogenerated) {
static constexpr char xml[] = R"(
<mujoco>
<asset>
<mesh name="example_mesh"
vertex="0 0 0 1 0 0 0 1 0 0 0 1"
normal="1 0 0 0 1 0 0 0 1 0.707 0 0.707"
face="0 2 1 0 3 2" />
</asset>
<worldbody>
<geom type="mesh" mesh="example_mesh"/>
</worldbody>
</mujoco>
)";
mjModel* model = LoadModelFromString(xml, 0, 0);
ASSERT_THAT(model, testing::NotNull());
mj_deleteModel(model);
}
// ------------- test inertia ------------------------------------------------- // ------------- test inertia -------------------------------------------------
TEST_F(MjCMeshTest, SmallInertiaLoads) { TEST_F(MjCMeshTest, SmallInertiaLoads) {
+8 -2
View File
@@ -1874,7 +1874,8 @@ public unsafe struct mjModel_ {
public int nlight; public int nlight;
public int nmesh; public int nmesh;
public int nmeshvert; public int nmeshvert;
public int nmeshtexvert; public int nmeshnormal;
public int nmeshtexcoord;
public int nmeshface; public int nmeshface;
public int nmeshgraph; public int nmeshgraph;
public int nskin; public int nskin;
@@ -2042,14 +2043,19 @@ public unsafe struct mjModel_ {
public float* light_specular; public float* light_specular;
public int* mesh_vertadr; public int* mesh_vertadr;
public int* mesh_vertnum; public int* mesh_vertnum;
public int* mesh_texcoordadr;
public int* mesh_faceadr; public int* mesh_faceadr;
public int* mesh_facenum; public int* mesh_facenum;
public int* mesh_normaladr;
public int* mesh_normalnum;
public int* mesh_texcoordadr;
public int* mesh_texcoordnum;
public int* mesh_graphadr; public int* mesh_graphadr;
public float* mesh_vert; public float* mesh_vert;
public float* mesh_normal; public float* mesh_normal;
public float* mesh_texcoord; public float* mesh_texcoord;
public int* mesh_face; public int* mesh_face;
public int* mesh_facenormal;
public int* mesh_facetexcoord;
public int* mesh_graph; public int* mesh_graph;
public int* skin_matid; public int* skin_matid;
public int* skin_group; public int* skin_group;