#include "ObjConverter2.h" #include "SimpleTri.h" #include "SimpleVec.h" #include "gzstream.h" #include using namespace std; static void LoadIndices(char *str, const VertexArray &vertices, const VertexArray &normals, const vector &texcoords, VertexArray &faceVertices, VertexArray &faceNormals, vector &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; } int idx[3]; // new triangle found if (indices.size() > 2) { // change orientation of faces? #if 1 idx[0] = 0; idx[1] = (int)indices.size() - 2; idx[2] = (int)indices.size() - 1; #else idx[2] = 0; idx[1] = (int)indices.size() - 2; idx[0] = (int)indices.size() - 1; #endif for (int j = 0; j < 3; ++ j) faceVertices.push_back(vertices[indices[idx[j]]]); if (!normals.empty()) { for (int j = 0; j < 3; ++ j) faceNormals.push_back(Normalize(normals[nIndices[idx[j]]])); } else { // no face normals? => create normals const SimpleTri tri(vertices[indices[idx[0]]], vertices[indices[idx[1]]], vertices[indices[idx[2]]]); const SimpleVec n = tri.GetNormal(); faceNormals.push_back(n); faceNormals.push_back(n); faceNormals.push_back(n); } if (!texcoords.empty()) { for (int j = 0; j < 3; ++ j) { //if (tIndices[idx[j]] >= (int)texcoords.size()) //cerr << "error: texcoord indices exceed array size " << texcoords.size() << " " << tIndices[idx[j]] << endl; const int tidx = min((int)texcoords.size() - 1, tIndices[idx[j]]); faceTexcoords.push_back(texcoords[tidx]); } } } } } ObjConverter2::ObjConverter2() {} ObjConverter2::~ObjConverter2() { for (size_t i = 0; i < mGeometry.size(); ++ i) delete mGeometry[i]; mGeometry.clear(); } void ObjConverter2::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 1 // 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 ObjConverter2::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 ObjConverter2::Convert2(const vector &filenames, const std::string &outputFilename) { mNumShapes = 0; vector::const_iterator it, it_end = filenames.end(); ofstream outstr("mydebug.log"); for (it = filenames.begin(); it != it_end; ++ it) { const string filename = *it; cout << "\n==================\n loading file " << filename << endl; outstr << "loading file: " << filename << endl; outstr << flush; if (!ReadFile(filename)) { cerr << "could not read file ... skipping " << filename << endl; //return false; } } cout << "\n*******************\nwriting file " << outputFilename << endl; if (!WriteFile(outputFilename)) { cerr << "could not write file" << endl; return false; } return true; } bool ObjConverter2::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) { //cout << "line: " << line << endl; switch (str[0]) { case 'v': // vertex or normal { // still an object to load if (!faceVertices.empty()) { ++ mNumShapes; //if (!currentMat){ cerr << "no mat!!" << endl; std::cin.get(); } LoadShape(faceVertices, faceNormals, faceTexcoords, currentMat); faceVertices.clear(); faceNormals.clear(); faceTexcoords.clear(); currentMat = NULL; } 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)); } break; } case 'f': { ////////// //-- indices in the current line //cout << "f: " << vertices.size() << " n: " << normals.size() << " " << texcoords.size() << endl; 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; //cout << "currentMat: " << currentMat << endl; } break; case 'm': // material library { string matLibName(model_path + string(str + 7)); // throw away linebreak character matLibName.resize(matLibName.size() - 1); cout << "loading mat library " << matLibName << endl; // load the materials if (!LoadMaterials(matLibName)) { cerr << "loading material library failed" << endl; return false; } } default: // throw away line break; } ++ line; } // convert the rest of the vertives if (!faceVertices.empty()) { ++ mNumShapes; LoadShape(faceVertices, faceNormals, faceTexcoords, currentMat); faceVertices.clear(); faceNormals.clear(); faceTexcoords.clear(); } fclose(file); return true; } void ObjConverter2::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); } bool ObjConverter2::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 = true; //bool cullFaceEnabled = false; 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 = mNumShapes; ofile.write(reinterpret_cast(&entityCount), sizeof(int)); ////////// //-- write scene entities // 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)); int numShapes = 1; ofile.write(reinterpret_cast(&numShapes), sizeof(int)); int shapeId = i; ofile.write(reinterpret_cast(&shapeId), sizeof(int)); } ofile.close(); return true; } bool ObjConverter2::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) || (strcmp(strings[0].c_str(),"map_Ka") == 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; } fclose(file); return true; }