Added a LoadObj function that reads from a std::istream.

+ Added a LoadObj function that reads from a std::istream.  This should allow
   more generic usage and possibly make testing a little easier.
   o This LoadObj accepts a function that returns a
     std::unique_ptr<std::istream> for a material.
 + Modified LoadMtl to read from a std::istream to allow more generic
   usage.
 + Modified test.cc to check that the changes work as expected and nothing was
   broken.

Tests:
 + Compiled test.cc, checked diff of output against pre change output.
   Same output where expected.
This commit is contained in:
E J Burns
2014-04-28 14:37:32 +01:00
parent c33b0cf6cc
commit 08ff49d2d0
3 changed files with 179 additions and 42 deletions

137
test.cc
View File

@@ -3,23 +3,15 @@
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <cstddef>
#include <iostream>
#include <sstream>
#include <fstream>
#include <functional>
#include <memory>
static bool
TestLoadObj(
const char* filename,
const char* basepath = NULL)
static void PrintInfo(const std::vector<tinyobj::shape_t>& shapes)
{
std::cout << "Loading " << filename << std::endl;
std::vector<tinyobj::shape_t> shapes;
std::string err = tinyobj::LoadObj(shapes, filename, basepath);
if (!err.empty()) {
std::cerr << err << std::endl;
return false;
}
std::cout << "# of shapes : " << shapes.size() << std::endl;
for (size_t i = 0; i < shapes.size(); i++) {
@@ -53,14 +45,124 @@ TestLoadObj(
printf(" material.map_Kd = %s\n", shapes[i].material.diffuse_texname.c_str());
printf(" material.map_Ks = %s\n", shapes[i].material.specular_texname.c_str());
printf(" material.map_Ns = %s\n", shapes[i].material.normal_texname.c_str());
std::map<std::string, std::string>::iterator it(shapes[i].material.unknown_parameter.begin());
std::map<std::string, std::string>::iterator itEnd(shapes[i].material.unknown_parameter.end());
auto it = shapes[i].material.unknown_parameter.begin();
auto itEnd = shapes[i].material.unknown_parameter.end();
for (; it != itEnd; it++) {
printf(" material.%s = %s\n", it->first.c_str(), it->second.c_str());
}
printf("\n");
}
}
static bool
TestLoadObj(
const char* filename,
const char* basepath = NULL)
{
std::cout << "Loading " << filename << std::endl;
std::vector<tinyobj::shape_t> shapes;
std::string err = tinyobj::LoadObj(shapes, filename, basepath);
if (!err.empty()) {
std::cerr << err << std::endl;
return false;
}
PrintInfo(shapes);
return true;
}
static bool
TestStreamLoadObj()
{
std::cout << "Stream Loading " << std::endl;
std::stringstream objStream;
objStream
<< "mtllib cube.mtl\n"
"\n"
"v 0.000000 2.000000 2.000000\n"
"v 0.000000 0.000000 2.000000\n"
"v 2.000000 0.000000 2.000000\n"
"v 2.000000 2.000000 2.000000\n"
"v 0.000000 2.000000 0.000000\n"
"v 0.000000 0.000000 0.000000\n"
"v 2.000000 0.000000 0.000000\n"
"v 2.000000 2.000000 0.000000\n"
"# 8 vertices\n"
"\n"
"g front cube\n"
"usemtl white\n"
"f 1 2 3 4\n"
"g back cube\n"
"# expects white material\n"
"f 8 7 6 5\n"
"g right cube\n"
"usemtl red\n"
"f 4 3 7 8\n"
"g top cube\n"
"usemtl white\n"
"f 5 1 4 8\n"
"g left cube\n"
"usemtl green\n"
"f 5 6 2 1\n"
"g bottom cube\n"
"usemtl white\n"
"f 2 6 7 3\n"
"# 6 elements";
auto getMatFileIStreamFunc =
[](const std::string& matId)
{
if (matId == "cube.mtl") {
std::unique_ptr<std::stringstream> matStream(
new std::stringstream(
"newmtl white\n"
"Ka 0 0 0\n"
"Kd 1 1 1\n"
"Ks 0 0 0\n"
"\n"
"newmtl red\n"
"Ka 0 0 0\n"
"Kd 1 0 0\n"
"Ks 0 0 0\n"
"\n"
"newmtl green\n"
"Ka 0 0 0\n"
"Kd 0 1 0\n"
"Ks 0 0 0\n"
"\n"
"newmtl blue\n"
"Ka 0 0 0\n"
"Kd 0 0 1\n"
"Ks 0 0 0\n"
"\n"
"newmtl light\n"
"Ka 20 20 20\n"
"Kd 1 1 1\n"
"Ks 0 0 0"));
return matStream;
}
std::unique_ptr<std::stringstream> emptyUP( nullptr );
return emptyUP;
};
std::vector<tinyobj::shape_t> shapes;
std::string err = tinyobj::LoadObj(shapes, objStream, getMatFileIStreamFunc);
if (!err.empty()) {
std::cerr << err << std::endl;
return false;
}
PrintInfo(shapes);
return true;
}
@@ -79,7 +181,8 @@ main(
} else {
assert(true == TestLoadObj("cornell_box.obj"));
assert(true == TestLoadObj("cube.obj"));
assert(true == TestStreamLoadObj());
}
return 0;
}

