changing obj loader to tinyobj

This commit is contained in:
Michal Marcinkowski
2015-02-22 13:10:47 -05:00
parent 423d778480
commit cc65ce1167
8 changed files with 898 additions and 151 deletions

View File

@@ -69,6 +69,8 @@ private:
std::string _source;
std::string _destination;
properties::BoolProperty _performShading;
};
} // namespace openspace

View File

@@ -26,6 +26,7 @@
#define __WAVEFRONTOBJECT_H__
#include <openspace/rendering/model/modelgeometry.h>
#include <openspace/util/tiny_obj_loader.h>
namespace openspace {
@@ -51,15 +52,20 @@ public:
//GLubyte padding[4]; // Pads the struct out to 64 bytes for performance increase
} Vertex;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
protected:
void loadObj(const char *filename);
private:
void createSphere();
GLuint _vaoID = 6;
GLuint _vBufferID = 7;
GLuint _iBufferID = 8;
GLuint _vaoID;
GLuint _vBufferID;
GLuint _iBufferID;
GLuint _tBufferID;
unsigned int _tsize;
float* _tarray;
unsigned int _isize;
unsigned int _vsize;

View File

@@ -0,0 +1,94 @@
//
// Copyright 2012-2013, Syoyo Fujita.
//
// Licensed under 2-clause BSD liecense.
//
#ifndef _TINY_OBJ_LOADER_H
#define _TINY_OBJ_LOADER_H
#include <string>
#include <vector>
#include <map>
namespace tinyobj {
typedef struct {
std::string name;
float ambient[3];
float diffuse[3];
float specular[3];
float transmittance[3];
float emission[3];
float shininess;
float ior; // index of refraction
float dissolve; // 1 == opaque; 0 == fully transparent
// illumination model (see http://www.fileformat.info/format/material/)
int illum;
std::string ambient_texname;
std::string diffuse_texname;
std::string specular_texname;
std::string normal_texname;
std::map<std::string, std::string> unknown_parameter;
} material_t;
typedef struct {
std::vector<float> positions;
std::vector<float> normals;
std::vector<float> texcoords;
std::vector<unsigned int> indices;
std::vector<int> material_ids; // per-mesh material ID
} mesh_t;
typedef struct {
std::string name;
mesh_t mesh;
} shape_t;
class MaterialReader {
public:
MaterialReader() {}
virtual ~MaterialReader() {}
virtual std::string operator()(const std::string &matId,
std::vector<material_t> &materials,
std::map<std::string, int> &matMap) = 0;
};
class MaterialFileReader : public MaterialReader {
public:
MaterialFileReader(const std::string &mtl_basepath)
: m_mtlBasePath(mtl_basepath) {}
virtual ~MaterialFileReader() {}
virtual std::string operator()(const std::string &matId,
std::vector<material_t> &materials,
std::map<std::string, int> &matMap);
private:
std::string m_mtlBasePath;
};
/// Loads .obj from a file.
/// 'shapes' will be filled with parsed shape data
/// The function returns error string.
/// Returns empty string when loading .obj success.
/// 'mtl_basepath' is optional, and used for base path for .mtl file.
std::string LoadObj(std::vector<shape_t> &shapes, // [output]
std::vector<material_t> &materials, // [output]
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::vector<material_t> &materials, // [output]
std::istream &inStream, MaterialReader &readMatFn);
/// Loads materials into std::map
/// Returns an empty string if successful
std::string LoadMtl(std::map<std::string, int> &material_map,
std::vector<material_t> &materials, std::istream &inStream);
}
#endif // _TINY_OBJ_LOADER_H

View File

