#ifndef OBJREADER_H
#define OBJREADER_H
#include "utils.h"
#include "vecmath.h"
#include <string>
#include <vector>
namespace bex {
class OBJReader {
public:
OBJReader()
:m_vertices(0),
m_vertexNormals(0),
m_UVs(0),
m_currentGroup(0)
{
m_dummyMaterial.diffuse = bex::ColorRGBA(0.9f,0.9f,0.9f,1.0f);
m_materialMap["dummy"] = m_dummyMaterial;
m_currentMaterial = &m_dummyMaterial;
m_currentMaterialName = "dummy";
}
struct Material {
Material() : shininess(0.0f) {}
bex::ColorRGBA diffuse;
bex::ColorRGBA emissive;
bex::ColorRGBA specular;
float shininess;
};
struct Group {
Material material;
std::string materialName;
std::vector<int> vertexIndexList;
std::vector<int> normalIndexList;
std::vector<int> uvIndexList;
};
void loadObjFile(const std::wstring file) {
size_t pos = file.find_last_of(L"/");
m_path = file.substr(0, pos+1);
loadFile(file);
}
void getMeshNames(std::vector<std::string>& meshNames) {
std::map<std::string, Group>::const_iterator iter;
iter = m_groupMap.begin();
while (iter != m_groupMap.end()) {
meshNames.push_back(iter->first);
iter++;
}
}
const std::map<std::string, Material>& getMaterials() {
return m_materialMap;
}
void getMeshData(const std::string& meshName, std::vector<bex::Vec3>& vertices, std::vector<bex::Vec3>& normals, std::vector<bex::Vec2>& UVs, std::string& materialName) {
if (m_groupMap.find(meshName) != m_groupMap.end()) {
Group& tempGroup = m_groupMap.find(meshName)->second;
for (unsigned int i=0; i<tempGroup.vertexIndexList.size(); i++) {
vertices.push_back(m_vertices[tempGroup.vertexIndexList[i]-1]);
normals.push_back(m_vertexNormals[tempGroup.normalIndexList[i]-1]);
UVs.push_back(m_UVs[tempGroup.uvIndexList[i]-1]);
}
materialName = tempGroup.materialName;
}
}
private:
std::vector<std::string> tokenizeString(const std::string& input, char breakChar) {
std::vector<std::string> result;
std::string::size_type firstPos = input.size();
for (std::string::size_type lastPos = 0; lastPos <= input.size(); ++lastPos) {
if (lastPos == input.size() || input[lastPos] == breakChar) {
if (firstPos < lastPos) {
result.push_back(input.substr(firstPos, lastPos - firstPos));
firstPos = input.size();
}
} else {
if (firstPos > lastPos) {
firstPos = lastPos;
}
}
}
return result;
}
void processTokens(const std::vector<std::string>& tokens) {
if (tokens.size() == 0) {
return;
}
else if (tokens[0].compare("v") == 0) {
m_vertices.push_back(bex::Vec3((float)atof(tokens[1].c_str()), (float)atof(tokens[2].c_str()), (float)atof(tokens[3].c_str())));
}
else if (tokens[0].compare("mtllib") == 0) {
loadFile(m_path + bex::string2tstring(tokens[1]));
}
else if (tokens[0].compare("vn") == 0) {
m_vertexNormals.push_back(bex::Vec3((float)atof(tokens[1].c_str()), (float)atof(tokens[2].c_str()), (float)atof(tokens[3].c_str())));
}
else if (tokens[0].compare("vt") == 0) {
m_UVs.push_back(bex::Vec2((float)atof(tokens[1].c_str()), (float)atof(tokens[2].c_str())));
}
else if (tokens[0].compare("f") == 0) {
if (m_currentGroup) {
for (int i=1; i<4; i++) {
std::vector<std::string> faces = tokenizeString(tokens[i], '/');
m_currentGroup->vertexIndexList.push_back(atoi(faces[0].c_str()));
m_currentGroup->uvIndexList.push_back(atoi(faces[1].c_str()));
m_currentGroup->normalIndexList.push_back(atoi(faces[2].c_str()));
}
}
}
else if (tokens[0].compare("g") == 0) {
std::string groupName = tokens[1];
for(std::vector<std::string>::const_iterator it = tokens.begin() + 2; it != tokens.end(); ++it) {
groupName += "." + *it;
}
if (m_groupMap.find(groupName) == m_groupMap.end()) {
Group g;
g.material = *m_currentMaterial;
g.materialName = m_currentMaterialName;
m_groupMap[groupName] = g;
m_currentGroup = &(m_groupMap.find(groupName)->second);
} else {
m_currentGroup = &(m_groupMap.find(groupName)->second);
}
}
else if (tokens[0].compare("usemtl") == 0) {
if (m_currentGroup) {
std::map<std::string, Material>::iterator it;
it = m_materialMap.find(tokens[1]);
if (it == m_materialMap.end()) {
m_currentGroup->material = m_dummyMaterial;
m_currentGroup->materialName = "dummy";
} else {
m_currentMaterial = &it->second;
m_currentMaterialName = it->first;
m_currentGroup->material = it->second;
m_currentGroup->materialName = it->first;
}
}
}
else if (tokens[0].compare("newmtl") == 0) {
m_materialMap[tokens[1]] = m_dummyMaterial;
m_currentMaterial = &(m_materialMap.find(tokens[1])->second);
m_currentMaterialName = tokens[1];
}
else if (tokens[0].compare("Kd") == 0) {
m_currentMaterial->diffuse = bex::ColorRGBA((float)atof(tokens[1].c_str()), (float)atof(tokens[2].c_str()), (float)atof(tokens[3].c_str()), 1.0f);
}
else if (tokens[0].compare("Ke") == 0) {
m_currentMaterial->emissive = bex::ColorRGBA((float)atof(tokens[1].c_str()), (float)atof(tokens[2].c_str()), (float)atof(tokens[3].c_str()), 1.0f);
}
else if (tokens[0].compare("Ks") == 0) {
m_currentMaterial->specular = bex::ColorRGBA((float)atof(tokens[1].c_str()), (float)atof(tokens[2].c_str()), (float)atof(tokens[3].c_str()), 1.0f);
}
else if (tokens[0].compare("Ns") == 0) {
m_currentMaterial->shininess = (float)atof(tokens[1].c_str());
}
}
void loadFile(const std::wstring file) {
std::ifstream fileStream;
fileStream.open(file.c_str());
char row[256];
while (fileStream.good()) {
fileStream.getline(row, 256);
std::string str = row;
std::vector<std::string> tokens = tokenizeString(str, ' ');
processTokens(tokens);
}
}
std::vector<bex::Vec3> m_vertices;
std::vector<bex::Vec3> m_vertexNormals;
std::vector<bex::Vec2> m_UVs;
std::map<std::string, Group> m_groupMap;
std::map<std::string, Material> m_materialMap;
std::wstring m_path;
Material m_dummyMaterial;
Group* m_currentGroup;
Material* m_currentMaterial;
std::string m_currentMaterialName;
};
}
#endif//OBJREADER_H