#include "ObjConverter.h" #include "SimpleTri.h" #include "SimpleVec.h" #include "gzstream.h" #include using namespace std; //#define USE_TEXTURE static void LoadIndices(char *str, const VertexArray &vertices, const VertexArray &normals, const TexcoordArray &texcoords, VertexArray &faceVertices, VertexArray &faceNormals, TexcoordArray &faceTexcoords ) { vector triples; char *next_token; // extract the triples of the form v/t/n v/t/n ... char *pch = strtok_s(str + 1, " ", &next_token); while (pch) { string s(pch); triples.push_back(s); pch = strtok_s(NULL, " ", &next_token); } // throw away last symbol (\n) triples.back().resize(triples.back().size() - 1); vector indices; vector nIndices; vector tIndices; char seps[] = " "; char seps2[] = "/"; for (size_t i = 0; i < triples.size(); ++ i) { //cout << "triple " << i << " " << triples[i] << endl; size_t found; found = triples[i].find_first_of(seps2); size_t prevfound = 0; // extract vertex, normal, texture indices string str = triples[i].substr(prevfound, found); int index = (int)strtol(str.c_str(), NULL, 10) - 1; int tIndex = index; int nIndex = index; // try to extract texture and normal indices prevfound = found + 1; found = triples[i].find_first_of(seps2, prevfound); if (found != string::npos) { str = triples[i].substr(prevfound, found); int idx = (int)strtol(str.c_str(), NULL, 10) - 1; if (idx > 0) tIndex = idx; } if (found != string::npos) { str = triples[i].substr(found + 1); int idx = (int)strtol(str.c_str(), NULL, 10) - 1; if (idx > 0) nIndex = idx; } // store indices if (index >= 0) { indices.push_back(index); nIndices.push_back(nIndex); tIndices.push_back(tIndex); //cout << index << " " << tIndex << " " << nIndex << endl; } // new triangle found if (indices.size() > 2) { // change orientation of faces? #if 1 int idx1 = 0; int idx2 = (int)indices.size() - 2; int idx3 = (int)indices.size() - 1; #else int idx3 = 0; int idx2 = (int)indices.size() - 2; int idx1 = (int)indices.size() - 1; #endif faceVertices.push_back(vertices[indices[idx1]]); faceVertices.push_back(vertices[indices[idx2]]); faceVertices.push_back(vertices[indices[idx3]]); if (!normals.empty()) { faceNormals.push_back(normals[nIndices[idx1]]); faceNormals.push_back(normals[nIndices[idx2]]); faceNormals.push_back(normals[nIndices[idx3]]); } else { // no face normals? => create normals const SimpleTri tri(vertices[indices[idx1]], vertices[indices[idx2]], vertices[indices[idx3]]); const SimpleVec n = tri.GetNormal(); faceNormals.push_back(n); faceNormals.push_back(n); faceNormals.push_back(n); } if (!texcoords.empty()) { faceTexcoords.push_back(texcoords[tIndices[idx1]]); faceTexcoords.push_back(texcoords[tIndices[idx2]]); faceTexcoords.push_back(texcoords[tIndices[idx3]]); } } } } ObjConverter::ObjConverter() {} ObjConverter::~ObjConverter() { for (size_t i = 0; i < mGeometry.size(); ++ i) delete mGeometry[i]; mGeometry.clear(); } void ObjConverter::LoadShape(const VertexArray &faceVertices, const VertexArray &faceNormals, const vector &faceTexcoords, Material *mat) { int numElements = (int)faceVertices.size(); Geometry *geom = new Geometry(); // convert the triangles to geometry geom->mVertices = new SimpleVec[numElements]; geom->mNormals = new SimpleVec[numElements]; geom->mTexcoords = new Texcoord[numElements]; geom->mVertexCount = numElements; geom->mTexcoordCount = (int)faceTexcoords.size(); geom->mMaterial = mat; cout << "creating new geometry with " << numElements << " vertices" << endl; for (int i = 0; i < numElements; ++ i) { #if 0 // convert to our camera system: change y and z geom->mVertices[i].x = faceVertices[i].x; geom->mVertices[i].y = -faceVertices[i].z; geom->mVertices[i].z = faceVertices[i].y; geom->mNormals[i].x = faceNormals[i].x; geom->mNormals[i].y = -faceNormals[i].z; geom->mNormals[i].z = faceNormals[i].y; #else geom->mVertices[i].x = faceVertices[i].x; geom->mVertices[i].y = faceVertices[i].y; geom->mVertices[i].z = faceVertices[i].z; geom->mNormals[i].x = faceNormals[i].x; geom->mNormals[i].y = faceNormals[i].y; geom->mNormals[i].z = faceNormals[i].z; #endif if (i < geom->mTexcoordCount) { geom->mTexcoords[i].first = faceTexcoords[i].first; geom->mTexcoords[i].second = faceTexcoords[i].second; } } mGeometry.push_back(geom); } bool ObjConverter::Convert(const string &filename, const std::string &outputFilename) { mNumShapes = 0; for (size_t i = 0; i < mGeometry.size(); ++ i) delete mGeometry[i]; mGeometry.clear(); if (!ReadFile(filename)) { cerr << "could not read file" << endl; return false; } if (!WriteFile(outputFilename)) { cerr << "could not write file" << endl; return false; } return true; } bool ObjConverter::ReadFile(const string &filename) { FILE *file; if ((file = fopen(filename.c_str(), "r")) == NULL) { return false; } Material *currentMat = NULL; VertexArray faceVertices; VertexArray faceNormals; vector faceTexcoords; VertexArray vertices; VertexArray normals; vector texcoords; vector indices; int line = 0; const int len = 10000; char str[len]; while (fgets(str, len, file) != NULL) { switch (str[0]) { case 'v': // vertex or normal { float x, y, z; switch (str[1]) { case 'n' : sscanf(str + 2, "%f %f %f", &x, &y, &z); normals.push_back(SimpleVec(x, y, z)); break; case 't': sscanf(str + 2, "%f %f", &x, &y); texcoords.push_back(pair(x, y)); break; default: sscanf(str + 1, "%f %f %f", &x, &y, &z); //const float scale = 5e-3f; const float scale = 5.0f; vertices.push_back(SimpleVec(x * scale, y * scale, z* scale)); //cout <<"v " << x << " " << y << " "<< z << " "; } break; } case 'f': { ////////// //-- indices in the current line LoadIndices(str, vertices, normals, texcoords, faceVertices, faceNormals, faceTexcoords); break; } // end face case 'g': // load a new shape { if (!faceVertices.empty()) { ++ mNumShapes; LoadShape(faceVertices, faceNormals, faceTexcoords, currentMat); faceVertices.clear(); faceNormals.clear(); faceTexcoords.clear(); currentMat = NULL; } } break; case 'u': // usemtl => material { string matName(str + 7); // throw away linebreak character matName.resize(matName.size() - 1); currentMat = mMaterialTable[matName]; //cout << "matname: " << matName << endl; } break; default: // throw away line break; } ++ line; } if (!faceVertices.empty()) { ++ mNumShapes; LoadShape(faceVertices, faceNormals, faceTexcoords, currentMat); faceVertices.clear(); faceNormals.clear(); faceTexcoords.clear(); } fclose(file); return true; } void ObjConverter::WriteGeometry(ogzstream &str, Geometry *geom) { int vertexCount = geom->mVertexCount; str.write(reinterpret_cast(&vertexCount), sizeof(int)); str.write(reinterpret_cast(geom->mVertices), sizeof(SimpleVec) * vertexCount); str.write(reinterpret_cast(geom->mNormals), sizeof(SimpleVec) * vertexCount); int texCoordCount = geom->mTexcoordCount; str.write(reinterpret_cast(&texCoordCount), sizeof(int)); if (texCoordCount) str.write(reinterpret_cast(geom->mTexcoords), sizeof(float) * texCoordCount * 2); /////// //-- texture /* #ifdef USE_TEXTURE int texId = 0; #else int texId = -1; #endif str.write(reinterpret_cast(&texId), sizeof(int)); bool alphaTestEnabled = false; //bool cullFaceEnabled = false; bool cullFaceEnabled = true; str.write(reinterpret_cast(&alphaTestEnabled), sizeof(bool)); str.write(reinterpret_cast(&cullFaceEnabled), sizeof(bool)); // material bool hasMaterial = true; //bool hasMaterial = false; str.write(reinterpret_cast(&hasMaterial), sizeof(bool)); if (hasMaterial) { SimpleVec ambient, diffuse, spec, emm; ambient.x = ambient.y = ambient.z = 0.2f; //diffuse.x = diffuse.y = diffuse.z = 1.0f; diffuse.x = 0.7f; diffuse.y = 0.5f; diffuse.z = 0.2f; spec.x = spec.y = spec.z = .0f; emm = spec; // only write rgb part of the material str.write(reinterpret_cast(&ambient), sizeof(SimpleVec)); str.write(reinterpret_cast(&diffuse), sizeof(SimpleVec)); str.write(reinterpret_cast(&spec), sizeof(SimpleVec)); str.write(reinterpret_cast(&emm), sizeof(SimpleVec)); } */ } #if 1 bool ObjConverter::WriteFile(const string &filename) { ogzstream ofile(filename.c_str()); if (!ofile.is_open()) return false; ///////// //-- write textures #ifdef USE_TEXTURE int textureCount = 1; #else int textureCount = 0; #endif ofile.write(reinterpret_cast(&textureCount), sizeof(int)); if (textureCount > 0) { // hack const string texName("wood.jpg"); int texnameSize = (int)texName.length() + 1; ofile.write(reinterpret_cast(&texnameSize), sizeof(int)); ofile.write(texName.c_str(), sizeof(char) * texnameSize); int boundS = 1, boundT = 1; ofile.write(reinterpret_cast(&boundS), sizeof(int)); ofile.write(reinterpret_cast(&boundT), sizeof(int)); } /////////// //-- write shapes ofile.write(reinterpret_cast(&mNumShapes), sizeof(int)); vector::const_iterator it, it_end = mGeometry.end(); for (it = mGeometry.begin(); it != it_end; ++ it) { WriteGeometry(ofile, *it); } int texId = 0; ofile.write(reinterpret_cast(&texId), sizeof(int)); bool alphaTestEnabled = false; //bool cullFaceEnabled = false; bool cullFaceEnabled = true; ofile.write(reinterpret_cast(&alphaTestEnabled), sizeof(bool)); ofile.write(reinterpret_cast(&cullFaceEnabled), sizeof(bool)); // material bool hasMaterial = true; ofile.write(reinterpret_cast(&hasMaterial), sizeof(bool)); SimpleVec ambient, diffuse, spec, emm; ambient.x = ambient.y = ambient.z = 0.2f; diffuse.x = diffuse.y = diffuse.z = 1.0f; spec.x = spec.y = spec.z = .0f; emm = spec; // only write rgb part of the material ofile.write(reinterpret_cast(&ambient), sizeof(SimpleVec)); ofile.write(reinterpret_cast(&diffuse), sizeof(SimpleVec)); ofile.write(reinterpret_cast(&spec), sizeof(SimpleVec)); ofile.write(reinterpret_cast(&emm), sizeof(SimpleVec)); int entityCount = 1; ofile.write(reinterpret_cast(&entityCount), sizeof(int)); ////////// //-- write single scene entity // no transformation bool hasTrafo = false; ofile.write(reinterpret_cast(&hasTrafo), sizeof(bool)); // a dummy lod int numLODs = 1; ofile.write(reinterpret_cast(&numLODs), sizeof(int)); float dist = 0; ofile.write(reinterpret_cast(&dist), sizeof(float)); ofile.write(reinterpret_cast(&mNumShapes), sizeof(int)); // all shapes belong to this scene entity for (int i = 0; i < mNumShapes; ++ i) { int shapeId = i; ofile.write(reinterpret_cast(&shapeId), sizeof(int)); } return true; } #else bool ObjConverter::WriteFile(const string &filename) { ogzstream ofile(filename.c_str()); if (!ofile.is_open()) return false; ///////// //-- write textures int textureCount = (int)mTextures.size(); ofile.write(reinterpret_cast(&textureCount), sizeof(int)); TextureArray::const_iterator tit, tit_end = mTextures.end(); for (tit = mTextures.begin(); tit != tit_end; ++ tit) { const string texName = (*tit); int texnameSize = (int)texName.length() + 1; ofile.write(reinterpret_cast(&texnameSize), sizeof(int)); ofile.write(texName.c_str(), sizeof(char) * texnameSize); int boundS = 1, boundT = 1; ofile.write(reinterpret_cast(&boundS), sizeof(int)); ofile.write(reinterpret_cast(&boundT), sizeof(int)); } /////////// //-- write shapes ofile.write(reinterpret_cast(&mNumShapes), sizeof(int)); vector::const_iterator it, it_end = mGeometry.end(); for (it = mGeometry.begin(); it != it_end; ++ it) { WriteGeometry(ofile, *it); ///////// //-- material Material *mat = (*it)->mMaterial; if (!mat) cerr << "error: no material specified!!" << endl; ofile.write(reinterpret_cast(&mat->texture), sizeof(int)); bool alphaTestEnabled = false; //bool cullFaceEnabled = false; bool cullFaceEnabled = true; ofile.write(reinterpret_cast(&alphaTestEnabled), sizeof(bool)); ofile.write(reinterpret_cast(&cullFaceEnabled), sizeof(bool)); // material bool hasMaterial = true; ofile.write(reinterpret_cast(&hasMaterial), sizeof(bool)); SimpleVec ambient, diffuse, spec, emm; ambient.x = ambient.y = ambient.z = 0.2f; //diffuse.x = diffuse.y = diffuse.z = 1.0f; diffuse.x = mat->rgb[0]; diffuse.y =mat->rgb[1]; diffuse.z = mat->rgb[2]; spec.x = spec.y = spec.z = .0f; emm = spec; // only write rgb part of the material ofile.write(reinterpret_cast(&ambient), sizeof(SimpleVec)); ofile.write(reinterpret_cast(&diffuse), sizeof(SimpleVec)); ofile.write(reinterpret_cast(&spec), sizeof(SimpleVec)); ofile.write(reinterpret_cast(&emm), sizeof(SimpleVec)); } //int entityCount = 1; int entityCount = mNumShapes; ofile.write(reinterpret_cast(&entityCount), sizeof(int)); ////////// //-- q: write single scene entity with all shapes or many entities with one shape // all shapes belong to this scene entity for (int i = 0; i < mNumShapes; ++ i) { // no transformation bool hasTrafo = false; ofile.write(reinterpret_cast(&hasTrafo), sizeof(bool)); // a dummy lod int numLODs = 1; ofile.write(reinterpret_cast(&numLODs), sizeof(int)); float dist = 0; ofile.write(reinterpret_cast(&dist), sizeof(float)); //ofile.write(reinterpret_cast(&mNumShapes), sizeof(int)); int one = 1; ofile.write(reinterpret_cast(&one), sizeof(int)); int shapeId = i; ofile.write(reinterpret_cast(&shapeId), sizeof(int)); } return true; } #endif bool ObjConverter::LoadMaterials(const std::string &matFileName) { FILE *file; if ((file = fopen(matFileName.c_str(), "r")) == NULL) { return false; } int line = 0; const int len = 10000; char str[len]; Material *currentMat = NULL; while (fgets(str, len, file) != NULL) { //sscanf(str + 1, "%f %f %f", &x, &y, &z); vector strings; char *next_token; // extract the triples of the form v/t/n v/t/n ... char *pch = strtok_s(str, " \n", &next_token); while (pch) { string s(pch); strings.push_back(s); pch = strtok_s(NULL, " \n", &next_token); } if ((strings.size() == 2) && (strcmp(strings[0].c_str(), "newmtl") == 0)) { currentMat = new Material(); mMaterialTable[strings[1].c_str()] = currentMat; } if ((strings.size() == 2) && (strcmp(strings[0].c_str(), "map_Kd") == 0)) { TextureTable::const_iterator it = mTextureTable.find(strings[1]); int id; if (it == mTextureTable.end()) // parameter not found { mTextures.push_back(strings[1]); id = (int)mTextures.size(); mTextureTable[strings[1]] = id; } else { id = (*it).second; } currentMat->texture = id; } if ((strings.size() == 4) && (strcmp(strings[0].c_str(),"Kd") == 0)) { currentMat->rgb[0] = (float)atof(strings[1].c_str()); currentMat->rgb[1] = (float)atof(strings[2].c_str()); currentMat->rgb[2] = (float)atof(strings[3].c_str()); } ++ line; } return true; }