Refactor and re-design tinyobjloader.

* Separete attribs(vtx,normal,texcoords) and shape.
* Support different index for vtx/normal/texcoord.
This commit is contained in:
Syoyo Fujita
2016-04-16 19:49:12 +09:00
parent 9c81fcb4cc
commit ee7d6cc0fd
10 changed files with 170 additions and 145 deletions

View File

@@ -21,6 +21,7 @@ Tiny but powerful single file wavefront obj loader written in C++. No dependency
What's new What's new
---------- ----------
* XX YY, ZZZZ : New data strcutre and API!
* Jan 29, 2016 : Support n-polygon(no triangulation) and OpenSubdiv crease tag! Thanks dboogert! * Jan 29, 2016 : Support n-polygon(no triangulation) and OpenSubdiv crease tag! Thanks dboogert!
* Nov 26, 2015 : Now single-header only!. * Nov 26, 2015 : Now single-header only!.
* Nov 08, 2015 : Improved API. * Nov 08, 2015 : Improved API.

74
test.cc
View File

@@ -8,11 +8,35 @@
#include <sstream> #include <sstream>
#include <fstream> #include <fstream>
static void PrintInfo(const std::vector<tinyobj::shape_t>& shapes, const std::vector<tinyobj::material_t>& materials, bool triangulate = true) static void PrintInfo(const tinyobj::attrib_t &attrib, const std::vector<tinyobj::shape_t>& shapes, const std::vector<tinyobj::material_t>& materials, bool triangulate = true)
{ {
std::cout << "# of positions : " << (attrib.positions.size() / 3) << std::endl;
std::cout << "# of normals : " << (attrib.normals.size() / 3) << std::endl;
std::cout << "# of texcoords : " << (attrib.texcoords.size() / 2) << std::endl;
std::cout << "# of shapes : " << shapes.size() << std::endl; std::cout << "# of shapes : " << shapes.size() << std::endl;
std::cout << "# of materials : " << materials.size() << std::endl; std::cout << "# of materials : " << materials.size() << std::endl;
for (size_t v = 0; v < attrib.positions.size() / 3; v++) {
printf(" v[%ld] = (%f, %f, %f)\n", v,
static_cast<const double>(attrib.positions[3*v+0]),
static_cast<const double>(attrib.positions[3*v+1]),
static_cast<const double>(attrib.positions[3*v+2]));
}
for (size_t v = 0; v < attrib.normals.size() / 3; v++) {
printf(" n[%ld] = (%f, %f, %f)\n", v,
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() / 2; v++) {
printf(" uv[%ld] = (%f, %f)\n", v,
static_cast<const double>(attrib.texcoords[2*v+0]),
static_cast<const double>(attrib.texcoords[2*v+1]));
}
for (size_t i = 0; i < shapes.size(); i++) { for (size_t i = 0; i < shapes.size(); i++) {
printf("shape[%ld].name = %s\n", i, shapes[i].name.c_str()); 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].indices: %ld\n", i, shapes[i].mesh.indices.size());
@@ -22,11 +46,19 @@ static void PrintInfo(const std::vector<tinyobj::shape_t>& shapes, const std::ve
printf("Size of shape[%ld].material_ids: %ld\n", i, shapes[i].mesh.material_ids.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); assert((shapes[i].mesh.indices.size() % 3) == 0);
for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) { 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]); tinyobj::index_t i0 = shapes[i].mesh.indices[3*f+0];
tinyobj::index_t i1 = shapes[i].mesh.indices[3*f+1];
tinyobj::index_t i2 = shapes[i].mesh.indices[3*f+2];
printf(" idx[%ld] = %d/%d/%d, %d/%d/%d, %d/%d/%d. mat_id = %d\n", f,
i0.vertex_index, i0.normal_index, i0.texcoord_index,
i1.vertex_index, i1.normal_index, i1.texcoord_index,
i2.vertex_index, i2.normal_index, i2.texcoord_index,
shapes[i].mesh.material_ids[f]);
} }
} else { } else {
for (size_t f = 0; f < shapes[i].mesh.indices.size(); f++) { for (size_t f = 0; f < shapes[i].mesh.indices.size(); f++) {
printf(" idx[%ld] = %d\n", f, shapes[i].mesh.indices[f]); tinyobj::index_t idx = shapes[i].mesh.indices[f];
printf(" idx[%ld] = %d/%d/%d\n", f, idx.vertex_index, idx.normal_index, idx.texcoord_index);
} }
printf("Size of shape[%ld].material_ids: %ld\n", i, shapes[i].mesh.material_ids.size()); printf("Size of shape[%ld].material_ids: %ld\n", i, shapes[i].mesh.material_ids.size());
@@ -44,14 +76,14 @@ static void PrintInfo(const std::vector<tinyobj::shape_t>& shapes, const std::ve
static_cast<long>(shapes[i].mesh.num_vertices[v])); static_cast<long>(shapes[i].mesh.num_vertices[v]));
} }
printf("shape[%ld].vertices: %ld\n", i, shapes[i].mesh.positions.size()); //printf("shape[%ld].vertices: %ld\n", i, shapes[i].mesh.positions.size());
assert((shapes[i].mesh.positions.size() % 3) == 0); //assert((shapes[i].mesh.positions.size() % 3) == 0);
for (size_t v = 0; v < shapes[i].mesh.positions.size() / 3; v++) { //for (size_t v = 0; v < shapes[i].mesh.positions.size() / 3; v++) {
printf(" v[%ld] = (%f, %f, %f)\n", v, // printf(" v[%ld] = (%f, %f, %f)\n", v,
shapes[i].mesh.positions[3*v+0], // static_cast<const double>(shapes[i].mesh.positions[3*v+0]),
shapes[i].mesh.positions[3*v+1], // static_cast<const double>(shapes[i].mesh.positions[3*v+1]),
shapes[i].mesh.positions[3*v+2]); // static_cast<const double>(shapes[i].mesh.positions[3*v+2]));
} //}
printf("shape[%ld].num_tags: %ld\n", i, shapes[i].mesh.tags.size()); printf("shape[%ld].num_tags: %ld\n", i, shapes[i].mesh.tags.size());
for (size_t t = 0; t < shapes[i].mesh.tags.size(); t++) { for (size_t t = 0; t < shapes[i].mesh.tags.size(); t++) {
@@ -70,7 +102,7 @@ static void PrintInfo(const std::vector<tinyobj::shape_t>& shapes, const std::ve
printf(" floats: ["); printf(" floats: [");
for (size_t j = 0; j < shapes[i].mesh.tags[t].floatValues.size(); ++j) for (size_t j = 0; j < shapes[i].mesh.tags[t].floatValues.size(); ++j)
{ {
printf("%f", shapes[i].mesh.tags[t].floatValues[j]); printf("%f", static_cast<const double>(shapes[i].mesh.tags[t].floatValues[j]));
if (j < (shapes[i].mesh.tags[t].floatValues.size()-1)) if (j < (shapes[i].mesh.tags[t].floatValues.size()-1))
{ {
printf(", "); printf(", ");
@@ -128,11 +160,12 @@ TestLoadObj(
{ {
std::cout << "Loading " << filename << std::endl; std::cout << "Loading " << filename << std::endl;
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes; std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials; std::vector<tinyobj::material_t> materials;
std::string err; std::string err;
bool ret = tinyobj::LoadObj(shapes, materials, err, filename, basepath, triangulate); bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename, basepath, triangulate);
if (!err.empty()) { if (!err.empty()) {
std::cerr << err << std::endl; std::cerr << err << std::endl;
@@ -143,7 +176,7 @@ TestLoadObj(
return false; return false;
} }
PrintInfo(shapes, materials, triangulate); PrintInfo(attrib, shapes, materials, triangulate);
return true; return true;
} }
@@ -223,13 +256,13 @@ std::string matStream(
virtual ~MaterialStringStreamReader() {} virtual ~MaterialStringStreamReader() {}
virtual bool operator() ( virtual bool operator() (
const std::string& matId, const std::string& matId,
std::vector<material_t>& materials, std::vector<material_t>* materials,
std::map<std::string, int>& matMap, std::map<std::string, int>* matMap,
std::string& err) std::string* err)
{ {
(void)matId; (void)matId;
(void)err; (void)err;
LoadMtl(matMap, materials, m_matSStream); LoadMtl(matMap, materials, &m_matSStream);
return true; return true;
} }
@@ -238,10 +271,11 @@ std::string matStream(
}; };
MaterialStringStreamReader matSSReader(matStream); MaterialStringStreamReader matSSReader(matStream);
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes; std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials; std::vector<tinyobj::material_t> materials;
std::string err; std::string err;
bool ret = tinyobj::LoadObj(shapes, materials, err, objStream, matSSReader); bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, &objStream, &matSSReader);
if (!err.empty()) { if (!err.empty()) {
std::cerr << err << std::endl; std::cerr << err << std::endl;
@@ -251,7 +285,7 @@ std::string matStream(
return false; return false;
} }
PrintInfo(shapes, materials); PrintInfo(attrib, shapes, materials);
return true; return true;
} }

View File

@@ -1,10 +1,11 @@
// //
// Copyright 2012-2015, Syoyo Fujita. // Copyright 2012-2016, Syoyo Fujita.
// //
// Licensed under 2-clause BSD license. // Licensed under 2-clause BSD license.
// //
// //
// version devel : Change data structure. Support different index for vertex/normal/texcoord(#73, #39)
// version 0.9.20: Fixes creating per-face material using `usemtl`(#68) // 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
@@ -81,11 +82,16 @@ typedef struct {
std::vector<std::string> stringValues; std::vector<std::string> stringValues;
} tag_t; } tag_t;
// Index struct to support differnt indices for vtx/normal/texcoord.
// -1 means not used.
typedef struct { typedef struct {
std::vector<float> positions; int vertex_index;
std::vector<float> normals; int normal_index;
std::vector<float> texcoords; int texcoord_index;
std::vector<unsigned int> indices; } index_t;
typedef struct {
std::vector<index_t> indices;
std::vector<unsigned char> std::vector<unsigned char>
num_vertices; // The number of vertices per face. Up to 255. num_vertices; // The number of vertices per face. Up to 255.
std::vector<int> material_ids; // per-face material ID std::vector<int> material_ids; // per-face material ID
@@ -97,15 +103,21 @@ typedef struct {
mesh_t mesh; mesh_t mesh;
} shape_t; } shape_t;
typedef struct {
std::vector<float> positions;
std::vector<float> normals;
std::vector<float> texcoords;
} attrib_t;
class MaterialReader { class MaterialReader {
public: public:
MaterialReader() {} MaterialReader() {}
virtual ~MaterialReader(); virtual ~MaterialReader();
virtual bool operator()(const std::string &matId, virtual bool operator()(const std::string &matId,
std::vector<material_t> &materials, std::vector<material_t> *materials,
std::map<std::string, int> &matMap, std::map<std::string, int> *matMap,
std::string &err) = 0; std::string *err) = 0;
}; };
class MaterialFileReader : public MaterialReader { class MaterialFileReader : public MaterialReader {
@@ -114,14 +126,15 @@ public:
: m_mtlBasePath(mtl_basepath) {} : m_mtlBasePath(mtl_basepath) {}
virtual ~MaterialFileReader() {} virtual ~MaterialFileReader() {}
virtual bool operator()(const std::string &matId, virtual bool operator()(const std::string &matId,
std::vector<material_t> &materials, std::vector<material_t> *materials,
std::map<std::string, int> &matMap, std::string &err); std::map<std::string, int> *matMap, std::string *err);
private: private:
std::string m_mtlBasePath; std::string m_mtlBasePath;
}; };
/// Loads .obj from a file. /// Loads .obj from a file.
/// 'attrib', 'shapes' and 'materials' will be filled with parsed shape data
/// 'shapes' will be filled with parsed shape data /// 'shapes' will be filled with parsed shape data
/// The function returns error string. /// The function returns error string.
/// Returns true when loading .obj become success. /// Returns true when loading .obj become success.
@@ -129,9 +142,10 @@ private:
/// 'mtl_basepath' is optional, and used for base path for .mtl file. /// 'mtl_basepath' is optional, and used for base path for .mtl file.
/// 'triangulate' is optional, and used whether triangulate polygon face in .obj /// 'triangulate' is optional, and used whether triangulate polygon face in .obj
/// or not. /// or not.
bool LoadObj(std::vector<shape_t> &shapes, // [output] bool LoadObj(attrib_t *attrib,
std::vector<material_t> &materials, // [output] std::vector<shape_t> *shapes,
std::string &err, // [output] std::vector<material_t> *materials,
std::string *err,
const char *filename, const char *mtl_basepath = NULL, const char *filename, const char *mtl_basepath = NULL,
bool triangulate = true); bool triangulate = true);
@@ -139,16 +153,17 @@ bool LoadObj(std::vector<shape_t> &shapes, // [output]
/// std::istream for materials. /// std::istream for materials.
/// Returns true when loading .obj become success. /// Returns true when loading .obj become success.
/// Returns warning and error message into `err` /// Returns warning and error message into `err`
bool LoadObj(std::vector<shape_t> &shapes, // [output] bool LoadObj(attrib_t *attrib,
std::vector<material_t> &materials, // [output] std::vector<shape_t> *shapes,
std::string &err, // [output] std::vector<material_t> *materials,
std::istream &inStream, MaterialReader &readMatFn, std::string *err,
std::istream *inStream, MaterialReader *readMatFn,
bool triangulate = true); bool triangulate = true);
/// Loads materials into std::map /// Loads materials into std::map
void LoadMtl(std::map<std::string, int> &material_map, // [output] void LoadMtl(std::map<std::string, int> *material_map,
std::vector<material_t> &materials, // [output] std::vector<material_t> *materials,
std::istream &inStream); std::istream *inStream);
} }
#ifdef TINYOBJLOADER_IMPLEMENTATION #ifdef TINYOBJLOADER_IMPLEMENTATION
@@ -204,7 +219,7 @@ struct obj_shape {
}; };
#define IS_SPACE( x ) ( ( (x) == ' ') || ( (x) == '\t') ) #define IS_SPACE( x ) ( ( (x) == ' ') || ( (x) == '\t') )
#define IS_DIGIT( x ) ( (unsigned int)( (x) - '0' ) < (unsigned int)10 ) #define IS_DIGIT( x ) ( static_cast<unsigned int>( (x) - '0' ) < static_cast<unsigned int>(10) )
#define IS_NEW_LINE( x ) ( ( (x) == '\r') || ( (x) == '\n') || ( (x) == '\0') ) #define IS_NEW_LINE( x ) ( ( (x) == '\r') || ( (x) == '\n') || ( (x) == '\0') )
// Make index zero-base, and also support relative index. // Make index zero-base, and also support relative index.
@@ -447,45 +462,6 @@ static vertex_index parseTriple(const char *&token, int vsize, int vnsize,
return vi; return vi;
} }
static unsigned int
updateVertex(std::map<vertex_index, unsigned int> &vertexCache,
std::vector<float> &positions, std::vector<float> &normals,
std::vector<float> &texcoords,
const std::vector<float> &in_positions,
const std::vector<float> &in_normals,
const std::vector<float> &in_texcoords, const vertex_index &i) {
const std::map<vertex_index, unsigned int>::iterator it = vertexCache.find(i);
if (it != vertexCache.end()) {
// found cache
return it->second;
}
assert(in_positions.size() > static_cast<unsigned int>(3 * i.v_idx + 2));
positions.push_back(in_positions[3 * static_cast<size_t>(i.v_idx) + 0]);
positions.push_back(in_positions[3 * static_cast<size_t>(i.v_idx) + 1]);
positions.push_back(in_positions[3 * static_cast<size_t>(i.v_idx) + 2]);
if ((i.vn_idx >= 0) &&
(static_cast<size_t>(i.vn_idx * 3 + 2) < in_normals.size())) {
normals.push_back(in_normals[3 * static_cast<size_t>(i.vn_idx) + 0]);
normals.push_back(in_normals[3 * static_cast<size_t>(i.vn_idx) + 1]);
normals.push_back(in_normals[3 * static_cast<size_t>(i.vn_idx) + 2]);
}
if ((i.vt_idx >= 0) &&
(static_cast<size_t>(i.vt_idx * 2 + 1) < in_texcoords.size())) {
texcoords.push_back(in_texcoords[2 * static_cast<size_t>(i.vt_idx) + 0]);
texcoords.push_back(in_texcoords[2 * static_cast<size_t>(i.vt_idx) + 1]);
}
unsigned int idx = static_cast<unsigned int>(positions.size() / 3 - 1);
vertexCache[i] = idx;
return idx;
}
static void InitMaterial(material_t &material) { static void InitMaterial(material_t &material) {
material.name = ""; material.name = "";
material.ambient_texname = ""; material.ambient_texname = "";
@@ -510,13 +486,13 @@ static void InitMaterial(material_t &material) {
} }
static bool exportFaceGroupToShape( static bool exportFaceGroupToShape(
shape_t &shape, std::map<vertex_index, unsigned int> vertexCache, shape_t &shape,
const std::vector<float> &in_positions, const std::vector<float> &in_positions,
const std::vector<float> &in_normals, const std::vector<float> &in_normals,
const std::vector<float> &in_texcoords, const std::vector<float> &in_texcoords,
const std::vector<std::vector<vertex_index> > &faceGroup, const std::vector<std::vector<vertex_index> > &faceGroup,
std::vector<tag_t> &tags, const int material_id, const std::string &name, std::vector<tag_t> &tags, const int material_id, const std::string &name,
bool clearCache, bool triangulate) { bool triangulate) {
if (faceGroup.empty()) { if (faceGroup.empty()) {
return false; return false;
} }
@@ -538,19 +514,20 @@ static bool exportFaceGroupToShape(
i1 = i2; i1 = i2;
i2 = face[k]; i2 = face[k];
unsigned int v0 = updateVertex( index_t idx0, idx1, idx2;
vertexCache, shape.mesh.positions, shape.mesh.normals, idx0.vertex_index = i0.v_idx;
shape.mesh.texcoords, in_positions, in_normals, in_texcoords, i0); idx0.normal_index = i0.vn_idx;
unsigned int v1 = updateVertex( idx0.texcoord_index = i0.vt_idx;
vertexCache, shape.mesh.positions, shape.mesh.normals, idx1.vertex_index = i1.v_idx;
shape.mesh.texcoords, in_positions, in_normals, in_texcoords, i1); idx1.normal_index = i1.vn_idx;
unsigned int v2 = updateVertex( idx1.texcoord_index = i1.vt_idx;
vertexCache, shape.mesh.positions, shape.mesh.normals, idx2.vertex_index = i2.v_idx;
shape.mesh.texcoords, in_positions, in_normals, in_texcoords, i2); idx2.normal_index = i2.vn_idx;
idx2.texcoord_index = i2.vt_idx;
shape.mesh.indices.push_back(v0); shape.mesh.indices.push_back(idx0);
shape.mesh.indices.push_back(v1); shape.mesh.indices.push_back(idx1);
shape.mesh.indices.push_back(v2); shape.mesh.indices.push_back(idx2);
shape.mesh.num_vertices.push_back(3); shape.mesh.num_vertices.push_back(3);
shape.mesh.material_ids.push_back(material_id); shape.mesh.material_ids.push_back(material_id);
@@ -558,12 +535,10 @@ static bool exportFaceGroupToShape(
} else { } else {
for (size_t k = 0; k < npolys; k++) { for (size_t k = 0; k < npolys; k++) {
unsigned int v = index_t idx;
updateVertex(vertexCache, shape.mesh.positions, shape.mesh.normals, idx.vertex_index = face[k].v_idx;
shape.mesh.texcoords, in_positions, in_normals, idx.normal_index = face[k].vn_idx;
in_texcoords, face[k]); idx.texcoord_index = face[k].vt_idx;
shape.mesh.indices.push_back(v);
} }
shape.mesh.num_vertices.push_back(static_cast<unsigned char>(npolys)); shape.mesh.num_vertices.push_back(static_cast<unsigned char>(npolys));
@@ -574,14 +549,11 @@ static bool exportFaceGroupToShape(
shape.name = name; shape.name = name;
shape.mesh.tags.swap(tags); shape.mesh.tags.swap(tags);
if (clearCache)
vertexCache.clear();
return true; return true;
} }
void LoadMtl(std::map<std::string, int> &material_map, void LoadMtl(std::map<std::string, int> *material_map,
std::vector<material_t> &materials, std::istream &inStream) { std::vector<material_t> *materials, std::istream *inStream) {
// Create a default material anyway. // Create a default material anyway.
material_t material; material_t material;
@@ -589,8 +561,8 @@ void LoadMtl(std::map<std::string, int> &material_map,
size_t maxchars = 8192; // Alloc enough size. size_t maxchars = 8192; // Alloc enough size.
std::vector<char> buf(maxchars); // Alloc enough size. std::vector<char> buf(maxchars); // Alloc enough size.
while (inStream.peek() != -1) { while (inStream->peek() != -1) {
inStream.getline(&buf[0], static_cast<std::streamsize>(maxchars)); inStream->getline(&buf[0], static_cast<std::streamsize>(maxchars));
std::string linebuf(&buf[0]); std::string linebuf(&buf[0]);
@@ -624,9 +596,9 @@ void LoadMtl(std::map<std::string, int> &material_map,
if ((0 == strncmp(token, "newmtl", 6)) && IS_SPACE((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>(
material.name, static_cast<int>(materials.size()))); material.name, static_cast<int>(materials->size())));
materials.push_back(material); materials->push_back(material);
} }
// initial temporary material // initial temporary material
@@ -803,15 +775,15 @@ void LoadMtl(std::map<std::string, int> &material_map,
} }
} }
// flush last material. // flush last material.
material_map.insert(std::pair<std::string, int>( material_map->insert(std::pair<std::string, int>(
material.name, static_cast<int>(materials.size()))); material.name, static_cast<int>(materials->size())));
materials.push_back(material); materials->push_back(material);
} }
bool MaterialFileReader::operator()(const std::string &matId, bool MaterialFileReader::operator()(const std::string &matId,
std::vector<material_t> &materials, std::vector<material_t> *materials,
std::map<std::string, int> &matMap, std::map<std::string, int> *matMap,
std::string &err) { std::string *err) {
std::string filepath; std::string filepath;
if (!m_mtlBasePath.empty()) { if (!m_mtlBasePath.empty()) {
@@ -821,29 +793,37 @@ bool MaterialFileReader::operator()(const std::string &matId,
} }
std::ifstream matIStream(filepath.c_str()); std::ifstream matIStream(filepath.c_str());
LoadMtl(matMap, materials, matIStream); LoadMtl(matMap, materials, &matIStream);
if (!matIStream) { if (!matIStream) {
std::stringstream ss; std::stringstream ss;
ss << "WARN: Material file [ " << filepath ss << "WARN: Material file [ " << filepath
<< " ] not found. Created a default material."; << " ] not found. Created a default material.";
err += ss.str(); if (err) {
(*err) += ss.str();
}
} }
return true; return true;
} }
bool LoadObj(std::vector<shape_t> &shapes, // [output] bool LoadObj(attrib_t *attrib,
std::vector<material_t> &materials, // [output] std::vector<shape_t> *shapes,
std::string &err, const char *filename, const char *mtl_basepath, std::vector<material_t> *materials,
std::string *err, const char *filename, const char *mtl_basepath,
bool trianglulate) { bool trianglulate) {
shapes.clear(); attrib->positions.clear();
attrib->normals.clear();
attrib->texcoords.clear();
shapes->clear();
std::stringstream errss; std::stringstream errss;
std::ifstream ifs(filename); std::ifstream ifs(filename);
if (!ifs) { if (!ifs) {
errss << "Cannot open file [" << filename << "]" << std::endl; errss << "Cannot open file [" << filename << "]" << std::endl;
err = errss.str(); if (err) {
(*err) = errss.str();
}
return false; return false;
} }
@@ -853,13 +833,14 @@ bool LoadObj(std::vector<shape_t> &shapes, // [output]
} }
MaterialFileReader matFileReader(basePath); MaterialFileReader matFileReader(basePath);
return LoadObj(shapes, materials, err, ifs, matFileReader, trianglulate); return LoadObj(attrib, shapes, materials, err, &ifs, &matFileReader, trianglulate);
} }
bool LoadObj(std::vector<shape_t> &shapes, // [output] bool LoadObj(attrib_t *attrib,
std::vector<material_t> &materials, // [output] std::vector<shape_t> *shapes,
std::string &err, std::istream &inStream, std::vector<material_t> *materials,
MaterialReader &readMatFn, bool triangulate) { std::string *err, std::istream *inStream,
MaterialReader *readMatFn, bool triangulate) {
std::stringstream errss; std::stringstream errss;
std::vector<float> v; std::vector<float> v;
@@ -871,15 +852,15 @@ bool LoadObj(std::vector<shape_t> &shapes, // [output]
// material // material
std::map<std::string, int> material_map; std::map<std::string, int> material_map;
std::map<vertex_index, unsigned int> vertexCache; //std::map<vertex_index, unsigned int> vertexCache;
int material = -1; int material = -1;
shape_t shape; shape_t shape;
int maxchars = 8192; // Alloc enough size. int maxchars = 8192; // Alloc enough size.
std::vector<char> buf(static_cast<size_t>(maxchars)); // Alloc enough size. std::vector<char> buf(static_cast<size_t>(maxchars)); // Alloc enough size.
while (inStream.peek() != -1) { while (inStream->peek() != -1) {
inStream.getline(&buf[0], maxchars); inStream->getline(&buf[0], maxchars);
std::string linebuf(&buf[0]); std::string linebuf(&buf[0]);
@@ -985,8 +966,8 @@ bool LoadObj(std::vector<shape_t> &shapes, // [output]
if (newMaterialId != material) { if (newMaterialId != material) {
// Create per-face material // Create per-face material
exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup, tags, exportFaceGroupToShape(shape, v, vn, vt, faceGroup, tags,
material, name, true, triangulate); material, name, triangulate);
faceGroup.clear(); faceGroup.clear();
material = newMaterialId; material = newMaterialId;
} }
@@ -1005,8 +986,10 @@ bool LoadObj(std::vector<shape_t> &shapes, // [output]
#endif #endif
std::string err_mtl; std::string err_mtl;
bool ok = readMatFn(namebuf, materials, material_map, err_mtl); bool ok = (*readMatFn)(namebuf, materials, &material_map, &err_mtl);
err += err_mtl; if (err) {
(*err) += err_mtl;
}
if (!ok) { if (!ok) {
faceGroup.clear(); // for safety faceGroup.clear(); // for safety
@@ -1021,10 +1004,10 @@ bool LoadObj(std::vector<shape_t> &shapes, // [output]
// flush previous face group. // flush previous face group.
bool ret = bool ret =
exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup, tags, exportFaceGroupToShape(shape, v, vn, vt, faceGroup, tags,
material, name, true, triangulate); material, name, triangulate);
if (ret) { if (ret) {
shapes.push_back(shape); shapes->push_back(shape);
} }
shape = shape_t(); shape = shape_t();
@@ -1058,10 +1041,10 @@ bool LoadObj(std::vector<shape_t> &shapes, // [output]
// flush previous face group. // flush previous face group.
bool ret = bool ret =
exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup, tags, exportFaceGroupToShape(shape, v, vn, vt, faceGroup, tags,
material, name, true, triangulate); material, name, triangulate);
if (ret) { if (ret) {
shapes.push_back(shape); shapes->push_back(shape);
} }
// material = -1; // material = -1;
@@ -1129,14 +1112,21 @@ bool LoadObj(std::vector<shape_t> &shapes, // [output]
// Ignore unknown command. // Ignore unknown command.
} }
bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup, bool ret = exportFaceGroupToShape(shape, v, vn, vt, faceGroup,
tags, material, name, true, triangulate); tags, material, name, triangulate);
if (ret) { if (ret) {
shapes.push_back(shape); shapes->push_back(shape);
} }
faceGroup.clear(); // for safety faceGroup.clear(); // for safety
err += errss.str(); if (err) {
(*err) += errss.str();
}
attrib->positions.swap(v);
attrib->normals.swap(vn);
attrib->texcoords.swap(vt);
return true; return true;
} }