22 Commits
devel ... afl

Author SHA1 Message Date
Syoyo Fujita
06f6139d1f Limit # of tags when parsing SubD tag('t'). 2018-05-31 16:25:39 +09:00
Syoyo Fujita
0c8db8ee23 Hardened implementation(fix some seg faults, out-of-bound access) found by fuzzer. 2018-05-31 15:49:56 +09:00
Syoyo Fujita
5113cd65cf print smoothing group id. 2018-02-04 15:19:42 +09:00
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
Syoyo Fujita
b29d34f2e1 Merge pull request #154 from ogrex/master
Bugfix for multiple texture
2017-12-21 01:23:03 +09:00
ogrex
cb085d1fb6 fix multiple texture wrong id problem 2017-12-20 22:17:19 +09:00
ogrex
b38e97b7ec trimming '\r' in material_file name 2017-12-20 22:13:58 +09:00
94 changed files with 1326 additions and 173 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

@@ -1440,7 +1440,9 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
// std::cout << "mtllib :" << material_filename << std::endl; // std::cout << "mtllib :" << material_filename << std::endl;
auto t1 = std::chrono::high_resolution_clock::now(); auto t1 = std::chrono::high_resolution_clock::now();
if (material_filename.back() == '\r') {
material_filename.pop_back();
}
std::ifstream ifs(material_filename); std::ifstream ifs(material_filename);
if (ifs.good()) { if (ifs.good()) {
LoadMtl(&material_map, materials, &ifs); LoadMtl(&material_map, materials, &ifs);
@@ -1516,13 +1518,13 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
StackVector<std::thread, 16> workers; StackVector<std::thread, 16> workers;
for (size_t t = 0; t < num_threads; t++) { for (size_t t = 0; t < num_threads; t++) {
int material_id = -1; // -1 = default unknown material.
workers->push_back(std::thread([&, t]() { workers->push_back(std::thread([&, t]() {
size_t v_count = v_offsets[t]; size_t v_count = v_offsets[t];
size_t n_count = n_offsets[t]; size_t n_count = n_offsets[t];
size_t t_count = t_offsets[t]; size_t t_count = t_offsets[t];
size_t f_count = f_offsets[t]; size_t f_count = f_offsets[t];
size_t face_count = face_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++) { for (size_t i = 0; i < commands[t].size(); i++) {
if (commands[t][i].type == COMMAND_EMPTY) { if (commands[t][i].type == COMMAND_EMPTY) {
@@ -1579,7 +1581,19 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
for (size_t t = 0; t < workers->size(); t++) { for (size_t t = 0; t < workers->size(); t++) {
workers[t].join(); 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; i<max_face_offset; ++i) {
if (attrib->material_ids[i] != -1) break;
attrib->material_ids[i] = prev_material_id;
}
}
}
}
auto t_end = std::chrono::high_resolution_clock::now(); auto t_end = std::chrono::high_resolution_clock::now();
ms_merge = t_end - t_start; ms_merge = t_end - t_start;
} }

20
fuzzer/runner.py Normal file
View File

@@ -0,0 +1,20 @@
import os, sys
import glob
import subprocess
def main():
for g in glob.glob("../tests/afl/id*"):
print(g)
cmd = ["../a.out", g]
proc = subprocess.Popen(cmd)
try:
outs, errs = proc.communicate(timeout=15)
print(outs)
except TimeoutExpired:
proc.kill()
outs, errs = proc.communicate()
main()

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
@@ -138,6 +145,9 @@ static void PrintInfo(const tinyobj::attrib_t& attrib,
assert(shapes[i].mesh.num_face_vertices.size() == assert(shapes[i].mesh.num_face_vertices.size() ==
shapes[i].mesh.material_ids.size()); shapes[i].mesh.material_ids.size());
assert(shapes[i].mesh.num_face_vertices.size() ==
shapes[i].mesh.smoothing_group_ids.size());
printf("shape[%ld].num_faces: %lu\n", static_cast<long>(i), printf("shape[%ld].num_faces: %lu\n", static_cast<long>(i),
static_cast<unsigned long>(shapes[i].mesh.num_face_vertices.size())); static_cast<unsigned long>(shapes[i].mesh.num_face_vertices.size()));
@@ -158,6 +168,8 @@ static void PrintInfo(const tinyobj::attrib_t& attrib,
printf(" face[%ld].material_id = %d\n", static_cast<long>(f), printf(" face[%ld].material_id = %d\n", static_cast<long>(f),
shapes[i].mesh.material_ids[f]); shapes[i].mesh.material_ids[f]);
printf(" face[%ld].smoothing_group_id = %d\n", static_cast<long>(f),
shapes[i].mesh.smoothing_group_ids[f]);
index_offset += fnum; index_offset += fnum;
} }

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

17
tests/afl/README.txt Normal file
View File

@@ -0,0 +1,17 @@
Command line used to find this crash:
afl-fuzz -i in -o out ./test_loader @@
If you can't reproduce a bug outside of afl-fuzz, be sure to set the same
memory limit. The limit used for this fuzzing session was 50.0 MB.
Need a tool to minimize test cases before investigating the crashes or sending
them to a vendor? Check out the afl-tmin that comes with the fuzzer!
Found any cool bugs in open-source tools using afl-fuzz? If yes, please drop
me a mail at <lcamtuf@coredump.cx> once the issues are fixed - I'd love to
add your finds to the gallery at:
http://lcamtuf.coredump.cx/afl/
Thanks :-)

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,33 @@
# cube.obj
#
} cube
v d.0 0.0 0.0
v 0.0 0.0 1.0
v 0.0 1.0 0.0
v 0.0 1.0 1.0
v 1.0 0.0 0.0
v 1.0 0.0 1.0
v 1.0 1.0 0.0
v 1.0 1.0 1.0
vn 0.0 0.0 1.0
vn 0.0 0.0 -1.0
vn 0.0 1.0 0.0
vn 0.0 -1.0 0.0
vn 1.0 0.0 0.0
vn -1.0 0.0 0.0
f 1//2 7//2 5//2
f 1//2 3//2 7//2
f 1/6 4//6 3//6
f 1//6 2//6 4//6
f 3//3 8//3 7//3
f 3//3 4//3 8//3
f 5//5 7//5 8//5
f 5//5 8//5 6//5
f 1//4 5//4 55555555555555 6//4
f 1//4 6//4 2//4
f 2//1 6//1 8//1
f 2//1 8//1 4//1

View File

@@ -0,0 +1,35 @@
# cube.obj
#
4
f 1//4 6//4 2//4
f
g cube
v 0.0 0.0
v 0.0 0.0 1.0
v 0.0 1.0 0.0
v 0.0 1.0 1.0
v 1.0 0.0 0.0
v 1.0 0.0 1.0
v 1.0 1.0 0.0
v 1.0 1.0 1.0
n 0.0 0.0 1.0
vn 0.0 0.0 -1.0
vn 0.0 1.0 0.0
vn 0.0 -1.0 0.0
vn 1.0 0.0 0.0
vn -1.0 0.0 0.0
f 1//2 7//2 5//2
f 1//2 3//2 7//2
f 1//6 4//6 3//6
f 1//6 2//6 4//6
f 3//3 8//3 7//3
f 3//3 4//3 8//3
f 5//5 7//5 //5
f 5//5 8//5 6//5
f 1//4 5//4 6//4
f 1//4 6//4 2//4
f 2//1 6//1 8//1
f 2//1 8//1 4//1

View File

@@ -0,0 +1,34 @@
# cube.ob7//3
f 3//3 4//3 8//3j
#
g cube
v 0.0 0.0 0.0
v 0.0 0.0 1.0
v 0.0 1˙0 0.0
v 0.0 1.0 1.0
v 1.0 0.0 0.0
v 1.0 0.0 1.0
v 1.0 1.0 0.0
v 1.0 1.0 1.0
vn 0.0 0.0 1.0
vn 0.0 0.0 -1.0
vn 0.0 1.0 0.0
vn 0.0 -1.0 0.0
vn 1.0 0.0 0.0
vn -1.0 0.0 0.0
f 1//2 7//2 5//2
f 1//2 3//2 7//2
f 1//6 4//6 3//6
f 1//6 2//6 4//6
f 3//3 8//3 7//3
f 3//3 4//3 8//3
f 5//5 7//5 8//5
f 5//5 8//5 6//5
f 1//4 5//4 6//4
f 1//4 6//4 2//4
f 2//1 6//1 8//1
f 2//1 8//1 4//1

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,29 @@
# cube.obj
#
g
v 0.0 0.0 0.0
v 0.0 0.0 1,0
T 0.0 1.0 0.0
v 4.0 1c0 1.0
v 1.0 0.0 0.0
v 1.0 0.0 1.0
v 1.0 1.0 0.0
v 1.0 1.0 /////////////////////////////.0 0.0 -1.0
vn 0.0 1.0 0.0
v˙ 0.0 -1.0 0.0
vn 1.0 0.0 0.0
vn -1.0 0.0 0.0
f 1//2 7//2 5//2
f 1//2 3//2 7//2
f 1//6 4//6 3//6
f 1//6 2//6 4//6
f 3//3 8//3 7//3
f 3//3 4//3 8K/31
f/6 4//6 3//6
f 1//6 2 5//5 7//5 8//5
f 5//5 8//5 6//5"
f 1//4 5//4 2222222222224f 1//2 7//2 5 6//4 2//4
f 3//1 6//1 8//1
f 2//1 8//1 4//1

View File

@@ -0,0 +1,33 @@
# cube.obj
#
g cube
v 0.0 0.0 0.0
v 0.0 0.0 1.0
v 0.0 1.0 0.0
v 0.0 1.0 1.0
v 1.0 0.0 0.0
v 1.0 0.0 1.0
v 1.0 1.0 0.0
v 1.0 1.0 1.0
vn 0.0 0.0 1.0
vn 0<>0 0.0 -1.0
vn 0.0 1.0 00
vn 0.0 -Ę.0 0.0
vn 1. 0.0 0.0
vn -1.0 0.0 0.0
f 1//2 7//2 5//2
f 1//2 3//2 7//2
f 1//6 40000000000000000vvvvvvvvvvvvvvvv00000080000000//6 3//6
f 1//6 2//6 4//6
f 3//3 8//3 7//3
f 3//3 4//3 8//3
f 5//5 7//5 8//5
f 5//5 8//5 6//5
f 1//4 56//4
f 1//4 6//4 2//4
f 2//1 6//1 8//1
f 2//1 8//1 4//1

Binary file not shown.

View File

@@ -0,0 +1,32 @@
# cube.obj
#
g cube
v 0.0 0.0 0.0
v 0.0 0.0 1.0
v 0.0 1.0 0.0
v 0.0 1.0 1.0
v 1.0 0.0 0.0
v 1.0 0.0 1.0
v 1.0 1.0 0.0
v 1.0 1.0 1.0
vn 0.0 0.0 1.0
vn 0.0 0.0 -1.0
vn 0.0 1.0 0.0
vn 0.0 -1.0 0.0
vn 1.0 0.0 0.
vn -1.0 0.0 0.0
f 1//2 7//2 5//2
f 1//2 3//2 7//2
f 1//6 4//6 3//6
f 1//6 2//6 4//6
f 3//3 8//3 7//3
f 3//3 4//3 08//3
f 5//5 7//5 8//5!Šf 5//5 8//5 6//5
f 1//4 5//4 65555//4
f 1//4 6//4 2//4
f 2//1 6//1 8//1
f 2//1 8//1 4//1

Binary file not shown.

View File

@@ -0,0 +1,33 @@
# cube.7//3
f 3//3 4//3 8//3
obj
#
g cube
v 0.0 0.0 0.0
v 0.0 0.0 1.0
v .0 1.0
v 1.0 0.0 0.0
v 1.0 0.0 1.0
v 1.0 1.0 0.0
v 1.0 1.0 1.0
vn 0.0 0.0 1.0
vn 0.0 0.0 -1.0
vn 0.0 1.0 0.0
vn 0.0 -1.0 0.0
vn 1.0 0.0 0.0
vn -1.0 0.0 0.0
f 1//2 7//2 5//2
f 1//2 3//2 7//2
f 1//6 4//6 3//6
f 1//6 2//6 4//6
f 3//3 8//3 7//3
f 3//3 4//3 8//3
f 5//5 7//5 8//5!Šf 5//5 8//5 6//5
f 1//4 5//4 6//4
f 1//4 6//4 2//4
f 2//1 6//1 8//1
f 2//1 8//1 4//1

Binary file not shown.

View File

@@ -0,0 +1,30 @@
# cube.obj
#
g cuvP 0.0 0.0 .0
v 0.0 0.0 1.0
v 0.0 1.0 0.0
v 0.0 1.0 1.0
v 1.0 0.0 0.0
v 1.0 0.0 1.0
v 1.0 1.0 0.0
v 1.0 1.0 1.0
vn 0.0 0.0 1.0
vn 0.0 0.0 -1.0
vn 0.0 1.0 0.0
vn 0.0 -1.0 0.0
vn 1.0 0.0 0.0
vn -1.0 0.0 0.0
f 1//2 7//2 5//2
f 1//2 3//2 7//2
f 1//6 4//6 3//6
f 1//6 2//6 4//6
f 3//3 8//3 7//3
f 3//3 4//3 8//3
f 5//5 7//5 8//5!Šf 5//5 8//5 6//5
f 1//4 5//4 6//4
f 1//4 6666666666666666//4 2//4
f 2//1 6//1 8//1
f 2//1 8//1 4//1

View File

@@ -0,0 +1,33 @@
# cube.obj
#
g cube
v 0.0 0.0 0.0
v 0.0 0.0 1.0
v 0.0 1.0 0.0
v 0.0 1.0 1.0
v 1.0 0.0 0.0
v 1.0 0.0 1.0
v 1.0 1.0 0.0
v 1.0 1.0 1.0
vn 0.0 0.0 1.0
vn 0.0 0.0 -1.0
vn 0.0 1.0 0.0
vn 0.0 -1.0 0.0
vn 1.0 0.0 0.0
vn -1.0 0.0 0.0
f 1//2 7//2 5//2
f 1//2 3//2 7//2
f 1//6 4//6 3//6
f 1//6 2//6 4//6
f 3//3 8//3 7//3
f 3//3 4//3 8//3
f 5//5 7//5 8//5
f 5//5 8//5 6//5
t 1//4 5/-4 6//4
f 1//4 6//4 2//4
f 2//1 6//1 8//1
f 2//1 8//1 4//1

View File

@@ -0,0 +1,33 @@
# cube.obj
#
g cube
v 0.0 0.0 0.0
v 0.0 0.0 1.0
v 0.0 1.0 0.0
v 0.0 1.0 1.0
v 1.0 0.0 0.0
v 1.0 0.0 1.0
v 1.0 1.0 0.0
v 1.0 1.0 1.0
vn 0.0 0.0 1.0
vn 0.0 0.0 -1.0
vn 0.0 1.0 0.0
vn 0.0 -1.0 0.0
vn 1.0 0.0 0.0
vn -1.0 0.0 0.0
f 1//2 7//2 5//2
f 1//2 3//2 7//2
f 1//6 4//6 3//6
f 1//6 2//6 4//6
f 3//3 8//3 7//3
f 3//3 4//3 8//3
f 5//5 7//5 8//5
f 5//5 8//5 6//5
t 1//4 -5//4 6//4
f 1//4 6//4 2//4
f 2//1 6//1 8//1
f 2//1 8//1 4//1

Binary file not shown.

View File

@@ -0,0 +1,35 @@
# cube.obj
#
g cube
v 0.0 0.0 0.0
v 0.0 0.0 1.0
v 0.0 1.0 0.0
v 0.0 1.0 1.0
v 1.0 0.0 0.0
v 1.0 0.0 1.0
v 1.0 1.0 0.0
v 1.0 1.0 1.0
vn 0.0 0.0 !.0
vn 0.0 0.0 -1.0
vn 0.0 1.0 0.0
vn 0.0 8//5 6//5
t 1//4 -1.0 0.0
vn 1.0 0.0 0.0
f 3//3 8//3 7//3
f 3//3
vn -1.0 0.0 0.0
f 1//2 7//2 5//2
f 1//2 3//2 7//2
f 1//6 4//6 3//6
f 1//6 2//6 4//6
f 3//3 8//3 7//3
f 3//3 4//3 8//3
f 5//5 7//5 8//5
f 5//5 8//5 6//5
t 1//4 5//4 6//4
f 1//4 6//4 2//4 f 2//1 6//1 8//1
f 2//1 8//1 4//1

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,32 @@
# cube.obj
#
g cube
v 0.0 0.0 0.0
v 0.0 0.0 1.0
v 0.0 1.0 0.0
v 0.0 1.0 1.0
v Ď1.0 0.0 0.0
v 1.0 0.0 1.0
v 1.0 1.0 0.0
v 1.0 1.0 1.0
vn 0.0 0.0 1.0
vn 0.0 0.0 -1.0
vn 0.0 1.0
vn 1.0 0.0 0.0
vn -1.0 0.0 0.0
f 1//2 7//2 5//2
f 1//2 3//2 7//2
f 1//6 4//6 3//6
f 1//6 2//6 4//6
f 3//3 8//3 7//3
f 3//3 4//3 811111//3
f 5//5 7//5 8//5
f 5//5 8//5 6//5
t 1//4 5//4 6//4
f 1//4 6//4 2//4
f 2B/1 6//1 8//1
f 2//1 8//1 4//1

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -717,6 +717,65 @@ 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]);
}
// Fuzzer test.
// Just check if it does not crash.
TEST_CASE("afl000000", "[AFL]") {
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, "./afl/id:000000,sig:11,src:000000,op:havoc,rep:128", gMtlBasePath);
REQUIRE(true == ret);
}
TEST_CASE("afl000001", "[AFL]") {
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, "./afl/id:000001,sig:11,src:000000,op:havoc,rep:64", gMtlBasePath);
REQUIRE(true == ret);
}
#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,8 @@ THE SOFTWARE.
*/ */
// //
// version 1.2.0 : Hardened implementation(#xxx)
// 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 +53,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 +227,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 +378,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 +684,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 +752,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 +760,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 +769,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 +822,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 +931,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 +995,255 @@ 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]; size_t npolys = face.vertex_indices.size();
vertex_index i1(-1);
vertex_index i2 = face[1];
size_t npolys = face.size(); if (npolys < 3) {
// ??? Invalid face definition.
continue;
}
vertex_index_t i0 = face.vertex_indices[0];
vertex_index_t i1(-1);
vertex_index_t i2 = face.vertex_indices[1];
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);
index_t idx0, idx1, idx2; if (((3 * vi0 + 2) >= v.size()) ||
idx0.vertex_index = i0.v_idx; ((3 * vi1 + 2) >= v.size()) ||
idx0.normal_index = i0.vn_idx; ((3 * vi2 + 2) >= v.size())) {
idx0.texcoord_index = i0.vt_idx; // Invalid triangle.
idx1.vertex_index = i1.v_idx; // FIXME(syoyo): Is it ok to simply skip this invalid triangle?
idx1.normal_index = i1.vn_idx; continue;
idx1.texcoord_index = i1.vt_idx; }
idx2.vertex_index = i2.v_idx; real_t v0x = v[vi0 * 3 + 0];
idx2.normal_index = i2.vn_idx; real_t v0y = v[vi0 * 3 + 1];
idx2.texcoord_index = i2.vt_idx; 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;
}
}
shape->mesh.indices.push_back(idx0); real_t area = 0;
shape->mesh.indices.push_back(idx1); for (size_t k = 0; k < npolys; ++k) {
shape->mesh.indices.push_back(idx2); 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) * 0.5f;
}
shape->mesh.num_face_vertices.push_back(3); int maxRounds =
shape->mesh.material_ids.push_back(material_id); 10; // arbitrary max loop count to protect against unexpected errors
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<real_t>(0.0);
vy[k] = static_cast<real_t>(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 < 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 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();
}
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 +1752,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 +1835,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 +1851,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 +1881,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 +1936,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 +1973,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);
} }
@@ -1768,6 +1992,7 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
} }
if (token[0] == 't' && IS_SPACE(token[1])) { if (token[0] == 't' && IS_SPACE(token[1])) {
const int max_tag_nums = 8192; // FIXME(syoyo): Parameterize.
tag_t tag; tag_t tag;
token += 2; token += 2;
@@ -1776,6 +2001,27 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
tag_sizes ts = parseTagTriple(&token); tag_sizes ts = parseTagTriple(&token);
if (ts.num_ints < 0) {
ts.num_ints = 0;
}
if (ts.num_ints > max_tag_nums) {
ts.num_ints = max_tag_nums;
}
if (ts.num_reals < 0) {
ts.num_reals = 0;
}
if (ts.num_reals > max_tag_nums) {
ts.num_reals = max_tag_nums;
}
if (ts.num_strings < 0) {
ts.num_strings = 0;
}
if (ts.num_strings > max_tag_nums) {
ts.num_strings = max_tag_nums;
}
tag.intValues.resize(static_cast<size_t>(ts.num_ints)); tag.intValues.resize(static_cast<size_t>(ts.num_ints));
for (size_t i = 0; i < static_cast<size_t>(ts.num_ints); ++i) { for (size_t i = 0; i < static_cast<size_t>(ts.num_ints); ++i) {
@@ -1793,13 +2039,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 +2189,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 +2382,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