16 Commits

Author SHA1 Message Date
Syoyo Fujita
65df7c4794 Add smooting group test .obj with two squares. 2018-02-04 14:16:14 +09:00
Syoyo Fujita
eaf8623e61 Fix build of viewer example.
Bump version.
2018-02-04 14:05:36 +09:00
Syoyo Fujita
dcad3e6c50 Add per-face smoothing groupd ids to mesh_t.
Add unit test for smoothing groups.
2018-01-31 22:13:52 +09:00
Syoyo Fujita
2dfc37a475 Introduce face_t structure which stores per-face smoothing group and vertex indices. 2018-01-31 20:07:34 +09:00
Syoyo Fujita
9a6390cdee Add padding. 2018-01-31 14:24:37 +09:00
gopalss
78c7c9011a Fix for #29 Normal Generation by Smoothing Group Id - Commit 4 2018-01-30 10:55:10 -05:00
gopalss
1754669b07 Fix for #29 Normal Generation by Smoothing Group Id - Commit 3 2018-01-30 10:31:02 -05:00
gopalss
1f7b4a49c0 Fix for #29 Normal Generation by Smoothing Group Id - Commit 2 2018-01-30 09:51:16 -05:00
gopalss
15f47e2e35 Fix for #29 Normal Generation by Smoothing Group Id 2018-01-29 18:37:39 -05:00
Syoyo Fujita
12bf4165be Fix assertion when there is no normal/texcoord assigned to the face. Fixes #161.
Suppress clang warnings.
2018-01-29 13:41:33 +09:00
Syoyo Fujita
f206a56362 Merge pull request #160 from arosh/master
PR for open bounty test 2.
2018-01-17 01:56:07 +09:00
Sho Iizuka
13951d6459 Remove empty lines at EOF 2018-01-16 18:11:05 +09:00
Syoyo Fujita
5383e3400a Merge pull request #156 from raspofabs/master
ear clipping using pnpoly algorithm
2018-01-15 17:26:11 +09:00
Richard Fabian
ac3c36ffda increase the size of the epsilon to give better axis choice for triangulation 2018-01-14 18:53:20 +00:00
Richard Fabian
3b681805aa fixed the bug where the wrong axis was selected for doing the pnpoly check 2018-01-14 16:45:59 +00:00
Richard Fabian
ca49183639 added ear clipping triangulation 2018-01-14 14:21:46 +00:00
12 changed files with 787 additions and 170 deletions

View File

@@ -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 * PBR material extension for .MTL. Its proposed here: http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr
* Callback API for custom loading. * Callback API for custom loading.
* Double precision support(for HPC application). * Double precision support(for HPC application).
* Smoothing group
## TODO ## TODO
@@ -105,8 +106,6 @@ TinyObjLoader is successfully used in ...
* [ ] Fix obj_sticker example. * [ ] Fix obj_sticker example.
* [ ] More unit test codes. * [ ] More unit test codes.
* [x] Texture options * [x] Texture options
* [ ] Normal vector generation
* [ ] Support smoothing groups
## License ## License

View File

@@ -172,5 +172,3 @@ bool WriteObj(const std::string& filename, const std::vector<tinyobj::shape_t>&
return ret; return ret;
} }

View File

