5 Commits

Author SHA1 Message Date
Syoyo Fujita
1e2cd27c64 Fix build on MSC + CXX11 codepath. 2017-12-28 18:05:00 +09:00
Syoyo Fujita
49726abcf1 Introduce TINYOBJ_USE_CXX11 to suppress clang 5.0's -Wzero-as-null-pointer-constatnt warninig. 2017-12-24 18:40:49 +09: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
15 changed files with 151 additions and 200 deletions

View File

@@ -26,20 +26,11 @@ Old version is available `v0.9.x` branch https://github.com/syoyo/tinyobjloader/
## What's new
### Version 2.x
* Refactor API
* Support triangulation for concave polygons(#151)
### Version 1.x
Avaiable in `v1.x.y` branch.
* 20 Aug, 2016 : Bump version v1.0.0. New data structure and API!
### Older version
### Old version
Older version is avaiable in `v0.9.x` branch.
Previous old version is avaiable in `v0.9.x` branch.
## Example
@@ -58,11 +49,7 @@ http://casual-effects.com/data/index.html
TinyObjLoader is successfully used in ...
### New version(v2.x)
* Your project here! (Letting us know via github issue is welcome!)
### Old version(v1.x)
### New version(v1.0.x)
* Double precision support through `TINYOBJLOADER_USE_DOUBLE` thanks to noma
* Loading models in Vulkan Tutorial https://vulkan-tutorial.com/Loading_models
@@ -73,7 +60,7 @@ TinyObjLoader is successfully used in ...
* VFPR - a Vulkan Forward Plus Renderer : https://github.com/WindyDarian/Vulkan-Forward-Plus-Renderer
* Your project here! (Letting us know via github issue is welcome!)
### Older version(v0.9.x)
### Old version(v0.9.x)
* bullet3 https://github.com/erwincoumans/bullet3
* pbrt-v2 https://github.com/mmp/pbrt-v2
@@ -241,12 +228,12 @@ for (size_t s = 0; s < shapes.size(); s++) {
for (size_t v = 0; v < fv; v++) {
// access to vertex
tinyobj::index_t idx = shapes[s].mesh.indices[index_offset + v];
tinyobj::real_t vx = attrib.vertices[idx.vertex_index].x;
tinyobj::real_t vy = attrib.vertices[idx.vertex_index].y;
tinyobj::real_t vz = attrib.vertices[idx.vertex_index].z;
tinyobj::real_t nx = attrib.normals[idx.normal_index].x;
tinyobj::real_t ny = attrib.normals[idx.normal_index].y;
tinyobj::real_t nz = attrib.normals[idx.normal_index].z;
tinyobj::real_t vx = attrib.vertices[3*idx.vertex_index+0];
tinyobj::real_t vy = attrib.vertices[3*idx.vertex_index+1];
tinyobj::real_t vz = attrib.vertices[3*idx.vertex_index+2];
tinyobj::real_t nx = attrib.normals[3*idx.normal_index+0];
tinyobj::real_t ny = attrib.normals[3*idx.normal_index+1];
tinyobj::real_t nz = attrib.normals[3*idx.normal_index+2];
tinyobj::real_t tx = attrib.texcoords[2*idx.texcoord_index+0];
tinyobj::real_t ty = attrib.texcoords[2*idx.texcoord_index+1];
// Optional: vertex colors

View File

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

View File

@@ -29,6 +29,13 @@ extern "C" {
#endif
#endif
#ifdef TINYOBJ_USE_CXX11
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wc++98-compat"
#endif
#endif
class timerutil {
public:
#ifdef _WIN32
@@ -63,7 +70,7 @@ class timerutil {
}
time_t current() {
struct timeval t;
gettimeofday(&t, NULL);
gettimeofday(&t, tobj_null);
return static_cast<time_t>(t.tv_sec * 1000 + t.tv_usec);
}
@@ -106,24 +113,24 @@ static void PrintInfo(const tinyobj::attrib_t& attrib,
std::cout << "# of shapes : " << shapes.size() << std::endl;
std::cout << "# of materials : " << materials.size() << std::endl;
for (size_t v = 0; v < attrib.vertices.size(); v++) {
for (size_t v = 0; v < attrib.vertices.size() / 3; v++) {
printf(" v[%ld] = (%f, %f, %f)\n", static_cast<long>(v),
static_cast<const double>(attrib.vertices[v].x),
static_cast<const double>(attrib.vertices[v].y),
static_cast<const double>(attrib.vertices[v].z));
static_cast<const double>(attrib.vertices[3 * v + 0]),
static_cast<const double>(attrib.vertices[3 * v + 1]),
static_cast<const double>(attrib.vertices[3 * v + 2]));
}
for (size_t v = 0; v < attrib.normals.size(); v++) {
for (size_t v = 0; v < attrib.normals.size() / 3; v++) {
printf(" n[%ld] = (%f, %f, %f)\n", static_cast<long>(v),
static_cast<const double>(attrib.normals[v].x),
static_cast<const double>(attrib.normals[v].y),
static_cast<const double>(attrib.normals[v].z));
static_cast<const double>(attrib.normals[3 * v + 0]),
static_cast<const double>(attrib.normals[3 * v + 1]),
static_cast<const double>(attrib.normals[3 * v + 2]));
}
for (size_t v = 0; v < attrib.texcoords.size(); v++) {
for (size_t v = 0; v < attrib.texcoords.size() / 2; v++) {
printf(" uv[%ld] = (%f, %f)\n", static_cast<long>(v),
static_cast<const double>(attrib.texcoords[v].x),
static_cast<const double>(attrib.texcoords[v].y));
static_cast<const double>(attrib.texcoords[2 * v + 0]),
static_cast<const double>(attrib.texcoords[2 * v + 1]));
}
// For each shape
@@ -261,7 +268,7 @@ static void PrintInfo(const tinyobj::attrib_t& attrib,
}
}
static bool TestLoadObj(const char* filename, const char* basepath = NULL,
static bool TestLoadObj(const char* filename, const char* basepath = tobj_null,
bool triangulate = true) {
std::cout << "Loading " << filename << std::endl;

View File

@@ -23,7 +23,6 @@ THE SOFTWARE.
*/
//
// version 2.0.0 : New API! And support triangulation of concave polygon(#151).
// version 1.1.0 : Support parsing vertex color(#144)
// version 1.0.8 : Fix parsing `g` tag just after `usemtl`(#138)
// version 1.0.7 : Support multiple tex options(#126)
@@ -52,6 +51,32 @@ THE SOFTWARE.
namespace tinyobj {
//
// Please define `TINYOBJ_USE_CXX11` flag when you compile tinyobjloader with C++11 compiler.
//
#ifdef TINYOBJ_USE_CXX11
#if defined(_MSC_VER)
# if _MSC_VER < 1800
# error This project needs at least Visual Studio 2013
# endif
#define tobj_null nullptr
#elif __cplusplus <= 199711L
# error This project can only be compiled with a compiler that supports C++11
#else
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wc++98-compat"
#endif
#define tobj_null nullptr
#endif
#else // !TINYOBJ_USE_CXX11
#define tobj_null NULL
#endif
// https://en.wikipedia.org/wiki/Wavefront_.obj_file says ...
//
// -blendu on | off # set horizontal texture blending
@@ -109,14 +134,6 @@ typedef double real_t;
typedef float real_t;
#endif
struct vec3_t { real_t x,y,z; };
inline vec3_t operator+(const vec3_t &l,const vec3_t &r){vec3_t o;o.x=l.x+r.x;o.y=l.y+r.y;o.z=l.z+r.z;return o;}
inline vec3_t operator-(const vec3_t &l,const vec3_t &r){vec3_t o;o.x=l.x-r.x;o.y=l.y-r.y;o.z=l.z-r.z;return o;}
inline vec3_t operator*(const vec3_t &l,real_t r){vec3_t o;o.x=l.x*r;o.y=l.y*r;o.z=l.z*r;return o;}
inline vec3_t cross(const vec3_t &l,const vec3_t &r){vec3_t o;o.x=l.y*r.z-r.y*l.z;o.y=l.z*r.x-r.z*l.x;o.z=l.x*r.y-r.x*l.y;return o;}
inline real_t dot(const vec3_t &l,const vec3_t &r){return l.x*r.x+l.y*r.y+l.z*r.z;}
struct vec2_t { real_t x,y; };
typedef enum {
TEXTURE_TYPE_NONE, // default
TEXTURE_TYPE_SPHERE,
@@ -237,9 +254,9 @@ typedef struct {
// Vertex attributes
typedef struct {
std::vector<vec3_t> vertices; // 'v'
std::vector<vec3_t> normals; // 'vn'
std::vector<vec2_t> texcoords; // 'vt'
std::vector<real_t> vertices; // 'v'
std::vector<real_t> normals; // 'vn'
std::vector<real_t> texcoords; // 'vt'
std::vector<real_t> colors; // extension: vertex colors
} attrib_t;
@@ -268,14 +285,14 @@ typedef struct callback_t_ {
void (*object_cb)(void *user_data, const char *name);
callback_t_()
: vertex_cb(NULL),
normal_cb(NULL),
texcoord_cb(NULL),
index_cb(NULL),
usemtl_cb(NULL),
mtllib_cb(NULL),
group_cb(NULL),
object_cb(NULL) {}
: vertex_cb(tobj_null),
normal_cb(tobj_null),
texcoord_cb(tobj_null),
index_cb(tobj_null),
usemtl_cb(tobj_null),
mtllib_cb(tobj_null),
group_cb(tobj_null),
object_cb(tobj_null) {}
} callback_t;
class MaterialReader {
@@ -327,7 +344,7 @@ class MaterialStreamReader : public MaterialReader {
/// or not.
bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
std::vector<material_t> *materials, std::string *err,
const char *filename, const char *mtl_basedir = NULL,
const char *filename, const char *mtl_basedir = tobj_null,
bool triangulate = true);
/// Loads .obj from a file with custom user callback.
@@ -337,9 +354,9 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
/// Returns warning and error message into `err`
/// See `examples/callback_api/` for how to use this function.
bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
void *user_data = NULL,
MaterialReader *readMatFn = NULL,
std::string *err = NULL);
void *user_data = tobj_null,
MaterialReader *readMatFn = tobj_null,
std::string *err = tobj_null);
/// Loads object from a std::istream, uses GetMtlIStreamFn to retrieve
/// std::istream for materials.
@@ -347,7 +364,7 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
/// Returns warning and error message into `err`
bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
std::vector<material_t> *materials, std::string *err,
std::istream *inStream, MaterialReader *readMatFn = NULL,
std::istream *inStream, MaterialReader *readMatFn = tobj_null,
bool triangulate = true);
/// Loads materials into std::map
@@ -357,6 +374,10 @@ void LoadMtl(std::map<std::string, int> *material_map,
} // namespace tinyobj
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#endif // TINY_OBJ_LOADER_H_
#ifdef TINYOBJLOADER_IMPLEMENTATION
@@ -373,6 +394,13 @@ void LoadMtl(std::map<std::string, int> *material_map,
namespace tinyobj {
#ifdef TINYOBJ_USE_CXX11
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wc++98-compat"
#endif
#endif
MaterialReader::~MaterialReader() {}
struct vertex_index {
@@ -983,7 +1011,7 @@ static void InitMaterial(material_t *material) {
static bool exportFaceGroupToShape(
shape_t *shape, const std::vector<std::vector<vertex_index> > &faceGroup,
const std::vector<tag_t> &tags, const int material_id,
const std::string &name, bool triangulate, const std::vector<vec3_t> &v ) {
const std::string &name, bool triangulate) {
if (faceGroup.empty()) {
return false;
}
@@ -999,124 +1027,29 @@ static bool exportFaceGroupToShape(
size_t npolys = face.size();
if (triangulate) {
vec3_t face_normal = {0,0,0};
// Polygon -> triangle fan conversion
for (size_t k = 2; k < npolys; k++) {
i1 = i2;
i2 = face[k];
for(size_t k = 0; k < npolys; ++k) {
int vi0 = face[(k+0)%npolys].v_idx;
int vi1 = face[(k+1)%npolys].v_idx;
int vi2 = face[(k+2)%npolys].v_idx;
const vec3_t &v0 = v[vi0];
const vec3_t &v1 = v[vi1];
const vec3_t &v2 = v[vi2];
const vec3_t e0 = v1 - v0;
const vec3_t e1 = v2 - v1;
const vec3_t n = cross( e0, e1 );
face_normal = face_normal + n;
}
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;
// face_normal is currently area of face
{
real_t l = sqrt(dot( face_normal, face_normal ));
face_normal = face_normal * (1.0/l);
}
shape->mesh.indices.push_back(idx0);
shape->mesh.indices.push_back(idx1);
shape->mesh.indices.push_back(idx2);
int maxRounds = 10; // arbitrary max loop count to protect against unexpected errors
std::vector<vertex_index> remainingFace = face;
size_t guess_vert = 0;
while( remainingFace.size() > 3 && maxRounds > 0 ) {
npolys = remainingFace.size();
if( guess_vert >= npolys ) {
maxRounds -= 1;
guess_vert -= npolys;
}
i0 = remainingFace[(guess_vert+0)%npolys];
i1 = remainingFace[(guess_vert+1)%npolys];
i2 = remainingFace[(guess_vert+2)%npolys];
int vi0 = i0.v_idx;
int vi1 = i1.v_idx;
int vi2 = i2.v_idx;
const vec3_t &v0 = v[vi0];
const vec3_t &v1 = v[vi1];
const vec3_t &v2 = v[vi2];
const vec3_t e0 = v1 - v0;
const vec3_t e1 = v2 - v1;
const vec3_t n = cross( e0, e1 );
bool internal = dot( n, face_normal ) < 0.0f;
if( internal ) { 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 ) {
int ovi = remainingFace[(guess_vert+otherVert)%npolys].v_idx;
const vec3_t &ov = v[ovi];
if( dot( face_normal, cross( e0, ov-v0 ) ) < 0 )
continue;
if( dot( face_normal, cross( e1, ov-v1 ) ) < 0 )
continue;
if( dot( face_normal, cross( v0-v2, ov-v2 ) ) < 0 )
continue;
// vert inside triangle
overlap = true;
break;
}
if( overlap ) { guess_vert += 1; continue; }
// this triangle is an ear
{
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);
}
// remove v1 from the list
size_t removed_vert_index = (guess_vert+1)%npolys;
while( removed_vert_index + 1 < npolys ) {
remainingFace[removed_vert_index] = remainingFace[removed_vert_index+1];
removed_vert_index += 1;
}
remainingFace.pop_back();
}
if( remainingFace.size() == 3 ) {
i0 = remainingFace[0];
i1 = remainingFace[1];
i2 = remainingFace[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.num_face_vertices.push_back(3);
shape->mesh.material_ids.push_back(material_id);
}
} else {
for (size_t k = 0; k < npolys; k++) {
index_t idx;
@@ -1632,9 +1565,9 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
bool triangulate) {
std::stringstream errss;
std::vector<vec3_t> v;
std::vector<vec3_t> vn;
std::vector<vec2_t> vt;
std::vector<real_t> v;
std::vector<real_t> vn;
std::vector<real_t> vt;
std::vector<real_t> vc;
std::vector<tag_t> tags;
std::vector<std::vector<vertex_index> > faceGroup;
@@ -1677,10 +1610,12 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
// vertex
if (token[0] == 'v' && IS_SPACE((token[1]))) {
token += 2;
vec3_t v3;
real_t x, y, z;
real_t r, g, b;
parseVertexWithColor(&v3.x, &v3.y, &v3.z, &r, &g, &b, &token);
v.push_back(v3);
parseVertexWithColor(&x, &y, &z, &r, &g, &b, &token);
v.push_back(x);
v.push_back(y);
v.push_back(z);
vc.push_back(r);
vc.push_back(g);
@@ -1691,18 +1626,21 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
// normal
if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) {
token += 3;
vec3_t v3;
parseReal3(&v3.x, &v3.y, &v3.z, &token);
vn.push_back(v3);
real_t x, y, z;
parseReal3(&x, &y, &z, &token);
vn.push_back(x);
vn.push_back(y);
vn.push_back(z);
continue;
}
// texcoord
if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) {
token += 3;
vec2_t v2;
parseReal2(&v2.x, &v2.y, &token);
vt.push_back(v2);
real_t x, y;
parseReal2(&x, &y, &token);
vt.push_back(x);
vt.push_back(y);
continue;
}
@@ -1716,9 +1654,9 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
while (!IS_NEW_LINE(token[0])) {
vertex_index vi;
if (!parseTriple(&token, static_cast<int>(v.size()),
static_cast<int>(vn.size()),
static_cast<int>(vt.size()), &vi)) {
if (!parseTriple(&token, static_cast<int>(v.size() / 3),
static_cast<int>(vn.size() / 3),
static_cast<int>(vt.size() / 2), &vi)) {
if (err) {
(*err) = "Failed parse `f' line(e.g. zero value for face index).\n";
}
@@ -1756,7 +1694,7 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
// this time.
// just clear `faceGroup` after `exportFaceGroupToShape()` call.
exportFaceGroupToShape(&shape, faceGroup, tags, material, name,
triangulate, v);
triangulate);
faceGroup.clear();
material = newMaterialId;
}
@@ -1811,7 +1749,7 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
if (token[0] == 'g' && IS_SPACE((token[1]))) {
// flush previous face group.
bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name,
triangulate, v);
triangulate);
(void)ret; // return value not used.
if (shape.mesh.indices.size() > 0) {
@@ -1848,7 +1786,7 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
if (token[0] == 'o' && IS_SPACE((token[1]))) {
// flush previous face group.
bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name,
triangulate, v);
triangulate);
if (ret) {
shapes->push_back(shape);
}
@@ -1898,7 +1836,7 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
}
bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name,
triangulate, v);
triangulate);
// exportFaceGroupToShape return false when `usemtl` is called in the last
// line.
// we also add `shape` to `shapes` when `shape.mesh` has already some
@@ -2129,7 +2067,7 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
static_cast<int>(names_out.size()));
} else {
callback.group_cb(user_data, NULL, 0);
callback.group_cb(user_data, tobj_null, 0);
}
}
@@ -2199,6 +2137,11 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
return true;
}
#ifdef __clang__
#pragma clang diagnostic pop
#endif
} // namespace tinyobj
#endif