13 Commits

Author SHA1 Message Date
Syoyo Fujita
bc42bc47ad Don't create new shape by usemtl. Fixes #68. 2016-03-12 02:14:52 +09:00
Syoyo Fujita
31611161df Merge pull request #67 from pra85/patch-1
Fix a typo and add syntax highlighting language
2016-02-22 13:33:00 +09:00
Prayag Verma
21c93c51ed Fix a typo and add syntax highlighting language
`poweful` → `powerful`
2016-02-22 09:38:18 +05:30
Syoyo Fujita
b48d363dd2 Merge pull request #66 from mrdoob/patch-1
Fixed links.
2016-02-22 12:30:04 +09:00
Mr.doob
0f00a3b3e8 Fixed links. 2016-02-22 10:04:33 +09:00
Syoyo Fujita
ab10b76eda Merge pull request #65 from Ambal/master
Performance tweaks
2016-02-20 19:49:12 +09:00
Ambal
cf52401ca7 Replace calls to 'isdigit', 'isSpace' and 'isNewLine' functions to macros. Other small performance tweaks. 2016-02-20 12:20:19 +02:00
Syoyo Fujita
9c1826381c Update travis script. 2016-02-19 12:25:16 +09:00
Syoyo Fujita
7eb029edaf Add link to Android Vulkan demo. 2016-02-17 16:58:05 +09:00
Syoyo Fujita
d55863ce50 Fix coverall basedir argument. 2016-02-16 16:52:00 +09:00
Syoyo Fujita
ecf1005c72 Exclude feature_tests.* from coverall. 2016-02-16 16:50:09 +09:00
Syoyo Fujita
60ba85e9cd Add all_branches: true. 2016-02-15 14:52:26 +09:00
Syoyo Fujita
45b4924c27 Fix travis script. 2016-02-14 17:03:59 +09:00
4 changed files with 155 additions and 146 deletions

View File

@@ -51,13 +51,16 @@ script:
.. ..
- ninja - ninja
- ./test_loader ../cornell_box.obj - ./test_loader ../cornell_box.obj
- if [ -n "$REPORT_COVERAGE" ]; then coveralls -b . -r . -e examples -e tools -e jni - if [ -n "$REPORT_COVERAGE" ]; then coveralls -b . -r .. -e examples -e tools -e jni
-e python -e images -E ".*CompilerId.*"; fi -e python -e images -E ".*CompilerId.*" -E ".*feature_tests.*" ; fi
- cd ..
deploy: deploy:
provider: releases provider: releases
api_key: api_key:
secure: AsXameK4GJn6h6wMmDrKTr7q/o9EI7hX7zWg1W6VaFBQKfkBvOmjJolWimjl6HMoRZ1NpMmK5GDm3zBlTUeABtgVBIyNWgE9vWS39ff6D5iQKcgScFsJkyILt0GikBqbN2pLGQ2t/M1Qh6n1sEIfzqekiCcF5Qvy5yYlYvHtaRGV02QeYAej/xx15/9SMuKTncHhjf63ClYPu8ODid7QUegJUvlQUeXoPsBDbaXMH2uDWoBWF7etX7G2Iob4NE8GX+ZP6dj+Ogi7p4HXThK650mzLL/pUl584EjjY/vePqx0cFhtpiRwvrW8SNPI1aJ1Phwa1enLRUgfS3bnkwQAMw/SCXSK2lnCvkUAXyTgpG03HWrZURj4vhEPXc7qHooO+dsfmi+JanYLaSDyrGpgQznLGjCMnVATimry0KxSufUY8Wt72Wh+nf7N0IgTUCjl32sWnQd/MRZPkxFuaf1h7r9RoH9KZY0yIOV09gABEFCGrOIZA2FcyhC2G26Bc4zyNrfMFpZ2DI76qdcWNdJGkRkpxtH9sGU8JgZu6Em2f1e6+SLgkBsPxbhRk5PwdhA9AXE2p9PmQqhO3jJKusGBZSoHAF7TlwagRY2J01yJxF7ge6zG9U8QuBqs1bB1zdnE34fHWOgs4st3inC+oBDOhvnEg1Nm/qeYVWMBzpwclSg= secure: AsXameK4GJn6h6wMmDrKTr7q/o9EI7hX7zWg1W6VaFBQKfkBvOmjJolWimjl6HMoRZ1NpMmK5GDm3zBlTUeABtgVBIyNWgE9vWS39ff6D5iQKcgScFsJkyILt0GikBqbN2pLGQ2t/M1Qh6n1sEIfzqekiCcF5Qvy5yYlYvHtaRGV02QeYAej/xx15/9SMuKTncHhjf63ClYPu8ODid7QUegJUvlQUeXoPsBDbaXMH2uDWoBWF7etX7G2Iob4NE8GX+ZP6dj+Ogi7p4HXThK650mzLL/pUl584EjjY/vePqx0cFhtpiRwvrW8SNPI1aJ1Phwa1enLRUgfS3bnkwQAMw/SCXSK2lnCvkUAXyTgpG03HWrZURj4vhEPXc7qHooO+dsfmi+JanYLaSDyrGpgQznLGjCMnVATimry0KxSufUY8Wt72Wh+nf7N0IgTUCjl32sWnQd/MRZPkxFuaf1h7r9RoH9KZY0yIOV09gABEFCGrOIZA2FcyhC2G26Bc4zyNrfMFpZ2DI76qdcWNdJGkRkpxtH9sGU8JgZu6Em2f1e6+SLgkBsPxbhRk5PwdhA9AXE2p9PmQqhO3jJKusGBZSoHAF7TlwagRY2J01yJxF7ge6zG9U8QuBqs1bB1zdnE34fHWOgs4st3inC+oBDOhvnEg1Nm/qeYVWMBzpwclSg=
file: tiny_obj_loader.h file: tiny_obj_loader.h
all_branches: true
on: on:
repo: syoyo/tinyobjloader repo: syoyo/tinyobjloader
tags: true tags: true
skip_cleanup: true