@@ -37,11 +37,11 @@ extern "C" {
#include <windows.h> #include <windows.h>
#ifdef max #ifdef max
#undef max #undef max
#endif #endif
#ifdef min #ifdef min
#undef min #undef min
#endif #endif
#include <mmsystem.h> #include <mmsystem.h>
@@ -143,15 +143,15 @@ float eye[3], lookat[3], up[3];
GLFWwindow* window; GLFWwindow* window;
static std::string GetBaseDir(const std::string &filepath) { static std::string GetBaseDir(const std::string& filepath) {
if (filepath.find_last_of("/\\") != std::string::npos) if (filepath.find_last_of("/\\") != std::string::npos)
return filepath.substr(0, filepath.find_last_of("/\\")); return filepath.substr(0, filepath.find_last_of("/\\"));
return ""; return "";
} }
static bool FileExists(const std::string &abs_filename) { static bool FileExists(const std::string& abs_filename) {
bool ret; bool ret;
FILE *fp = fopen(abs_filename.c_str(), "rb"); FILE* fp = fopen(abs_filename.c_str(), "rb");
if (fp) { if (fp) {
ret = true; ret = true;
fclose(fp); fclose(fp);
@@ -191,14 +191,106 @@ static void CalcNormal(float N[3], float v0[3], float v1[3], float v2[3]) {
N[0] /= len; N[0] /= len;
N[1] /= len; N[1] /= len;
N[2] /= len;
} }
} }
namespace // Local utility functions
{
struct vec3 {
float v[3];
vec3() {
v[0] = 0.0f;
v[1] = 0.0f;
v[2] = 0.0f;
}
};
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);
v.v[0] /= len;
v.v[1] /= len;
v.v[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(const tinyobj::attrib_t& attrib, const tinyobj::shape_t& shape,
std::map<int, vec3>& smoothVertexNormals) {
smoothVertexNormals.clear();
std::map<int, vec3>::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];
// 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);
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]);
// 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
// Normalize the normals, that is, make them unit vectors
for (iter = smoothVertexNormals.begin(); iter != smoothVertexNormals.end();
iter++) {
normalizeVector(iter->second);
}
} // computeSmoothingNormals
} // namespace
static bool LoadObjAndConvert(float bmin[3], float bmax[3], static bool LoadObjAndConvert(float bmin[3], float bmax[3],
std::vector<DrawObject>* drawObjects, std::vector<DrawObject>* drawObjects,
std::vector<tinyobj::material_t>& materials, std::vector<tinyobj::material_t>& materials,
std::map<std::string, GLuint>& textures, std::map<std::string, GLuint>& textures,
const char* filename) { const char* filename) {
tinyobj::attrib_t attrib; tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes; std::vector<tinyobj::shape_t> shapes;
@@ -217,8 +309,8 @@ static bool LoadObjAndConvert(float bmin[3], float bmax[3],
#endif #endif
std::string err; std::string err;
bool ret = bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename,
tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename, base_dir.c_str()); base_dir.c_str());
if (!err.empty()) { if (!err.empty()) {
std::cerr << err << std::endl; std::cerr << err << std::endl;
} }
@@ -242,56 +334,62 @@ static bool LoadObjAndConvert(float bmin[3], float bmax[3],
materials.push_back(tinyobj::material_t()); materials.push_back(tinyobj::material_t());
for (size_t i = 0; i < materials.size(); i++) { for (size_t i = 0; i < materials.size(); i++) {
printf("material[%d].diffuse_texname = %s\n", int(i), materials[i].diffuse_texname.c_str()); printf("material[%d].diffuse_texname = %s\n", int(i),
materials[i].diffuse_texname.c_str());
} }
// Load diffuse textures // Load diffuse textures
{ {
for (size_t m = 0; m < materials.size(); m++) { for (size_t m = 0; m < materials.size(); m++) {
tinyobj::material_t* mp = &materials[m]; tinyobj::material_t* mp = &materials[m];
if (mp->diffuse_texname.length() > 0) {
// Only load the texture if it is not already loaded
if (textures.find(mp->diffuse_texname) == textures.end()) {
GLuint texture_id;
int w, h;
int comp;
std::string texture_filename = mp->diffuse_texname; if (mp->diffuse_texname.length() > 0) {
if (!FileExists(texture_filename)) { // Only load the texture if it is not already loaded
// Append base dir. if (textures.find(mp->diffuse_texname) == textures.end()) {
texture_filename = base_dir + mp->diffuse_texname; GLuint texture_id;
if (!FileExists(texture_filename)) { int w, h;
std::cerr << "Unable to find file: " << mp->diffuse_texname << std::endl; int comp;
exit(1);
}
}
unsigned char* image = stbi_load(texture_filename.c_str(), &w, &h, &comp, STBI_default);
if (!image) {
std::cerr << "Unable to load texture: " << texture_filename << std::endl;
exit(1);
}
std::cout << "Loaded texture: " << texture_filename << ", w = " << w << ", h = " << h << ", comp = " << comp << std::endl;
glGenTextures(1, &texture_id); std::string texture_filename = mp->diffuse_texname;
glBindTexture(GL_TEXTURE_2D, texture_id); if (!FileExists(texture_filename)) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // Append base dir.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); texture_filename = base_dir + mp->diffuse_texname;
if (comp == 3) { if (!FileExists(texture_filename)) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, image); std::cerr << "Unable to find file: " << mp->diffuse_texname
} << std::endl;
else if (comp == 4) { exit(1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image); }
} else {
assert(0); // TODO
}
glBindTexture(GL_TEXTURE_2D, 0);
stbi_image_free(image);
textures.insert(std::make_pair(mp->diffuse_texname, texture_id));
}
} }
unsigned char* image =
stbi_load(texture_filename.c_str(), &w, &h, &comp, STBI_default);
if (!image) {
std::cerr << "Unable to load texture: " << texture_filename
<< std::endl;
exit(1);
}
std::cout << "Loaded texture: " << texture_filename << ", w = " << w
<< ", h = " << h << ", comp = " << comp << std::endl;
glGenTextures(1, &texture_id);
glBindTexture(GL_TEXTURE_2D, texture_id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if (comp == 3) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB,
GL_UNSIGNED_BYTE, image);
} else if (comp == 4) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
GL_UNSIGNED_BYTE, image);
} else {
assert(0); // TODO
}
glBindTexture(GL_TEXTURE_2D, 0);
stbi_image_free(image);
textures.insert(std::make_pair(mp->diffuse_texname, texture_id));
}
} }
}
} }
bmin[0] = bmin[1] = bmin[2] = std::numeric_limits<float>::max(); bmin[0] = bmin[1] = bmin[2] = std::numeric_limits<float>::max();
@@ -301,30 +399,55 @@ static bool LoadObjAndConvert(float bmin[3], float bmax[3],
for (size_t s = 0; s < shapes.size(); s++) { for (size_t s = 0; s < shapes.size(); s++) {
DrawObject o; DrawObject o;
std::vector<float> buffer; // pos(3float), normal(3float), color(3float) std::vector<float> buffer; // pos(3float), normal(3float), color(3float)
// Check for smoothing group and compute smoothing normals
std::map<int, vec3> 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 idx0 = shapes[s].mesh.indices[3 * f + 0];
tinyobj::index_t idx1 = shapes[s].mesh.indices[3 * f + 1]; tinyobj::index_t idx1 = shapes[s].mesh.indices[3 * f + 1];
tinyobj::index_t idx2 = shapes[s].mesh.indices[3 * f + 2]; tinyobj::index_t idx2 = shapes[s].mesh.indices[3 * f + 2];
int current_material_id = shapes[s].mesh.material_ids[f]; int current_material_id = shapes[s].mesh.material_ids[f];
if ((current_material_id < 0) || (current_material_id >= static_cast<int>(materials.size()))) { if ((current_material_id < 0) ||
(current_material_id >= static_cast<int>(materials.size()))) {
// Invaid material ID. Use default material. // Invaid material ID. Use default material.
current_material_id = materials.size() - 1; // Default material is added to the last item in `materials`. current_material_id =
materials.size() -
1; // Default material is added to the last item in `materials`.
} }
//if (current_material_id >= materials.size()) { // if (current_material_id >= materials.size()) {
// std::cerr << "Invalid material index: " << current_material_id << std::endl; // std::cerr << "Invalid material index: " << current_material_id <<
// std::endl;
//} //}
// //
float diffuse[3]; float diffuse[3];
for (size_t i = 0; i < 3; i++) { for (size_t i = 0; i < 3; i++) {
diffuse[i] = materials[current_material_id].diffuse[i]; diffuse[i] = materials[current_material_id].diffuse[i];
} }
float tc[3][2]; float tc[3][2];
if (attrib.texcoords.size() > 0) { if (attrib.texcoords.size() > 0) {
assert(attrib.texcoords.size() > 2 * idx0.texcoord_index + 1); if ((idx0.texcoord_index < 0) || (idx1.texcoord_index < 0) ||
assert(attrib.texcoords.size() > 2 * idx1.texcoord_index + 1); (idx2.texcoord_index < 0)) {
assert(attrib.texcoords.size() > 2 * idx2.texcoord_index + 1); // face does not contain valid uv index.
tc[0][0] = 0.0f;
tc[0][1] = 0.0f;
tc[1][0] = 0.0f;
tc[1][1] = 0.0f;
tc[2][0] = 0.0f;
tc[2][1] = 0.0f;
} else {
assert(attrib.texcoords.size() >
size_t(2 * idx0.texcoord_index + 1));
assert(attrib.texcoords.size() >
size_t(2 * idx1.texcoord_index + 1));
assert(attrib.texcoords.size() >
size_t(2 * idx2.texcoord_index + 1));
// Flip Y coord. // Flip Y coord.
tc[0][0] = attrib.texcoords[2 * idx0.texcoord_index]; tc[0][0] = attrib.texcoords[2 * idx0.texcoord_index];
@@ -333,13 +456,14 @@ static bool LoadObjAndConvert(float bmin[3], float bmax[3],
tc[1][1] = 1.0f - attrib.texcoords[2 * idx1.texcoord_index + 1]; tc[1][1] = 1.0f - attrib.texcoords[2 * idx1.texcoord_index + 1];
tc[2][0] = attrib.texcoords[2 * idx2.texcoord_index]; tc[2][0] = attrib.texcoords[2 * idx2.texcoord_index];
tc[2][1] = 1.0f - attrib.texcoords[2 * idx2.texcoord_index + 1]; tc[2][1] = 1.0f - attrib.texcoords[2 * idx2.texcoord_index + 1];
}
} else { } else {
tc[0][0] = 0.0f; tc[0][0] = 0.0f;
tc[0][1] = 0.0f; tc[0][1] = 0.0f;
tc[1][0] = 0.0f; tc[1][0] = 0.0f;
tc[1][1] = 0.0f; tc[1][1] = 0.0f;
tc[2][0] = 0.0f; tc[2][0] = 0.0f;
tc[2][1] = 0.0f; tc[2][1] = 0.0f;
} }
float v[3][3]; float v[3][3];
@@ -363,27 +487,63 @@ static bool LoadObjAndConvert(float bmin[3], float bmax[3],
} }
float n[3][3]; float n[3][3];
if (attrib.normals.size() > 0) { {
int f0 = idx0.normal_index; bool invalid_normal_index = false;
int f1 = idx1.normal_index; if (attrib.normals.size() > 0) {
int f2 = idx2.normal_index; int nf0 = idx0.normal_index;
assert(f0 >= 0); int nf1 = idx1.normal_index;
assert(f1 >= 0); int nf2 = idx2.normal_index;
assert(f2 >= 0);
for (int k = 0; k < 3; k++) { if ((nf0 < 0) || (nf1 < 0) || (nf2 < 0)) {
n[0][k] = attrib.normals[3 * f0 + k]; // normal index is missing from this face.
n[1][k] = attrib.normals[3 * f1 + k]; invalid_normal_index = true;
n[2][k] = attrib.normals[3 * f2 + k]; } else {
for (int k = 0; k < 3; k++) {
assert(size_t(3 * nf0 + k) < attrib.normals.size());
assert(size_t(3 * nf1 + k) < attrib.normals.size());
assert(size_t(3 * nf2 + k) < attrib.normals.size());
n[0][k] = attrib.normals[3 * nf0 + k];
n[1][k] = attrib.normals[3 * nf1 + k];
n[2][k] = attrib.normals[3 * nf2 + k];
}
}
} else {
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 (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
CalcNormal(n[0], v[0], v[1], v[2]);
n[1][0] = n[0][0];
n[1][1] = n[0][1];
n[1][2] = n[0][2];
n[2][0] = n[0][0];
n[2][1] = n[0][1];
n[2][2] = n[0][2];
} }
} else {
// compute geometric normal
CalcNormal(n[0], v[0], v[1], v[2]);
n[1][0] = n[0][0];
n[1][1] = n[0][1];
n[1][2] = n[0][2];
n[2][0] = n[0][0];
n[2][1] = n[0][1];
n[2][2] = n[0][2];
} }
for (int k = 0; k < 3; k++) { for (int k = 0; k < 3; k++) {
@@ -396,11 +556,9 @@ static bool LoadObjAndConvert(float bmin[3], float bmax[3],
// Combine normal and diffuse to get color. // Combine normal and diffuse to get color.
float normal_factor = 0.2; float normal_factor = 0.2;
float diffuse_factor = 1 - normal_factor; float diffuse_factor = 1 - normal_factor;
float c[3] = { float c[3] = {n[k][0] * normal_factor + diffuse[0] * diffuse_factor,
n[k][0] * normal_factor + diffuse[0] * diffuse_factor, n[k][1] * normal_factor + diffuse[1] * diffuse_factor,
n[k][1] * normal_factor + diffuse[1] * diffuse_factor, n[k][2] * normal_factor + diffuse[2] * diffuse_factor};
n[k][2] * normal_factor + diffuse[2] * diffuse_factor
};
float len2 = c[0] * c[0] + c[1] * c[1] + c[2] * c[2]; float len2 = c[0] * c[0] + c[1] * c[1] + c[2] * c[2];
if (len2 > 0.0f) { if (len2 > 0.0f) {
float len = sqrtf(len2); float len = sqrtf(len2);
@@ -412,7 +570,7 @@ static bool LoadObjAndConvert(float bmin[3], float bmax[3],
buffer.push_back(c[0] * 0.5 + 0.5); buffer.push_back(c[0] * 0.5 + 0.5);
buffer.push_back(c[1] * 0.5 + 0.5); buffer.push_back(c[1] * 0.5 + 0.5);
buffer.push_back(c[2] * 0.5 + 0.5); buffer.push_back(c[2] * 0.5 + 0.5);
buffer.push_back(tc[k][0]); buffer.push_back(tc[k][0]);
buffer.push_back(tc[k][1]); buffer.push_back(tc[k][1]);
} }
@@ -422,19 +580,22 @@ static bool LoadObjAndConvert(float bmin[3], float bmax[3],
o.numTriangles = 0; o.numTriangles = 0;
// OpenGL viewer does not support texturing with per-face material. // OpenGL viewer does not support texturing with per-face material.
if (shapes[s].mesh.material_ids.size() > 0 && shapes[s].mesh.material_ids.size() > s) { if (shapes[s].mesh.material_ids.size() > 0 &&
o.material_id = shapes[s].mesh.material_ids[0]; // use the material ID of the first face. shapes[s].mesh.material_ids.size() > s) {
o.material_id = shapes[s].mesh.material_ids[0]; // use the material ID
// of the first face.
} else { } else {
o.material_id = materials.size() - 1; // = ID for default material. o.material_id = materials.size() - 1; // = ID for default material.
} }
printf("shape[%d] material_id %d\n", int(s), int(o.material_id)); printf("shape[%d] material_id %d\n", int(s), int(o.material_id));
if (buffer.size() > 0) { if (buffer.size() > 0) {
glGenBuffers(1, &o.vb_id); glGenBuffers(1, &o.vb_id);
glBindBuffer(GL_ARRAY_BUFFER, o.vb_id); glBindBuffer(GL_ARRAY_BUFFER, o.vb_id);
glBufferData(GL_ARRAY_BUFFER, buffer.size() * sizeof(float), &buffer.at(0), glBufferData(GL_ARRAY_BUFFER, buffer.size() * sizeof(float),
GL_STATIC_DRAW); &buffer.at(0), GL_STATIC_DRAW);
o.numTriangles = buffer.size() / (3 + 3 + 3 + 2) / 3; // 3:vtx, 3:normal, 3:col, 2:texcoord o.numTriangles = buffer.size() / (3 + 3 + 3 + 2) /
3; // 3:vtx, 3:normal, 3:col, 2:texcoord
printf("shape[%d] # of triangles = %d\n", static_cast<int>(s), printf("shape[%d] # of triangles = %d\n", static_cast<int>(s),
o.numTriangles); o.numTriangles);
@@ -467,7 +628,7 @@ static void reshapeFunc(GLFWwindow* window, int w, int h) {
} }
static void keyboardFunc(GLFWwindow* window, int key, int scancode, int action, static void keyboardFunc(GLFWwindow* window, int key, int scancode, int action,
int mods) { int mods) {
(void)window; (void)window;
(void)scancode; (void)scancode;
(void)mods; (void)mods;
@@ -549,7 +710,9 @@ static void motionFunc(GLFWwindow* window, double mouse_x, double mouse_y) {
prevMouseY = mouse_y; prevMouseY = mouse_y;
} }
static void Draw(const std::vector<DrawObject>& drawObjects, std::vector<tinyobj::material_t>& materials, std::map<std::string, GLuint>& textures) { static void Draw(const std::vector<DrawObject>& drawObjects,
std::vector<tinyobj::material_t>& materials,
std::map<std::string, GLuint>& textures) {
glPolygonMode(GL_FRONT, GL_FILL); glPolygonMode(GL_FRONT, GL_FILL);
glPolygonMode(GL_BACK, GL_FILL); glPolygonMode(GL_BACK, GL_FILL);
@@ -572,7 +735,7 @@ static void Draw(const std::vector<DrawObject>& drawObjects, std::vector<tinyobj
if ((o.material_id < materials.size())) { if ((o.material_id < materials.size())) {
std::string diffuse_texname = materials[o.material_id].diffuse_texname; std::string diffuse_texname = materials[o.material_id].diffuse_texname;
if (textures.find(diffuse_texname) != textures.end()) { if (textures.find(diffuse_texname) != textures.end()) {
glBindTexture(GL_TEXTURE_2D, textures[diffuse_texname]); glBindTexture(GL_TEXTURE_2D, textures[diffuse_texname]);
} }
} }
glVertexPointer(3, GL_FLOAT, stride, (const void*)0); glVertexPointer(3, GL_FLOAT, stride, (const void*)0);
@@ -668,7 +831,8 @@ int main(int argc, char** argv) {
float bmin[3], bmax[3]; float bmin[3], bmax[3];
std::vector<tinyobj::material_t> materials; std::vector<tinyobj::material_t> materials;
std::map<std::string, GLuint> textures; std::map<std::string, GLuint> textures;
if (false == LoadObjAndConvert(bmin, bmax, &gDrawObjects, materials, textures, argv[1])) { if (false == LoadObjAndConvert(bmin, bmax, &gDrawObjects, materials, textures,
argv[1])) {
return -1; return -1;
} }

View File

@@ -29,6 +29,13 @@ extern "C" {
#endif #endif
#endif #endif
#ifdef __clang__
#pragma clang diagnostic push
#if __has_warning("-Wzero-as-null-pointer-constant")
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
#endif
#endif
class timerutil { class timerutil {
public: public:
#ifdef _WIN32 #ifdef _WIN32

View File

@@ -0,0 +1,37 @@
o cube
mtllib cube.mtl
v -0.500000 -0.500000 0.500000
v 0.500000 -0.500000 0.500000
v -0.500000 0.500000 0.500000
v 0.500000 0.500000 0.500000
v -0.500000 0.500000 -0.500000
v 0.500000 0.500000 -0.500000
v -0.500000 -0.500000 -0.500000
v 0.500000 -0.500000 -0.500000
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 0.000000 1.000000
vt 1.000000 1.000000
g cube
usemtl cube
s 1
f 1/1 2/2 3/3
f 3/3 2/2 4/4
s 2
f 3/1 4/2 5/3
f 5/3 4/2 6/4
s 3
f 5/4 6/3 7/2
f 7/2 6/3 8/1
s 4
f 7/1 8/2 1/3
f 1/3 8/2 2/4
s 5
f 2/1 8/2 4/3
f 4/3 8/2 6/4
s 6
f 7/1 1/2 5/3
f 5 1 3

View File

@@ -0,0 +1,23 @@
newmtl test1
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.0000 0.0000 0.0000
Kd 0.5 0.2 0.2
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl test2
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.0000 0.0000 0.0000
Kd 0.2 0.5 0.2
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000

View File

@@ -0,0 +1,51 @@
# cube.obj
#
mtllib issue-162-smoothing-group.mtl
v -0.500000 -0.500000 0.500000
v 0.500000 -0.500000 0.500000
v -0.500000 0.500000 0.500000
v 0.500000 0.500000 0.500000
v -0.500000 0.500000 -0.500000
v 0.500000 0.500000 -0.500000
v -0.500000 -0.500000 -0.500000
v 0.500000 -0.500000 -0.500000
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 0.000000 1.000000
vt 1.000000 1.000000
vn 0.000000 0.000000 1.000000
vn 0.000000 1.000000 0.000000
vn 0.000000 0.000000 -1.000000
vn 0.000000 -1.000000 0.000000
vn 1.000000 0.000000 0.000000
vn -1.000000 0.000000 0.000000
usemtl test1
g test1
s 1
f 1/1/1 2/2/1 3/3/1
f 3/3/1 2/2/1 4/4/1
usemtl test2
g test2
s off
f 3/1/2 4/2/2 5/3/2
f 5/3/2 4/2/2 6/4/2
s 3
f 5/4/3 6/3/3 7/2/3
f 7/2/3 6/3/3 8/1/3
s 4
f 7/1/4 8/2/4 1/3/4
f 1/3/4 8/2/4 2/4/4
s 0
f 2/1/5 8/2/5 4/3/5
f 4/3/5 8/2/5 6/4/5
s 6
f 7/1/6 1/2/6 5/3/6
f 5/3/6 1/2/6 3/4/6

View File

@@ -0,0 +1,22 @@
# from tinyobjloader issue #29
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
v 4.000000 0.000000 -3.255298
v 4.000000 2.000000 -3.255298
#vn 0.000000 0.000000 1.000000
#vn 0.000000 0.000000 1.000000
#vn 0.276597 0.000000 0.960986
#vn 0.276597 0.000000 0.960986
#vn 0.531611 0.000000 0.846988
#vn 0.531611 0.000000 0.846988
# 6 vertices
# 6 normals
g all
s 1
f 1//1 2//2 3//3 4//4
f 4//4 3//3 5//5

View File

@@ -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

View File

@@ -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

View File

@@ -717,6 +717,40 @@ TEST_CASE("texture-name-whitespace", "[Issue145]") {
} }
TEST_CASE("smoothing-group", "[Issue162]") {
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string err;
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/issue-162-smoothing-group.obj", gMtlBasePath);
if (!err.empty()) {
std::cerr << "[Issue162] " << err << std::endl;
}
REQUIRE(true == ret);
REQUIRE(2 == shapes.size());
REQUIRE(2 == shapes[0].mesh.smoothing_group_ids.size());
REQUIRE(1 == shapes[0].mesh.smoothing_group_ids[0]);
REQUIRE(1 == shapes[0].mesh.smoothing_group_ids[1]);
REQUIRE(10 == shapes[1].mesh.smoothing_group_ids.size());
REQUIRE(0 == shapes[1].mesh.smoothing_group_ids[0]);
REQUIRE(0 == shapes[1].mesh.smoothing_group_ids[1]);
REQUIRE(3 == shapes[1].mesh.smoothing_group_ids[2]);
REQUIRE(3 == shapes[1].mesh.smoothing_group_ids[3]);
REQUIRE(4 == shapes[1].mesh.smoothing_group_ids[4]);
REQUIRE(4 == shapes[1].mesh.smoothing_group_ids[5]);
REQUIRE(0 == shapes[1].mesh.smoothing_group_ids[6]);
REQUIRE(0 == shapes[1].mesh.smoothing_group_ids[7]);
REQUIRE(6 == shapes[1].mesh.smoothing_group_ids[8]);
REQUIRE(6 == shapes[1].mesh.smoothing_group_ids[9]);
}
#if 0 #if 0
int int
main( main(

View File

@@ -1,7 +1,7 @@
/* /*
The MIT License (MIT) 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 Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal 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.1.0 : Support parsing vertex color(#144)
// version 1.0.8 : Fix parsing `g` tag just after `usemtl`(#138) // version 1.0.8 : Fix parsing `g` tag just after `usemtl`(#138)
// version 1.0.7 : Support multiple tex options(#126) // version 1.0.7 : Support multiple tex options(#126)
@@ -51,6 +52,13 @@ THE SOFTWARE.
namespace tinyobj { namespace tinyobj {
#ifdef __clang__
#pragma clang diagnostic push
#if __has_warning("-Wzero-as-null-pointer-constant")
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
#endif
#endif
// https://en.wikipedia.org/wiki/Wavefront_.obj_file says ... // https://en.wikipedia.org/wiki/Wavefront_.obj_file says ...
// //
// -blendu on | off # set horizontal texture blending // -blendu on | off # set horizontal texture blending
@@ -218,7 +226,10 @@ typedef struct {
// face. 3 = polygon, 4 = quad, // face. 3 = polygon, 4 = quad,
// ... Up to 255. // ... Up to 255.
std::vector<int> material_ids; // per-face material ID std::vector<int> material_ids; // per-face material ID
std::vector<tag_t> tags; // SubD tag std::vector<unsigned int> smoothing_group_ids; // per-face smoothing group
// ID(0 = off. positive value
// = group id)
std::vector<tag_t> tags; // SubD tag
} mesh_t; } mesh_t;
typedef struct { typedef struct {
@@ -366,14 +377,25 @@ namespace tinyobj {
MaterialReader::~MaterialReader() {} MaterialReader::~MaterialReader() {}
struct vertex_index { struct vertex_index_t {
int v_idx, vt_idx, vn_idx; int v_idx, vt_idx, vn_idx;
vertex_index() : v_idx(-1), vt_idx(-1), vn_idx(-1) {} vertex_index_t() : v_idx(-1), vt_idx(-1), vn_idx(-1) {}
explicit vertex_index(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) {} explicit vertex_index_t(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) {}
vertex_index(int vidx, int vtidx, int vnidx) vertex_index_t(int vidx, int vtidx, int vnidx)
: v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx) {} : v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx) {}
}; };
// Internal data structure for face representation
// index + smoothing group.
struct face_t {
unsigned int
smoothing_group_id; // smoothing group id. 0 = smoothing groupd is off.
int pad_;
std::vector<vertex_index_t> vertex_indices; // face vertex indices.
face_t() : smoothing_group_id(0) {}
};
struct tag_sizes { struct tag_sizes {
tag_sizes() : num_ints(0), num_reals(0), num_strings(0) {} tag_sizes() : num_ints(0), num_reals(0), num_strings(0) {}
int num_ints; int num_ints;
@@ -661,11 +683,12 @@ static inline void parseV(real_t *x, real_t *y, real_t *z, real_t *w,
} }
// Extension: parse vertex with colors(6 items) // Extension: parse vertex with colors(6 items)
static inline bool parseVertexWithColor(real_t *x, real_t *y, real_t *z, real_t *r, static inline bool parseVertexWithColor(real_t *x, real_t *y, real_t *z,
real_t *g, real_t *b, real_t *r, real_t *g, real_t *b,
const char **token, const double default_x = 0.0, const char **token,
const double default_y = 0.0, const double default_x = 0.0,
const double default_z = 0.0) { const double default_y = 0.0,
const double default_z = 0.0) {
(*x) = parseReal(token, default_x); (*x) = parseReal(token, default_x);
(*y) = parseReal(token, default_y); (*y) = parseReal(token, default_y);
(*z) = parseReal(token, default_z); (*z) = parseReal(token, default_z);
@@ -728,7 +751,7 @@ static tag_sizes parseTagTriple(const char **token) {
return ts; return ts;
} }
(*token)++; // Skip '/' (*token)++; // Skip '/'
(*token) += strspn((*token), " \t"); (*token) += strspn((*token), " \t");
ts.num_reals = atoi((*token)); ts.num_reals = atoi((*token));
@@ -736,7 +759,7 @@ static tag_sizes parseTagTriple(const char **token) {
if ((*token)[0] != '/') { if ((*token)[0] != '/') {
return ts; return ts;
} }
(*token)++; // Skip '/' (*token)++; // Skip '/'
ts.num_strings = parseInt(token); ts.num_strings = parseInt(token);
@@ -745,12 +768,12 @@ static tag_sizes parseTagTriple(const char **token) {
// Parse triples with index offsets: i, i/j/k, i//k, i/j // Parse triples with index offsets: i, i/j/k, i//k, i/j
static bool parseTriple(const char **token, int vsize, int vnsize, int vtsize, static bool parseTriple(const char **token, int vsize, int vnsize, int vtsize,
vertex_index *ret) { vertex_index_t *ret) {
if (!ret) { if (!ret) {
return false; return false;
} }
vertex_index vi(-1); vertex_index_t vi(-1);
if (!fixIndex(atoi((*token)), vsize, &(vi.v_idx))) { if (!fixIndex(atoi((*token)), vsize, &(vi.v_idx))) {
return false; return false;
@@ -798,8 +821,8 @@ static bool parseTriple(const char **token, int vsize, int vnsize, int vtsize,
} }
// Parse raw triples: i, i/j/k, i//k, i/j // Parse raw triples: i, i/j/k, i//k, i/j
static vertex_index parseRawTriple(const char **token) { static vertex_index_t parseRawTriple(const char **token) {
vertex_index vi(static_cast<int>(0)); // 0 is an invalid index in OBJ vertex_index_t vi(static_cast<int>(0)); // 0 is an invalid index in OBJ
vi.v_idx = atoi((*token)); vi.v_idx = atoi((*token));
(*token) += strcspn((*token), "/ \t\r"); (*token) += strcspn((*token), "/ \t\r");
@@ -907,7 +930,7 @@ static bool ParseTextureNameAndOption(std::string *texname,
token += 4; token += 4;
parseReal2(&(texopt->brightness), &(texopt->contrast), &token, 0.0, 1.0); parseReal2(&(texopt->brightness), &(texopt->contrast), &token, 0.0, 1.0);
} else { } else {
// Assume texture filename // Assume texture filename
#if 0 #if 0
size_t len = strcspn(token, " \t\r"); // untile next space size_t len = strcspn(token, " \t\r"); // untile next space
texture_name = std::string(token, token + len); texture_name = std::string(token, token + len);
@@ -971,60 +994,215 @@ static void InitMaterial(material_t *material) {
material->unknown_parameter.clear(); material->unknown_parameter.clear();
} }
static bool exportFaceGroupToShape( // code from https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html
shape_t *shape, const std::vector<std::vector<vertex_index> > &faceGroup, static int pnpoly(int nvert, float *vertx, float *verty, float testx,
const std::vector<tag_t> &tags, const int material_id, float testy) {
const std::string &name, bool triangulate) { int i, j, c = 0;
for (i = 0, j = nvert - 1; i < nvert; j = i++) {
if (((verty[i] > testy) != (verty[j] > testy)) &&
(testx <
(vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) +
vertx[i]))
c = !c;
}
return c;
}
// TODO(syoyo): refactor function.
static bool exportFaceGroupToShape(shape_t *shape,
const std::vector<face_t> &faceGroup,
const std::vector<tag_t> &tags,
const int material_id,
const std::string &name, bool triangulate,
const std::vector<real_t> &v) {
if (faceGroup.empty()) { if (faceGroup.empty()) {
return false; return false;
} }
// Flatten vertices and indices // Flatten vertices and indices
for (size_t i = 0; i < faceGroup.size(); i++) { for (size_t i = 0; i < faceGroup.size(); i++) {
const std::vector<vertex_index> &face = faceGroup[i]; const face_t &face = faceGroup[i];
vertex_index i0 = face[0]; vertex_index_t i0 = face.vertex_indices[0];
vertex_index i1(-1); vertex_index_t i1(-1);
vertex_index i2 = face[1]; vertex_index_t i2 = face.vertex_indices[1];
size_t npolys = face.size(); size_t npolys = face.vertex_indices.size();
if (triangulate) { if (triangulate) {
// Polygon -> triangle fan conversion // find the two axes to work in
for (size_t k = 2; k < npolys; k++) { size_t axes[2] = {1, 2};
i1 = i2; for (size_t k = 0; k < npolys; ++k) {
i2 = face[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);
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;
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;
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;
}
}
index_t idx0, idx1, idx2; real_t area = 0;
idx0.vertex_index = i0.v_idx; for (size_t k = 0; k < npolys; ++k) {
idx0.normal_index = i0.vn_idx; i0 = face.vertex_indices[(k + 0) % npolys];
idx0.texcoord_index = i0.vt_idx; i1 = face.vertex_indices[(k + 1) % npolys];
idx1.vertex_index = i1.v_idx; size_t vi0 = size_t(i0.v_idx);
idx1.normal_index = i1.vn_idx; size_t vi1 = size_t(i1.v_idx);
idx1.texcoord_index = i1.vt_idx; real_t v0x = v[vi0 * 3 + axes[0]];
idx2.vertex_index = i2.v_idx; real_t v0y = v[vi0 * 3 + axes[1]];
idx2.normal_index = i2.vn_idx; real_t v1x = v[vi1 * 3 + axes[0]];
idx2.texcoord_index = i2.vt_idx; real_t v1y = v[vi1 * 3 + axes[1]];
area += (v0x * v1y - v0y * v1x) * 0.5f;
}
shape->mesh.indices.push_back(idx0); int maxRounds =
shape->mesh.indices.push_back(idx1); 10; // arbitrary max loop count to protect against unexpected errors
shape->mesh.indices.push_back(idx2);
shape->mesh.num_face_vertices.push_back(3); face_t remainingFace = face; // copy
shape->mesh.material_ids.push_back(material_id); 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);
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 < 0.0f) {
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 ovi = size_t(
remainingFace.vertex_indices[(guess_vert + otherVert) % npolys]
.v_idx);
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();
}
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 { } else {
for (size_t k = 0; k < npolys; k++) { for (size_t k = 0; k < npolys; k++) {
index_t idx; index_t idx;
idx.vertex_index = face[k].v_idx; idx.vertex_index = face.vertex_indices[k].v_idx;
idx.normal_index = face[k].vn_idx; idx.normal_index = face.vertex_indices[k].vn_idx;
idx.texcoord_index = face[k].vt_idx; idx.texcoord_index = face.vertex_indices[k].vt_idx;
shape->mesh.indices.push_back(idx); shape->mesh.indices.push_back(idx);
} }
shape->mesh.num_face_vertices.push_back( shape->mesh.num_face_vertices.push_back(
static_cast<unsigned char>(npolys)); static_cast<unsigned char>(npolys));
shape->mesh.material_ids.push_back(material_id); // per face shape->mesh.material_ids.push_back(material_id); // per face
shape->mesh.smoothing_group_ids.push_back(
face.smoothing_group_id); // per face
} }
} }
@@ -1533,13 +1711,17 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
std::vector<real_t> vt; std::vector<real_t> vt;
std::vector<real_t> vc; std::vector<real_t> vc;
std::vector<tag_t> tags; std::vector<tag_t> tags;
std::vector<std::vector<vertex_index> > faceGroup; std::vector<face_t> faceGroup;
std::string name; std::string name;
// material // material
std::map<std::string, int> material_map; std::map<std::string, int> material_map;
int material = -1; int material = -1;
// smoothing group id
unsigned int current_smoothing_id =
0; // Initial value. 0 means no smoothing.
shape_t shape; shape_t shape;
std::string linebuf; std::string linebuf;
@@ -1612,11 +1794,13 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
token += 2; token += 2;
token += strspn(token, " \t"); token += strspn(token, " \t");
std::vector<vertex_index> face; face_t face;
face.reserve(3);
face.smoothing_group_id = current_smoothing_id;
face.vertex_indices.reserve(3);
while (!IS_NEW_LINE(token[0])) { while (!IS_NEW_LINE(token[0])) {
vertex_index vi; vertex_index_t vi;
if (!parseTriple(&token, static_cast<int>(v.size() / 3), if (!parseTriple(&token, static_cast<int>(v.size() / 3),
static_cast<int>(vn.size() / 3), static_cast<int>(vn.size() / 3),
static_cast<int>(vt.size() / 2), &vi)) { static_cast<int>(vt.size() / 2), &vi)) {
@@ -1626,14 +1810,13 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
return false; return false;
} }
face.push_back(vi); face.vertex_indices.push_back(vi);
size_t n = strspn(token, " \t\r"); size_t n = strspn(token, " \t\r");
token += n; token += n;
} }
// replace with emplace_back + std::move on C++11 // replace with emplace_back + std::move on C++11
faceGroup.push_back(std::vector<vertex_index>()); faceGroup.push_back(face);
faceGroup[faceGroup.size() - 1].swap(face);
continue; continue;
} }
@@ -1657,7 +1840,7 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
// this time. // this time.
// just clear `faceGroup` after `exportFaceGroupToShape()` call. // just clear `faceGroup` after `exportFaceGroupToShape()` call.
exportFaceGroupToShape(&shape, faceGroup, tags, material, name, exportFaceGroupToShape(&shape, faceGroup, tags, material, name,
triangulate); triangulate, v);
faceGroup.clear(); faceGroup.clear();
material = newMaterialId; material = newMaterialId;
} }
@@ -1712,7 +1895,7 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
if (token[0] == 'g' && IS_SPACE((token[1]))) { if (token[0] == 'g' && IS_SPACE((token[1]))) {
// flush previous face group. // flush previous face group.
bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name, bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name,
triangulate); triangulate, v);
(void)ret; // return value not used. (void)ret; // return value not used.
if (shape.mesh.indices.size() > 0) { if (shape.mesh.indices.size() > 0) {
@@ -1749,7 +1932,7 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
if (token[0] == 'o' && IS_SPACE((token[1]))) { if (token[0] == 'o' && IS_SPACE((token[1]))) {
// flush previous face group. // flush previous face group.
bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name, bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name,
triangulate); triangulate, v);
if (ret) { if (ret) {
shapes->push_back(shape); shapes->push_back(shape);
} }
@@ -1793,13 +1976,49 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
} }
tags.push_back(tag); tags.push_back(tag);
continue;
} }
if (token[0] == 's' && IS_SPACE(token[1])) {
// smoothing group id
token += 2;
// skip space.
token += strspn(token, " \t"); // skip space
if (token[0] == '\0') {
continue;
}
if (token[0] == '\r' || token[1] == '\n') {
continue;
}
if (strlen(token) >= 3) {
if (token[0] == 'o' && token[1] == 'f' && token[2] == 'f') {
current_smoothing_id = 0;
}
} else {
// assume number
int smGroupId = parseInt(&token);
if (smGroupId < 0) {
// parse error. force set to 0.
// FIXME(syoyo): Report warning.
current_smoothing_id = 0;
} else {
current_smoothing_id = static_cast<unsigned int>(smGroupId);
}
}
continue;
} // smoothing group id
// Ignore unknown command. // Ignore unknown command.
} }
bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name, bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name,
triangulate); triangulate, v);
// exportFaceGroupToShape return false when `usemtl` is called in the last // exportFaceGroupToShape return false when `usemtl` is called in the last
// line. // line.
// we also add `shape` to `shapes` when `shape.mesh` has already some // we also add `shape` to `shapes` when `shape.mesh` has already some
@@ -1907,7 +2126,7 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
indices.clear(); indices.clear();
while (!IS_NEW_LINE(token[0])) { while (!IS_NEW_LINE(token[0])) {
vertex_index vi = parseRawTriple(&token); vertex_index_t vi = parseRawTriple(&token);
index_t idx; index_t idx;
idx.vertex_index = vi.v_idx; idx.vertex_index = vi.v_idx;
@@ -2100,6 +2319,10 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
return true; return true;
} }
#ifdef __clang__
#pragma clang diagnostic pop
#endif
} // namespace tinyobj } // namespace tinyobj
#endif #endif