From 73e9b4dc3a9dc2aeb0d3ab63ac19a186156e9340 Mon Sep 17 00:00:00 2001 From: Neptilo Date: Fri, 9 Feb 2018 11:46:43 +0100 Subject: [PATCH 01/12] Fix bug when line starts before a chunk and ends after it When a line started in a chunk, the algorithm only looked for its end in the following chunk, but the end of the line may actually be located even further. Instead, in this commit we look for the next new line until the end of the whole buffer. --- experimental/tinyobj_loader_opt.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/experimental/tinyobj_loader_opt.h b/experimental/tinyobj_loader_opt.h index 154611f..c7175e3 100644 --- a/experimental/tinyobj_loader_opt.h +++ b/experimental/tinyobj_loader_opt.h @@ -1329,11 +1329,10 @@ bool parseObj(attrib_t *attrib, std::vector *shapes, } } - // Find extra line which spand across chunk boundary. - if ((t < num_threads) && (buf[end_idx - 1] != '\n')) { - auto extra_span_idx = (std::min)(end_idx - 1 + chunk_size, len); - for (size_t i = end_idx; i < extra_span_idx; i++) { - if (is_line_ending(buf, i, extra_span_idx)) { + // If at least one line started in this chunk, find where it ends in the rest of the buffer + if ((prev_pos != start_idx) && (t < num_threads) && (buf[end_idx - 1] != '\n')) { + for (size_t i = end_idx; i < len; i++) { + if (is_line_ending(buf, i, len)) { LineInfo info; info.pos = prev_pos; info.len = i - prev_pos; From 72529f02fe17c4dc05dbe13cfbef7be61c1b0908 Mon Sep 17 00:00:00 2001 From: Victor Ripplinger Date: Wed, 21 Feb 2018 11:36:36 +0100 Subject: [PATCH 02/12] Material ids were not assigned to faces if the first face with this material was in another thread buffer than where the usemtl command was --- experimental/tinyobj_loader_opt.h | 65 +++++++++++++++++++------------ 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/experimental/tinyobj_loader_opt.h b/experimental/tinyobj_loader_opt.h index c7175e3..c926245 100644 --- a/experimental/tinyobj_loader_opt.h +++ b/experimental/tinyobj_loader_opt.h @@ -1492,7 +1492,7 @@ bool parseObj(attrib_t *attrib, std::vector *shapes, attrib->texcoords.resize(num_vt * 2); attrib->indices.resize(num_f); attrib->face_num_verts.resize(num_indices); - attrib->material_ids.resize(num_indices); + attrib->material_ids.resize(num_indices, -1); size_t v_offsets[kMaxThreads]; size_t n_offsets[kMaxThreads]; @@ -1523,22 +1523,44 @@ bool parseObj(attrib_t *attrib, std::vector *shapes, size_t t_count = t_offsets[t]; size_t f_count = f_offsets[t]; size_t face_count = face_offsets[t]; - int material_id = -1; // -1 = default unknown material. + int material_id = -1; // -1 = default unknown material. for (size_t i = 0; i < commands[t].size(); i++) { if (commands[t][i].type == COMMAND_EMPTY) { continue; } else if (commands[t][i].type == COMMAND_USEMTL) { if (commands[t][i].material_name && - commands[t][i].material_name_len > 0) { - std::string material_name(commands[t][i].material_name, - commands[t][i].material_name_len); - - if (material_map.find(material_name) != material_map.end()) { - material_id = material_map[material_name]; - } else { - // Assign invalid material ID - material_id = -1; + commands[t][i].material_name_len > 0 && + // check if there are still faces after this command + face_count < num_indices) { + // Find next face + bool found = false; + size_t i_start = i + 1, t_next, i_next; + for (t_next = t; t_next < num_threads; t_next++) { + for (i_next = i_start; i_next < commands[t_next].size(); i_next++) { + if (commands[t_next][i_next].type == COMMAND_F) { + found = true; + break; + } + } + if (found) + break; + i_start = 0; + } + // Assign material to this face + if (found) { + std::string material_name(commands[t][i].material_name, + commands[t][i].material_name_len); + for (size_t k = 0; k < commands[t_next][i_next].f_num_verts.size(); k++) { + if (material_map.find(material_name) != material_map.end()) { + attrib->material_ids[face_count + k] = material_map[material_name]; + } else { + // Assign invalid material ID + // Set a different value than the default, to + // prevent following faces from being assigned a valid material + attrib->material_ids[face_count + k] = -2; + } + } } } } else if (commands[t][i].type == COMMAND_V) { @@ -1565,7 +1587,6 @@ bool parseObj(attrib_t *attrib, std::vector *shapes, index_t(vertex_index, texcoord_index, normal_index); } for (size_t k = 0; k < commands[t][i].f_num_verts.size(); k++) { - attrib->material_ids[face_count + k] = material_id; attrib->face_num_verts[face_count + k] = commands[t][i].f_num_verts[k]; } @@ -1580,19 +1601,13 @@ bool parseObj(attrib_t *attrib, std::vector *shapes, for (size_t t = 0; t < workers->size(); t++) { workers[t].join(); } - if(material_map.size()>1&& num_threads>1) { - for (size_t t = 0; t < num_threads; t++) { - size_t face_count = face_offsets[t]; - if (-1 == attrib->material_ids[face_count]) { - int prev_material_id = attrib->material_ids[face_count - 1]; - size_t max_face_offset = (t == num_threads - 1) ? attrib->material_ids.size() : face_offsets[t + 1]; - for (int i = face_count; imaterial_ids[i] != -1) break; - attrib->material_ids[i] = prev_material_id; - } - } - } - } + + // To each face with uninitialized material id, + // assign the material id of the last face preceding it that has one + for (size_t face_count = 1; face_count < num_indices; ++face_count) + if (attrib->material_ids[face_count] == -1) + attrib->material_ids[face_count] = attrib->material_ids[face_count - 1]; + auto t_end = std::chrono::high_resolution_clock::now(); ms_merge = t_end - t_start; } From e060b4f4aa4f74c1e90d4c5c6efa951f8cce0ca7 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Wed, 21 Feb 2018 20:53:30 +0900 Subject: [PATCH 03/12] Remove unused variable. --- experimental/tinyobj_loader_opt.h | 1 - 1 file changed, 1 deletion(-) diff --git a/experimental/tinyobj_loader_opt.h b/experimental/tinyobj_loader_opt.h index c926245..9d17597 100644 --- a/experimental/tinyobj_loader_opt.h +++ b/experimental/tinyobj_loader_opt.h @@ -1523,7 +1523,6 @@ bool parseObj(attrib_t *attrib, std::vector *shapes, size_t t_count = t_offsets[t]; size_t f_count = f_offsets[t]; size_t face_count = face_offsets[t]; - int material_id = -1; // -1 = default unknown material. for (size_t i = 0; i < commands[t].size(); i++) { if (commands[t][i].type == COMMAND_EMPTY) { From b85714b4cf15f51e928d62e043014056e95e0629 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Fri, 23 Feb 2018 20:25:13 +0900 Subject: [PATCH 04/12] Skip parsing incomplete or invalid face definition(e.g. `f` definition only contains 1 or 2 indices). --- models/invalid-face-definition.mtl | 24 ++++++++++++++++++++++++ models/invalid-face-definition.obj | 18 ++++++++++++++++++ tests/tester.cc | 18 ++++++++++++++++++ tiny_obj_loader.h | 5 +++++ 4 files changed, 65 insertions(+) create mode 100644 models/invalid-face-definition.mtl create mode 100644 models/invalid-face-definition.obj diff --git a/models/invalid-face-definition.mtl b/models/invalid-face-definition.mtl new file mode 100644 index 0000000..d3a1c7a --- /dev/null +++ b/models/invalid-face-definition.mtl @@ -0,0 +1,24 @@ +newmtl white +Ka 0 0 0 +Kd 1 1 1 +Ks 0 0 0 + +newmtl red +Ka 0 0 0 +Kd 1 0 0 +Ks 0 0 0 + +newmtl green +Ka 0 0 0 +Kd 0 1 0 +Ks 0 0 0 + +newmtl blue +Ka 0 0 0 +Kd 0 0 1 +Ks 0 0 0 + +newmtl light +Ka 20 20 20 +Kd 1 1 1 +Ks 0 0 0 diff --git a/models/invalid-face-definition.obj b/models/invalid-face-definition.obj new file mode 100644 index 0000000..73ca678 --- /dev/null +++ b/models/invalid-face-definition.obj @@ -0,0 +1,18 @@ +mtllib invalid-face-definition.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 front cube +usemtl white +f 1 +g back cube +# expects white material +f 8 7 diff --git a/tests/tester.cc b/tests/tester.cc index d5d83c6..e3bc006 100644 --- a/tests/tester.cc +++ b/tests/tester.cc @@ -751,6 +751,24 @@ TEST_CASE("smoothing-group", "[Issue162]") { } +TEST_CASE("invalid-face-definition", "[face]") { + tinyobj::attrib_t attrib; + std::vector shapes; + std::vector materials; + + std::string err; + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/invalid-face-definition.obj", gMtlBasePath); + + + if (!err.empty()) { + std::cerr << "[face] " << err << std::endl; + } + + REQUIRE(true == ret); + REQUIRE(1 == shapes.size()); + REQUIRE(0 == shapes[0].mesh.indices.size()); +} + #if 0 int main( diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index ac1421e..a876842 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -1023,6 +1023,11 @@ static bool exportFaceGroupToShape(shape_t *shape, for (size_t i = 0; i < faceGroup.size(); i++) { const face_t &face = faceGroup[i]; + if (face.vertex_indices.size() < 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]; From 12837cc8b2456445f64f912100d1c6558301031b Mon Sep 17 00:00:00 2001 From: LZaw <28070263+LZaw@users.noreply.github.com> Date: Thu, 8 Mar 2018 15:47:06 +0100 Subject: [PATCH 05/12] Adapt v 1.0.x Added an attributes parameter --- examples/obj_sticher/obj_writer.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/obj_sticher/obj_writer.h b/examples/obj_sticher/obj_writer.h index bb367b6..aa3b269 100644 --- a/examples/obj_sticher/obj_writer.h +++ b/examples/obj_sticher/obj_writer.h @@ -1,9 +1,8 @@ #ifndef __OBJ_WRITER_H__ #define __OBJ_WRITER_H__ -#include "../../tiny_obj_loader.h" - -extern bool WriteObj(const std::string& filename, const std::vector& shapes, const std::vector& materials, bool coordTransform = false); +#include "tiny_obj_loader.h" +extern bool WriteObj(const std::string& filename, const tinyobj::attrib_t& attributes, const std::vector& shapes, const std::vector& materials, bool coordTransform = false); #endif // __OBJ_WRITER_H__ From 707014f843f44279845fa36794a82de68f37c96d Mon Sep 17 00:00:00 2001 From: LZaw <28070263+LZaw@users.noreply.github.com> Date: Thu, 8 Mar 2018 15:48:45 +0100 Subject: [PATCH 06/12] Update obj_writer.cc --- examples/obj_sticher/obj_writer.cc | 174 ++++++++++++++--------------- 1 file changed, 82 insertions(+), 92 deletions(-) diff --git a/examples/obj_sticher/obj_writer.cc b/examples/obj_sticher/obj_writer.cc index 694ce45..9ea8d7c 100644 --- a/examples/obj_sticher/obj_writer.cc +++ b/examples/obj_sticher/obj_writer.cc @@ -30,6 +30,8 @@ bool WriteMat(const std::string& filename, const std::vector& shapes, const std::vector& materials, bool coordTransform) { +bool WriteObj(const std::string& filename, const tinyobj::attrib_t& attributes, const std::vector& shapes, const std::vector& materials, bool coordTransform) { FILE* fp = fopen(filename.c_str(), "w"); if (!fp) { fprintf(stderr, "Failed to open file [ %s ] for write.\n", filename.c_str()); @@ -48,17 +50,53 @@ bool WriteObj(const std::string& filename, const std::vector& std::string basename = GetFileBasename(filename); std::string material_filename = basename + ".mtl"; - int v_offset = 0; - int vn_offset = 0; - int vt_offset = 0; int prev_material_id = -1; - fprintf(fp, "mtllib %s\n", material_filename.c_str()); + fprintf(fp, "mtllib %s\n\n", material_filename.c_str()); + + // facevarying vtx + for (size_t k = 0; k < attributes.vertices.size(); k+=3) { + if (coordTransform) { + fprintf(fp, "v %f %f %f\n", + attributes.vertices[k + 0], + attributes.vertices[k + 2], + -attributes.vertices[k + 1]); + } else { + fprintf(fp, "v %f %f %f\n", + attributes.vertices[k + 0], + attributes.vertices[k + 1], + attributes.vertices[k + 2]); + } + } + + fprintf(fp, "\n"); + + // facevarying normal + for (size_t k = 0; k < attributes.normals.size(); k += 3) { + if (coordTransform) { + fprintf(fp, "vn %f %f %f\n", + attributes.normals[k + 0], + attributes.normals[k + 2], + -attributes.normals[k + 1]); + } else { + fprintf(fp, "vn %f %f %f\n", + attributes.normals[k + 0], + attributes.normals[k + 1], + attributes.normals[k + 2]); + } + } + + fprintf(fp, "\n"); + + // facevarying texcoord + for (size_t k = 0; k < attributes.texcoords.size(); k += 2) { + fprintf(fp, "vt %f %f\n", + attributes.texcoords[k + 0], + attributes.texcoords[k + 1]); + } for (size_t i = 0; i < shapes.size(); i++) { - - bool has_vn = false; - bool has_vt = false; + fprintf(fp, "\n"); if (shapes[i].name.empty()) { fprintf(fp, "g Unknown\n"); @@ -66,101 +104,53 @@ bool WriteObj(const std::string& filename, const std::vector& fprintf(fp, "g %s\n", shapes[i].name.c_str()); } - //if (!shapes[i].material.name.empty()) { - // fprintf(fp, "usemtl %s\n", shapes[i].material.name.c_str()); - //} - - // facevarying vtx - for (size_t k = 0; k < shapes[i].mesh.indices.size() / 3; k++) { - for (int j = 0; j < 3; j++) { - int idx = shapes[i].mesh.indices[3*k+j]; - if (coordTransform) { - fprintf(fp, "v %f %f %f\n", - shapes[i].mesh.positions[3*idx+0], - shapes[i].mesh.positions[3*idx+2], - -shapes[i].mesh.positions[3*idx+1]); - } else { - fprintf(fp, "v %f %f %f\n", - shapes[i].mesh.positions[3*idx+0], - shapes[i].mesh.positions[3*idx+1], - shapes[i].mesh.positions[3*idx+2]); - } - } + bool has_vn = false; + bool has_vt = false; + // Assumes normals and textures are set shape-wise. + if(shapes[i].mesh.indices.size() > 0){ + has_vn = shapes[i].mesh.indices[0].normal_index != -1; + has_vt = shapes[i].mesh.indices[0].texcoord_index != -1; } - // facevarying normal - if (shapes[i].mesh.normals.size() > 0) { - for (size_t k = 0; k < shapes[i].mesh.indices.size() / 3; k++) { - for (int j = 0; j < 3; j++) { - int idx = shapes[i].mesh.indices[3*k+j]; - if (coordTransform) { - fprintf(fp, "vn %f %f %f\n", - shapes[i].mesh.normals[3*idx+0], - shapes[i].mesh.normals[3*idx+2], - -shapes[i].mesh.normals[3*idx+1]); - } else { - fprintf(fp, "vn %f %f %f\n", - shapes[i].mesh.normals[3*idx+0], - shapes[i].mesh.normals[3*idx+1], - shapes[i].mesh.normals[3*idx+2]); - } - } - } - } - if (shapes[i].mesh.normals.size() > 0) has_vn = true; - - // facevarying texcoord - if (shapes[i].mesh.texcoords.size() > 0) { - for (size_t k = 0; k < shapes[i].mesh.indices.size() / 3; k++) { - for (int j = 0; j < 3; j++) { - int idx = shapes[i].mesh.indices[3*k+j]; - fprintf(fp, "vt %f %f\n", - shapes[i].mesh.texcoords[2*idx+0], - shapes[i].mesh.texcoords[2*idx+1]); - } - } - } - if (shapes[i].mesh.texcoords.size() > 0) has_vt = true; - // face - for (size_t k = 0; k < shapes[i].mesh.indices.size() / 3; k++) { - - // Face index is 1-base. - //int v0 = shapes[i].mesh.indices[3*k+0] + 1 + v_offset; - //int v1 = shapes[i].mesh.indices[3*k+1] + 1 + v_offset; - //int v2 = shapes[i].mesh.indices[3*k+2] + 1 + v_offset; - int v0 = (3*k + 0) + 1 + v_offset; - int v1 = (3*k + 1) + 1 + v_offset; - int v2 = (3*k + 2) + 1 + v_offset; - - int vt0 = (3*k + 0) + 1 + vt_offset; - int vt1 = (3*k + 1) + 1 + vt_offset; - int vt2 = (3*k + 2) + 1 + vt_offset; - - int material_id = shapes[i].mesh.material_ids[k]; + int face_index = 0; + for (size_t k = 0; k < shapes[i].mesh.indices.size(); k += shapes[i].mesh.num_face_vertices[face_index++]) { + // Check Materials + int material_id = shapes[i].mesh.material_ids[face_index]; if (material_id != prev_material_id) { std::string material_name = materials[material_id].name; fprintf(fp, "usemtl %s\n", material_name.c_str()); prev_material_id = material_id; } - if (has_vn && has_vt) { - fprintf(fp, "f %d/%d/%d %d/%d/%d %d/%d/%d\n", - v0, vt0, v0, v1, vt1, v1, v2, vt2, v2); - } else if (has_vn && !has_vt) { - fprintf(fp, "f %d//%d %d//%d %d//%d\n", v0, v0, v1, v1, v2, v2); - } else if (!has_vn && has_vt) { - fprintf(fp, "f %d/%d %d/%d %d/%d\n", v0, v0, v1, v1, v2, v2); - } else { - fprintf(fp, "f %d %d %d\n", v0, v1, v2); + unsigned char v_per_f = shapes[i].mesh.num_face_vertices[face_index]; + // Imperformant, but if you want to have variable vertices per face, you need some kind of a dynamic loop. + fprintf(fp, "f"); + for(int l = 0; l < v_per_f; l++){ + const tinyobj::index_t& ref = shapes[i].mesh.indices[k + l]; + if(has_vn && has_vt){ + // v0/t0/vn0 + fprintf(fp, " %d/%d/%d", ref.vertex_index + 1, ref.texcoord_index + 1, ref.normal_index + 1); + continue; + } + if(has_vn && !has_vt){ + // v0//vn0 + fprintf(fp, " %d//%d", ref.vertex_index + 1, ref.normal_index + 1); + continue; + } + if(!has_vn && has_vt){ + // v0/vt0 + fprintf(fp, " %d/%d", ref.vertex_index + 1, ref.texcoord_index + 1); + continue; + } + if(!has_vn && !has_vt){ + // v0 v1 v2 + fprintf(fp, " %d", ref.vertex_index + 1); + continue; + } } - + fprintf(fp, "\n"); } - - v_offset += shapes[i].mesh.indices.size(); - //vn_offset += shapes[i].mesh.normals.size() / 3; - vt_offset += shapes[i].mesh.texcoords.size() / 2; - } fclose(fp); From d174e625a22a80bd9cdb60c77461d93b6de32437 Mon Sep 17 00:00:00 2001 From: LZaw <28070263+LZaw@users.noreply.github.com> Date: Thu, 8 Mar 2018 15:58:39 +0100 Subject: [PATCH 07/12] Update obj_writer.h --- examples/obj_sticher/obj_writer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/obj_sticher/obj_writer.h b/examples/obj_sticher/obj_writer.h index aa3b269..262c681 100644 --- a/examples/obj_sticher/obj_writer.h +++ b/examples/obj_sticher/obj_writer.h @@ -1,7 +1,7 @@ #ifndef __OBJ_WRITER_H__ #define __OBJ_WRITER_H__ -#include "tiny_obj_loader.h" +#include "../../tiny_obj_loader.h" extern bool WriteObj(const std::string& filename, const tinyobj::attrib_t& attributes, const std::vector& shapes, const std::vector& materials, bool coordTransform = false); From a4eabf54b11f8f693c7849a32ca426c085ff83d1 Mon Sep 17 00:00:00 2001 From: LZaw <28070263+LZaw@users.noreply.github.com> Date: Fri, 9 Mar 2018 13:43:52 +0100 Subject: [PATCH 08/12] Adapt 1.0.x datastructure --- examples/obj_sticher/obj_sticher.cc | 117 ++++++++++++++++++++-------- 1 file changed, 83 insertions(+), 34 deletions(-) diff --git a/examples/obj_sticher/obj_sticher.cc b/examples/obj_sticher/obj_sticher.cc index f59fee4..ae3b093 100644 --- a/examples/obj_sticher/obj_sticher.cc +++ b/examples/obj_sticher/obj_sticher.cc @@ -1,6 +1,8 @@ // -// Stiches multiple .obj files into one .obj. +// Stiches multiple .obj files into one .obj. // + +#define TINYOBJLOADER_IMPLEMENTATION #include "../../tiny_obj_loader.h" #include "obj_writer.h" @@ -11,27 +13,59 @@ typedef std::vector Shape; typedef std::vector Material; +typedef tinyobj::attrib_t Attribute; void StichObjs( + tinyobj::attrib_t& out_attribute, std::vector& out_shape, std::vector& out_material, + const std::vector& attributes, const std::vector& shapes, const std::vector& materials) { - int numShapes = 0; - for (size_t i = 0; i < shapes.size(); i++) { - numShapes += (int)shapes[i].size(); + // The amount of attributes, shape-vectors and material-vecotrs should be the same. + if(attributes.size() != shapes.size() && attributes.size() != materials.size()){ + std::cerr << "Size of attributes, shapes and Materials don't fit!" << attributes.size() << " " << shapes.size() <<" " << materials.size() << std::endl;; + exit(1); + } + int num_shapes = 0; + // 4 values (vertices, normals, texcoords, colors) + std::vector num_attributes(4, 0); + int num_materials = 0; + for(int i = 0; i < shapes.size(); i++){ + num_shapes += shapes[i].size(); + } + for(int i = 0; i < attributes.size(); i++){ + num_attributes[0] += attributes[i].vertices.size(); + num_attributes[1] += attributes[i].normals.size(); + num_attributes[2] += attributes[i].texcoords.size(); + num_attributes[3] += attributes[i].colors.size(); + } + for(int i = 0; i < materials.size(); i++){ + num_materials += materials[i].size(); } - printf("Total # of shapes = %d\n", numShapes); - int materialIdOffset = 0; + // More performant, than push_back + out_attribute.vertices.resize(num_attributes[0]); + out_attribute.normals.resize(num_attributes[1]); + out_attribute.texcoords.resize(num_attributes[2]); + out_attribute.colors.resize(num_attributes[3]); + out_shape.resize(num_shapes); + out_material.resize(num_materials); - size_t face_offset = 0; + int material_id_offset = 0; + int shape_id_offset = 0; + int vertex_idx_offset = 0; + int normal_idx_offset = 0; + int texcoord_idx_offset = 0; + int color_idx_offset = 0; + + // shapes.size() = attributes.size() = materials.size() for (size_t i = 0; i < shapes.size(); i++) { + // Copy shapes for (size_t k = 0; k < shapes[i].size(); k++) { - std::string new_name = shapes[i][k].name; // Add suffix char buf[1024]; @@ -39,36 +73,51 @@ StichObjs( new_name += std::string(buf); printf("shape[%ld][%ld].name = %s\n", i, k, shapes[i][k].name.c_str()); - assert((shapes[i][k].mesh.indices.size() % 3) == 0); - assert((shapes[i][k].mesh.positions.size() % 3) == 0); tinyobj::shape_t new_shape = shapes[i][k]; - // Add offset. - for (size_t f = 0; f < new_shape.mesh.material_ids.size(); f++) { - new_shape.mesh.material_ids[f] += materialIdOffset; + // Add material offset. + for(size_t f = 0; f < new_shape.mesh.material_ids.size(); f++) { + new_shape.mesh.material_ids[f] += material_id_offset; + } + // Add indices offset. + for(size_t f = 0; f < new_shape.mesh.indices.size(); f++){ + tinyobj::index_t& ref = new_shape.mesh.indices[f]; + if(ref.vertex_index > -1){ + ref.vertex_index += vertex_idx_offset; + } + if(ref.normal_index > -1){ + ref.normal_index += normal_idx_offset; + } + if(ref.texcoord_index > -1){ + ref.texcoord_index += texcoord_idx_offset; + } } new_shape.name = new_name; printf("shape[%ld][%ld].new_name = %s\n", i, k, new_shape.name.c_str()); - out_shape.push_back(new_shape); + out_shape[shape_id_offset++] = new_shape; } - materialIdOffset += materials[i].size(); - } - - for (size_t i = 0; i < materials.size(); i++) { + // Copy materials for (size_t k = 0; k < materials[i].size(); k++) { - out_material.push_back(materials[i][k]); + out_material[material_id_offset++] = materials[i][k]; } - } + // Copy attributes (3 floats per vertex, 3 floats per normal, 2 floats per texture-coordinate, 3 floats per color) + // You could also include a check here, if the sizes are dividable by 3 (resp. 2), but it's safe to simply assume, they do. + std::copy(attributes[i].vertices.begin(), attributes[i].vertices.end(), out_attribute.vertices.begin() + vertex_idx_offset * 3); + vertex_idx_offset += attributes[i].vertices.size() / 3; + std::copy(attributes[i].normals.begin(), attributes[i].normals.end(), out_attribute.normals.begin() + normal_idx_offset * 3); + normal_idx_offset += attributes[i].normals.size() / 3; + std::copy(attributes[i].texcoords.begin(), attributes[i].texcoords.end(), out_attribute.texcoords.begin() + texcoord_idx_offset * 2); + texcoord_idx_offset += attributes[i].texcoords.size() / 2; + std::copy(attributes[i].colors.begin(), attributes[i].colors.end(), out_attribute.colors.begin() + color_idx_offset * 3); + color_idx_offset += attributes[i].colors.size(); + } } -int -main( - int argc, - char **argv) +int main(int argc, char **argv) { if (argc < 3) { printf("Usage: obj_sticher input0.obj input1.obj ... output.obj\n"); @@ -78,16 +127,15 @@ main( int num_objfiles = argc - 2; std::string out_filename = std::string(argv[argc-1]); // last element - std::vector shapes; - std::vector materials; - shapes.resize(num_objfiles); - materials.resize(num_objfiles); + std::vector attributes(num_objfiles); + std::vector shapes(num_objfiles); + std::vector materials(num_objfiles); for (int i = 0; i < num_objfiles; i++) { std::cout << "Loading " << argv[i+1] << " ... " << std::flush; std::string err; - bool ret = tinyobj::LoadObj(shapes[i], materials[i], err, argv[i+1]); + bool ret = tinyobj::LoadObj(&attributes[i], &shapes[i], &materials[i], &err, argv[i+1]); if (!err.empty()) { std::cerr << err << std::endl; } @@ -98,12 +146,13 @@ main( std::cout << "DONE." << std::endl; } - std::vector out_shape; - std::vector out_material; - StichObjs(out_shape, out_material, shapes, materials); + Attribute out_attribute; + Shape out_shape; + Material out_material; + StichObjs(out_attribute, out_shape, out_material, attributes, shapes, materials); - bool coordTransform = true; - bool ret = WriteObj(out_filename, out_shape, out_material, coordTransform); + bool coordTransform = false; + bool ret = WriteObj(out_filename, out_attribute, out_shape, out_material, coordTransform); assert(ret); return 0; From 94f1dc15b3b07c011ef7ae2029d1b3646942aaad Mon Sep 17 00:00:00 2001 From: LZaw <28070263+LZaw@users.noreply.github.com> Date: Fri, 9 Mar 2018 13:48:05 +0100 Subject: [PATCH 09/12] Update obj_sticher.cc --- examples/obj_sticher/obj_sticher.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/obj_sticher/obj_sticher.cc b/examples/obj_sticher/obj_sticher.cc index ae3b093..9032722 100644 --- a/examples/obj_sticher/obj_sticher.cc +++ b/examples/obj_sticher/obj_sticher.cc @@ -151,7 +151,7 @@ int main(int argc, char **argv) Material out_material; StichObjs(out_attribute, out_shape, out_material, attributes, shapes, materials); - bool coordTransform = false; + bool coordTransform = true; bool ret = WriteObj(out_filename, out_attribute, out_shape, out_material, coordTransform); assert(ret); From f95510b04b949fa0865f3e86114787073fd0824b Mon Sep 17 00:00:00 2001 From: LZaw <28070263+LZaw@users.noreply.github.com> Date: Fri, 9 Mar 2018 13:57:18 +0100 Subject: [PATCH 10/12] Removed multiplication for color_idx_offset --- examples/obj_sticher/obj_sticher.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/obj_sticher/obj_sticher.cc b/examples/obj_sticher/obj_sticher.cc index 9032722..47c06eb 100644 --- a/examples/obj_sticher/obj_sticher.cc +++ b/examples/obj_sticher/obj_sticher.cc @@ -112,7 +112,7 @@ StichObjs( normal_idx_offset += attributes[i].normals.size() / 3; std::copy(attributes[i].texcoords.begin(), attributes[i].texcoords.end(), out_attribute.texcoords.begin() + texcoord_idx_offset * 2); texcoord_idx_offset += attributes[i].texcoords.size() / 2; - std::copy(attributes[i].colors.begin(), attributes[i].colors.end(), out_attribute.colors.begin() + color_idx_offset * 3); + std::copy(attributes[i].colors.begin(), attributes[i].colors.end(), out_attribute.colors.begin() + color_idx_offset); color_idx_offset += attributes[i].colors.size(); } } From 7fcfafb39a35af3b545dca53879dfd7aeba591e3 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Mon, 26 Mar 2018 20:09:56 +0900 Subject: [PATCH 11/12] Fix compilation for double precision mode(Fixes #167). --- experimental/README.md | 10 ++++++ tiny_obj_loader.h | 79 ++++++++++++++++++++++-------------------- 2 files changed, 52 insertions(+), 37 deletions(-) diff --git a/experimental/README.md b/experimental/README.md index 99378ce..a0603af 100644 --- a/experimental/README.md +++ b/experimental/README.md @@ -6,11 +6,21 @@ * C++-11 compiler +## How to build + +``` +$ premak5 gmake +``` + ## Compile options * zstd compressed .obj support. `--with-zstd` premake option. * gzip compressed .obj support. `--with-zlib` premake option. +## Notes on AMD GPU + Linux + +You may need to link with libdrm(`-ldrm`). + ## Licenses * lfpAlloc : MIT license. diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index ac1421e..a6c4a02 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -57,6 +57,9 @@ namespace tinyobj { #if __has_warning("-Wzero-as-null-pointer-constant") #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" #endif + +#pragma clang diagnostic ignored "-Wpadded" + #endif // https://en.wikipedia.org/wiki/Wavefront_.obj_file says ... @@ -369,6 +372,7 @@ void LoadMtl(std::map *material_map, #include #include #include +#include #include #include @@ -866,22 +870,22 @@ static bool ParseTextureNameAndOption(std::string *texname, } else { texopt->imfchan = 'm'; } - texopt->bump_multiplier = 1.0f; + texopt->bump_multiplier = static_cast(1.0); texopt->clamp = false; texopt->blendu = true; texopt->blendv = true; - texopt->sharpness = 1.0f; - texopt->brightness = 0.0f; - texopt->contrast = 1.0f; - texopt->origin_offset[0] = 0.0f; - texopt->origin_offset[1] = 0.0f; - texopt->origin_offset[2] = 0.0f; - texopt->scale[0] = 1.0f; - texopt->scale[1] = 1.0f; - texopt->scale[2] = 1.0f; - texopt->turbulence[0] = 0.0f; - texopt->turbulence[1] = 0.0f; - texopt->turbulence[2] = 0.0f; + texopt->sharpness = static_cast(1.0); + texopt->brightness = static_cast(0.0); + texopt->contrast = static_cast(1.0); + texopt->origin_offset[0] = static_cast(0.0); + texopt->origin_offset[1] = static_cast(0.0); + texopt->origin_offset[2] = static_cast(0.0); + texopt->scale[0] = static_cast(1.0); + texopt->scale[1] = static_cast(1.0); + texopt->scale[2] = static_cast(1.0); + texopt->turbulence[0] = static_cast(0.0); + texopt->turbulence[1] = static_cast(0.0); + texopt->turbulence[2] = static_cast(0.0); texopt->type = TEXTURE_TYPE_NONE; const char *token = linebuf; // Assume line ends with NULL @@ -967,24 +971,24 @@ static void InitMaterial(material_t *material) { material->reflection_texname = ""; material->alpha_texname = ""; for (int i = 0; i < 3; i++) { - material->ambient[i] = 0.f; - material->diffuse[i] = 0.f; - material->specular[i] = 0.f; - material->transmittance[i] = 0.f; - material->emission[i] = 0.f; + material->ambient[i] = static_cast(0.0); + material->diffuse[i] = static_cast(0.0); + material->specular[i] = static_cast(0.0); + material->transmittance[i] = static_cast(0.0); + material->emission[i] = static_cast(0.0); } material->illum = 0; - material->dissolve = 1.f; - material->shininess = 1.f; - material->ior = 1.f; + material->dissolve = static_cast(1.0); + material->shininess = static_cast(1.0); + material->ior = static_cast(1.0); - material->roughness = 0.f; - material->metallic = 0.f; - material->sheen = 0.f; - material->clearcoat_thickness = 0.f; - material->clearcoat_roughness = 0.f; - material->anisotropy_rotation = 0.f; - material->anisotropy = 0.f; + material->roughness = static_cast(0.0); + material->metallic = static_cast(0.0); + material->sheen = static_cast(0.0); + material->clearcoat_thickness = static_cast(0.0); + material->clearcoat_roughness = static_cast(0.0); + material->anisotropy_rotation = static_cast(0.0); + material->anisotropy = static_cast(0.0); material->roughness_texname = ""; material->metallic_texname = ""; material->sheen_texname = ""; @@ -995,8 +999,9 @@ static void InitMaterial(material_t *material) { } // code from https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html -static int pnpoly(int nvert, float *vertx, float *verty, float testx, - float 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)) && @@ -1054,10 +1059,10 @@ static bool exportFaceGroupToShape(shape_t *shape, real_t e1x = v2x - v1x; real_t e1y = v2y - v1y; real_t e1z = v2z - v1z; - float cx = std::fabs(e0y * e1z - e0z * e1y); - float cy = std::fabs(e0z * e1x - e0x * e1z); - float cz = std::fabs(e0x * e1y - e0y * e1x); - const float epsilon = 0.0001f; + 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) { @@ -1079,7 +1084,7 @@ static bool exportFaceGroupToShape(shape_t *shape, 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) * 0.5f; + area += (v0x * v1y - v0y * v1x) * static_cast(0.5); } int maxRounds = @@ -1108,7 +1113,7 @@ static bool exportFaceGroupToShape(shape_t *shape, real_t e1y = vy[2] - vy[1]; real_t cross = e0x * e1y - e0y * e1x; // if an internal angle - if (cross * area < 0.0f) { + if (cross * area < static_cast(0.0)) { guess_vert += 1; continue; } @@ -1396,7 +1401,7 @@ void LoadMtl(std::map *material_map, // We invert value of Tr(assume Tr is in range [0, 1]) // NOTE: Interpretation of Tr is application(exporter) dependent. For // some application(e.g. 3ds max obj exporter), Tr = d(Issue 43) - material.dissolve = 1.0f - parseReal(&token); + material.dissolve = static_cast(1.0) - parseReal(&token); } has_tr = true; continue; From a39a6b481c9e4251aae2dd10393a9e21f8cd7c7d Mon Sep 17 00:00:00 2001 From: Victor Ripplinger Date: Mon, 26 Mar 2018 16:28:13 +0200 Subject: [PATCH 12/12] Fix bug that discarded some chunks. The OBJ loader was discarding the first line of a file or a chunk following one that ends with a newline. --- experimental/tinyobj_loader_opt.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/experimental/tinyobj_loader_opt.h b/experimental/tinyobj_loader_opt.h index 9d17597..e6c0b20 100644 --- a/experimental/tinyobj_loader_opt.h +++ b/experimental/tinyobj_loader_opt.h @@ -1306,15 +1306,17 @@ bool parseObj(attrib_t *attrib, std::vector *shapes, end_idx = len - 1; } + // true if the line currently read must be added to the current line info + bool new_line_found = (t == 0) || is_line_ending(buf, start_idx - 1, end_idx); + size_t prev_pos = start_idx; for (size_t i = start_idx; i < end_idx; i++) { if (is_line_ending(buf, i, end_idx)) { - if ((t > 0) && (prev_pos == start_idx) && - (!is_line_ending(buf, start_idx - 1, end_idx))) { + if (!new_line_found) { // first linebreak found in (chunk > 0), and a line before this // linebreak belongs to previous chunk, so skip it. prev_pos = i + 1; - continue; + new_line_found = true; } else { LineInfo info; info.pos = prev_pos; @@ -1330,7 +1332,7 @@ bool parseObj(attrib_t *attrib, std::vector *shapes, } // If at least one line started in this chunk, find where it ends in the rest of the buffer - if ((prev_pos != start_idx) && (t < num_threads) && (buf[end_idx - 1] != '\n')) { + if (new_line_found && (t < num_threads) && (buf[end_idx - 1] != '\n')) { for (size_t i = end_idx; i < len; i++) { if (is_line_ending(buf, i, len)) { LineInfo info;