178
README.md
View File

@@ -13,7 +13,7 @@ tinyobjloader
http://syoyo.github.io/tinyobjloader/ http://syoyo.github.io/tinyobjloader/
Tiny but poweful single file wavefront obj loader written in C++. No dependency except for C++ STL. It can parse 10M over polygons with moderate memory and time. Tiny but powerful single file wavefront obj loader written in C++. No dependency except for C++ STL. It can parse 10M over polygons with moderate memory and time.
`tinyobjloader` is good for embedding .obj loader to your (global illumination) renderer ;-) `tinyobjloader` is good for embedding .obj loader to your (global illumination) renderer ;-)
@@ -50,15 +50,16 @@ Use case
TinyObjLoader is successfully used in ... TinyObjLoader is successfully used in ...
* bullet3 https://github.com/erwincoumans/bullet3 * bullet3 https://github.com/erwincoumans/bullet3
* pbrt-v2 https://https://github.com/mmp/pbrt-v2 * pbrt-v2 https://github.com/mmp/pbrt-v2
* OpenGL game engine development http://swarminglogic.com/jotting/2013_10_gamedev01 * OpenGL game engine development http://swarminglogic.com/jotting/2013_10_gamedev01
* mallie https://lighttransport.github.io/mallie * mallie https://lighttransport.github.io/mallie
* IBLBaker (Image Based Lighting Baker). http://www.derkreature.com/iblbaker/ * IBLBaker (Image Based Lighting Baker). http://www.derkreature.com/iblbaker/
* Stanford CS148 http://web.stanford.edu/class/cs148/assignments/assignment3.pdf * Stanford CS148 http://web.stanford.edu/class/cs148/assignments/assignment3.pdf
* Awesome Bump http://awesomebump.besaba.com/about/ * Awesome Bump http://awesomebump.besaba.com/about/
* sdlgl3-wavefront OpenGL .obj viewer https://github.com/chrisliebert/sdlgl3-wavefront * sdlgl3-wavefront OpenGL .obj viewer https://github.com/chrisliebert/sdlgl3-wavefront
* pbrt-v3 https://https://github.com/mmp/pbrt-v3 * pbrt-v3 https://github.com/mmp/pbrt-v3
* cocos2d-x https://github.com/cocos2d/cocos2d-x/ * cocos2d-x https://github.com/cocos2d/cocos2d-x/
* Android Vulkan demo https://github.com/SaschaWillems/Vulkan
* Your project here! * Your project here!
Features Features
@@ -87,106 +88,107 @@ Usage
----- -----
TinyObjLoader triangulate input .obj by default. TinyObjLoader triangulate input .obj by default.
```c++
#define TINYOBJLOADER_IMPLEMENTATION // define this in only *one* .cc
#include "tiny_obj_loader.h"
#define TINYOBJLOADER_IMPLEMENTATION // define this in only *one* .cc std::string inputfile = "cornell_box.obj";
#include "tiny_obj_loader.h" std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string inputfile = "cornell_box.obj"; std::string err;
std::vector<tinyobj::shape_t> shapes; bool ret = tinyobj::LoadObj(shapes, materials, err, inputfile.c_str());
std::vector<tinyobj::material_t> materials;
std::string err; if (!err.empty()) { // `err` may contain warning message.
bool ret = tinyobj::LoadObj(shapes, materials, err, inputfile.c_str()); std::cerr << err << std::endl;
}
if (!err.empty()) { // `err` may contain warning message. if (!ret) {
std::cerr << err << std::endl; exit(1);
} }
if (!ret) { std::cout << "# of shapes : " << shapes.size() << std::endl;
exit(1); std::cout << "# of materials : " << materials.size() << std::endl;
}
std::cout << "# of shapes : " << shapes.size() << std::endl; for (size_t i = 0; i < shapes.size(); i++) {
std::cout << "# of materials : " << materials.size() << std::endl; printf("shape[%ld].name = %s\n", i, shapes[i].name.c_str());
printf("Size of shape[%ld].indices: %ld\n", i, shapes[i].mesh.indices.size());
printf("Size of shape[%ld].material_ids: %ld\n", i, shapes[i].mesh.material_ids.size());
assert((shapes[i].mesh.indices.size() % 3) == 0);
for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) {
printf(" idx[%ld] = %d, %d, %d. mat_id = %d\n", f, shapes[i].mesh.indices[3*f+0], shapes[i].mesh.indices[3*f+1], shapes[i].mesh.indices[3*f+2], shapes[i].mesh.material_ids[f]);
}
for (size_t i = 0; i < shapes.size(); i++) { printf("shape[%ld].vertices: %ld\n", i, shapes[i].mesh.positions.size());
printf("shape[%ld].name = %s\n", i, shapes[i].name.c_str()); assert((shapes[i].mesh.positions.size() % 3) == 0);
printf("Size of shape[%ld].indices: %ld\n", i, shapes[i].mesh.indices.size()); for (size_t v = 0; v < shapes[i].mesh.positions.size() / 3; v++) {
printf("Size of shape[%ld].material_ids: %ld\n", i, shapes[i].mesh.material_ids.size()); printf(" v[%ld] = (%f, %f, %f)\n", v,
assert((shapes[i].mesh.indices.size() % 3) == 0); shapes[i].mesh.positions[3*v+0],
for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) { shapes[i].mesh.positions[3*v+1],
printf(" idx[%ld] = %d, %d, %d. mat_id = %d\n", f, shapes[i].mesh.indices[3*f+0], shapes[i].mesh.indices[3*f+1], shapes[i].mesh.indices[3*f+2], shapes[i].mesh.material_ids[f]); shapes[i].mesh.positions[3*v+2]);
} }
}
printf("shape[%ld].vertices: %ld\n", i, shapes[i].mesh.positions.size());
assert((shapes[i].mesh.positions.size() % 3) == 0);
for (size_t v = 0; v < shapes[i].mesh.positions.size() / 3; v++) {
printf(" v[%ld] = (%f, %f, %f)\n", v,
shapes[i].mesh.positions[3*v+0],
shapes[i].mesh.positions[3*v+1],
shapes[i].mesh.positions[3*v+2]);
}
}
for (size_t i = 0; i < materials.size(); i++) {
printf("material[%ld].name = %s\n", i, materials[i].name.c_str());
printf(" material.Ka = (%f, %f ,%f)\n", materials[i].ambient[0], materials[i].ambient[1], materials[i].ambient[2]);
printf(" material.Kd = (%f, %f ,%f)\n", materials[i].diffuse[0], materials[i].diffuse[1], materials[i].diffuse[2]);
printf(" material.Ks = (%f, %f ,%f)\n", materials[i].specular[0], materials[i].specular[1], materials[i].specular[2]);
printf(" material.Tr = (%f, %f ,%f)\n", materials[i].transmittance[0], materials[i].transmittance[1], materials[i].transmittance[2]);
printf(" material.Ke = (%f, %f ,%f)\n", materials[i].emission[0], materials[i].emission[1], materials[i].emission[2]);
printf(" material.Ns = %f\n", materials[i].shininess);
printf(" material.Ni = %f\n", materials[i].ior);
printf(" material.dissolve = %f\n", materials[i].dissolve);
printf(" material.illum = %d\n", materials[i].illum);
printf(" material.map_Ka = %s\n", materials[i].ambient_texname.c_str());
printf(" material.map_Kd = %s\n", materials[i].diffuse_texname.c_str());
printf(" material.map_Ks = %s\n", materials[i].specular_texname.c_str());
printf(" material.map_Ns = %s\n", materials[i].specular_highlight_texname.c_str());
std::map<std::string, std::string>::const_iterator it(materials[i].unknown_parameter.begin());
std::map<std::string, std::string>::const_iterator itEnd(materials[i].unknown_parameter.end());
for (; it != itEnd; it++) {
printf(" material.%s = %s\n", it->first.c_str(), it->second.c_str());
}
printf("\n");
}
for (size_t i = 0; i < materials.size(); i++) {
printf("material[%ld].name = %s\n", i, materials[i].name.c_str());
printf(" material.Ka = (%f, %f ,%f)\n", materials[i].ambient[0], materials[i].ambient[1], materials[i].ambient[2]);
printf(" material.Kd = (%f, %f ,%f)\n", materials[i].diffuse[0], materials[i].diffuse[1], materials[i].diffuse[2]);
printf(" material.Ks = (%f, %f ,%f)\n", materials[i].specular[0], materials[i].specular[1], materials[i].specular[2]);
printf(" material.Tr = (%f, %f ,%f)\n", materials[i].transmittance[0], materials[i].transmittance[1], materials[i].transmittance[2]);
printf(" material.Ke = (%f, %f ,%f)\n", materials[i].emission[0], materials[i].emission[1], materials[i].emission[2]);
printf(" material.Ns = %f\n", materials[i].shininess);
printf(" material.Ni = %f\n", materials[i].ior);
printf(" material.dissolve = %f\n", materials[i].dissolve);
printf(" material.illum = %d\n", materials[i].illum);
printf(" material.map_Ka = %s\n", materials[i].ambient_texname.c_str());
printf(" material.map_Kd = %s\n", materials[i].diffuse_texname.c_str());
printf(" material.map_Ks = %s\n", materials[i].specular_texname.c_str());
printf(" material.map_Ns = %s\n", materials[i].specular_highlight_texname.c_str());
std::map<std::string, std::string>::const_iterator it(materials[i].unknown_parameter.begin());
std::map<std::string, std::string>::const_iterator itEnd(materials[i].unknown_parameter.end());
for (; it != itEnd; it++) {
printf(" material.%s = %s\n", it->first.c_str(), it->second.c_str());
}
printf("\n");
}
```
Reading .obj without triangulation. Use `num_vertices[i]` to iterate over faces(indices). `num_vertices[i]` stores the number of vertices for ith face. Reading .obj without triangulation. Use `num_vertices[i]` to iterate over faces(indices). `num_vertices[i]` stores the number of vertices for ith face.
```c++
#define TINYOBJLOADER_IMPLEMENTATION // define this in only *one* .cc
#include "tiny_obj_loader.h"
#define TINYOBJLOADER_IMPLEMENTATION // define this in only *one* .cc std::string inputfile = "cornell_box.obj";
#include "tiny_obj_loader.h" std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string inputfile = "cornell_box.obj"; std::string err;
std::vector<tinyobj::shape_t> shapes; bool triangulate = false;
std::vector<tinyobj::material_t> materials; bool ret = tinyobj::LoadObj(shapes, materials, err, inputfile.c_str(), triangulate);
std::string err; if (!err.empty()) { // `err` may contain warning message.
bool triangulate = false; std::cerr << err << std::endl;
bool ret = tinyobj::LoadObj(shapes, materials, err, inputfile.c_str(), triangulate); }
if (!err.empty()) { // `err` may contain warning message. if (!ret) {
std::cerr << err << std::endl; exit(1);
} }
if (!ret) { for (size_t i = 0; i < shapes.size(); i++) {
exit(1);
}
for (size_t i = 0; i < shapes.size(); i++) { size_t indexOffset = 0;
for (size_t n = 0; n < shapes[i].mesh.num_vertices.size(); n++) {
size_t indexOffset = 0; int ngon = shapes[i].mesh.num_vertices[n];
for (size_t n = 0; n < shapes[i].mesh.num_vertices.size(); n++) { for (size_t f = 0; f < ngon; f++) {
int ngon = shapes[i].mesh.num_vertices[n]; unsigend int v = shapes[i].mesh.indices[indexOffset + f];
for (size_t f = 0; f < ngon; f++) { printf(" face[%ld] v[%ld] = (%f, %f, %f)\n", n,
unsigend int v = shapes[i].mesh.indices[indexOffset + f]; shapes[i].mesh.positions[3*v+0],
printf(" face[%ld] v[%ld] = (%f, %f, %f)\n", n, shapes[i].mesh.positions[3*v+1],
shapes[i].mesh.positions[3*v+0], shapes[i].mesh.positions[3*v+2]);
shapes[i].mesh.positions[3*v+1],
shapes[i].mesh.positions[3*v+2]);
}
indexOffset += ngon;
}
} }
indexOffset += ngon;
}
}
```

