// // ObjLoader.cpp -- modified for glm // modified from https://github.com/mortennobel/OpenGL_3_2_Utils // /*! * OpenGL 3.2 Utils - Extension to the Angel library (from the book Interactive Computer Graphics 6th ed * https://github.com/mortennobel/OpenGL_3_2_Utils * * New BSD License * * Copyright (c) 2011, Morten Nobel-Joergensen * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the * following conditions are met: * * - Redistributions of source code must retain the above copyright notice, this list of conditions and the following * disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "ObjLoader.h" #define GLM_ENABLE_EXPERIMENTAL #include using namespace glm; #include #include #include #include #include using namespace std; const int DEBUG = 1; void printDebug(vector &positions, vector &indices); void printDebug(Material *m); vec3 toVec3(istringstream &iss){ float x,y,z; iss >> x; iss >> y; iss >> z; return vec3(x,y,z); } vec2 toVec2(istringstream &iss){ float x,y; iss >> x; iss >> y; return vec2(x,y); } struct TriangleIndex{ // this is a vertex, represented as the indices into their // respective arrays of positions, normals, and texture coords int position; int normal; int uv; bool replace(std::string& str, const std::string& from, const std::string& to) { size_t start_pos = str.find(from); if(start_pos == std::string::npos) return false; str.replace(start_pos, from.length(), to); return true; } TriangleIndex(string p):position(-1),normal(-1),uv(-1) { // position/uv/normal replace(p, "//","/-1/"); stringstream ss(p); char buffer[50]; ss.getline(buffer,50, '/'); position = atoi(buffer); if (ss.good()){ ss.getline(buffer,50, '/'); uv = atoi(buffer); } if (ss.good()){ ss.getline(buffer,50, '/'); normal = atoi(buffer); } } // needed to use TriangleIndex as key in map bool operator <(const TriangleIndex& Rhs) const { return (position < Rhs.position) || (position == Rhs.position && normal < Rhs.normal) || (position == Rhs.position && normal == Rhs.normal && uv < Rhs.uv); } }; struct TriangleString{ TriangleIndex v0; TriangleIndex v1; TriangleIndex v2; TriangleString(string v0, string v1, string v2):v0(v0),v1(v1),v2(v2){ } TriangleIndex get(int index){ if (index == 0) { return v0; } else if (index == 1) { return v1; } return v2; } }; std::string makeMtlFilename ( std::string mtlfile, std::string objfile ) { // pull off the path to the directory from the objfile size_t pos = objfile.rfind ('/'); // tack the directory onto the materal "mtllib" filename if ( pos == std::string::npos ) { return mtlfile; } else { std::string path; path = objfile.substr(0,pos); return path+"/"+mtlfile; } } bool loadMaterialLibrary ( string mtlText, map &outMaterials) { Material *mat = nullptr; stringstream ifs(mtlText); char buffer[512]; while (ifs.good()) { ifs.getline(buffer,512); string line(buffer); istringstream iss(line); string token; vec3 color; iss >> token; if (token.compare("newmtl") ==0) { // create a new material and store in map mat = new Material; iss >> token; outMaterials[token] = mat; } else if (token.compare("Ka") == 0 && mat) { mat->ambient = toVec3 ( iss ); } else if (token.compare("Kd") == 0 && mat) { mat->diffuse = toVec3 ( iss ); } else if (token.compare("Ks") == 0 && mat) { mat->specular = toVec3 ( iss ); } else if (token.compare("Ns") == 0 && mat) { iss >> mat->shininess; } else if (token.compare("map_Kd") == 0 && mat) { iss >> mat->diffuseTexture; } else if (token.compare("map_Disp") == 0 && mat) { iss >> mat->bumpTexture; } } return (mat != nullptr); } bool loadObject(string objText, vector &outPositions, vector &outNormal, vector &outUv, vector &outIndices, std::string &outMtlfilename, float scale) { vector positions; vector normals; vector uvs; vector triangles; stringstream ifs(objText); char buffer[512]; while (ifs.good()){ ifs.getline(buffer,512); string line(buffer); istringstream iss(line); string token; iss >> token; if (token.compare("o")==0){ // does not support multiple objects } else if (token.compare("g")==0){ } else if (token.compare("mtllib")==0){ // read the name of .mtl file iss >> outMtlfilename; } else if (token.compare("usemtl")==0){ // does not support multiple materials } else if (token.compare("v")==0){ positions.push_back( toVec3(iss)); } else if (token.compare("vn")==0){ normals.push_back( toVec3(iss)); } else if (token.compare("vt")==0){ uvs.push_back( toVec2(iss)); } else if (token.compare("f")==0){ vector polygon; do { string index; iss >> index; if (index.length() > 0) { polygon.push_back(index); } } while (iss); // triangulate polygon (assumes convex ) TriangleString triangle(polygon[0], polygon[1], polygon[2]); triangles.push_back(triangle); for (int i=3;i cache; for (int i=0;i::iterator cachedIndex = cache.find(index); if (cachedIndex != cache.end()) { outIndices.push_back(cachedIndex->second); } else { int vertexIndex = outPositions.size(); outPositions.push_back(positions[index.position-1] * scale); if (index.normal != -1){ outNormal.push_back(normals[index.normal-1]); } if (index.uv != -1) { outUv.push_back(uvs[index.uv-1]); } outIndices.push_back(vertexIndex); cache[index] = vertexIndex; } } } //cout <<"Indices "<< outIndices.size() << endl; //cout <<"Positions "<< outPositions.size() << endl; //printDebug(outPositions, outIndices); return true; } Material * makeDefaultMaterial() { Material *m = new Material; m->ambient = vec3 ( 0.1, 0.1, 0.1 ); m->diffuse = vec3 ( 0.8, 0.8, 0.8 ); m->specular = vec3 ( 1.0, 1.0, 1.0 ); m->shininess = 200.0f; return m; } struct Group { vector triangles; Material *mat; }; // // loadObjectGroups loads a .obj file containing materials and textures, // creating a ModelNode that draws it. bool loadObjectGroups ( string filename, vector &outPositions, vector &outNormal, vector &outUv, vector< vector > &outIndices, // per group vector< Material* >&outMaterials, // per group float scale) { vector positions; vector normals; vector uvs; map groups; string currentGroupName(""); map materials; vector diffuseTextures; vector bumpTextures; static int groupnum = 1; // currentGroupName = "dummy1"; // groups[currentGroupName] = new Group; // Material* defaultMaterial = makeDefaultMaterial(); // groups[currentGroupName]->mat = defaultMaterial; // materials[currentGroupName] = defaultMaterial; // outMaterials.push_back ( defaultMaterial ); string path = std::string( filename ); ifstream ifs ( path.c_str() , ifstream::in ); char buffer[512]; while (ifs.good()){ ifs.getline(buffer,512); string line(buffer); istringstream iss(line); string token; iss >> token; if (token.compare("o")==0){ // does not support multiple objects } else if (token.compare("g")==0){ // for a new group of faces (eg with a diff material) // create a group iss >> currentGroupName; groups[currentGroupName] = new Group; } else if (token.compare("mtllib")==0){ // read the .mtl file and create the Materials string mtlfile; iss >> mtlfile; // loadMaterialLibrary( mtlfile.c_str(), // filename, // materials); } else if (token.compare("usemtl")==0){ // create group if none exists, or if this is a "usemtl" line without a preceding "g" line if ( currentGroupName=="" || groups[currentGroupName]->triangles.size() ) { string name; ostringstream oss(name); // static int groupnum = 1; oss << "dummy" << groupnum++; currentGroupName=oss.str(); groups[currentGroupName] = new Group; } iss >> token; if (token=="usemtl") { if (DEBUG) std::cout << "null token" << std::endl; token = "dummy1"; } if (DEBUG) { std::cout << "group is " << currentGroupName << std::endl; std::cout << "usemtl " << token << std::endl; std::cout << "materials[token] == " << materials[token] << std::endl; } if ( materials[token] == 0 ) { materials[token] = makeDefaultMaterial(); } printDebug ( materials[token] ); groups[currentGroupName]->mat = materials[token]; outMaterials.push_back((materials[token])); } else if (token.compare("v")==0){ positions.push_back( toVec3(iss)); } else if (token.compare("vn")==0){ normals.push_back( toVec3(iss)); } else if (token.compare("vt")==0){ uvs.push_back( toVec2(iss)); } else if (token.compare("f")==0){ // assign face to current group vector polygon; do { string index; iss >> index; if (index.length() > 0) { polygon.push_back(index); } } while (iss); // oops no usemtl or mtllib or group seen // if ( currentGroupName=="" || groups[currentGroupName]->triangles.size()==0 ) { if ( currentGroupName=="" ) { string name; ostringstream oss(name); // static int groupnum = 1; oss << "dummy" << groupnum++; currentGroupName=oss.str(); groups[currentGroupName] = new Group; outMaterials.push_back( groups[currentGroupName]->mat = makeDefaultMaterial() ); } // triangulate polygon if ( polygon.size() >= 3 ) { TriangleString triangle(polygon[0], polygon[1], polygon[2]); groups[currentGroupName]->triangles.push_back(triangle); for (int i=3;itriangles.push_back(triangle2); } } } } ifs.close(); // XXX // repeat this for each group. // return vectors must be made vectors of vectors (or maybe just the indices?) // for each group // deref and pack the vertex positions, normal, and uv, // pack the element array for each group separately map cache; for ( map::iterator g = groups.begin(); g != groups.end();g++ ) { // each group gets its own element array vector groupIndices; for (int i=0;isecond->triangles.size();i++){ TriangleString triangleString = g->second->triangles[i]; for (int j=0;j<3;j++){ TriangleIndex index = triangleString.get(j); map::iterator cachedIndex = cache.find(index); if (cachedIndex != cache.end()) { groupIndices.push_back(cachedIndex->second); } else { int vertexIndex = outPositions.size(); outPositions.push_back(positions[index.position-1] * scale); if (index.normal != -1){ outNormal.push_back(normals[index.normal-1]); } if (index.uv != -1) { outUv.push_back(uvs[index.uv-1]); } groupIndices.push_back(vertexIndex); cache[index] = vertexIndex; } } } // pack the indices for this group outIndices.push_back(groupIndices); } //cout <<"Indices "<< outIndices.size() << endl; //cout <<"Positions "<< outPositions.size() << endl; //printDebug(outPositions, outIndices); return true; } void printDebug(vector &positions, vector &indices){ if (!DEBUG) return; for (int i=0;iambient) << endl; cout << "diffuse " << glm::to_string(m->diffuse) << endl; cout << "specular " << glm::to_string(m->specular) << endl; cout << "shininess " << m->shininess << endl; cout << "diffuse texture " << m->diffuseTexture << endl; cout << "bump texture " << m->bumpTexture << endl; } //// //// ObjFilePrimitive //// //ObjFilePrimitive::ObjFilePrimitive ( const char *filename ) //{ // bool okay = loadObjectGroups( filename, // points_, // normals_, // texCoords_, // groupIndices_, // materials_, // 1.0 ); // if ( !okay ) { // std::cout << "load error\n" ; // } else { // if (DEBUG) // std::cout << "loaded " << filename << std::endl // << points_.size() << " points\n" // << normals_.size() << " normals\n" // << texCoords_.size() << " tex coords\n" // << groupIndices_.size() << " groups\n"; // } // // // copy to unsigned ints. *sigh* // // for (int i = 0; i < tmp_indices.size(); i++ ) // // indices_.push_back( static_cast (tmp_indices[i]) ); // drawingPrimitive_ = GL_TRIANGLES; // generateAndLoadArrayBuffer(); // // generate the element buffers // for ( int i = 0; i < groupIndices_.size(); i++ ) { // groupElementArrayIds_.push_back(0); // glGenBuffers(1, &groupElementArrayIds_[i] ); // int sizeofIndices = groupIndices_[i].size()*sizeof(unsigned int); // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, groupElementArrayIds_[i] ); // glBufferData ( GL_ELEMENT_ARRAY_BUFFER, sizeofIndices, &(groupIndices_[i][0]), GL_STATIC_DRAW); // } // glBindVertexArray(0); //} //void //ObjFilePrimitive::draw ( glm::mat4 modelview, // glm::mat4 projection, // Ptr material) { // // loop over all groups as they have different materials. // for (int i = 0; i < groupIndices_.size(); i++ ) { // if (!material.get()) { // setupShader ( modelview, projection, materials_[i] ); // } else { // setupShader ( modelview, projection, material ); // } // // // // draw // // // glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, groupElementArrayIds_[i] ); // glDrawElements( drawingPrimitive_, groupIndices_[i].size(), GL_UNSIGNED_INT, 0 ); // glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, 0 ); // endShader(); // } //}