From 3edca81a75eb74950beba4bd8368d90c2b95acbb Mon Sep 17 00:00:00 2001 From: Holden Green Date: Wed, 4 Jul 2018 21:21:56 -0700 Subject: [PATCH 01/10] Added line paths. --- tiny_obj_loader.h | 70 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 9 deletions(-) diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index 9e43a7d..4045404 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -236,9 +236,14 @@ typedef struct { std::vector tags; // SubD tag } mesh_t; +typedef struct { + std::vector indices; // pairs of indices for lines +} path_t; + typedef struct { std::string name; mesh_t mesh; + path_t path; } shape_t; // Vertex attributes @@ -401,6 +406,11 @@ struct face_t { face_t() : smoothing_group_id(0) {} }; +struct line_t { + int idx0; + int idx1; +}; + struct tag_sizes { tag_sizes() : num_ints(0), num_reals(0), num_strings(0) {} int num_ints; @@ -1015,15 +1025,17 @@ static int pnpoly(int nvert, T *vertx, T *verty, T testx, } // TODO(syoyo): refactor function. -static bool exportFaceGroupToShape(shape_t *shape, +static bool exportGroupsToShape(shape_t *shape, const std::vector &faceGroup, + const std::vector &lineGroup, const std::vector &tags, const int material_id, const std::string &name, bool triangulate, const std::vector &v) { - if (faceGroup.empty()) { - return false; - } + + + + if (!faceGroup.empty()) { // Flatten vertices and indices for (size_t i = 0; i < faceGroup.size(); i++) { @@ -1031,10 +1043,11 @@ static bool exportFaceGroupToShape(shape_t *shape, size_t npolys = face.vertex_indices.size(); + /* if (npolys < 3) { // Face must have 3+ vertices. continue; - } + }*/ vertex_index_t i0 = face.vertex_indices[0]; vertex_index_t i1(-1); @@ -1254,6 +1267,12 @@ static bool exportFaceGroupToShape(shape_t *shape, shape->name = name; shape->mesh.tags = tags; + } + + + if(!lineGroup.empty()){ + shape->path.indices = std::move(lineGroup); + } return true; } @@ -1765,6 +1784,7 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, std::vector vc; std::vector tags; std::vector faceGroup; + std::vector lineGroup; std::string name; // material @@ -1842,6 +1862,32 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, continue; } + // line + if (token[0] == 'l' && IS_SPACE((token[1]))){ + token += 2; + + line_t line_cache; + bool end_line_bit = 0; + while(!IS_NEW_LINE(token[0])){ + //get index from string + int idx; + fixIndex(parseInt(&token), 0, &idx); + // move to next space or end of string (\0 / \n) + token += strcspn(token, " \t\r")+1; + + if(!end_line_bit){ + line_cache.idx0 = idx; + } else { + line_cache.idx1 = idx; + lineGroup.push_back(line_cache.idx0); + lineGroup.push_back(line_cache.idx1); + line_cache = line_t(); + } + end_line_bit = !end_line_bit; + } + + continue; + } // face if (token[0] == 'f' && IS_SPACE((token[1]))) { token += 2; @@ -1892,7 +1938,7 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, // Create per-face material. Thus we don't add `shape` to `shapes` at // this time. // just clear `faceGroup` after `exportFaceGroupToShape()` call. - exportFaceGroupToShape(&shape, faceGroup, tags, material, name, + exportGroupsToShape(&shape, faceGroup, lineGroup, tags, material, name, triangulate, v); faceGroup.clear(); material = newMaterialId; @@ -1947,7 +1993,7 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, // group name if (token[0] == 'g' && IS_SPACE((token[1]))) { // flush previous face group. - bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name, + bool ret = exportGroupsToShape(&shape, faceGroup, lineGroup, tags, material, name, triangulate, v); (void)ret; // return value not used. @@ -1984,7 +2030,7 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, // object name if (token[0] == 'o' && IS_SPACE((token[1]))) { // flush previous face group. - bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name, + bool ret = exportGroupsToShape(&shape, faceGroup, lineGroup, tags, material, name, triangulate, v); if (ret) { shapes->push_back(shape); @@ -2092,7 +2138,7 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, // Ignore unknown command. } - bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name, + bool ret = exportGroupsToShape(&shape, faceGroup, lineGroup, tags, material, name, triangulate, v); // exportFaceGroupToShape return false when `usemtl` is called in the last // line. @@ -2103,6 +2149,12 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, } faceGroup.clear(); // for safety + + for(int i=0;i Date: Wed, 4 Jul 2018 21:44:09 -0700 Subject: [PATCH 02/10] Removed print message. --- tiny_obj_loader.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index 4045404..13b7471 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -2149,12 +2149,6 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, } faceGroup.clear(); // for safety - - for(int i=0;i Date: Wed, 4 Jul 2018 21:46:42 -0700 Subject: [PATCH 03/10] Fixed comments --- tiny_obj_loader.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index 13b7471..4dcbe90 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -1937,7 +1937,7 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, if (newMaterialId != material) { // Create per-face material. Thus we don't add `shape` to `shapes` at // this time. - // just clear `faceGroup` after `exportFaceGroupToShape()` call. + // just clear `faceGroup` after `exportGroupsToShape()` call. exportGroupsToShape(&shape, faceGroup, lineGroup, tags, material, name, triangulate, v); faceGroup.clear(); @@ -2140,7 +2140,7 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, bool ret = exportGroupsToShape(&shape, faceGroup, lineGroup, tags, material, name, triangulate, v); - // exportFaceGroupToShape return false when `usemtl` is called in the last + // exportGroupsToShape return false when `usemtl` is called in the last // line. // we also add `shape` to `shapes` when `shape.mesh` has already some // faces(indices) From 7befd59de4398d1aae9f1e9af5af5782545ba723 Mon Sep 17 00:00:00 2001 From: Holden Green Date: Wed, 4 Jul 2018 21:59:56 -0700 Subject: [PATCH 04/10] Removed comment --- tiny_obj_loader.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index 4dcbe90..06bb03a 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -1043,11 +1043,10 @@ static bool exportGroupsToShape(shape_t *shape, size_t npolys = face.vertex_indices.size(); - /* if (npolys < 3) { // Face must have 3+ vertices. continue; - }*/ + } vertex_index_t i0 = face.vertex_indices[0]; vertex_index_t i1(-1); From 6650dbf397705630b3114fc61ac59cd2f4a686cb Mon Sep 17 00:00:00 2001 From: Holden Green Date: Wed, 4 Jul 2018 22:10:45 -0700 Subject: [PATCH 05/10] Used swap in favor of move --- tiny_obj_loader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index 06bb03a..cc894d6 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -1270,7 +1270,7 @@ static bool exportGroupsToShape(shape_t *shape, if(!lineGroup.empty()){ - shape->path.indices = std::move(lineGroup); + shape->path.indices.swap(lineGroup); } return true; From 3cbf45a5720069aab361b0b44f651856031a8bb5 Mon Sep 17 00:00:00 2001 From: Holden Green Date: Wed, 4 Jul 2018 22:13:39 -0700 Subject: [PATCH 06/10] Non const --- tiny_obj_loader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index cc894d6..2a0f66f 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -1027,7 +1027,7 @@ static int pnpoly(int nvert, T *vertx, T *verty, T testx, // TODO(syoyo): refactor function. static bool exportGroupsToShape(shape_t *shape, const std::vector &faceGroup, - const std::vector &lineGroup, + std::vector &lineGroup, const std::vector &tags, const int material_id, const std::string &name, bool triangulate, From a4b115a584bf966262dc21985886b4f36fdd6fb7 Mon Sep 17 00:00:00 2001 From: Holden Green Date: Tue, 24 Jul 2018 23:47:59 -0700 Subject: [PATCH 07/10] Hopefully fixed compiling error --- tiny_obj_loader.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index 2a0f66f..d459047 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -1035,6 +1035,10 @@ static bool exportGroupsToShape(shape_t *shape, + if (faceGroup.empty() && lineGroup.empty()) { + return false; + } + if (!faceGroup.empty()) { // Flatten vertices and indices From 0d6826224659a109f497ee13ac05744ecde824a9 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Wed, 25 Jul 2018 16:24:40 +0900 Subject: [PATCH 08/10] Fix buffer overrun when parsing 'l' line. --- loader_example.cc | 4 +- tiny_obj_loader.h | 512 +++++++++++++++++++++++----------------------- 2 files changed, 255 insertions(+), 261 deletions(-) diff --git a/loader_example.cc b/loader_example.cc index 0d6d7ad..595ae3a 100644 --- a/loader_example.cc +++ b/loader_example.cc @@ -137,8 +137,10 @@ static void PrintInfo(const tinyobj::attrib_t& attrib, for (size_t i = 0; i < shapes.size(); i++) { printf("shape[%ld].name = %s\n", static_cast(i), shapes[i].name.c_str()); - printf("Size of shape[%ld].indices: %lu\n", static_cast(i), + printf("Size of shape[%ld].mesh.indices: %lu\n", static_cast(i), static_cast(shapes[i].mesh.indices.size())); + printf("Size of shape[%ld].path.indices: %lu\n", static_cast(i), + static_cast(shapes[i].path.indices.size())); size_t index_offset = 0; diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index 140d926..70fb4b9 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -23,6 +23,7 @@ THE SOFTWARE. */ // +// version 1.2.1 : Added initial support for line('l') primitive(PR #178) // version 1.2.0 : Hardened implementation(#175) // version 1.1.1 : Support smoothing groups(#162) // version 1.1.0 : Support parsing vertex color(#144) @@ -237,7 +238,7 @@ typedef struct { } mesh_t; typedef struct { - std::vector indices; // pairs of indices for lines + std::vector indices; // pairs of indices for lines } path_t; typedef struct { @@ -377,8 +378,8 @@ void LoadMtl(std::map *material_map, #include #include #include -#include #include +#include #include #include @@ -407,8 +408,8 @@ struct face_t { }; struct line_t { - int idx0; - int idx1; + int idx0; + int idx1; }; struct tag_sizes { @@ -945,7 +946,7 @@ static bool ParseTextureNameAndOption(std::string *texname, token += 4; parseReal2(&(texopt->brightness), &(texopt->contrast), &token, 0.0, 1.0); } else { - // Assume texture filename + // Assume texture filename #if 0 size_t len = strcspn(token, " \t\r"); // untile next space texture_name = std::string(token, token + len); @@ -1010,9 +1011,8 @@ static void InitMaterial(material_t *material) { } // code from https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html -template -static int pnpoly(int nvert, T *vertx, T *verty, T testx, - T testy) { +template +static int pnpoly(int nvert, T *vertx, T *verty, T testx, T testy) { int i, j, c = 0; for (i = 0, j = nvert - 1; i < nvert; j = i++) { if (((verty[i] > testy) != (verty[j] > testy)) && @@ -1026,255 +1026,247 @@ static int pnpoly(int nvert, T *vertx, T *verty, T testx, // TODO(syoyo): refactor function. static bool exportGroupsToShape(shape_t *shape, - const std::vector &faceGroup, - std::vector &lineGroup, - const std::vector &tags, - const int material_id, - const std::string &name, bool triangulate, - const std::vector &v) { - - - + const std::vector &faceGroup, + std::vector &lineGroup, + const std::vector &tags, + const int material_id, const std::string &name, + bool triangulate, + const std::vector &v) { if (faceGroup.empty() && lineGroup.empty()) { return false; } if (!faceGroup.empty()) { + // Flatten vertices and indices + for (size_t i = 0; i < faceGroup.size(); i++) { + const face_t &face = faceGroup[i]; - // Flatten vertices and indices - for (size_t i = 0; i < faceGroup.size(); i++) { - const face_t &face = faceGroup[i]; + size_t npolys = face.vertex_indices.size(); - size_t npolys = face.vertex_indices.size(); - - if (npolys < 3) { - // Face must have 3+ vertices. - continue; - } - - vertex_index_t i0 = face.vertex_indices[0]; - vertex_index_t i1(-1); - vertex_index_t i2 = face.vertex_indices[1]; - - if (triangulate) { - // find the two axes to work in - size_t axes[2] = {1, 2}; - for (size_t k = 0; k < npolys; ++k) { - i0 = face.vertex_indices[(k + 0) % npolys]; - i1 = face.vertex_indices[(k + 1) % npolys]; - i2 = face.vertex_indices[(k + 2) % npolys]; - size_t vi0 = size_t(i0.v_idx); - size_t vi1 = size_t(i1.v_idx); - size_t vi2 = size_t(i2.v_idx); - - if (((3 * vi0 + 2) >= v.size()) || - ((3 * vi1 + 2) >= v.size()) || - ((3 * vi2 + 2) >= v.size())) { - // Invalid triangle. - // FIXME(syoyo): Is it ok to simply skip this invalid triangle? - continue; - } - real_t v0x = v[vi0 * 3 + 0]; - real_t v0y = v[vi0 * 3 + 1]; - real_t v0z = v[vi0 * 3 + 2]; - real_t v1x = v[vi1 * 3 + 0]; - real_t v1y = v[vi1 * 3 + 1]; - real_t v1z = v[vi1 * 3 + 2]; - real_t v2x = v[vi2 * 3 + 0]; - real_t v2y = v[vi2 * 3 + 1]; - real_t v2z = v[vi2 * 3 + 2]; - real_t e0x = v1x - v0x; - real_t e0y = v1y - v0y; - real_t e0z = v1z - v0z; - real_t e1x = v2x - v1x; - real_t e1y = v2y - v1y; - real_t e1z = v2z - v1z; - real_t cx = std::fabs(e0y * e1z - e0z * e1y); - real_t cy = std::fabs(e0z * e1x - e0x * e1z); - real_t cz = std::fabs(e0x * e1y - e0y * e1x); - const real_t epsilon = std::numeric_limits::epsilon(); - if (cx > epsilon || cy > epsilon || cz > epsilon) { - // found a corner - if (cx > cy && cx > cz) { - } else { - axes[0] = 0; - if (cz > cx && cz > cy) axes[1] = 1; - } - break; - } + if (npolys < 3) { + // Face must have 3+ vertices. + continue; } - real_t area = 0; - for (size_t k = 0; k < npolys; ++k) { - i0 = face.vertex_indices[(k + 0) % npolys]; - i1 = face.vertex_indices[(k + 1) % npolys]; - size_t vi0 = size_t(i0.v_idx); - size_t vi1 = size_t(i1.v_idx); - if (((vi0 * 3 + axes[0]) >= v.size()) || - ((vi0 * 3 + axes[1]) >= v.size()) || - ((vi1 * 3 + axes[0]) >= v.size()) || - ((vi1 * 3 + axes[1]) >= v.size())) { - // Invalid index. - continue; - } - real_t v0x = v[vi0 * 3 + axes[0]]; - real_t v0y = v[vi0 * 3 + axes[1]]; - real_t v1x = v[vi1 * 3 + axes[0]]; - real_t v1y = v[vi1 * 3 + axes[1]]; - area += (v0x * v1y - v0y * v1x) * static_cast(0.5); - } + vertex_index_t i0 = face.vertex_indices[0]; + vertex_index_t i1(-1); + vertex_index_t i2 = face.vertex_indices[1]; - int maxRounds = - 10; // arbitrary max loop count to protect against unexpected errors + if (triangulate) { + // find the two axes to work in + size_t axes[2] = {1, 2}; + for (size_t k = 0; k < npolys; ++k) { + i0 = face.vertex_indices[(k + 0) % npolys]; + i1 = face.vertex_indices[(k + 1) % npolys]; + i2 = face.vertex_indices[(k + 2) % npolys]; + size_t vi0 = size_t(i0.v_idx); + size_t vi1 = size_t(i1.v_idx); + size_t vi2 = size_t(i2.v_idx); - face_t remainingFace = face; // copy - size_t guess_vert = 0; - vertex_index_t ind[3]; - real_t vx[3]; - real_t vy[3]; - while (remainingFace.vertex_indices.size() > 3 && maxRounds > 0) { - npolys = remainingFace.vertex_indices.size(); - if (guess_vert >= npolys) { - maxRounds -= 1; - guess_vert -= npolys; - } - for (size_t k = 0; k < 3; k++) { - ind[k] = remainingFace.vertex_indices[(guess_vert + k) % npolys]; - size_t vi = size_t(ind[k].v_idx); - if (((vi * 3 + axes[0]) >= v.size()) || - ((vi * 3 + axes[1]) >= v.size())) { - // ??? - vx[k] = static_cast(0.0); - vy[k] = static_cast(0.0); - } else { - vx[k] = v[vi * 3 + axes[0]]; - vy[k] = v[vi * 3 + axes[1]]; - } - } - real_t e0x = vx[1] - vx[0]; - real_t e0y = vy[1] - vy[0]; - real_t e1x = vx[2] - vx[1]; - real_t e1y = vy[2] - vy[1]; - real_t cross = e0x * e1y - e0y * e1x; - // if an internal angle - if (cross * area < static_cast(0.0)) { - guess_vert += 1; - continue; - } - - // check all other verts in case they are inside this triangle - bool overlap = false; - for (size_t otherVert = 3; otherVert < npolys; ++otherVert) { - size_t idx = (guess_vert + otherVert) % npolys; - - if (idx >= remainingFace.vertex_indices.size()) { - // ??? + if (((3 * vi0 + 2) >= v.size()) || ((3 * vi1 + 2) >= v.size()) || + ((3 * vi2 + 2) >= v.size())) { + // Invalid triangle. + // FIXME(syoyo): Is it ok to simply skip this invalid triangle? continue; } - - size_t ovi = size_t( - remainingFace.vertex_indices[idx] - .v_idx); - - if (((ovi * 3 + axes[0]) >= v.size()) || - ((ovi * 3 + axes[1]) >= v.size())) { - // ??? - continue; - } - real_t tx = v[ovi * 3 + axes[0]]; - real_t ty = v[ovi * 3 + axes[1]]; - if (pnpoly(3, vx, vy, tx, ty)) { - overlap = true; + real_t v0x = v[vi0 * 3 + 0]; + real_t v0y = v[vi0 * 3 + 1]; + real_t v0z = v[vi0 * 3 + 2]; + real_t v1x = v[vi1 * 3 + 0]; + real_t v1y = v[vi1 * 3 + 1]; + real_t v1z = v[vi1 * 3 + 2]; + real_t v2x = v[vi2 * 3 + 0]; + real_t v2y = v[vi2 * 3 + 1]; + real_t v2z = v[vi2 * 3 + 2]; + real_t e0x = v1x - v0x; + real_t e0y = v1y - v0y; + real_t e0z = v1z - v0z; + real_t e1x = v2x - v1x; + real_t e1y = v2y - v1y; + real_t e1z = v2z - v1z; + real_t cx = std::fabs(e0y * e1z - e0z * e1y); + real_t cy = std::fabs(e0z * e1x - e0x * e1z); + real_t cz = std::fabs(e0x * e1y - e0y * e1x); + const real_t epsilon = std::numeric_limits::epsilon(); + if (cx > epsilon || cy > epsilon || cz > epsilon) { + // found a corner + if (cx > cy && cx > cz) { + } else { + axes[0] = 0; + if (cz > cx && cz > cy) axes[1] = 1; + } break; } } - if (overlap) { - guess_vert += 1; - continue; + real_t area = 0; + for (size_t k = 0; k < npolys; ++k) { + i0 = face.vertex_indices[(k + 0) % npolys]; + i1 = face.vertex_indices[(k + 1) % npolys]; + size_t vi0 = size_t(i0.v_idx); + size_t vi1 = size_t(i1.v_idx); + if (((vi0 * 3 + axes[0]) >= v.size()) || + ((vi0 * 3 + axes[1]) >= v.size()) || + ((vi1 * 3 + axes[0]) >= v.size()) || + ((vi1 * 3 + axes[1]) >= v.size())) { + // Invalid index. + continue; + } + real_t v0x = v[vi0 * 3 + axes[0]]; + real_t v0y = v[vi0 * 3 + axes[1]]; + real_t v1x = v[vi1 * 3 + axes[0]]; + real_t v1y = v[vi1 * 3 + axes[1]]; + area += (v0x * v1y - v0y * v1x) * static_cast(0.5); } - // this triangle is an ear - { - index_t idx0, idx1, idx2; - idx0.vertex_index = ind[0].v_idx; - idx0.normal_index = ind[0].vn_idx; - idx0.texcoord_index = ind[0].vt_idx; - idx1.vertex_index = ind[1].v_idx; - idx1.normal_index = ind[1].vn_idx; - idx1.texcoord_index = ind[1].vt_idx; - idx2.vertex_index = ind[2].v_idx; - idx2.normal_index = ind[2].vn_idx; - idx2.texcoord_index = ind[2].vt_idx; + int maxRounds = 10; // arbitrary max loop count to protect against + // unexpected errors - shape->mesh.indices.push_back(idx0); - shape->mesh.indices.push_back(idx1); - shape->mesh.indices.push_back(idx2); + face_t remainingFace = face; // copy + size_t guess_vert = 0; + vertex_index_t ind[3]; + real_t vx[3]; + real_t vy[3]; + while (remainingFace.vertex_indices.size() > 3 && maxRounds > 0) { + npolys = remainingFace.vertex_indices.size(); + if (guess_vert >= npolys) { + maxRounds -= 1; + guess_vert -= npolys; + } + for (size_t k = 0; k < 3; k++) { + ind[k] = remainingFace.vertex_indices[(guess_vert + k) % npolys]; + size_t vi = size_t(ind[k].v_idx); + if (((vi * 3 + axes[0]) >= v.size()) || + ((vi * 3 + axes[1]) >= v.size())) { + // ??? + vx[k] = static_cast(0.0); + vy[k] = static_cast(0.0); + } else { + vx[k] = v[vi * 3 + axes[0]]; + vy[k] = v[vi * 3 + axes[1]]; + } + } + real_t e0x = vx[1] - vx[0]; + real_t e0y = vy[1] - vy[0]; + real_t e1x = vx[2] - vx[1]; + real_t e1y = vy[2] - vy[1]; + real_t cross = e0x * e1y - e0y * e1x; + // if an internal angle + if (cross * area < static_cast(0.0)) { + guess_vert += 1; + continue; + } - shape->mesh.num_face_vertices.push_back(3); - shape->mesh.material_ids.push_back(material_id); - shape->mesh.smoothing_group_ids.push_back(face.smoothing_group_id); + // check all other verts in case they are inside this triangle + bool overlap = false; + for (size_t otherVert = 3; otherVert < npolys; ++otherVert) { + size_t idx = (guess_vert + otherVert) % npolys; + + if (idx >= remainingFace.vertex_indices.size()) { + // ??? + continue; + } + + size_t ovi = size_t(remainingFace.vertex_indices[idx].v_idx); + + if (((ovi * 3 + axes[0]) >= v.size()) || + ((ovi * 3 + axes[1]) >= v.size())) { + // ??? + continue; + } + real_t tx = v[ovi * 3 + axes[0]]; + real_t ty = v[ovi * 3 + axes[1]]; + if (pnpoly(3, vx, vy, tx, ty)) { + overlap = true; + break; + } + } + + if (overlap) { + guess_vert += 1; + continue; + } + + // this triangle is an ear + { + index_t idx0, idx1, idx2; + idx0.vertex_index = ind[0].v_idx; + idx0.normal_index = ind[0].vn_idx; + idx0.texcoord_index = ind[0].vt_idx; + idx1.vertex_index = ind[1].v_idx; + idx1.normal_index = ind[1].vn_idx; + idx1.texcoord_index = ind[1].vt_idx; + idx2.vertex_index = ind[2].v_idx; + idx2.normal_index = ind[2].vn_idx; + idx2.texcoord_index = ind[2].vt_idx; + + shape->mesh.indices.push_back(idx0); + shape->mesh.indices.push_back(idx1); + shape->mesh.indices.push_back(idx2); + + shape->mesh.num_face_vertices.push_back(3); + shape->mesh.material_ids.push_back(material_id); + shape->mesh.smoothing_group_ids.push_back(face.smoothing_group_id); + } + + // remove v1 from the list + size_t removed_vert_index = (guess_vert + 1) % npolys; + while (removed_vert_index + 1 < npolys) { + remainingFace.vertex_indices[removed_vert_index] = + remainingFace.vertex_indices[removed_vert_index + 1]; + removed_vert_index += 1; + } + remainingFace.vertex_indices.pop_back(); } - // remove v1 from the list - size_t removed_vert_index = (guess_vert + 1) % npolys; - while (removed_vert_index + 1 < npolys) { - remainingFace.vertex_indices[removed_vert_index] = - remainingFace.vertex_indices[removed_vert_index + 1]; - removed_vert_index += 1; + if (remainingFace.vertex_indices.size() == 3) { + i0 = remainingFace.vertex_indices[0]; + i1 = remainingFace.vertex_indices[1]; + i2 = remainingFace.vertex_indices[2]; + { + index_t idx0, idx1, idx2; + idx0.vertex_index = i0.v_idx; + idx0.normal_index = i0.vn_idx; + idx0.texcoord_index = i0.vt_idx; + idx1.vertex_index = i1.v_idx; + idx1.normal_index = i1.vn_idx; + idx1.texcoord_index = i1.vt_idx; + idx2.vertex_index = i2.v_idx; + idx2.normal_index = i2.vn_idx; + idx2.texcoord_index = i2.vt_idx; + + shape->mesh.indices.push_back(idx0); + shape->mesh.indices.push_back(idx1); + shape->mesh.indices.push_back(idx2); + + shape->mesh.num_face_vertices.push_back(3); + shape->mesh.material_ids.push_back(material_id); + shape->mesh.smoothing_group_ids.push_back(face.smoothing_group_id); + } } - remainingFace.vertex_indices.pop_back(); + } else { + for (size_t k = 0; k < npolys; k++) { + index_t idx; + idx.vertex_index = face.vertex_indices[k].v_idx; + idx.normal_index = face.vertex_indices[k].vn_idx; + idx.texcoord_index = face.vertex_indices[k].vt_idx; + shape->mesh.indices.push_back(idx); + } + + shape->mesh.num_face_vertices.push_back( + static_cast(npolys)); + shape->mesh.material_ids.push_back(material_id); // per face + shape->mesh.smoothing_group_ids.push_back( + face.smoothing_group_id); // per face } - - if (remainingFace.vertex_indices.size() == 3) { - i0 = remainingFace.vertex_indices[0]; - i1 = remainingFace.vertex_indices[1]; - i2 = remainingFace.vertex_indices[2]; - { - index_t idx0, idx1, idx2; - idx0.vertex_index = i0.v_idx; - idx0.normal_index = i0.vn_idx; - idx0.texcoord_index = i0.vt_idx; - idx1.vertex_index = i1.v_idx; - idx1.normal_index = i1.vn_idx; - idx1.texcoord_index = i1.vt_idx; - idx2.vertex_index = i2.v_idx; - idx2.normal_index = i2.vn_idx; - idx2.texcoord_index = i2.vt_idx; - - shape->mesh.indices.push_back(idx0); - shape->mesh.indices.push_back(idx1); - shape->mesh.indices.push_back(idx2); - - shape->mesh.num_face_vertices.push_back(3); - shape->mesh.material_ids.push_back(material_id); - shape->mesh.smoothing_group_ids.push_back(face.smoothing_group_id); - } - } - } else { - for (size_t k = 0; k < npolys; k++) { - index_t idx; - idx.vertex_index = face.vertex_indices[k].v_idx; - idx.normal_index = face.vertex_indices[k].vn_idx; - idx.texcoord_index = face.vertex_indices[k].vt_idx; - shape->mesh.indices.push_back(idx); - } - - shape->mesh.num_face_vertices.push_back( - static_cast(npolys)); - shape->mesh.material_ids.push_back(material_id); // per face - shape->mesh.smoothing_group_ids.push_back( - face.smoothing_group_id); // per face } + + shape->name = name; + shape->mesh.tags = tags; } - shape->name = name; - shape->mesh.tags = tags; - } - - - if(!lineGroup.empty()){ - shape->path.indices.swap(lineGroup); + if (!lineGroup.empty()) { + shape->path.indices.swap(lineGroup); } return true; @@ -1759,14 +1751,13 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, } std::string baseDir = mtl_basedir ? mtl_basedir : ""; - if (!baseDir.empty()) { + if (!baseDir.empty()) { #ifndef _WIN32 const char dirsep = '/'; #else const char dirsep = '\\'; #endif - if (baseDir[baseDir.length() - 1] != dirsep) - baseDir += dirsep; + if (baseDir[baseDir.length() - 1] != dirsep) baseDir += dirsep; } MaterialFileReader matFileReader(baseDir); @@ -1865,30 +1856,31 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, } // line - if (token[0] == 'l' && IS_SPACE((token[1]))){ - token += 2; + if (token[0] == 'l' && IS_SPACE((token[1]))) { + token += 2; - line_t line_cache; - bool end_line_bit = 0; - while(!IS_NEW_LINE(token[0])){ - //get index from string - int idx; - fixIndex(parseInt(&token), 0, &idx); - // move to next space or end of string (\0 / \n) - token += strcspn(token, " \t\r")+1; + line_t line_cache; + bool end_line_bit = 0; + while (!IS_NEW_LINE(token[0])) { + // get index from string + int idx; + fixIndex(parseInt(&token), 0, &idx); - if(!end_line_bit){ - line_cache.idx0 = idx; - } else { - line_cache.idx1 = idx; - lineGroup.push_back(line_cache.idx0); - lineGroup.push_back(line_cache.idx1); - line_cache = line_t(); - } - end_line_bit = !end_line_bit; + size_t n = strspn(token, " \t\r"); + token += n; + + if (!end_line_bit) { + line_cache.idx0 = idx; + } else { + line_cache.idx1 = idx; + lineGroup.push_back(line_cache.idx0); + lineGroup.push_back(line_cache.idx1); + line_cache = line_t(); } + end_line_bit = !end_line_bit; + } - continue; + continue; } // face if (token[0] == 'f' && IS_SPACE((token[1]))) { @@ -1941,7 +1933,7 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, // this time. // just clear `faceGroup` after `exportGroupsToShape()` call. exportGroupsToShape(&shape, faceGroup, lineGroup, tags, material, name, - triangulate, v); + triangulate, v); faceGroup.clear(); material = newMaterialId; } @@ -1995,8 +1987,8 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, // group name if (token[0] == 'g' && IS_SPACE((token[1]))) { // flush previous face group. - bool ret = exportGroupsToShape(&shape, faceGroup, lineGroup, tags, material, name, - triangulate, v); + bool ret = exportGroupsToShape(&shape, faceGroup, lineGroup, tags, + material, name, triangulate, v); (void)ret; // return value not used. if (shape.mesh.indices.size() > 0) { @@ -2032,8 +2024,8 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, // object name if (token[0] == 'o' && IS_SPACE((token[1]))) { // flush previous face group. - bool ret = exportGroupsToShape(&shape, faceGroup, lineGroup, tags, material, name, - triangulate, v); + bool ret = exportGroupsToShape(&shape, faceGroup, lineGroup, tags, + material, name, triangulate, v); if (ret) { shapes->push_back(shape); } @@ -2052,7 +2044,7 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, } if (token[0] == 't' && IS_SPACE(token[1])) { - const int max_tag_nums = 8192; // FIXME(syoyo): Parameterize. + const int max_tag_nums = 8192; // FIXME(syoyo): Parameterize. tag_t tag; token += 2; @@ -2140,8 +2132,8 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, // Ignore unknown command. } - bool ret = exportGroupsToShape(&shape, faceGroup, lineGroup, tags, material, name, - triangulate, v); + bool ret = exportGroupsToShape(&shape, faceGroup, lineGroup, tags, material, + name, triangulate, v); // exportGroupsToShape return false when `usemtl` is called in the last // line. // we also add `shape` to `shapes` when `shape.mesh` has already some From fd06fa49e4e3f4811df4ee566a0ea41832eb0cca Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Wed, 25 Jul 2018 21:42:23 +0900 Subject: [PATCH 09/10] Add unit test for parsing 'l'. --- models/line-prim.obj | 16 ++++++++++++++++ tests/tester.cc | 17 +++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 models/line-prim.obj diff --git a/models/line-prim.obj b/models/line-prim.obj new file mode 100644 index 0000000..5f8fb0c --- /dev/null +++ b/models/line-prim.obj @@ -0,0 +1,16 @@ +mtllib cube.mtl + +v 0.000000 2.000000 2.000000 +v 0.000000 0.000000 2.000000 +v 2.000000 0.000000 2.000000 +v 2.000000 2.000000 2.000000 +v 0.000000 2.000000 0.000000 +v 0.000000 0.000000 0.000000 +v 2.000000 0.000000 0.000000 +v 2.000000 2.000000 0.000000 +# 8 vertices + +g g0 +usemtl white +l 1 2 3 4 +l 5 6 7 diff --git a/tests/tester.cc b/tests/tester.cc index 638507d..0a26d2e 100644 --- a/tests/tester.cc +++ b/tests/tester.cc @@ -790,6 +790,23 @@ TEST_CASE("Empty mtl basedir", "[Issue177]") { REQUIRE(true == ret); } +TEST_CASE("line-primitive", "[line]") { + tinyobj::attrib_t attrib; + std::vector shapes; + std::vector materials; + + std::string err; + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/line-prim.obj", gMtlBasePath); + + if (!err.empty()) { + std::cerr << "[line] " << err << std::endl; + } + + REQUIRE(true == ret); + REQUIRE(1 == shapes.size()); + REQUIRE(6 == shapes[0].path.indices.size()); +} + // Fuzzer test. // Just check if it does not crash. // Disable by default since Windows filesystem can't create filename of afl testdata From 1a7bdc619225e5042536257795ee25971f39ec08 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 11 Aug 2018 15:21:17 +0900 Subject: [PATCH 10/10] Parse multiple group names. Fixes #146 --- models/cube.obj | 3 ++- tests/tester.cc | 18 ++++++++++++++++++ tiny_obj_loader.h | 41 +++++++++++++++++++++++++++-------------- 3 files changed, 47 insertions(+), 15 deletions(-) diff --git a/models/cube.obj b/models/cube.obj index 9213e17..f12451f 100644 --- a/models/cube.obj +++ b/models/cube.obj @@ -13,7 +13,8 @@ v 2.000000 2.000000 0.000000 g front cube usemtl white f 1 2 3 4 -g back cube +# two white spaces between 'back' and 'cube' +g back cube # expects white material f 8 7 6 5 g right cube diff --git a/tests/tester.cc b/tests/tester.cc index 0a26d2e..25b8836 100644 --- a/tests/tester.cc +++ b/tests/tester.cc @@ -807,6 +807,24 @@ TEST_CASE("line-primitive", "[line]") { REQUIRE(6 == shapes[0].path.indices.size()); } +TEST_CASE("multiple-group-names", "[group]") { + tinyobj::attrib_t attrib; + std::vector shapes; + std::vector materials; + + std::string err; + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/cube.obj", gMtlBasePath); + + if (!err.empty()) { + std::cerr << "[group] " << err << std::endl; + } + + REQUIRE(true == ret); + REQUIRE(6 == shapes.size()); + REQUIRE(0 == shapes[0].name.compare("front cube")); + REQUIRE(0 == shapes[1].name.compare("back cube")); // multiple whitespaces are aggregated as single white space. +} + // Fuzzer test. // Just check if it does not crash. // Disable by default since Windows filesystem can't create filename of afl testdata diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index 70fb4b9..025f112 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -23,6 +23,7 @@ THE SOFTWARE. */ // +// version 1.2.2 : Parse multiple group names. // version 1.2.1 : Added initial support for line('l') primitive(PR #178) // version 1.2.0 : Hardened implementation(#175) // version 1.1.1 : Support smoothing groups(#162) @@ -1790,10 +1791,13 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, shape_t shape; + size_t line_num = 0; std::string linebuf; while (inStream->peek() != -1) { safeGetline(*inStream, linebuf); + line_num++; + // Trim newline '\r\n' or '\n' if (linebuf.size() > 0) { if (linebuf[linebuf.size() - 1] == '\n') @@ -2001,7 +2005,6 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, faceGroup.clear(); std::vector names; - names.reserve(2); while (!IS_NEW_LINE(token[0])) { std::string str = parseString(&token); @@ -2009,13 +2012,31 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, token += strspn(token, " \t\r"); // skip tag } - assert(names.size() > 0); + // names[0] must be 'g' - // names[0] must be 'g', so skip the 0th element. - if (names.size() > 1) { - name = names[1]; + if (names.size() < 2) { + // 'g' with empty names + if (err) { + std::stringstream ss; + ss << "WARN: Empty group name. line: " << line_num << "\n"; + (*err) += ss.str(); + name = ""; + } } else { - name = ""; + + std::stringstream ss; + ss << names[1]; + + // tinyobjloader does not support multiple groups for a primitive. + // Currently we concatinate multiple group names with a space to get + // single group name. + + for (size_t i = 2; i < names.size(); i++) { + ss << " " << names[i]; + } + + name = ss.str(); + } continue; @@ -2169,7 +2190,6 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, std::vector materials; std::vector names; names.reserve(2); - std::string name; std::vector names_out; std::string linebuf; @@ -2346,13 +2366,6 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, assert(names.size() > 0); - // names[0] must be 'g', so skip the 0th element. - if (names.size() > 1) { - name = names[1]; - } else { - name.clear(); - } - if (callback.group_cb) { if (names.size() > 1) { // create const char* array.