@@ -55,6 +55,7 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary)
, _programObject(nullptr)
, _texture(nullptr)
, _geometry(nullptr)
, _performShading("performShading", "Perform Shading", true)
{
std::string name;
std::string path;
@@ -150,6 +151,9 @@ void RenderableModel::render(const RenderData& data)
_programObject->setUniform("ModelTransform", transform);
setPscUniforms(_programObject, &data.camera, data.position);
_programObject->setUniform("_performShading", _performShading);
// Bind texture
ghoul::opengl::TextureUnit unit;
unit.activate();

View File

@@ -29,6 +29,7 @@
#include <fstream>
namespace {
const std::string _loggerCat = "WavefrontGeometry";
}
@@ -42,6 +43,7 @@ WavefrontGeometry::WavefrontGeometry(const ghoul::Dictionary& dictionary)
, _vsize(0)
, _varray(nullptr)
, _iarray(nullptr)
, _tarray(nullptr)
{
using constants::scenegraphnode::keyName;
@@ -70,161 +72,102 @@ WavefrontGeometry::WavefrontGeometry(const ghoul::Dictionary& dictionary)
void WavefrontGeometry::loadObj(const char *filename){
// temporary
int vertexSize = 0;
int vertexNormalSize = 0;
int vertexTextureSize = 0;
int indicesSize = 0;
const char *mtl_basepat = filename;
float f1, f2, f3;
int i1, i2, i3, i4, i5, i6, i7, i8, i9;
char line[150];
//float maxtex = 0.0;
std::string err = tinyobj::LoadObj(shapes, materials, filename, mtl_basepat);
_isize = shapes[0].mesh.indices.size();
_vsize = shapes[0].mesh.indices.size(); // shapes[0].mesh.positions.size() + shapes[0].mesh.positions.size() / 3;
_tsize = shapes[0].mesh.texcoords.size();
FILE *fi;
// START LINE COUNT
fi = fopen(filename, "r");
if (fi == NULL) {
LERROR("Null Object\n");
}
while (fgets(line, 150, fi) != NULL)
{
if (sscanf(line, "v %f%f%f", &f1, &f2, &f3)) {
vertexSize += 4;
}
if (sscanf(line, "vn %f%f%f", &f1, &f2, &f3)) {
vertexNormalSize += 3;
}
if (sscanf(line, "vt %f%f%f", &f1, &f2, &f3)) {
vertexTextureSize += 3;
}
if (vertexTextureSize > 0) {
if (sscanf(line, "f %i/%i/%i %i/%i/%i %i/%i/%i", &i1, &i2, &i3, &i4, &i5, &i6, &i7, &i8, &i9)) {
indicesSize += 3;
}
}
else {
if (sscanf(line, "f %i//%i %i//%i %i//%i", &i1, &i2, &i3, &i4, &i5, &i6)) {
indicesSize += 3;
}
}
}
// allocate memory for all arrays
_isize = indicesSize;
_vsize = indicesSize;
// float arrays
float* tempVertexArray = new float[vertexSize];
float* tempVertexNormalArray = new float[vertexNormalSize];
float* tempVertexTextureArray = new float[vertexTextureSize];
_varray = new Vertex[_vsize];
// int arrays
_iarray = new int[_isize];
int *tempNormalIndicesArray = new int[_isize];
int *tempTextureIndicesArray = new int[_isize];
// keeping track of the array indexes
unsigned int i = 0;
unsigned int n = 0;
unsigned int m = 0;
unsigned int w = 0;
//copy indices
for (int f = 0; f < shapes[0].mesh.indices.size(); f++) {
_iarray[f] = shapes[0].mesh.indices[f];
}
// Go back to beginning of file
fseek(fi, 0, SEEK_SET);
while (fgets(line, 150, fi) != NULL){
if (sscanf(line, "v %f%f%f", &f1, &f2, &f3)){
(tempVertexArray)[i] = f1;
i++;
(tempVertexArray)[i] = f2;
i++;
(tempVertexArray)[i] = f3;
i++;
}
if (sscanf(line, "vn %f%f%f", &f1, &f2, &f3)){
(tempVertexNormalArray)[n] = f1;
n++;
(tempVertexNormalArray)[n] = f2;
n++;
(tempVertexNormalArray)[n] = f3;
n++;
}
if (sscanf(line, "vt %f%f%f", &f1, &f2, &f3)){
(tempVertexTextureArray)[w] = f1;
w++;
(tempVertexTextureArray)[w] = f2;
w++;
(tempVertexTextureArray)[w] = f3;
w++;
}
if (vertexTextureSize > 0){
if (sscanf(line, "f %i/%i/%i %i/%i/%i %i/%i/%i", &i1, &i2, &i3, &i4, &i5, &i6, &i7, &i8, &i9)){
(_iarray)[m] = i1 - 1;
(tempTextureIndicesArray)[m] = i2 - 1;
(tempNormalIndicesArray)[m] = i3 - 1;
m++;
(_iarray)[m] = i4 - 1;
(tempTextureIndicesArray)[m] = i5 - 1;
(tempNormalIndicesArray)[m] = i6 - 1;
m++;
(_iarray)[m] = i7 - 1;
(tempTextureIndicesArray)[m] = i8 - 1;
(tempNormalIndicesArray)[m] = i9 - 1;
m++;
}
}
else{
if (sscanf(line, "f %i//%i %i//%i %i//%i", &i1, &i2, &i3, &i4, &i5, &i6)){
(_iarray)[m] = i1 - 1;
(tempNormalIndicesArray)[m] = i2 - 1;
m++;
(_iarray)[m] = i3 - 1;
(tempNormalIndicesArray)[m] = i4 - 1;
m++;
(_iarray)[m] = i5 - 1;
(tempNormalIndicesArray)[m] = i6 - 1;
m++;
//shapes[0].mesh.texcoords.resize(2 * _isize);
int p = 0;
for (auto v : shapes[0].mesh.indices) {
_varray[p].location[0] = shapes[0].mesh.positions[3 * v + 0];
_varray[p].location[1] = shapes[0].mesh.positions[3 * v + 1];
_varray[p].location[2] = shapes[0].mesh.positions[3 * v + 2];
_varray[p].location[3] = 5;
_varray[p].normal[0] = shapes[0].mesh.normals[3 * v + 0];
_varray[p].normal[1] = shapes[0].mesh.normals[3 * v + 1];
_varray[p].normal[2] = shapes[0].mesh.normals[3 * v + 2];
p++;
}
p = 0;
//TEXCOORDS NOT MAPPING PROPERLY? WHY?
/*
if (shapes[0].mesh.texcoords.size() > 0) {
for (size_t k = 0; k < shapes[0].mesh.indices.size() / 3; k++) {
for (int j = 0; j < 3; j++) {
int idx = shapes[0].mesh.indices[3 * k + j];
_varray[p].tex[0] = shapes[0].mesh.texcoords[2 * idx + 0];
_varray[p].tex[1] = shapes[0].mesh.texcoords[2 * idx + 1];
std::cout << shapes[0].mesh.texcoords[2 * idx + 0] << " "
<< shapes[0].mesh.texcoords[2 * idx + 1] << std::endl;
p++;
}
}
}
fclose(fi);
*/
// end of file read
// creating the vertex array
i = 0; n = 0; m = 0;
int normalIndex = 0;
int textureIndex = 0;
int vertexIndex = 0;
while (m < _vsize){
normalIndex = tempNormalIndicesArray[m] * 3;
textureIndex = tempTextureIndicesArray[m] * 3;
vertexIndex = _iarray[m] * 3;
_iarray[m] = m;
//testing with one triangle - works.
/*
_varray[0].location[0] = 0;
_varray[0].location[1] = 0;
_varray[0].location[2] = 0;
_varray[0].location[3] = 5;
_varray[m].location[3] = 6; // I need to set this proper at some point.
int q = 0;
while (q < 3){
_varray[m].location[q] = tempVertexArray[vertexIndex + q];
_varray[m].normal[q] = tempVertexNormalArray[normalIndex + q];
q++;
}
_varray[0].normal[0] = 1;
_varray[0].normal[1] = 1;
_varray[0].normal[2] = 1;
if (vertexTextureSize > 0){
_varray[m].tex[0] = tempVertexTextureArray[textureIndex];
_varray[m].tex[1] = tempVertexTextureArray[textureIndex + 1];
}
else{
_varray[m].tex[0] = 1.0;
_varray[m].tex[1] = 1.0;
}
m++;
}
// free up memory
delete[] tempVertexArray;
delete[] tempVertexNormalArray;
delete[] tempNormalIndicesArray;
delete[] tempVertexTextureArray;
delete[] tempTextureIndicesArray;
_varray[0].tex[0] = 0;
_varray[0].tex[1] = 0;
_varray[1].location[0] = 1;
_varray[1].location[1] = 0;
_varray[1].location[2] = 0;
_varray[1].location[3] = 5;
_varray[1].normal[0] = 1;
_varray[1].normal[1] = 1;
_varray[1].normal[2] = 1;
_varray[1].tex[0] = 1;
_varray[1].tex[1] = 0;
_varray[2].location[0] = 0;
_varray[2].location[1] = 1;
_varray[2].location[2] = 0;
_varray[2].location[3] = 5;
_varray[2].normal[0] = 1;
_varray[2].normal[1] = 1;
_varray[2].normal[2] = 1;
_varray[2].tex[0] = 0;
_varray[2].tex[1] = 1;
_vsize = 3;
_isize = 3;
_iarray[0] = 0;
_iarray[1] = 1;
_iarray[2] = 2;
*/
}
@@ -250,7 +193,7 @@ bool WavefrontGeometry::initialize(RenderableModel* parent){
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex),
reinterpret_cast<const GLvoid*>(offsetof(Vertex, location)));
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),
reinterpret_cast<const GLvoid*>(offsetof(Vertex, tex)));
reinterpret_cast<const GLvoid*>(offsetof(Vertex, tex)));
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
reinterpret_cast<const GLvoid*>(offsetof(Vertex, normal)));

View File

@@ -178,7 +178,7 @@ bool ImageSequencer::parsePlaybookFile(const std::string& fileName, std::string
if (size_t position = fileName.find_last_of(".") + 1){
if (position != std::string::npos){
std::string extension = ghoul::filesystem::File(fileName).fileExtension();
/*
if (extension == "csv"){ // comma separated playbook
std::cout << "USING COMMA SEPARATED TIMELINE V9F" << std::endl;
@@ -240,7 +240,7 @@ bool ImageSequencer::parsePlaybookFile(const std::string& fileName, std::string
}
}
*/
if (extension == "txt"){// Hong Kang. pre-parsed playbook
std::cout << "USING PREPARSED PLAYBOOK V9H" << std::endl;

View File

@@ -0,0 +1,698 @@
//
// Copyright 2012-2015, Syoyo Fujita.
//
// Licensed under 2-clause BSD liecense.
//
//
// version 0.9.8: Fix multi-materials(per-face material ID).
// version 0.9.7: Support multi-materials(per-face material ID) per
// object/group.
// version 0.9.6: Support Ni(index of refraction) mtl parameter.
// Parse transmittance material parameter correctly.
// version 0.9.5: Parse multiple group name.
// Add support of specifying the base path to load material file.
// version 0.9.4: Initial suupport of group tag(g)
// version 0.9.3: Fix parsing triple 'x/y/z'
// version 0.9.2: Add more .mtl load support
// version 0.9.1: Add initial .mtl load support
// version 0.9.0: Initial
//
#include <cstdlib>
#include <cstring>
#include <cassert>
#include <string>
#include <vector>
#include <map>
#include <fstream>
#include <sstream>
#include <openspace/util/tiny_obj_loader.h>
namespace tinyobj {
struct vertex_index {
int v_idx, vt_idx, vn_idx;
vertex_index(){};
vertex_index(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx){};
vertex_index(int vidx, int vtidx, int vnidx)
: v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx){};
};
// for std::map
static inline bool operator<(const vertex_index &a, const vertex_index &b) {
if (a.v_idx != b.v_idx)
return (a.v_idx < b.v_idx);
if (a.vn_idx != b.vn_idx)
return (a.vn_idx < b.vn_idx);
if (a.vt_idx != b.vt_idx)
return (a.vt_idx < b.vt_idx);
return false;
}
struct obj_shape {
std::vector<float> v;
std::vector<float> vn;
std::vector<float> vt;
};
static inline bool isSpace(const char c) { return (c == ' ') || (c == '\t'); }
static inline bool isNewLine(const char c) {
return (c == '\r') || (c == '\n') || (c == '\0');
}
// Make index zero-base, and also support relative index.
static inline int fixIndex(int idx, int n) {
if (idx > 0) return idx - 1;
if (idx == 0) return 0;
return n + idx; // negative value = relative
}
static inline std::string parseString(const char *&token) {
std::string s;
token += strspn(token, " \t");
int e = strcspn(token, " \t\r");
s = std::string(token, &token[e]);
token += e;
return s;
}
static inline int parseInt(const char *&token) {
token += strspn(token, " \t");
int i = atoi(token);
token += strcspn(token, " \t\r");
return i;
}
static inline float parseFloat(const char *&token) {
token += strspn(token, " \t");
float f = (float)atof(token);
token += strcspn(token, " \t\r");
return f;
}
static inline void parseFloat2(float &x, float &y, const char *&token) {
x = parseFloat(token);
y = parseFloat(token);
}
static inline void parseFloat3(float &x, float &y, float &z,
const char *&token) {
x = parseFloat(token);
y = parseFloat(token);
z = parseFloat(token);
}
// Parse triples: i, i/j/k, i//k, i/j
static vertex_index parseTriple(const char *&token, int vsize, int vnsize,
int vtsize) {
vertex_index vi(-1);
vi.v_idx = fixIndex(atoi(token), vsize);
token += strcspn(token, "/ \t\r");
if (token[0] != '/') {
return vi;
}
token++;
// i//k
if (token[0] == '/') {
token++;
vi.vn_idx = fixIndex(atoi(token), vnsize);
token += strcspn(token, "/ \t\r");
return vi;
}
// i/j/k or i/j
vi.vt_idx = fixIndex(atoi(token), vtsize);
token += strcspn(token, "/ \t\r");
if (token[0] != '/') {
return vi;
}
// i/j/k
token++; // skip '/'
vi.vn_idx = fixIndex(atoi(token), vnsize);
token += strcspn(token, "/ \t\r");
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() > (unsigned int)(3 * i.v_idx + 2));
positions.push_back(in_positions[3 * i.v_idx + 0]);
positions.push_back(in_positions[3 * i.v_idx + 1]);
positions.push_back(in_positions[3 * i.v_idx + 2]);
if (i.vn_idx >= 0) {
normals.push_back(in_normals[3 * i.vn_idx + 0]);
normals.push_back(in_normals[3 * i.vn_idx + 1]);
normals.push_back(in_normals[3 * i.vn_idx + 2]);
}
if (i.vt_idx >= 0) {
texcoords.push_back(in_texcoords[2 * i.vt_idx + 0]);
texcoords.push_back(in_texcoords[2 * i.vt_idx + 1]);
}
unsigned int idx = positions.size() / 3 - 1;
vertexCache[i] = idx;
return idx;
}
void InitMaterial(material_t &material) {
material.name = "";
material.ambient_texname = "";
material.diffuse_texname = "";
material.specular_texname = "";
material.normal_texname = "";
for (int i = 0; i < 3; i++) {
material.ambient[i] = 0.f;
material.diffuse[i] = 0.f;
material.specular[i] = 0.f;
material.transmittance[i] = 0.f;
material.emission[i] = 0.f;
}
material.illum = 0;
material.dissolve = 1.f;
material.shininess = 1.f;
material.ior = 1.f;
material.unknown_parameter.clear();
}
static bool exportFaceGroupToShape(
shape_t &shape, std::map<vertex_index, unsigned int> vertexCache,
const std::vector<float> &in_positions,
const std::vector<float> &in_normals,
const std::vector<float> &in_texcoords,
const std::vector<std::vector<vertex_index> > &faceGroup,
const int material_id, const std::string &name, bool clearCache) {
if (faceGroup.empty()) {
return false;
}
// Flatten vertices and indices
for (size_t i = 0; i < faceGroup.size(); i++) {
const std::vector<vertex_index> &face = faceGroup[i];
vertex_index i0 = face[0];
vertex_index i1(-1);
vertex_index i2 = face[1];
size_t npolys = face.size();
// Polygon -> triangle fan conversion
for (size_t k = 2; k < npolys; k++) {
i1 = i2;
i2 = face[k];
unsigned int v0 = updateVertex(
vertexCache, shape.mesh.positions, shape.mesh.normals,
shape.mesh.texcoords, in_positions, in_normals, in_texcoords, i0);
unsigned int v1 = updateVertex(
vertexCache, shape.mesh.positions, shape.mesh.normals,
shape.mesh.texcoords, in_positions, in_normals, in_texcoords, i1);
unsigned int v2 = updateVertex(
vertexCache, shape.mesh.positions, shape.mesh.normals,
shape.mesh.texcoords, in_positions, in_normals, in_texcoords, i2);
shape.mesh.indices.push_back(v0);
shape.mesh.indices.push_back(v1);
shape.mesh.indices.push_back(v2);
shape.mesh.material_ids.push_back(material_id);
}
}
shape.name = name;
if (clearCache)
vertexCache.clear();
return true;
}
std::string LoadMtl(std::map<std::string, int> &material_map,
std::vector<material_t> &materials,
std::istream &inStream) {
material_map.clear();
std::stringstream err;
material_t material;
int maxchars = 8192; // Alloc enough size.
std::vector<char> buf(maxchars); // Alloc enough size.
while (inStream.peek() != -1) {
inStream.getline(&buf[0], maxchars);
std::string linebuf(&buf[0]);
// Trim newline '\r\n' or '\n'
if (linebuf.size() > 0) {
if (linebuf[linebuf.size() - 1] == '\n')
linebuf.erase(linebuf.size() - 1);
}
if (linebuf.size() > 0) {
if (linebuf[linebuf.size() - 1] == '\r')
linebuf.erase(linebuf.size() - 1);
}
// Skip if empty line.
if (linebuf.empty()) {
continue;
}
// Skip leading space.
const char *token = linebuf.c_str();
token += strspn(token, " \t");
assert(token);
if (token[0] == '\0')
continue; // empty line
if (token[0] == '#')
continue; // comment line
// new mtl
if ((0 == strncmp(token, "newmtl", 6)) && isSpace((token[6]))) {
// flush previous material.
if (!material.name.empty()) {
material_map.insert(
std::pair<std::string, int>(material.name, materials.size()));
materials.push_back(material);
}
// initial temporary material
InitMaterial(material);
// set new mtl name
char namebuf[4096];
token += 7;
sscanf(token, "%s", namebuf);
material.name = namebuf;
continue;
}
// ambient
if (token[0] == 'K' && token[1] == 'a' && isSpace((token[2]))) {
token += 2;
float r, g, b;
parseFloat3(r, g, b, token);
material.ambient[0] = r;
material.ambient[1] = g;
material.ambient[2] = b;
continue;
}
// diffuse
if (token[0] == 'K' && token[1] == 'd' && isSpace((token[2]))) {
token += 2;
float r, g, b;
parseFloat3(r, g, b, token);
material.diffuse[0] = r;
material.diffuse[1] = g;
material.diffuse[2] = b;
continue;
}
// specular
if (token[0] == 'K' && token[1] == 's' && isSpace((token[2]))) {
token += 2;
float r, g, b;
parseFloat3(r, g, b, token);
material.specular[0] = r;
material.specular[1] = g;
material.specular[2] = b;
continue;
}
// transmittance
if (token[0] == 'K' && token[1] == 't' && isSpace((token[2]))) {
token += 2;
float r, g, b;
parseFloat3(r, g, b, token);
material.transmittance[0] = r;
material.transmittance[1] = g;
material.transmittance[2] = b;
continue;
}
// ior(index of refraction)
if (token[0] == 'N' && token[1] == 'i' && isSpace((token[2]))) {
token += 2;
material.ior = parseFloat(token);
continue;
}
// emission
if (token[0] == 'K' && token[1] == 'e' && isSpace(token[2])) {
token += 2;
float r, g, b;
parseFloat3(r, g, b, token);
material.emission[0] = r;
material.emission[1] = g;
material.emission[2] = b;
continue;
}
// shininess
if (token[0] == 'N' && token[1] == 's' && isSpace(token[2])) {
token += 2;
material.shininess = parseFloat(token);
continue;
}
// illum model
if (0 == strncmp(token, "illum", 5) && isSpace(token[5])) {
token += 6;
material.illum = parseInt(token);
continue;
}
// dissolve
if ((token[0] == 'd' && isSpace(token[1]))) {
token += 1;
material.dissolve = parseFloat(token);
continue;
}
if (token[0] == 'T' && token[1] == 'r' && isSpace(token[2])) {
token += 2;
material.dissolve = parseFloat(token);
continue;
}
// ambient texture
if ((0 == strncmp(token, "map_Ka", 6)) && isSpace(token[6])) {
token += 7;
material.ambient_texname = token;
continue;
}
// diffuse texture
if ((0 == strncmp(token, "map_Kd", 6)) && isSpace(token[6])) {
token += 7;
material.diffuse_texname = token;
continue;
}
// specular texture
if ((0 == strncmp(token, "map_Ks", 6)) && isSpace(token[6])) {
token += 7;
material.specular_texname = token;
continue;
}
// normal texture
if ((0 == strncmp(token, "map_Ns", 6)) && isSpace(token[6])) {
token += 7;
material.normal_texname = token;
continue;
}
// unknown parameter
const char *_space = strchr(token, ' ');
if (!_space) {
_space = strchr(token, '\t');
}
if (_space) {
int len = _space - token;
std::string key(token, len);
std::string value = _space + 1;
material.unknown_parameter.insert(
std::pair<std::string, std::string>(key, value));
}
}
// flush last material.
material_map.insert(
std::pair<std::string, int>(material.name, materials.size()));
materials.push_back(material);
return err.str();
}
std::string MaterialFileReader::operator()(const std::string &matId,
std::vector<material_t> &materials,
std::map<std::string, int> &matMap) {
std::string filepath;
if (!m_mtlBasePath.empty()) {
filepath = std::string(m_mtlBasePath) + matId;
} else {
filepath = matId;
}
std::ifstream matIStream(filepath.c_str());
return LoadMtl(matMap, materials, matIStream);
}
std::string LoadObj(std::vector<shape_t> &shapes,
std::vector<material_t> &materials, // [output]
const char *filename, const char *mtl_basepath) {
shapes.clear();
std::stringstream err;
std::ifstream ifs(filename);
if (!ifs) {
err << "Cannot open file [" << filename << "]" << std::endl;
return err.str();
}
std::string basePath;
if (mtl_basepath) {
basePath = mtl_basepath;
}
MaterialFileReader matFileReader(basePath);
return LoadObj(shapes, materials, ifs, matFileReader);
}
std::string LoadObj(std::vector<shape_t> &shapes,
std::vector<material_t> &materials, // [output]
std::istream &inStream, MaterialReader &readMatFn) {
std::stringstream err;
std::vector<float> v;
std::vector<float> vn;
std::vector<float> vt;
std::vector<std::vector<vertex_index> > faceGroup;
std::string name;
// material
std::map<std::string, int> material_map;
std::map<vertex_index, unsigned int> vertexCache;
int material = -1;
shape_t shape;
int maxchars = 8192; // Alloc enough size.
std::vector<char> buf(maxchars); // Alloc enough size.
while (inStream.peek() != -1) {
inStream.getline(&buf[0], maxchars);
std::string linebuf(&buf[0]);
// Trim newline '\r\n' or '\n'
if (linebuf.size() > 0) {
if (linebuf[linebuf.size() - 1] == '\n')
linebuf.erase(linebuf.size() - 1);
}
if (linebuf.size() > 0) {
if (linebuf[linebuf.size() - 1] == '\r')
linebuf.erase(linebuf.size() - 1);
}
// Skip if empty line.
if (linebuf.empty()) {
continue;
}
// Skip leading space.
const char *token = linebuf.c_str();
token += strspn(token, " \t");
assert(token);
if (token[0] == '\0')
continue; // empty line
if (token[0] == '#')
continue; // comment line
// vertex
if (token[0] == 'v' && isSpace((token[1]))) {
token += 2;
float x, y, z;
parseFloat3(x, y, z, token);
v.push_back(x);
v.push_back(y);
v.push_back(z);
continue;
}
// normal
if (token[0] == 'v' && token[1] == 'n' && isSpace((token[2]))) {
token += 3;
float x, y, z;
parseFloat3(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' && isSpace((token[2]))) {
token += 3;
float x, y;
parseFloat2(x, y, token);
vt.push_back(x);
vt.push_back(y);
continue;
}
// face
if (token[0] == 'f' && isSpace((token[1]))) {
token += 2;
token += strspn(token, " \t");
std::vector<vertex_index> face;
while (!isNewLine(token[0])) {
vertex_index vi =
parseTriple(token, v.size() / 3, vn.size() / 3, vt.size() / 2);
face.push_back(vi);
int n = strspn(token, " \t\r");
token += n;
}
faceGroup.push_back(face);
continue;
}
// use mtl
if ((0 == strncmp(token, "usemtl", 6)) && isSpace((token[6]))) {
char namebuf[4096];
token += 7;
sscanf(token, "%s", namebuf);
// Create face group per material.
bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt,
faceGroup, material, name, true);
if (ret) {
faceGroup.clear();
}
if (material_map.find(namebuf) != material_map.end()) {
material = material_map[namebuf];
} else {
// { error!! material not found }
material = -1;
}
continue;
}
// load mtl
if ((0 == strncmp(token, "mtllib", 6)) && isSpace((token[6]))) {
char namebuf[4096];
token += 7;
sscanf(token, "%s", namebuf);
std::string err_mtl = readMatFn(namebuf, materials, material_map);
if (!err_mtl.empty()) {
faceGroup.clear(); // for safety
return err_mtl;
}
continue;
}
// group name
if (token[0] == 'g' && isSpace((token[1]))) {
// flush previous face group.
bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt,
faceGroup, material, name, true);
if (ret) {
shapes.push_back(shape);
}
shape = shape_t();
// material = -1;
faceGroup.clear();
std::vector<std::string> names;
while (!isNewLine(token[0])) {
std::string str = parseString(token);
names.push_back(str);
token += strspn(token, " \t\r"); // skip tag
}
assert(names.size() > 0);
// names[0] must be 'g', so skipt 0th element.
if (names.size() > 1) {
name = names[1];
} else {
name = "";
}
continue;
}
// object name
if (token[0] == 'o' && isSpace((token[1]))) {
// flush previous face group.
bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt,
faceGroup, material, name, true);
if (ret) {
shapes.push_back(shape);
}
// material = -1;
faceGroup.clear();
shape = shape_t();
// @todo { multiple object name? }
char namebuf[4096];
token += 2;
sscanf(token, "%s", namebuf);
name = std::string(namebuf);
continue;
}
// Ignore unknown command.
}
bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup,
material, name, true);
if (ret) {
shapes.push_back(shape);
}
faceGroup.clear(); // for safety
return err.str();
}
}