View File

@@ -290,35 +290,19 @@ exportFaceGroupToShape(
}
std::string LoadMtl (
std::map<std::string, material_t>& material_map,
const char* filename,
const char* mtl_basepath)
std::istream& inStream)
{
material_map.clear();
std::stringstream err;
std::string filepath;
if (mtl_basepath) {
filepath = std::string(mtl_basepath) + std::string(filename);
} else {
filepath = std::string(filename);
}
std::ifstream ifs(filepath.c_str());
if (!ifs) {
err << "Cannot open file [" << filepath << "]" << std::endl;
return err.str();
}
material_t material;
int maxchars = 8192; // Alloc enough size.
std::vector<char> buf(maxchars); // Alloc enough size.
while (ifs.peek() != -1) {
ifs.getline(&buf[0], maxchars);
while (inStream.peek() != -1) {
inStream.getline(&buf[0], maxchars);
std::string linebuf(&buf[0]);
@@ -511,6 +495,31 @@ LoadObj(
return err.str();
}
auto getMatFileIStreamFunc =
[&](const std::string& matId)
{
std::string filepath;
if (mtl_basepath) {
filepath = std::string(mtl_basepath) + matId;
} else {
filepath = matId;
}
std::unique_ptr<std::ifstream> ifs( new std::ifstream(filepath.c_str()) );
return ifs;
};
return LoadObj(shapes, ifs, getMatFileIStreamFunc);
}
std::string LoadObj(
std::vector<shape_t>& shapes,
std::istream& inStream,
GetMtlIStreamFn getMatFn)
{
std::stringstream err;
std::vector<float> v;
std::vector<float> vn;
std::vector<float> vt;
@@ -524,8 +533,8 @@ LoadObj(
int maxchars = 8192; // Alloc enough size.
std::vector<char> buf(maxchars); // Alloc enough size.
while (ifs.peek() != -1) {
ifs.getline(&buf[0], maxchars);
while (inStream.peek() != -1) {
inStream.getline(&buf[0], maxchars);
std::string linebuf(&buf[0]);
@@ -625,11 +634,20 @@ LoadObj(
token += 7;
sscanf(token, "%s", namebuf);
std::string err_mtl = LoadMtl(material_map, namebuf, mtl_basepath);
if (!err_mtl.empty()) {
faceGroup.clear(); // for safety
return err_mtl;
if (!getMatFn) {
err << "Could not read material, no callable function target.";
return err.str();
}
std::unique_ptr<std::istream> matIStream = getMatFn(namebuf);
if (matIStream) {
std::string err_mtl = LoadMtl(material_map, *matIStream);
if (!err_mtl.empty()) {
faceGroup.clear(); // for safety
return err_mtl;
}
}
continue;
}

View File

@@ -9,6 +9,8 @@
#include <string>
#include <vector>
#include <map>
#include <functional>
#include <memory>
namespace tinyobj {
@@ -49,6 +51,13 @@ typedef struct
mesh_t mesh;
} shape_t;
/// typedef for a function that returns a pointer to an istream for
/// the material identified by the const std::string&
typedef std::function<
std::unique_ptr<std::istream>(
const std::string&)
> GetMtlIStreamFn;
/// Loads .obj from a file.
/// 'shapes' will be filled with parsed shape data
/// The function returns error string.
@@ -59,6 +68,13 @@ std::string LoadObj(
const char* filename,
const char* mtl_basepath = NULL);
/// Loads object from a std::istream, uses GetMtlIStreamFn to retrieve
/// std::istream for materials.
/// Returns empty string when loading .obj success.
std::string LoadObj(
std::vector<shape_t>& shapes, // [output]
std::istream& inStream,
GetMtlIStreamFn getMatFn);
};
#endif // _TINY_OBJ_LOADER_H