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
nmeshvert 0
nmeshface 0
nmeshtexvert 0
nmeshtexcoord 0
nmeshgraph 0
nskin 0
nskinvert 0
+2
View File
@@ -10,6 +10,8 @@ General
- 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.
- 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
^^^^^^^^^^^^^^^
+11 -5
View File
@@ -746,7 +746,8 @@ struct mjModel_ {
int nlight; // number of lights
int nmesh; // number of 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 nmeshgraph; // number of ints in mesh auxiliary data
int nskin; // number of skins
@@ -942,14 +943,19 @@ struct mjModel_ {
// meshes
int* mesh_vertadr; // first vertex address (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_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)
float* mesh_vert; // vertex positions for all meshes (nmeshvert x 3)
float* mesh_normal; // vertex normals for all meshes (nmeshvert x 3)
float* mesh_texcoord; // vertex texcoords for all meshes (nmeshtexvert x 2)
int* mesh_face; // triangle face data (nmeshface x 3)
float* mesh_normal; // normals for all meshes (nmeshnormal x 3)
float* mesh_texcoord; // vertex texcoords for all meshes (nmeshtexcoord x 2)
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)
// skins
+11 -5
View File
@@ -542,7 +542,8 @@ struct mjModel_ {
int nlight; // number of lights
int nmesh; // number of 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 nmeshgraph; // number of ints in mesh auxiliary data
int nskin; // number of skins
@@ -738,14 +739,19 @@ struct mjModel_ {
// meshes
int* mesh_vertadr; // first vertex address (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_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)
float* mesh_vert; // vertex positions for all meshes (nmeshvert x 3)
float* mesh_normal; // vertex normals for all meshes (nmeshvert x 3)
float* mesh_texcoord; // vertex texcoords for all meshes (nmeshtexvert x 2)
int* mesh_face; // triangle face data (nmeshface x 3)
float* mesh_normal; // normals for all meshes (nmeshnormal x 3)
float* mesh_texcoord; // vertex texcoords for all meshes (nmeshtexcoord x 2)
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)
// skins
+9 -3
View File
@@ -74,7 +74,8 @@
X( nlight ) \
X( nmesh ) \
X( nmeshvert ) \
X( nmeshtexvert ) \
X( nmeshnormal ) \
X( nmeshtexcoord ) \
X( nmeshface ) \
X( nmeshgraph ) \
X( nskin ) \
@@ -263,14 +264,19 @@
X( float, light_specular, nlight, 3 ) \
X( int, mesh_vertadr, 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_texcoordnum, nmesh, 1 ) \
X( int, mesh_faceadr, nmesh, 1 ) \
X( int, mesh_facenum, nmesh, 1 ) \
X( int, mesh_graphadr, nmesh, 1 ) \
X( float, mesh_vert, nmeshvert, 3 ) \
X( float, mesh_normal, nmeshvert, 3 ) \
X( float, mesh_texcoord, nmeshtexvert, 2 ) \
X( float, mesh_normal, nmeshnormal, 3 ) \
X( float, mesh_texcoord, nmeshtexcoord, 2 ) \
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, skin_matid, 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
mjModel* mj_makeModel(int nq, int nv, int nu, int na, int nbody, int njnt,
int ngeom, int nsite, int ncam, int nlight,
int nmesh, int nmeshvert, int nmeshtexvert, int nmeshface, int nmeshgraph,
int nskin, int nskinvert, int nskintexvert, int nskinface,
int nmesh, int nmeshvert, int nmeshnormal, int nmeshtexcoord, int nmeshface,
int nmeshgraph, int nskin, int nskinvert, int nskintexvert, int nskinface,
int nskinbone, int nskinbonevert, int nhfield, int nhfielddata,
int ntex, int ntexdata, int nmat, int npair, int nexclude,
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->nmesh = nmesh;
m->nmeshvert = nmeshvert;
m->nmeshtexvert = nmeshtexvert;
m->nmeshnormal = nmeshnormal;
m->nmeshtexcoord = nmeshtexcoord;
m->nmeshface = nmeshface;
m->nmeshgraph = nmeshgraph;
m->nskin = nskin;
@@ -512,7 +513,7 @@ mjModel* mj_copyModel(mjModel* dest, const mjModel* src) {
if (!dest) {
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->nmeshtexvert, src->nmeshface, src->nmeshgraph,
src->nmeshnormal, src->nmeshtexcoord, src->nmeshface, src->nmeshgraph,
src->nskin, src->nskinvert, src->nskintexvert, src->nskinface,
src->nskinbone, src->nskinbonevert, src->nhfield, src->nhfielddata,
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[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[49], info[50]);
info[49], info[50], info[51]);
if (!m || m->nbuffer!=info[getnint()-1]) {
if (fp) {
fclose(fp);
@@ -1361,7 +1362,8 @@ const char* mj_validateReferences(const mjModel* m) {
X(light_bodyid, nlight, nbody , 0 ) \
X(light_targetbodyid, nlight, nbody , 0 ) \
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_graphadr, nmesh, nmeshgraph , 0 ) \
X(skin_matid, nskin, nmat , 0 ) \
+2 -2
View File
@@ -49,8 +49,8 @@ void mj_defaultStatistic(mjStatistic* stat);
// allocate mjModel
mjModel* mj_makeModel(int nq, int nv, int nu, int na, int nbody, int njnt,
int ngeom, int nsite, int ncam, int nlight,
int nmesh, int nmeshvert, int nmeshtexvert, int nmeshface, int nmeshgraph,
int nskin, int nskinvert, int nskintexvert, int nskinface,
int nmesh, int nmeshvert, int nmeshnormal, int nmeshtexcoord, int nmeshface,
int nmeshgraph, int nskin, int nskinvert, int nskintexvert, int nskinface,
int nskinbone, int nskinbonevert, int nhfield, int nhfielddata,
int ntex, int ntexdata, int nmat, int npair, int nexclude,
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
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;
// 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
vertadr = m->mesh_vertadr[meshid];
normaladr = m->mesh_normaladr[meshid];
texcoordadr = m->mesh_texcoordadr[meshid];
// 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);
// compute normal addresses
n1 = m->mesh_normal + 3*(m->mesh_face[3*face] + vertadr);
n2 = m->mesh_normal + 3*(m->mesh_face[3*face+1] + vertadr);
n3 = m->mesh_normal + 3*(m->mesh_face[3*face+2] + vertadr);
n1 = m->mesh_normal + 3*(m->mesh_facenormal[3*face] + normaladr);
n2 = m->mesh_normal + 3*(m->mesh_facenormal[3*face+1] + normaladr);
n3 = m->mesh_normal + 3*(m->mesh_facenormal[3*face+2] + normaladr);
// compute texcoord addresses
if (texcoordadr>=0) {
t1 = m->mesh_texcoord + 2*(m->mesh_face[3*face] + texcoordadr);
t2 = m->mesh_texcoord + 2*(m->mesh_face[3*face+1] + texcoordadr);
t3 = m->mesh_texcoord + 2*(m->mesh_face[3*face+2] + texcoordadr);
t1 = m->mesh_texcoord + 2*(m->mesh_facetexcoord[3*face] + texcoordadr);
t2 = m->mesh_texcoord + 2*(m->mesh_facetexcoord[3*face+1] + texcoordadr);
t3 = m->mesh_texcoord + 2*(m->mesh_facetexcoord[3*face+2] + texcoordadr);
} else {
t1 = t2 = t3 = NULL;
}
+146 -124
View File
@@ -95,6 +95,8 @@ mjCMesh::mjCMesh(mjCModel* _model, mjCDef* _def) {
usernormal.clear();
usertexcoord.clear();
userface.clear();
userfacenormal.clear();
userfacetexcoord.clear();
useredge.clear();
// clear internal variables
@@ -106,12 +108,16 @@ mjCMesh::mjCMesh(mjCModel* _model, mjCDef* _def) {
mjuu_setvec(boxsz_volume, 0, 0, 0);
mjuu_setvec(aabb, 0, 0, 0);
nvert = 0;
nnormal = 0;
ntexcoord = 0;
nface = 0;
szgraph = 0;
vert = NULL;
normal = NULL;
texcoord = NULL;
face = NULL;
facenormal = NULL;
facetexcoord = NULL;
graph = NULL;
needhull = false;
invalidorientation.first = -1;
@@ -141,17 +147,37 @@ mjCMesh::~mjCMesh() {
usernormal.clear();
usertexcoord.clear();
userface.clear();
userfacenormal.clear();
userfacetexcoord.clear();
useredge.clear();
if (vert) mju_free(vert);
if (normal) mju_free(normal);
if (texcoord) mju_free(texcoord);
if (face) mju_free(face);
if (facenormal) mju_free(facenormal);
if (facetexcoord) mju_free(facetexcoord);
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
void mjCMesh::Compile(const mjVFS* vfs) {
// load file
@@ -191,8 +217,7 @@ void mjCMesh::Compile(const mjVFS* vfs) {
// copy from user
nvert = (int)uservert.size()/3;
vert = (float*) mju_malloc(3*nvert*sizeof(float));
memcpy(vert, uservert.data(), 3*nvert*sizeof(float));
vert = VecToArray(uservert, !file.empty());
}
// copy user normal
@@ -203,13 +228,13 @@ void mjCMesh::Compile(const mjVFS* vfs) {
}
// check size
if (usernormal.size()!=3*nvert) {
throw mjCError(this, "vertex and normal data incompatible size");
if (usernormal.size()%3) {
throw mjCError(this, "normal data must be a multiple of 3");
}
// copy from user
normal = (float*) mju_malloc(3*nvert*sizeof(float));
memcpy(normal, usernormal.data(), 3*nvert*sizeof(float));
nnormal = (int)usernormal.size()/3;
normal = VecToArray(usernormal, !file.empty());
}
// copy user texcoord
@@ -220,13 +245,13 @@ void mjCMesh::Compile(const mjVFS* vfs) {
}
// check size
if (usertexcoord.size()!=2*nvert) {
throw mjCError(this, "vertex and texcoord data incompatible size");
if (usertexcoord.size()%2) {
throw mjCError(this, "texcoord must be a multiple of 2");
}
// copy from user
texcoord = (float*) mju_malloc(2*nvert*sizeof(float));
memcpy(texcoord, usertexcoord.data(), 2*nvert*sizeof(float));
ntexcoord = (int)usertexcoord.size()/2;
texcoord = VecToArray(usertexcoord, !file.empty());
}
// copy user face
@@ -243,8 +268,7 @@ void mjCMesh::Compile(const mjVFS* vfs) {
// copy from user
nface = (int)userface.size()/3;
face = (int*) mju_malloc(3*nface*sizeof(int));
memcpy(face, userface.data(), 3*nface*sizeof(int));
face = VecToArray(userface, !file.empty());
// check vertices exist
for (auto vertex_index : userface) {
@@ -301,6 +325,36 @@ void mjCMesh::Compile(const mjVFS* vfs) {
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
Process();
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
void mjCMesh::LoadOBJ(const mjVFS* vfs) {
@@ -587,105 +628,73 @@ void mjCMesh::LoadOBJ(const mjVFS* vfs) {
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()) {
uservert = attrib.vertices; // copy from one std::vector to another
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();
if (!objReader.GetShapes().empty()) {
const auto& mesh = objReader.GetShapes()[0].mesh;
bool righthand = (scale[0]*scale[1]*scale[2] > 0);
// iterate over mesh faces
int index_in_mesh_indices = 0;
for (int face = 0; face < mesh.num_face_vertices.size(); face++) {
if (mesh.num_face_vertices[face] > 4) {
std::vector<tinyobj::index_t> face_indices;
for (int face = 0, idx = 0; idx < mesh.indices.size();) {
int nfacevert = mesh.num_face_vertices[face];
if (nfacevert < 3 || nfacevert > 4) {
throw mjCError(
this, "only tri or quad meshes are supported for OBJ (file '%s')",
filename.c_str());
}
// add face
std::vector<std::array<tinyobj::index_t, 3>> faces;
tinyobj::index_t v0 = mesh.indices[index_in_mesh_indices];
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);
face_indices.push_back(mesh.indices[idx]);
face_indices.push_back(mesh.indices[idx + (righthand==1 ? 1 : 2)]);
face_indices.push_back(mesh.indices[idx + (righthand==1 ? 2 : 1)]);
// handle quad: add second triangle with 4th vertex
if (mesh.num_face_vertices[face] == 4) {
tinyobj::index_t v3 = mesh.indices[index_in_mesh_indices+3];
std::array<tinyobj::index_t, 3> face2 = {v0, v2, v3};
faces.push_back(face2);
if (nfacevert == 4) {
face_indices.push_back(mesh.indices[idx]);
face_indices.push_back(mesh.indices[idx + (righthand==1 ? 2 : 3)]);
face_indices.push_back(mesh.indices[idx + (righthand==1 ? 3 : 2)]);
}
for (const auto& face_indices : faces) {
int index_of_first_vertex = uservert.size()/3;
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);
idx += nfacevert;
++face;
}
// for each vertex, add its normal
if (has_normals) {
usernormal.insert(
usernormal.end(),
attrib.normals.begin() + 3*tinyobj_index.normal_index,
attrib.normals.begin() + 3*tinyobj_index.normal_index + 3);
}
// for each vertex, store index, normal, and texcoord
for (const auto& mesh_index : face_indices) {
userface.push_back(mesh_index.vertex_index);
// for each vertex, add two entries to usertexcoord
if (has_texcoords) {
usertexcoord.push_back(
attrib.texcoords[2*tinyobj_index.texcoord_index]);
usertexcoord.push_back( // flip the v coordinate
1-attrib.texcoords[2*tinyobj_index.texcoord_index + 1]);
}
}
int i0 = index_of_first_vertex;
int i1 = index_of_first_vertex+1;
int i2 = index_of_first_vertex+2;
// add edges
const float *v0 = uservert.data() + 3*i0;
const float *v1 = uservert.data() + 3*i1;
const float *v2 = uservert.data() + 3*i2;
mjtNum normal[3];
// only consider edges if the face contribution is significant
if (_triangle(normal, nullptr, v0, v1, v2)>sqrt(mjMINVAL)) {
useredge.push_back(std::pair(face_indices[0].vertex_index, face_indices[1].vertex_index));
useredge.push_back(std::pair(face_indices[1].vertex_index, face_indices[2].vertex_index));
useredge.push_back(std::pair(face_indices[2].vertex_index, face_indices[0].vertex_index));
} 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);
}
if (!usernormal.empty()) {
userfacenormal.push_back(mesh_index.normal_index);
}
if (!usertexcoord.empty()) {
userfacetexcoord.push_back(mesh_index.texcoord_index);
}
}
for (int i = 0; i < face_indices.size(); i += 3) {
// add edges
const float *v0 = uservert.data() + 3*face_indices[i+0].vertex_index;
const float *v1 = uservert.data() + 3*face_indices[i+1].vertex_index;
const float *v2 = uservert.data() + 3*face_indices[i+2].vertex_index;
// only consider edges if the face contribution is significant
mjtNum normal[3];
if (_triangle(normal, nullptr, v0, v1, v2)>sqrt(mjMINVAL)) {
useredge.push_back(std::pair(face_indices[i+0].vertex_index, face_indices[i+1].vertex_index));
useredge.push_back(std::pair(face_indices[i+1].vertex_index, face_indices[i+2].vertex_index));
useredge.push_back(std::pair(face_indices[i+2].vertex_index, face_indices[i+0].vertex_index));
} else {
// TODO(b/255525326)
}
index_in_mesh_indices += mesh.num_face_vertices[face];
}
}
nvert = (int)uservert.size()/3;
nface = (int)userface.size()/3;
vert = VecToArray(uservert);
face = VecToArray(userface);
normal = VecToArray(usernormal);
texcoord = VecToArray(usertexcoord);
// flip the second texcoord
for (int i=1; i<usertexcoord.size()/2; i++) {
usertexcoord[2*i+1] = 1-usertexcoord[2*i+1];
}
}
@@ -857,8 +866,8 @@ void mjCMesh::LoadMSH(const mjVFS* vfs) {
// get sizes from header
nvert = ((int*)buffer)[0];
int nnormal = ((int*)buffer)[1];
int ntexcoord = ((int*)buffer)[2];
nnormal = ((int*)buffer)[1];
ntexcoord = ((int*)buffer)[2];
nface = ((int*)buffer)[3];
// check sizes
@@ -899,7 +908,13 @@ void mjCMesh::LoadMSH(const mjVFS* vfs) {
}
if (nface) {
face = (int*) mju_malloc(3*nface*sizeof(int));
facenormal = (int*) mju_malloc(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
@@ -940,12 +955,9 @@ void mjCMesh::Process() {
// process vertices
for (i=0; i<nvert; i++) {
// positions
vert[3*i] -= rp[0];
vert[3*i+1] -= rp[1];
vert[3*i+2] -= rp[2];
// normals not affected by translation
}
}
@@ -959,14 +971,15 @@ void mjCMesh::Process() {
// process vertices
for (i=0; i<nvert; i++) {
// positions
mjtNum p1[3], p0[3] = {vert[3*i], vert[3*i+1], vert[3*i+2]};
mju_rotVecMatT(p1, p0, mat);
vert[3*i] = (float) p1[0];
vert[3*i+1] = (float) p1[1];
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]};
mju_rotVecMatT(n1, n0, mat);
normal[3*i] = (float) n1[0];
@@ -978,12 +991,12 @@ void mjCMesh::Process() {
// scale
if (scale[0]!=1 || scale[1]!=1 || scale[2]!=1) {
for (i=0; i<nvert; i++) {
// positions
vert[3*i] *= scale[0];
vert[3*i+1] *= scale[1];
vert[3*i+2] *= scale[2];
}
// normals
for (i=0; i<nnormal; i++) {
normal[3*i] *= scale[0];
normal[3*i+1] *= scale[1];
normal[3*i+2] *= scale[2];
@@ -991,7 +1004,7 @@ void mjCMesh::Process() {
}
// normalize normals
for (i=0; i<nvert; i++) {
for (i=0; i<nnormal; i++) {
// 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];
@@ -1168,9 +1181,11 @@ void mjCMesh::Process() {
for (j=0; j<3; j++) {
vert[3*i+j] = (float) res[j];
}
}
for (i=0; i<nnormal; i++) {
// normals
const double nrm[3] = {normal[3*i], normal[3*i+1], normal[3*i+2]};
double res[3];
mjuu_mulvecmat(res, nrm, mat);
for (j=0; j<3; j++) {
normal[3*i+j] = (float) res[j];
@@ -1452,8 +1467,14 @@ void mjCMesh::MakeNormal(void) {
}
// allocate and clear normals
normal = (float*) mju_malloc(3*nvert*sizeof(float));
memset(normal, 0, 3*nvert*sizeof(float));
nnormal = nvert;
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
for (i=0; i<nface; i++) {
@@ -1480,14 +1501,15 @@ void mjCMesh::MakeNormal(void) {
for (k=0; k<3; k++) {
normal[3*vertid[j]+k] += nrm[k]*area;
}
facenormal[3*i+j] = vertid[j];
}
}
// remove large-angle faces
if (!smoothnormal) {
// allocate removal and clear
float* nremove = (float*) mju_malloc(3*nvert*sizeof(float));
memset(nremove, 0, 3*nvert*sizeof(float));
float* nremove = (float*) mju_malloc(3*nnormal*sizeof(float));
memset(nremove, 0, 3*nnormal*sizeof(float));
// remove contributions from faces at large angles with vertex normal
for (i=0; i<nface; i++) {
@@ -1525,14 +1547,14 @@ void mjCMesh::MakeNormal(void) {
}
// apply removal, free nremove
for (i=0; i<3*nvert; i++) {
for (i=0; i<3*nnormal; i++) {
normal[i] -= nremove[i];
}
mju_free(nremove);
}
// normalize normals
for (i=0; i<nvert; i++) {
for (i=0; i<nnormal; i++) {
// compute length
float len = sqrtf(normal[3*i]*normal[3*i] +
normal[3*i+1]*normal[3*i+1] +
+19 -8
View File
@@ -258,7 +258,8 @@ void mjCModel::Clear(void) {
nu = 0;
na = 0;
nmeshvert = 0;
nmeshtexvert = 0;
nmeshnormal = 0;
nmeshtexcoord = 0;
nmeshface = 0;
nmeshgraph = 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++) {
nmeshvert += meshes[i]->nvert;
nmeshnormal += meshes[i]->nnormal;
nmeshface += meshes[i]->nface;
nmeshtexvert += (meshes[i]->texcoord ? meshes[i]->nvert : 0);
nmeshtexcoord += (meshes[i]->texcoord ? meshes[i]->ntexcoord : 0);
nmeshgraph += meshes[i]->szgraph;
}
@@ -1645,7 +1647,7 @@ void mjCModel::CopyTree(mjModel* m) {
// copy objects outside kinematic tree
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;
// sizes outside call to mj_makeModel
@@ -1657,6 +1659,7 @@ void mjCModel::CopyObjects(mjModel* m) {
// meshes
vert_adr = 0;
normal_adr = 0;
texcoord_adr = 0;
face_adr = 0;
graph_adr = 0;
@@ -1667,17 +1670,24 @@ void mjCModel::CopyObjects(mjModel* m) {
// set fields
m->mesh_vertadr[i] = vert_adr;
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_texcoordnum[i] = pme->ntexcoord;
m->mesh_faceadr[i] = face_adr;
m->mesh_facenum[i] = pme->nface;
m->mesh_graphadr[i] = (pme->szgraph ? graph_adr : -1);
// copy vertices, normals, faces, texcoords, aux data
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_facenormal + 3*face_adr, pme->facenormal, 3*pme->nface*sizeof(int));
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) {
memcpy(m->mesh_graph + graph_adr, pme->graph, pme->szgraph*sizeof(int));
@@ -1685,7 +1695,8 @@ void mjCModel::CopyObjects(mjModel* m) {
// advance counters
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;
graph_adr += pme->szgraph;
}
@@ -2619,7 +2630,7 @@ void mjCModel::TryCompile(mjModel*& m, mjData*& d, const mjVFS* vfs) {
// create low-level model
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,
nhfield, nhfielddata, ntex, ntexdata, nmat, npair, nexclude,
neq, ntendon, nwrap, nsensor,
+2 -1
View File
@@ -216,7 +216,8 @@ class mjCModel {
int nu; // number of actuators/controls
int na; // number of activation variables
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 nmeshgraph; // number of shorts in mesh auxiliary data
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> usernormal; // user normal 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
private:
@@ -514,12 +516,16 @@ class mjCMesh: public mjCBase {
// mesh data to be copied into mjModel
int nvert; // number of vertices
int nnormal; // number of normals
int ntexcoord; // number of texcoords
int nface; // number of faces
int szgraph; // size of graph data in ints
float* vert; // vertex data (3*nvert), relative to (pos, quat)
float* normal; // vertex normal data (3*nvert)
float* texcoord; // vertex texcoord data (2*nvert, or NULL)
float* normal; // vertex normal data (3*nnormal)
float* texcoord; // vertex texcoord data (2*ntexcoord or NULL)
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
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;
static const char* const kMeshPath =
"user/testdata/mesh.xml";
static const char* const kDuplicateVerticesPath =
"user/testdata/duplicate_vertices.xml";
static const char* const kCubePath =
@@ -100,6 +102,17 @@ TEST_F(MjCMeshTest, DeDuplicateSTLVertices) {
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 ----------------------------------------------
using MjCMeshTest = MujocoTest;
@@ -109,6 +122,7 @@ TEST_F(MjCMeshTest, LoadCube) {
mjModel* model = mj_loadXML(xml_path.c_str(), 0, nullptr, 0);
ASSERT_GT(model->ngeom, 0);
ASSERT_EQ(model->nmeshvert, 8);
ASSERT_EQ(model->nmeshface, 12);
mj_deleteModel(model);
}
@@ -146,7 +160,7 @@ TEST_F(MjCMeshTest, KeepDuplicateOBJVertices) {
char error[1024];
size_t error_sz = 1024;
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);
}
@@ -175,6 +189,26 @@ TEST_F(MjCMeshTest, TinyMeshLoads) {
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_F(MjCMeshTest, SmallInertiaLoads) {
+8 -2
View File
@@ -1874,7 +1874,8 @@ public unsafe struct mjModel_ {
public int nlight;
public int nmesh;
public int nmeshvert;
public int nmeshtexvert;
public int nmeshnormal;
public int nmeshtexcoord;
public int nmeshface;
public int nmeshgraph;
public int nskin;
@@ -2042,14 +2043,19 @@ public unsafe struct mjModel_ {
public float* light_specular;
public int* mesh_vertadr;
public int* mesh_vertnum;
public int* mesh_texcoordadr;
public int* mesh_faceadr;
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 float* mesh_vert;
public float* mesh_normal;
public float* mesh_texcoord;
public int* mesh_face;
public int* mesh_facenormal;
public int* mesh_facetexcoord;
public int* mesh_graph;
public int* skin_matid;
public int* skin_group;