View File

@@ -139,6 +139,7 @@ TestLoadObj(
} }
if (!ret) { if (!ret) {
printf("Failed to load/parse .obj.\n");
return false; return false;
} }

View File

@@ -5,6 +5,7 @@
// //
// //
// version 0.9.20: Fixes creating per-face material using `usemtl`(#68)
// version 0.9.17: Support n-polygon and crease tag(OpenSubdiv extension) // version 0.9.17: Support n-polygon and crease tag(OpenSubdiv extension)
// version 0.9.16: Make tinyobjloader header-only // version 0.9.16: Make tinyobjloader header-only
// version 0.9.15: Change API to handle no mtl file case correctly(#58) // version 0.9.15: Change API to handle no mtl file case correctly(#58)
@@ -205,11 +206,9 @@ struct obj_shape {
std::vector<float> vt; std::vector<float> vt;
}; };
static inline bool isSpace(const char c) { return (c == ' ') || (c == '\t'); } #define IS_SPACE( x ) ( ( (x) == ' ') || ( (x) == '\t') )
#define IS_DIGIT( x ) ( (unsigned int)( (x) - '0' ) < (unsigned int)10 )
static inline bool isNewLine(const char c) { #define IS_NEW_LINE( x ) ( ( (x) == '\r') || ( (x) == '\n') || ( (x) == '\0') )
return (c == '\r') || (c == '\n') || (c == '\0');
}
// Make index zero-base, and also support relative index. // Make index zero-base, and also support relative index.
static inline int fixIndex(int idx, int n) { static inline int fixIndex(int idx, int n) {
@@ -297,13 +296,13 @@ static bool tryParseDouble(const char *s, const char *s_end, double *result) {
if (*curr == '+' || *curr == '-') { if (*curr == '+' || *curr == '-') {
sign = *curr; sign = *curr;
curr++; curr++;
} else if (isdigit(*curr)) { /* Pass through. */ } else if (IS_DIGIT(*curr)) { /* Pass through. */
} else { } else {
goto fail; goto fail;
} }
// Read the integer part. // Read the integer part.
while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) { while ((end_not_reached = (curr != s_end)) && IS_DIGIT(*curr)) {
mantissa *= 10; mantissa *= 10;
mantissa += static_cast<int>(*curr - 0x30); mantissa += static_cast<int>(*curr - 0x30);
curr++; curr++;
@@ -321,7 +320,7 @@ static bool tryParseDouble(const char *s, const char *s_end, double *result) {
if (*curr == '.') { if (*curr == '.') {
curr++; curr++;
read = 1; read = 1;
while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) { while ((end_not_reached = (curr != s_end)) && IS_DIGIT(*curr)) {
// NOTE: Don't use powf here, it will absolutely murder precision. // NOTE: Don't use powf here, it will absolutely murder precision.
mantissa += static_cast<int>(*curr - 0x30) * pow(10.0, -read); mantissa += static_cast<int>(*curr - 0x30) * pow(10.0, -read);
read++; read++;
@@ -342,14 +341,14 @@ static bool tryParseDouble(const char *s, const char *s_end, double *result) {
if ((end_not_reached = (curr != s_end)) && (*curr == '+' || *curr == '-')) { if ((end_not_reached = (curr != s_end)) && (*curr == '+' || *curr == '-')) {
exp_sign = *curr; exp_sign = *curr;
curr++; curr++;
} else if (isdigit(*curr)) { /* Pass through. */ } else if (IS_DIGIT(*curr)) { /* Pass through. */
} else { } else {
// Empty E is not allowed. // Empty E is not allowed.
goto fail; goto fail;
} }
read = 0; read = 0;
while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) { while ((end_not_reached = (curr != s_end)) && IS_DIGIT(*curr)) {
exponent *= 10; exponent *= 10;
exponent += static_cast<int>(*curr - 0x30); exponent += static_cast<int>(*curr - 0x30);
curr++; curr++;
@@ -625,7 +624,7 @@ void LoadMtl(std::map<std::string, int> &material_map,
continue; // comment line continue; // comment line
// new mtl // new mtl
if ((0 == strncmp(token, "newmtl", 6)) && isSpace((token[6]))) { if ((0 == strncmp(token, "newmtl", 6)) && IS_SPACE((token[6]))) {
// flush previous material. // flush previous material.
if (!material.name.empty()) { if (!material.name.empty()) {
material_map.insert(std::pair<std::string, int>( material_map.insert(std::pair<std::string, int>(
@@ -649,7 +648,7 @@ void LoadMtl(std::map<std::string, int> &material_map,
} }
// ambient // ambient
if (token[0] == 'K' && token[1] == 'a' && isSpace((token[2]))) { if (token[0] == 'K' && token[1] == 'a' && IS_SPACE((token[2]))) {
token += 2; token += 2;
float r, g, b; float r, g, b;
parseFloat3(r, g, b, token); parseFloat3(r, g, b, token);
@@ -660,7 +659,7 @@ void LoadMtl(std::map<std::string, int> &material_map,
} }
// diffuse // diffuse
if (token[0] == 'K' && token[1] == 'd' && isSpace((token[2]))) { if (token[0] == 'K' && token[1] == 'd' && IS_SPACE((token[2]))) {
token += 2; token += 2;
float r, g, b; float r, g, b;
parseFloat3(r, g, b, token); parseFloat3(r, g, b, token);
@@ -671,7 +670,7 @@ void LoadMtl(std::map<std::string, int> &material_map,
} }
// specular // specular
if (token[0] == 'K' && token[1] == 's' && isSpace((token[2]))) { if (token[0] == 'K' && token[1] == 's' && IS_SPACE((token[2]))) {
token += 2; token += 2;
float r, g, b; float r, g, b;
parseFloat3(r, g, b, token); parseFloat3(r, g, b, token);
@@ -682,7 +681,7 @@ void LoadMtl(std::map<std::string, int> &material_map,
} }
// transmittance // transmittance
if (token[0] == 'K' && token[1] == 't' && isSpace((token[2]))) { if (token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) {
token += 2; token += 2;
float r, g, b; float r, g, b;
parseFloat3(r, g, b, token); parseFloat3(r, g, b, token);
@@ -693,14 +692,14 @@ void LoadMtl(std::map<std::string, int> &material_map,
} }
// ior(index of refraction) // ior(index of refraction)
if (token[0] == 'N' && token[1] == 'i' && isSpace((token[2]))) { if (token[0] == 'N' && token[1] == 'i' && IS_SPACE((token[2]))) {
token += 2; token += 2;
material.ior = parseFloat(token); material.ior = parseFloat(token);
continue; continue;
} }
// emission // emission
if (token[0] == 'K' && token[1] == 'e' && isSpace(token[2])) { if (token[0] == 'K' && token[1] == 'e' && IS_SPACE(token[2])) {
token += 2; token += 2;
float r, g, b; float r, g, b;
parseFloat3(r, g, b, token); parseFloat3(r, g, b, token);
@@ -711,26 +710,26 @@ void LoadMtl(std::map<std::string, int> &material_map,
} }
// shininess // shininess
if (token[0] == 'N' && token[1] == 's' && isSpace(token[2])) { if (token[0] == 'N' && token[1] == 's' && IS_SPACE(token[2])) {
token += 2; token += 2;
material.shininess = parseFloat(token); material.shininess = parseFloat(token);
continue; continue;
} }
// illum model // illum model
if (0 == strncmp(token, "illum", 5) && isSpace(token[5])) { if (0 == strncmp(token, "illum", 5) && IS_SPACE(token[5])) {
token += 6; token += 6;
material.illum = parseInt(token); material.illum = parseInt(token);
continue; continue;
} }
// dissolve // dissolve
if ((token[0] == 'd' && isSpace(token[1]))) { if ((token[0] == 'd' && IS_SPACE(token[1]))) {
token += 1; token += 1;
material.dissolve = parseFloat(token); material.dissolve = parseFloat(token);
continue; continue;
} }
if (token[0] == 'T' && token[1] == 'r' && isSpace(token[2])) { if (token[0] == 'T' && token[1] == 'r' && IS_SPACE(token[2])) {
token += 2; token += 2;
// Invert value of Tr(assume Tr is in range [0, 1]) // Invert value of Tr(assume Tr is in range [0, 1])
material.dissolve = 1.0f - parseFloat(token); material.dissolve = 1.0f - parseFloat(token);
@@ -738,56 +737,56 @@ void LoadMtl(std::map<std::string, int> &material_map,
} }
// ambient texture // ambient texture
if ((0 == strncmp(token, "map_Ka", 6)) && isSpace(token[6])) { if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) {
token += 7; token += 7;
material.ambient_texname = token; material.ambient_texname = token;
continue; continue;
} }
// diffuse texture // diffuse texture
if ((0 == strncmp(token, "map_Kd", 6)) && isSpace(token[6])) { if ((0 == strncmp(token, "map_Kd", 6)) && IS_SPACE(token[6])) {
token += 7; token += 7;
material.diffuse_texname = token; material.diffuse_texname = token;
continue; continue;
} }
// specular texture // specular texture
if ((0 == strncmp(token, "map_Ks", 6)) && isSpace(token[6])) { if ((0 == strncmp(token, "map_Ks", 6)) && IS_SPACE(token[6])) {
token += 7; token += 7;
material.specular_texname = token; material.specular_texname = token;
continue; continue;
} }
// specular highlight texture // specular highlight texture
if ((0 == strncmp(token, "map_Ns", 6)) && isSpace(token[6])) { if ((0 == strncmp(token, "map_Ns", 6)) && IS_SPACE(token[6])) {
token += 7; token += 7;
material.specular_highlight_texname = token; material.specular_highlight_texname = token;
continue; continue;
} }
// bump texture // bump texture
if ((0 == strncmp(token, "map_bump", 8)) && isSpace(token[8])) { if ((0 == strncmp(token, "map_bump", 8)) && IS_SPACE(token[8])) {
token += 9; token += 9;
material.bump_texname = token; material.bump_texname = token;
continue; continue;
} }
// alpha texture // alpha texture
if ((0 == strncmp(token, "map_d", 5)) && isSpace(token[5])) { if ((0 == strncmp(token, "map_d", 5)) && IS_SPACE(token[5])) {
token += 6; token += 6;
material.alpha_texname = token; material.alpha_texname = token;
continue; continue;
} }
// bump texture // bump texture
if ((0 == strncmp(token, "bump", 4)) && isSpace(token[4])) { if ((0 == strncmp(token, "bump", 4)) && IS_SPACE(token[4])) {
token += 5; token += 5;
material.bump_texname = token; material.bump_texname = token;
continue; continue;
} }
// displacement texture // displacement texture
if ((0 == strncmp(token, "disp", 4)) && isSpace(token[4])) { if ((0 == strncmp(token, "disp", 4)) && IS_SPACE(token[4])) {
token += 5; token += 5;
material.displacement_texname = token; material.displacement_texname = token;
continue; continue;
@@ -914,7 +913,7 @@ bool LoadObj(std::vector<shape_t> &shapes, // [output]
continue; // comment line continue; // comment line
// vertex // vertex
if (token[0] == 'v' && isSpace((token[1]))) { if (token[0] == 'v' && IS_SPACE((token[1]))) {
token += 2; token += 2;
float x, y, z; float x, y, z;
parseFloat3(x, y, z, token); parseFloat3(x, y, z, token);
@@ -925,7 +924,7 @@ bool LoadObj(std::vector<shape_t> &shapes, // [output]
} }
// normal // normal
if (token[0] == 'v' && token[1] == 'n' && isSpace((token[2]))) { if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) {
token += 3; token += 3;
float x, y, z; float x, y, z;
parseFloat3(x, y, z, token); parseFloat3(x, y, z, token);
@@ -936,7 +935,7 @@ bool LoadObj(std::vector<shape_t> &shapes, // [output]
} }
// texcoord // texcoord
if (token[0] == 'v' && token[1] == 't' && isSpace((token[2]))) { if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) {
token += 3; token += 3;
float x, y; float x, y;
parseFloat2(x, y, token); parseFloat2(x, y, token);
@@ -946,12 +945,14 @@ bool LoadObj(std::vector<shape_t> &shapes, // [output]
} }
// face // face
if (token[0] == 'f' && isSpace((token[1]))) { if (token[0] == 'f' && IS_SPACE((token[1]))) {
token += 2; token += 2;
token += strspn(token, " \t"); token += strspn(token, " \t");
std::vector<vertex_index> face; std::vector<vertex_index> face;
while (!isNewLine(token[0])) { face.reserve(3);
while (!IS_NEW_LINE(token[0])) {
vertex_index vi = parseTriple(token, static_cast<int>(v.size() / 3), vertex_index vi = 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)); static_cast<int>(vt.size() / 2));
@@ -960,13 +961,15 @@ bool LoadObj(std::vector<shape_t> &shapes, // [output]
token += n; token += n;
} }
faceGroup.push_back(face); // replace with emplace_back + std::move on C++11
faceGroup.push_back(std::vector<vertex_index>());
faceGroup[faceGroup.size() - 1].swap(face);
continue; continue;
} }
// use mtl // use mtl
if ((0 == strncmp(token, "usemtl", 6)) && isSpace((token[6]))) { if ((0 == strncmp(token, "usemtl", 6)) && IS_SPACE((token[6]))) {
char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
token += 7; token += 7;
@@ -976,28 +979,26 @@ bool LoadObj(std::vector<shape_t> &shapes, // [output]
sscanf(token, "%s", namebuf); sscanf(token, "%s", namebuf);
#endif #endif
// Create face group per material. int newMaterialId = -1;
bool ret =
exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup, tags,
material, name, true, triangulate);
if (ret) {
shapes.push_back(shape);
}
shape = shape_t();
faceGroup.clear();
if (material_map.find(namebuf) != material_map.end()) { if (material_map.find(namebuf) != material_map.end()) {
material = material_map[namebuf]; newMaterialId = material_map[namebuf];
} else { } else {
// { error!! material not found } // { error!! material not found }
material = -1; }
if (newMaterialId != material) {
// Create per-face material
exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup, tags,
material, name, true, triangulate);
faceGroup.clear();
material = newMaterialId;
} }
continue; continue;
} }
// load mtl // load mtl
if ((0 == strncmp(token, "mtllib", 6)) && isSpace((token[6]))) { if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) {
char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
token += 7; token += 7;
#ifdef _MSC_VER #ifdef _MSC_VER
@@ -1019,7 +1020,7 @@ bool LoadObj(std::vector<shape_t> &shapes, // [output]
} }
// group name // group name
if (token[0] == 'g' && isSpace((token[1]))) { if (token[0] == 'g' && IS_SPACE((token[1]))) {
// flush previous face group. // flush previous face group.
bool ret = bool ret =
@@ -1035,7 +1036,9 @@ bool LoadObj(std::vector<shape_t> &shapes, // [output]
faceGroup.clear(); faceGroup.clear();
std::vector<std::string> names; std::vector<std::string> names;
while (!isNewLine(token[0])) { names.reserve(2);
while (!IS_NEW_LINE(token[0])) {
std::string str = parseString(token); std::string str = parseString(token);
names.push_back(str); names.push_back(str);
token += strspn(token, " \t\r"); // skip tag token += strspn(token, " \t\r"); // skip tag
@@ -1054,7 +1057,7 @@ bool LoadObj(std::vector<shape_t> &shapes, // [output]
} }
// object name // object name
if (token[0] == 'o' && isSpace((token[1]))) { if (token[0] == 'o' && IS_SPACE((token[1]))) {
// flush previous face group. // flush previous face group.
bool ret = bool ret =
@@ -1081,7 +1084,7 @@ bool LoadObj(std::vector<shape_t> &shapes, // [output]
continue; continue;
} }
if (token[0] == 't' && isSpace(token[1])) { if (token[0] == 't' && IS_SPACE(token[1])) {
tag_t tag; tag_t tag;
char namebuf[4096]; char namebuf[4096];