blob: e8356b0aa19b09f27fbf19131edba98250922bb3 [file] [log] [blame]
#ifndef RTVIEW_OBJ_PARSER_HXX
#define RTVIEW_OBJ_PARSER_HXX
#include "RTTL/common/RTInclude.hxx"
#include "RTTL/common/RTVec.hxx"
#include "RTTL/common/RTShader.hxx"
#include "ImagePPM.hxx"
#include <map>
#include <set>
#include <algorithm>
#include <vector>
#include <fstream>
#include <string>
using namespace std;
#define NOT_SUPPORTED(x) cout << x << " currently not supported" << endl
class ObjParser {
private:
RTBox3f sceneAABB;
std::vector<RTVec3f> vertex;
std::vector<RTVec3f> normal;
std::vector<RTVec2f> textureCoord;
/* temporary vertex data arrays, required for vertex mapping */
std::vector<RTVec3f> tmpVtx;
std::vector<RTVec3f> tmpNor;
std::vector<RTVec2f> tmpTxt;
map<pair<int, pair<int,int> >,int> vertexMap;
/* ---------------------------- */
std::vector<RTVec3i> triangle;
std::vector<int> triangleShaderId;
std::vector<RTVec4i> quad;
std::vector<int> quadShaderId;
std::vector<RTMaterial> material;
map<string,int> material_map;
std::vector<ImagePPM*> texture;
void ParseMTL(const string base, const string fname)
{
DBG_PRINT(base);
DBG_PRINT(fname);
string name;
const string filename = base+"/"+fname;
fstream file;
file.open(filename.c_str());
cout << "parsing material file " << filename << endl;
if(!file.is_open())
{
FATAL("Error: cannot open %s for reading!\n" << fname.c_str());
}
while(!file.eof())
{
string s;
file >> s;
if (s[0] == '#'){
getline(file, s, '\n'); // skip an entire line ...
continue;
}
else if (s == "newmtl")
{
file >> name;
material_map[name] = material.size();
//cout << "New material with shader ID " << material.size() << endl;
material.push_back(RTMaterial());
}
else if (s == "Ns")
{
RTMaterial &m = material[material_map[name]];
file >> m.m_shininess;
}
else if (s == "Tr")
{
RTMaterial &m = material[material_map[name]];
file >> m.m_transparency;
}
else if (s == "Ka")
{
RTMaterial &m = material[material_map[name]];
file >> m.m_ambient.x;
file >> m.m_ambient.y;
file >> m.m_ambient.z;
}
else if (s == "Kd")
{
RTMaterial &m = material[material_map[name]];
file >> m.m_diffuse.x;
file >> m.m_diffuse.y;
file >> m.m_diffuse.z;
}
else if (s == "Ks")
{
RTMaterial &m = material[material_map[name]];
file >> m.m_specular.x;
file >> m.m_specular.y;
file >> m.m_specular.z;
}
else if (s == "map_Ka")
{
string textureName;
file >> textureName;
texture.push_back(new ImagePPM((base+"/"+textureName).c_str()));
}
else if (s == "map_Kd")
{
string textureName;
file >> textureName;
texture.push_back(new ImagePPM((base+"/"+textureName).c_str()));
RTMaterial &m = material[material_map[name]];
m.m_textureId = 0;
}
else if (s == "map_Ks")
{
string textureName;
file >> textureName;
texture.push_back(new ImagePPM((base+"/"+textureName).c_str()));
}
else {
getline(file, s, '\n');
}
}
cout << "finished parsing material file." << endl;
}
_INLINE int getVertexID(int vtxID,int norID, int txtID)
{
pair<int, pair < int, int > > v(vtxID, pair<int, int>(txtID, norID));
if (vertexMap.find(v) == vertexMap.end())
{
vertexMap[v] = vertex.size();
vertex.push_back(tmpVtx[vtxID]);
if (norID >= 0) normal.push_back(tmpNor[norID]); else normal.push_back(RTVec3f(0.0f,0.0f,0.0f));
if (txtID >= 0) textureCoord.push_back(tmpTxt[txtID]); else textureCoord.push_back(RTVec2f(0.0f,0.0f));
}
return vertexMap[v];
}
public:
_INLINE int quads() { return quad.size(); }
_INLINE int vertices() { return vertex.size(); }
_INLINE int triangles() { return triangle.size(); }
_INLINE int normals() { return normal.size(); }
_INLINE int textureCoordinates() { return textureCoord.size(); }
_INLINE int materials() { return material.size(); }
_INLINE int textures() { return texture.size(); }
_INLINE int triangleShaderIDs() { return triangleShaderId.size(); }
_INLINE int quadShaderIDs() { return quadShaderId.size(); }
_INLINE RTBox3f &getSceneAABB() { return sceneAABB; }
_INLINE RTVec3f *getVertexPtr() { return vertex.size() ? &*vertex.begin() : NULL; }
_INLINE RTVec3i *getTrianglePtr() { return triangle.size() ? &*triangle.begin() : NULL; }
_INLINE RTVec4i *getQuadPtr() { return quad.size() ? &*quad.begin() : NULL; }
_INLINE RTVec3f *getNormalPtr() { return normal.size() ? &*normal.begin() : NULL; }
_INLINE RTVec2f *getTextureCoordinatePtr() { return textureCoord.size() ? &*textureCoord.begin() : NULL; }
_INLINE RTMaterial *getMaterialPtr() { return material.size() ? &*material.begin() : NULL; }
_INLINE int *getTriangleShaderPtr() { return triangleShaderId.size() ? &*triangleShaderId.begin() : NULL; }
_INLINE int *getQuadShaderPtr() { return quadShaderId.size() ? &*quadShaderId.begin() : NULL ; }
// might replace this with a more general texture interface (not restricted to PPM)
_INLINE ImagePPM *getTexture(const int i) { return texture[i]; }
ObjParser()
{
sceneAABB.reset();
}
_INLINE void Free()
{
tmpVtx.clear();
tmpNor.clear();
tmpTxt.clear();
vertexMap.clear();
vertex.clear();
triangle.clear();
normal.clear();
textureCoord.clear();
triangleShaderId.clear();
quadShaderId.clear();
for (unsigned int i=0;i<texture.size();i++)
delete texture[i];
texture.clear();
material_map.clear();
}
void Parse(string fileName)
{
fstream mFile;
const char* fn = fileName.c_str();
mFile.open(fn, ios_base::in);
/* extrace base directory */
int p = -1;
for (unsigned int i=0; i<fileName.size(); i++)
#if !defined(_WIN32)
if (fileName[i] == '/')
#else
if (fileName[i] == '\\')
#endif
p = i;
string base;
if (p != -1)
base = fileName.substr(0,p);
else
{
//FIXME: Implement proper base extraction
base = ".";
}
if(!mFile.is_open())
{
cout << "Error: cannot open " << fileName << " for reading!" << endl;
perror("Error code");
exit(-1);
}
int shaderId = 0;
int count = 0;
while(!mFile.eof())
{
count++;
string s;
mFile >> s;
if (s[0] == '#'){
continue;
}
else if (s == "mtllib")
{
string mtllib;
mFile >> mtllib;
ParseMTL(base,mtllib);
}
else if (s == "g")
{
string temp;
mFile >> temp;
}
else if (s == "usemtl")
{
string material;
mFile >> material;
shaderId = material_map[material];
//cout << "using material " << material << " -> active shaderId " << shaderId << endl;
}
else if (s == "v")
{
RTVec3f v;
mFile >> v[0];
mFile >> v[1];
mFile >> v[2];
sceneAABB.extend(v);
tmpVtx.push_back(v);
}
else if (s == "vt")
{
RTVec2f c;
mFile >> c[0];
mFile >> c[1];
tmpTxt.push_back(c);
}
else if (s == "vn")
{
RTVec3f n;
mFile >> n[0];
mFile >> n[1];
mFile >> n[2];
tmpNor.push_back(n);
}
else if (s == "f" || s == "fo")
{
string dash;
int ta = -1,tta=0,tna=0;
int tb = -1,ttb=0,tnb=0;
int tc = -1,ttc=0,tnc=0;
int td = -1,ttd=0,tnd=0;
mFile >> dash;
if (sscanf(dash.c_str(),"%d", &ta) != 1&&
sscanf(dash.c_str(),"%d/%d", &ta ,&tta) != 2&&
sscanf(dash.c_str(),"%d//%d", &ta ,&tna) != 2&&
sscanf(dash.c_str(),"%d/%d/%d", &ta ,&tta, &tna) != 3
)
{
printf("Parsing error v0: %i\n",count);
exit(-1);
}
mFile >> dash;
if (sscanf(dash.c_str(),"%d", &tb) != 1&&
sscanf(dash.c_str(),"%d/%d", &tb ,&ttb) != 2 &&
sscanf(dash.c_str(),"%d//%d", &tb ,&tnb) != 2 &&
sscanf(dash.c_str(),"%d/%d/%d", &tb, &ttb, &tnb) != 3
)
{
printf("Parsing error v1: %i\n",count);
exit(-1);
};
mFile >> dash;
if (sscanf(dash.c_str(),"%d", &tc) != 1 &&
sscanf(dash.c_str(),"%d/%d", &tc ,&ttc) != 2 &&
sscanf(dash.c_str(),"%d//%d", &tc ,&tnc) != 2 &&
sscanf(dash.c_str(),"%d/%d/%d", &tc, &ttc, &tnc) != 3
)
{
printf("Parsing error v2: %i\n",count);
exit(-1);
};
bool isQuad = true;
char c;
while (mFile.peek() == ' ') mFile.get(c);
if (mFile.peek() == 0x0D || mFile.peek() == 0x0A) {
while (mFile.peek() == 0x0D || mFile.peek() == 0x0A) mFile.get(c);
isQuad = false;
}
else
{
mFile >> dash;
if (sscanf(dash.c_str(),"%d", &td) != 1 &&
sscanf(dash.c_str(),"%d/%d", &td ,&ttd) != 2 &&
sscanf(dash.c_str(),"%d//%d", &td ,&tnd) != 2 &&
sscanf(dash.c_str(),"%d/%d/%d", &td, &ttd, &tnd) != 3
)
{
isQuad = false;
}
}
bool has_na = tna != 0;
bool has_nb = tnb != 0;
bool has_nc = tnc != 0;
bool has_nd = tnd != 0;
bool has_ta = tta != 0;
bool has_tb = ttb != 0;
bool has_tc = ttc != 0;
bool has_td = ttd != 0;
if (ta > 0) ta--;
if (tb > 0) tb--;
if (tc > 0) tc--;
if (td > 0) td--;
if (tta > 0) tta--;
if (ttb > 0) ttb--;
if (ttc > 0) ttc--;
if (ttd > 0) ttd--;
if (tna > 0) tna--;
if (tnb > 0) tnb--;
if (tnc > 0) tnc--;
if (tnd > 0) tnd--;
if (ta < 0) ta += tmpVtx.size();
if (tb < 0) tb += tmpVtx.size();
if (tc < 0) tc += tmpVtx.size();
if (td < 0) td += tmpVtx.size();
if (tta < 0) tta += tmpTxt.size();
if (ttb < 0) ttb += tmpTxt.size();
if (ttc < 0) ttc += tmpTxt.size();
if (ttd < 0) ttd += tmpTxt.size();
if (tna < 0) tna += tmpNor.size();
if (tnb < 0) tnb += tmpNor.size();
if (tnc < 0) tnc += tmpNor.size();
if (tnd < 0) tnc += tmpNor.size();
/*
DBG_PRINT(ta);
DBG_PRINT(tb);
DBG_PRINT(tc);
DBG_PRINT(td);
DBG_PRINT(quad);
*/
if (ta < 0 || ta >= (int)tmpVtx.size() ||
tb < 0 || tb >= (int)tmpVtx.size() ||
tc < 0 || tc >= (int)tmpVtx.size() ||
(isQuad && (td < 0 || td >= (int)tmpVtx.size())))
/*
(tta < 0 || tta >= (int)textureCoord.size()) && has_ta ||
(ttb < 0 || ttb >= (int)textureCoord.size()) && has_tb ||
(ttc < 0 || ttc >= (int)textureCoord.size()) && has_tc ||
(ttd < 0 || ttd >= (int)textureCoord.size()) && has_td ||
(tna < 0 || tna >= (int)normal.size()) && has_na ||
(tnb < 0 || tnb >= (int)normal.size()) && has_nb ||
(tnc < 0 || tnc >= (int)normal.size()) && has_nc ||
(tnd < 0 || tnd >= (int)normal.size()) && has_nd
*/
{
DBG_PRINT(td);
DBG_PRINT(tmpVtx.size());
printf("Invalid triangle %i: (%i,%i,%i) (%i,%i,%i) (%i,%i,%i) (%i,%i,%i) (%i,%i,%i) %i %i %i\n",
count,ta,tb,tc,tta,ttb,ttc,tna,tnb,tnc,has_na,has_nb,has_nc,has_ta,has_tb,has_tc,
int(tmpVtx.size()),
int(tmpTxt.size()),
int(tmpNor.size()));
exit(-1);
}
if (!has_ta) tta = -1;
if (!has_tb) ttb = -1;
if (!has_tc) ttc = -1;
if (!has_td) ttd = -1;
if (!has_na) tna = -1;
if (!has_nb) tnb = -1;
if (!has_nc) tnc = -1;
if (!has_nd) tnd = -1;
if (isQuad)
{
const int newA = getVertexID(ta,tna,tta);
const int newB = getVertexID(tb,tnb,ttb);
const int newC = getVertexID(tc,tnc,ttc);
const int newD = getVertexID(td,tnd,ttd);
quad.push_back(RTVec4i(newA,newB,newC,newD));
quadShaderId.push_back(shaderId);
}
else
{
const int newA = getVertexID(ta,tna,tta);
const int newB = getVertexID(tb,tnb,ttb);
const int newC = getVertexID(tc,tnc,ttc);
triangle.push_back(RTVec3i(newA,newB,newC));
triangleShaderId.push_back(shaderId);
}
}
else
continue;
}
assert( textureCoordinates() == vertices() );
assert( normals() == vertices() );
}
};
#endif