diff --git a/WFObj/Makefile b/WFObj/Makefile new file mode 100644 index 0000000..4813fa3 --- /dev/null +++ b/WFObj/Makefile @@ -0,0 +1,10 @@ + +TARGET := WFObj.o + +all: $(TARGET) + +%.o: %.cc + $(CXX) -c -o $@ $(CPPFLAGS) $(CXXFLAGS) $< + +clean: + -rm -f *~ *.o diff --git a/WFObj/Makefile.driver b/WFObj/Makefile.driver new file mode 100644 index 0000000..b6432bd --- /dev/null +++ b/WFObj/Makefile.driver @@ -0,0 +1,18 @@ + +CXX := g++ +CXXFLAGS := -O2 +SOURCE := WFObj.cc driver.cc +OBJS := $(SOURCE:.cc=.o) +LDFLAGS := -lGL +TARGET := driver + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CXX) -o $@ $^ $(LDFLAGS) + +%.o: %.cc %.hh + $(CXX) -c -o $@ $< $(CXXFLAGS) + +clean: + -rm -f *~ *.o $(TARGET) diff --git a/WFObj/WFObj.cc b/WFObj/WFObj.cc new file mode 100644 index 0000000..68b0ab6 --- /dev/null +++ b/WFObj/WFObj.cc @@ -0,0 +1,703 @@ + +#include +#include +#include +#include +#include // isspace() +#include // strlen() +#include +#include +#include +#include +#include +#include +#include +#include "WFObj.h" +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 ""; +} + +//#define DEBUG_GL_ERROR +#ifdef DEBUG_GL_ERROR +#define checkGLError() checkGLErrorLine(__FUNCTION__, __LINE__) +static void checkGLErrorLine(const char * function, int line) +{ + GLenum err = glGetError(); + if (err != 0) + { + cerr << "gl error in " << function + << ": " << err << " (0x" << hex << err << ") at line " + << dec << line << endl; + } +} +#else +#define checkGLError() +#endif + + +/****** WFObj functions ******/ + +WFObj::WFObj() +{ + init(); +} + +WFObj::WFObj(FileLoader & fileLoader) +{ + init(&fileLoader); +} + +WFObj::WFObj(TextureLoader & textureLoader) +{ + init(NULL, &textureLoader); +} + +WFObj::WFObj(FileLoader & fileLoader, TextureLoader & textureLoader) +{ + init(&fileLoader, &textureLoader); +} + +void WFObj::init (FileLoader * fileLoader, + TextureLoader * textureLoader) +{ + m_fileLoader = fileLoader; + if (m_fileLoader == NULL) + { + m_fileLoader = new WFFileLoader(); + m_iCreatedFileLoader = true; + } + else + { + m_iCreatedFileLoader = false; + } + m_textureLoader = textureLoader; +} + +WFObj::~WFObj() +{ + if (m_iCreatedFileLoader) + delete m_fileLoader; +} + +void WFObj::clear() +{ + m_data = std::vector< std::vector >(); + m_loadedVertex = false; +} + +bool WFObj::load(const FileLoader::Path & path) +{ + clear(); + + FileLoader::Buffer buff = m_fileLoader->load(path); + if (buff.size <= 0) + return false; + + m_path = path; + string str(buff.data, buff.size); + stringstream istr(str, ios_base::in); + load(istr, buff.size); + + return true; +} + +bool WFObj::load(std::istream & istr, unsigned int size) +{ + char buf[size+1]; + + string buildup; + while (istr.good()) + { + istr.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); + } + 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(bool doTextureInfo, bool enableBlending) +{ + checkGLError(); + 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(this); + for (int i = 0; i < len; i++) + { + string type = m_data[i][0]; + if (type == "v") + { + Vertex v = readVertex(m_data[i]); + updateAABB(v.getData()); + vertices[VERTEX].push_back(v); + } + 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] > (int) 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, doTextureInfo, + enableBlending); + inMaterial = false; + } + if (m_data[i].size() >= 2) + { + currentMaterialName = m_data[i][1]; + material.renderBegin(currentMaterialName, doTextureInfo, + enableBlending); + inMaterial = true; + } + } + else if (type == "mtllib") + { + if (m_data[i].size() >= 2) + { + FileLoader::Path path( + basePath(m_path.fullPath) + m_data[i][1], + m_data[i][1]); + material.load(path); + } + } + } + if (inFace) + { +#ifdef DEBUGGL + cout << "glEnd()" << endl; +#endif + glEnd(); + inFace = false; + } + if (inMaterial) + { + material.renderEnd(currentMaterialName, doTextureInfo, enableBlending); + inMaterial = false; + } + glEndList(); + checkGLError(); + return list; +} + +void WFObj::updateAABB(const float * const vertex) +{ + if (m_loadedVertex) + { + if (vertex[0] < m_aabb[0]) + m_aabb[0] = vertex[0]; + else if (vertex[0] > m_aabb[3]) + m_aabb[3] = vertex[0]; + if (vertex[1] < m_aabb[1]) + m_aabb[1] = vertex[1]; + else if (vertex[1] > m_aabb[4]) + m_aabb[4] = vertex[1]; + if (vertex[2] < m_aabb[2]) + m_aabb[2] = vertex[2]; + else if (vertex[2] > m_aabb[5]) + m_aabb[5] = vertex[2]; + } + else + { + m_aabb[0] = m_aabb[3] = vertex[0]; + m_aabb[1] = m_aabb[4] = vertex[1]; + m_aabb[2] = m_aabb[5] = vertex[2]; + m_loadedVertex = true; + } +} + +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 WFObj::WFMtl::clear() +{ + m_data = map< string, vector< vector > >(); + m_currentMaterialName = ""; + m_attributesPushed = false; +} + +bool WFObj::WFMtl::load(const FileLoader::Path & path) +{ + clear(); + + FileLoader::Buffer buff = m_obj->m_fileLoader->load(path); + if (buff.size <= 0) + return false; + + m_path = path; + string str(buff.data, buff.size); + stringstream istr(str, ios_base::in); + load(istr, buff.size); + + return true; +} + +bool WFObj::WFMtl::load(std::istream & istr, unsigned int size) +{ + char buf[size+1]; + + string buildup; + while (istr.good()) + { + istr.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); + } + return true; +} + +void WFObj::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 WFObj::WFMtl::renderBegin(const string & mtlname, bool doTextureInfo, + bool enableBlending) +{ + 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; + checkGLError(); + 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 == "Ns") /* set shininess */ + { + if (stmts[i].size() == 2) + { + pushAttributes(); + GLfloat shininess = 0.0f; + sscanf(stmts[i][1].c_str(), "%f", &shininess); +#ifdef DEBUGGL + cout << " glMaterialf(GL_FRONT, GL_SHININESS, " + << shininess << ")" << endl; +#endif + glMaterialf(GL_FRONT, GL_SHININESS, shininess); + didSomething = true; + } + } + else if (type == "map_Kd") /* load a diffuse texture */ + { + if (doTextureInfo) + { + if (stmts[i].size() == 2) + { + if (m_obj->m_textureLoader != NULL) + { + FileLoader::Path path( + basePath(m_path.fullPath + stmts[i][1]), + stmts[i][1]); + GLuint tex = m_obj->m_textureLoader->load(path, + *m_obj->m_fileLoader); + if (tex > 0) + { + pushAttributes(); /* jh 2009-11-16 */ +#ifdef DEBUGGL + cout << " glBindTexture(GL_TEXTURE_2D, " << tex << ")" << endl; +#endif + glBindTexture(GL_TEXTURE_2D, tex); + foundTexture = true; + didSomething = true; + } + } + } + } + } + } + + checkGLError(); + if (didSomething) + { + pushAttributes(); + if (doTextureInfo) + { + if (enableBlending) + { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + } + 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); + } + } + } + checkGLError(); +} + +void WFObj::WFMtl::pushAttributes() +{ + if (m_attributesPushed) + return; + m_attributesPushed = true; +#ifdef DEBUGGL + cout << " glPushAttrib(GL_LIGHTING_BIT | GL_TEXTURE_BIT | GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT)" + << endl; +#endif + checkGLError(); + glPushAttrib(GL_LIGHTING_BIT | GL_TEXTURE_BIT | GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT); + checkGLError(); +} + +void WFObj::WFMtl::renderEnd(const string & mtlname, bool doTextureInfo, + bool enableBlending) +{ + 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 + checkGLError(); + glPopAttrib(); + checkGLError(); + m_attributesPushed = false; + } +} + + +/****** WFFileLoader functions ******/ + +int WFObj::WFFileLoader::getSize(const Path & path) +{ + struct stat st; + + if (path.fullPath == "") + return -1; + if (stat(path.fullPath.c_str(), &st)) + return -2; + return st.st_size; +} + +FileLoader::Buffer WFObj::WFFileLoader::load(const Path & path) +{ + int size = getSize(path); + if (size > 0) + { + int fd = open(path.fullPath.c_str(), O_RDONLY); + if (fd > 0) + { + Buffer buf(size); + int num_read = read(fd, buf.data, size); + close(fd); + if (num_read > 0) + return buf; + } + } + return Buffer(0); +} diff --git a/WFObj/WFObj.h b/WFObj/WFObj.h new file mode 100644 index 0000000..d8815ca --- /dev/null +++ b/WFObj/WFObj.h @@ -0,0 +1,92 @@ + +#ifndef WFOBJ_H +#define WFOBJ_H + +#include "FileLoader/FileLoader.h" +#include "TextureLoader/TextureLoader.h" +#include +#include +#include +#include + +class WFObj +{ +public: + WFObj(); + WFObj(FileLoader & fileLoader); + WFObj(TextureLoader & textureLoader); + WFObj(FileLoader & fileLoader, TextureLoader & textureLoader); + ~WFObj(); + + class WFMtl + { + public: + WFMtl(WFObj * obj) { m_obj = obj; } + bool load(const FileLoader::Path & path); + void renderBegin(const std::string & mtlname, + bool doTextureInfo = true, + bool enableBlending = false); + void renderEnd(const std::string & mtlname, + bool doTextureInfo = true, + bool enableBlending = false); + + protected: + /* methods */ + void clear(); + void processInputLine(const std::string & input); + void pushAttributes(); + bool load(std::istream & istr, unsigned int size); + + /* variables */ + std::map< std::string, std::vector< std::vector > > m_data; + std::string m_currentMaterialName; + bool m_attributesPushed; + FileLoader::Path m_path; + WFObj * m_obj; + }; + + class WFFileLoader : public FileLoader + { + public: + virtual int getSize(const Path & path); + virtual Buffer load(const Path & path); + }; + + bool load(const FileLoader::Path & path); + GLuint render(bool doTextureInfo = true, + bool enableBlending = false); + const float * const getAABB() { return m_aabb; } + +protected: + /* types */ + class Vertex + { + public: + float operator[](int idx) const { return data[idx]; } + float & operator[](int idx) { return data[idx]; } + float * getData() { return data; } + private: + float data[4]; + }; + + /* methods */ + void init(FileLoader * fileLoader = NULL, + TextureLoader * textureLoader = NULL); + void clear(); + void processInputLine(const std::string & input); + Vertex readVertex(const std::vector & parts); + void parseVertexIndices(const std::string & vtxref, int * ret); + void updateAABB(const float * const vertex); + bool load(std::istream & istr, unsigned int size); + + /* variables */ + std::vector< std::vector > m_data; + FileLoader::Path m_path; + float m_aabb[6]; + bool m_loadedVertex; + FileLoader * m_fileLoader; + TextureLoader * m_textureLoader; + bool m_iCreatedFileLoader; +}; + +#endif diff --git a/WFObj/driver.cc b/WFObj/driver.cc new file mode 100644 index 0000000..eeb8d54 --- /dev/null +++ b/WFObj/driver.cc @@ -0,0 +1,28 @@ + +#include +#include +#include "WFObj.hh" +using namespace std; + +GLuint loadTexture(const char * texture) +{ + cout << "got texture request: '" << texture << "'" << endl; + return 33; +} + +int main(int argc, char * argv[]) +{ + WFObj w; + if (argc < 2) + { + cout << "Usage: " << argv[0] << " " << endl; + return -2; + } + if (!w.load(argv[1], &loadTexture)) + { + cout << "Couldn't open '" << argv[1] << "'!" << endl; + return -1; + } + w.render(); + return 0; +}