diff --git a/README.md b/README.md index 47477f8..69ffb89 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,7 @@ TinyObjLoader is successfully used in ... * PBR material extension for .MTL. Its proposed here: http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr * Callback API for custom loading. * Double precision support(for HPC application). +* Smoothing group ## TODO @@ -105,8 +106,6 @@ TinyObjLoader is successfully used in ... * [ ] Fix obj_sticker example. * [ ] More unit test codes. * [x] Texture options -* [ ] Normal vector generation - * [ ] Support smoothing groups ## License diff --git a/examples/viewer/viewer.cc b/examples/viewer/viewer.cc index 8985809..b5de2e6 100644 --- a/examples/viewer/viewer.cc +++ b/examples/viewer/viewer.cc @@ -190,90 +190,101 @@ static void CalcNormal(float N[3], float v0[3], float v1[3], float v2[3]) { float len = sqrtf(len2); N[0] /= len; - N[1] /= len; - N[2] /= len; + N[1] /= len; + N[2] /= len; } } -namespace // Local utility functions +namespace // Local utility functions { - void addBtoA(float a[3], float b[3]) - { - for (size_t i = 0; i < 3; ++i) - a[i] += b[i]; - } +struct vec3 { + float v[3]; + vec3() { + v[0] = 0.0f; + v[1] = 0.0f; + v[2] = 0.0f; + } +}; - void assignBtoA(float a[3], float b[3]) - { - for (size_t i = 0; i < 3; ++i) - a[i] = b[i]; - } +void normalizeVector(vec3 &v) { + float len2 = v.v[0] * v.v[0] + v.v[1] * v.v[1] + v.v[2] * v.v[2]; + if (len2 > 0.0f) { + float len = sqrtf(len2); - void normalizeVector(float N[3]) - { - float len2 = N[0] * N[0] + N[1] * N[1] + N[2] * N[2]; - if (len2 > 0.0f) { - float len = sqrtf(len2); + v.v[0] /= len; + v.v[1] /= len; + v.v[2] /= len; + } +} - N[0] /= len; - N[1] /= len; - N[2] /= len; - } - } +// Check if `mesh_t` contains smoothing group id. +bool hasSmoothingGroup(const tinyobj::shape_t& shape) +{ + for (size_t i = 0; i < shape.mesh.smoothing_group_ids.size(); i++) { + if (shape.mesh.smoothing_group_ids[i] > 0) { + return true; + } + } + return false; +} - void computeSmoothingNormals(tinyobj::attrib_t &attrib, tinyobj::shape_t &shape, - std::map& smoothVertexNormals) - { - smoothVertexNormals.clear(); - std::map::iterator iter; +void computeSmoothingNormals(const tinyobj::attrib_t& attrib, const tinyobj::shape_t& shape, + std::map& smoothVertexNormals) { + smoothVertexNormals.clear(); + std::map::iterator iter; - for (size_t f = 0; f < shape.mesh.indices.size() / 3; f++) { - // Get the three indexes of the face (all faces are triangular) - tinyobj::index_t idx0 = shape.mesh.indices[3 * f + 0]; - tinyobj::index_t idx1 = shape.mesh.indices[3 * f + 1]; - tinyobj::index_t idx2 = shape.mesh.indices[3 * f + 2]; + for (size_t f = 0; f < shape.mesh.indices.size() / 3; f++) { + // Get the three indexes of the face (all faces are triangular) + tinyobj::index_t idx0 = shape.mesh.indices[3 * f + 0]; + tinyobj::index_t idx1 = shape.mesh.indices[3 * f + 1]; + tinyobj::index_t idx2 = shape.mesh.indices[3 * f + 2]; - // Get the three vertex indexes and coordinates - int vi[3]; // indexes - float v[3][3]; // coordinates + // Get the three vertex indexes and coordinates + int vi[3]; // indexes + float v[3][3]; // coordinates - for (int k = 0; k < 3; k++) { - vi[0] = idx0.vertex_index; - vi[1] = idx1.vertex_index; - vi[2] = idx2.vertex_index; - assert(vi[0] >= 0); - assert(vi[1] >= 0); - assert(vi[2] >= 0); + for (int k = 0; k < 3; k++) { + vi[0] = idx0.vertex_index; + vi[1] = idx1.vertex_index; + vi[2] = idx2.vertex_index; + assert(vi[0] >= 0); + assert(vi[1] >= 0); + assert(vi[2] >= 0); - v[0][k] = attrib.vertices[3 * vi[0] + k]; - v[1][k] = attrib.vertices[3 * vi[1] + k]; - v[2][k] = attrib.vertices[3 * vi[2] + k]; - } + v[0][k] = attrib.vertices[3 * vi[0] + k]; + v[1][k] = attrib.vertices[3 * vi[1] + k]; + v[2][k] = attrib.vertices[3 * vi[2] + k]; + } - // Compute the normal of the face - float normal[3]; - CalcNormal(normal, v[0], v[1], v[2]); + // Compute the normal of the face + float normal[3]; + CalcNormal(normal, v[0], v[1], v[2]); - // Add the normal to the three vertexes - for (size_t i = 0; i < 3; ++i) - { - iter = smoothVertexNormals.find(vi[i]); - if (iter != smoothVertexNormals.end()) - addBtoA(iter->second, normal); - else - assignBtoA(smoothVertexNormals[vi[i]], normal); - } + // Add the normal to the three vertexes + for (size_t i = 0; i < 3; ++i) { + iter = smoothVertexNormals.find(vi[i]); + if (iter != smoothVertexNormals.end()) { + // add + iter->second.v[0] += normal[0]; + iter->second.v[1] += normal[1]; + iter->second.v[2] += normal[2]; + } else { + smoothVertexNormals[vi[i]].v[0] = normal[0]; + smoothVertexNormals[vi[i]].v[1] = normal[1]; + smoothVertexNormals[vi[i]].v[2] = normal[2]; + } + } - } // f + } // f - // Normalize the normals, that is, make them unit vectors - for (iter = smoothVertexNormals.begin(); iter != smoothVertexNormals.end(); iter++) - { - normalizeVector(iter->second); - } + // Normalize the normals, that is, make them unit vectors + for (iter = smoothVertexNormals.begin(); iter != smoothVertexNormals.end(); + iter++) { + normalizeVector(iter->second); + } - } // computeSmoothingNormals -} // namespace +} // computeSmoothingNormals +} // namespace static bool LoadObjAndConvert(float bmin[3], float bmax[3], std::vector* drawObjects, @@ -389,12 +400,14 @@ static bool LoadObjAndConvert(float bmin[3], float bmax[3], DrawObject o; std::vector buffer; // pos(3float), normal(3float), color(3float) - // Check for smoothing group and compute smoothing normals - std::map smoothVertexNormals; - if (shapes[s].smoothingGroupId > 0) - computeSmoothingNormals(attrib, shapes[s], smoothVertexNormals); + // Check for smoothing group and compute smoothing normals + std::map smoothVertexNormals; + if (hasSmoothingGroup(shapes[s]) > 0) { + std::cout << "Compute smoothingNormal for shape [" << s << "]" << std::endl; + computeSmoothingNormals(attrib, shapes[s], smoothVertexNormals); + } - for (size_t f = 0; f < shapes[s].mesh.indices.size() / 3; f++) { + for (size_t f = 0; f < shapes[s].mesh.indices.size() / 3; f++) { tinyobj::index_t idx0 = shapes[s].mesh.indices[3 * f + 0]; tinyobj::index_t idx1 = shapes[s].mesh.indices[3 * f + 1]; tinyobj::index_t idx2 = shapes[s].mesh.indices[3 * f + 2]; @@ -498,19 +511,28 @@ static bool LoadObjAndConvert(float bmin[3], float bmax[3], invalid_normal_index = true; } - if (invalid_normal_index && !smoothVertexNormals.empty()) { - // Use smoothing normals - int f0 = idx0.vertex_index; - int f1 = idx1.vertex_index; - int f2 = idx2.vertex_index; + if (invalid_normal_index && !smoothVertexNormals.empty()) { + // Use smoothing normals + int f0 = idx0.vertex_index; + int f1 = idx1.vertex_index; + int f2 = idx2.vertex_index; - if (f0 >= 0 && f1 >= 0 && f2 >= 0) { - assignBtoA(n[0], smoothVertexNormals[f0]); - assignBtoA(n[1], smoothVertexNormals[f1]); - assignBtoA(n[2], smoothVertexNormals[f2]); - invalid_normal_index = false; - } - } + if (f0 >= 0 && f1 >= 0 && f2 >= 0) { + n[0][0] = smoothVertexNormals[f0].v[0]; + n[0][1] = smoothVertexNormals[f0].v[1]; + n[0][2] = smoothVertexNormals[f0].v[2]; + + n[1][0] = smoothVertexNormals[f1].v[0]; + n[1][1] = smoothVertexNormals[f1].v[1]; + n[1][2] = smoothVertexNormals[f1].v[2]; + + n[2][0] = smoothVertexNormals[f2].v[0]; + n[2][1] = smoothVertexNormals[f2].v[1]; + n[2][2] = smoothVertexNormals[f2].v[2]; + + invalid_normal_index = false; + } + } if (invalid_normal_index) { // compute geometric normal diff --git a/models/smoothing-normal.mtl b/models/smoothing-normal.mtl new file mode 100644 index 0000000..d3a1c7a --- /dev/null +++ b/models/smoothing-normal.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/smoothing-normal.obj b/models/smoothing-normal.obj new file mode 100644 index 0000000..8a89dec --- /dev/null +++ b/models/smoothing-normal.obj @@ -0,0 +1,35 @@ +mtllib smoothing-normal.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 +s 1 +f 1 2 3 4 +#g bottom cube +#usemtl white +s 1 +f 2 6 7 3 + +g back cube +# expects white material +s off +f 8 7 6 5 +#g right cube +#usemtl red +#f 4 3 7 8 +#g top cube +#usemtl white +#f 5 1 4 8 +#g left cube +#usemtl green +#f 5 6 2 1 +# 6 elements diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index 9c1f521..ac1421e 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2012-2017 Syoyo Fujita and many contributors. +Copyright (c) 2012-2018 Syoyo Fujita and many contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -23,6 +23,7 @@ THE SOFTWARE. */ // +// version 1.1.1 : Support smoothing groups(#162) // version 1.1.0 : Support parsing vertex color(#144) // version 1.0.8 : Fix parsing `g` tag just after `usemtl`(#138) // version 1.0.7 : Support multiple tex options(#126)