#include #include #include #include // isspace() #include // strlen() #include #include #include #include #include #include #include "WFObj.hh" using namespace std; #define WHITESPACE " \n\r\t\v" #define DEBUGGL /****** static functions ******/ static string trimString(string s) { size_t lastpos = s.find_last_not_of(WHITESPACE); if (lastpos == string::npos) return ""; s.erase(lastpos + 1); s.erase(0, s.find_first_not_of(WHITESPACE)); return s; } static string stripFirstToken(string & input) { size_t firstnonspace = input.find_first_not_of(WHITESPACE); if (firstnonspace == string::npos) return ""; size_t spaceafter = input.find_first_of(WHITESPACE, firstnonspace); string token = input.substr(firstnonspace, spaceafter - firstnonspace); input.erase(0, spaceafter); return token; } static vector splitString(const string & str, char delim) { vector ret; string s = str; size_t pos; while ( (pos = s.find(delim)) != string::npos ) { string t = s.substr(0, pos); ret.push_back(t); s.erase(0, pos + 1); } if (s != "") ret.push_back(s); return ret; } static string basePath(const string & str) { string path = str; size_t pos; if ( (pos = path.find_last_of("/\\")) != string::npos ) { path.erase(pos + 1); return path; } return ""; } /****** WFObj functions ******/ void WFObj::clear() { m_data = std::vector< std::vector >(); } int WFObj::filesize(const char * filename) { struct stat st; if (stat(filename, &st)) return -1; return st.st_size; } bool WFObj::load(const string & filename, loadTextureFunc_t loadTexture) { clear(); int size = filesize(filename.c_str()); if (size < 1) return false; ifstream ifs(filename.c_str()); if (!ifs.is_open()) return false; char buf[size+1]; string buildup; while (ifs.good()) { ifs.getline(buf, size+1); string input = trimString(buf); int sz = input.size(); if (sz == 0 || input[0] == '#') continue; if (input[sz-1] == '\\') { input[sz-1] = ' '; buildup = input; continue; } if (buildup != "") input = buildup + input; buildup = ""; processInputLine(input); } ifs.close(); m_loadTexture = loadTexture; m_fileName = filename; return true; } void WFObj::processInputLine(const std::string & input) { string line = input; vector lineParts; for (;;) { string token = stripFirstToken(line); if (token == "") break; lineParts.push_back(token); } if (lineParts.size() > 0) m_data.push_back(lineParts); } GLuint WFObj::render() { GLuint list = glGenLists(1); glNewList(list, GL_COMPILE); int len = m_data.size(); enum { VERTEX, VERTEX_TEXTURE, VERTEX_NORMAL, VERTEX_TYPES }; vector vertices[VERTEX_TYPES]; int numVertsLast = 0; bool inFace = false; bool inMaterial = false; string currentMaterialName; WFMtl material; for (int i = 0; i < len; i++) { string type = m_data[i][0]; if (type == "v") vertices[VERTEX].push_back(readVertex(m_data[i])); else if (type == "vt") vertices[VERTEX_TEXTURE].push_back(readVertex(m_data[i])); else if (type == "vn") vertices[VERTEX_NORMAL].push_back(readVertex(m_data[i])); else if (type == "f") { int numVerts = m_data[i].size() - 1; if (inFace && (numVerts != numVertsLast || numVertsLast > 4)) { #ifdef DEBUGGL cout << "glEnd()" << endl; #endif glEnd(); inFace = false; } if (!inFace) { if (numVerts == 3) { #ifdef DEBUGGL cout << "glBegin(GL_TRIANGLES)" << endl; #endif glBegin(GL_TRIANGLES); } else if (numVerts == 4) { #ifdef DEBUGGL cout << "glBegin(GL_QUADS)" << endl; #endif glBegin(GL_QUADS); } else { #ifdef DEBUGGL cout << "glBegin(GL_POLYGON)" << endl; #endif glBegin(GL_POLYGON); } inFace = true; } for (int v = 1; v <= numVerts; v++) { int vertexIndices[3] = {0, 0, 0}; parseVertexIndices(m_data[i][v], vertexIndices); for (int j = 0; j < 3; j++) { if (vertexIndices[j] < 0) vertexIndices[j] += vertices[j].size() + 1; } bool valid = true; for (int j = 0; j < 3; j++) { if (vertexIndices[j] > vertices[j].size()) { valid = false; break; } } if (vertexIndices[VERTEX] <= 0) valid = false; if (!valid) continue; if (vertexIndices[VERTEX_NORMAL] != 0) { /* a normal is present */ #ifdef DEBUGGL cout << " glNormal3f(" << vertices[VERTEX_NORMAL][vertexIndices[VERTEX_NORMAL]-1] [0] << ", " << vertices[VERTEX_NORMAL][vertexIndices[VERTEX_NORMAL]-1] [1] << ", " << vertices[VERTEX_NORMAL][vertexIndices[VERTEX_NORMAL]-1] [2] << ")" << endl; #endif glNormal3fv(vertices[VERTEX_NORMAL] [vertexIndices[VERTEX_NORMAL]-1].getData()); } if (vertexIndices[VERTEX_TEXTURE] != 0) { /* a texture coordinate is present */ #ifdef DEBUGGL cout << " glTexCoord2f(" << vertices[VERTEX_TEXTURE][vertexIndices[VERTEX_TEXTURE]-1] [0] << ", " << vertices[VERTEX_TEXTURE][vertexIndices[VERTEX_TEXTURE]-1] [1] << ')' << endl; #endif glTexCoord2fv(vertices[VERTEX_TEXTURE] [vertexIndices[VERTEX_TEXTURE]-1].getData()); } #ifdef DEBUGGL cout << " glVertex3f(" << vertices[VERTEX][vertexIndices[VERTEX]-1][0] << ", " << vertices[VERTEX][vertexIndices[VERTEX]-1][1] << ", " << vertices[VERTEX][vertexIndices[VERTEX]-1][2] << ")" << " [" << vertexIndices[VERTEX] << "]" << endl; #endif glVertex3fv(vertices[VERTEX][vertexIndices[VERTEX]-1].getData()); } numVertsLast = numVerts; } else if (type == "usemtl") { if (inFace) { #ifdef DEBUGGL cout << "glEnd()" << endl; #endif glEnd(); inFace = false; } if (inMaterial) { material.renderEnd(currentMaterialName); inMaterial = false; } if (m_data[i].size() >= 2) { currentMaterialName = m_data[i][1]; material.renderBegin(currentMaterialName); inMaterial = true; } } else if (type == "mtllib") { if (m_data[i].size() >= 2) { material.load(basePath(m_fileName) + m_data[i][1], m_loadTexture); } } } if (inFace) { #ifdef DEBUGGL cout << "glEnd()" << endl; #endif glEnd(); inFace = false; } if (inMaterial) { material.renderEnd(currentMaterialName); inMaterial = false; } glEndList(); return list; } WFObj::Vertex WFObj::readVertex(const vector & parts) { int partslen = parts.size(); Vertex v; for (int i = 1; i < partslen && i <= 4; i++) { sscanf(parts[i].c_str(), "%f", &v[i - 1]); } return v; } void WFObj::parseVertexIndices(const string & vtxref, int * ret) { vector parts = splitString(vtxref, '/'); int num = parts.size(); for (int i = 0; i < num && i < 3; i++) sscanf(parts[i].c_str(), "%d", ret + i); } /****** WFMtl functions ******/ void WFMtl::clear() { m_data = map< string, vector< vector > >(); m_currentMaterialName = ""; m_attributesPushed = false; } int WFMtl::filesize(const char * filename) { struct stat st; if (stat(filename, &st)) return -1; return st.st_size; } bool WFMtl::load(const string & filename, WFObj::loadTextureFunc_t loadTexture) { clear(); int size = filesize(filename.c_str()); if (size < 1) return false; ifstream ifs(filename.c_str()); if (!ifs.is_open()) return false; char buf[size+1]; string buildup; while (ifs.good()) { ifs.getline(buf, size+1); string input = trimString(buf); int sz = input.size(); if (sz == 0 || input[0] == '#') continue; if (input[sz-1] == '\\') { input[sz-1] = ' '; buildup = input; continue; } if (buildup != "") input = buildup + input; buildup = ""; processInputLine(input); } /* DEBUG */ map > >::iterator it = m_data.begin(); while (it != m_data.end()) { cout << "Material '" << it->first << "':" << endl; for (int i = 0; i < it->second.size(); i++) { cout << " "; for (int j = 0; j < it->second[i].size(); j++) { cout << '\'' << it->second[i][j] << "' "; } cout << endl; } it++; } /* END DEBUG */ ifs.close(); m_loadTexture = loadTexture; m_fileName = filename; return true; } void WFMtl::processInputLine(const std::string & input) { string line = input; vector lineParts; for (;;) { string token = stripFirstToken(line); if (token == "") break; lineParts.push_back(token); } if (lineParts.size() > 0) { if ( (lineParts.size() >= 2) && (lineParts[0] == "newmtl") ) { m_currentMaterialName = lineParts[1]; } else if (m_currentMaterialName != "") { m_data[m_currentMaterialName].push_back(lineParts); } } } void WFMtl::renderBegin(const string & mtlname) { map< string, vector< vector > >::iterator it = m_data.find(mtlname); if (it == m_data.end()) return; vector< vector > & stmts = it->second; int num_stmts = stmts.size(); bool foundTexture = false; bool didSomething = false; for (int i = 0; i < num_stmts; i++) { string & type = stmts[i][0]; if (type == "Ka") /* set ambient color */ { if ( (stmts[i].size() == 4) && (stmts[i][1] != "spectral") ) { pushAttributes(); float mat[4] = {0.0f, 0.0f, 0.0f, 1.0f}; for (int j = 0; j < 3; j++) sscanf(stmts[i][j+1].c_str(), "%f", &mat[j]); #ifdef DEBUGGL cout << " glMaterialfv(GL_FRONT, GL_AMBIENT, {"; for (int j = 0; j < 4; j++) cout << mat[j] << (j < 3 ? ", " : "})"); cout << endl; #endif glMaterialfv(GL_FRONT, GL_AMBIENT, mat); didSomething = true; } } else if (type == "Kd") /* set diffuse color */ { if ( (stmts[i].size() == 4) && (stmts[i][1] != "spectral") ) { pushAttributes(); float mat[4] = {0.0f, 0.0f, 0.0f, 1.0f}; for (int j = 0; j < 3; j++) sscanf(stmts[i][j+1].c_str(), "%f", &mat[j]); #ifdef DEBUGGL cout << " glMaterialfv(GL_FRONT, GL_DIFFUSE, {"; for (int j = 0; j < 4; j++) cout << mat[j] << (j < 3 ? ", " : "})"); cout << endl; #endif glMaterialfv(GL_FRONT, GL_DIFFUSE, mat); didSomething = true; } } else if (type == "Ks") /* set specular color */ { if ( (stmts[i].size() == 4) && (stmts[i][1] != "spectral") ) { pushAttributes(); float mat[4] = {0.0f, 0.0f, 0.0f, 1.0f}; for (int j = 0; j < 3; j++) sscanf(stmts[i][j+1].c_str(), "%f", &mat[j]); #ifdef DEBUGGL cout << " glMaterialfv(GL_FRONT, GL_SPECULAR, {"; for (int j = 0; j < 4; j++) cout << mat[j] << (j < 3 ? ", " : "})"); cout << endl; #endif glMaterialfv(GL_FRONT, GL_SPECULAR, mat); didSomething = true; } } else if (type == "map_Kd") /* load a diffuse texture */ { /* TODO: figure out how i want to load the texture */ if (stmts[i].size() == 2) { GLuint tex = m_loadTexture( (basePath(m_fileName) + stmts[i][1]).c_str() ); if (tex > 0) { #ifdef DEBUGGL cout << " glBindTexture(GL_TEXTURE_2D, " << tex << ")" << endl; #endif glBindTexture(GL_TEXTURE_2D, tex); foundTexture = true; didSomething = true; } } } } if (didSomething) { pushAttributes(); if (foundTexture) { #ifdef DEBUGGL cout << " glEnable(GL_TEXTURE_2D)" << endl; #endif glEnable(GL_TEXTURE_2D); } else { #ifdef DEBUGGL cout << " glDisable(GL_TEXTURE_2D)" << endl; #endif glDisable(GL_TEXTURE_2D); } } } void WFMtl::pushAttributes() { if (m_attributesPushed) return; m_attributesPushed = true; #ifdef DEBUGGL cout << " glPushAttrib(GL_LIGHTING_BIT | GL_TEXTURE_BIT)" << endl; #endif glPushAttrib(GL_LIGHTING_BIT | GL_TEXTURE_BIT); } void WFMtl::renderEnd(const string & mtlname) { map< string, vector< vector > >::iterator it = m_data.find(mtlname); if (it == m_data.end()) return; if (m_attributesPushed) { #ifdef DEBUGGL cout << " glPopAttrib()" << endl; #endif glPopAttrib(); } }