.obj Parser + Render GLUT

So, I have a small .obj parser that can parse the vertices and draw them on the screen:

 void loadObj(char *fname) { FILE *fp; int read; GLfloat x, y, z; char ch; _model = glGenLists(1); fp = fopen(fname, "r"); if (!fp) { printf("can't open file %s\n", fname); exit(1); } glPointSize(2.0); glNewList(_model, GL_COMPILE); { glPushMatrix(); glBegin(GL_POINTS); while (!(feof(fp))) { read = fscanf(fp, "%c %f %f %f", &ch, &x, &y, &z); if (read == 4 && ch == 'v') { glVertex3f(x, y, z); } } glEnd(); } glPopMatrix(); glEndList(); fclose(fp); } void drawModel() { glPushMatrix(); glTranslatef(0, 0.00, 0.00); glColor3f(1.0, 0.23, 0.27); glScalef(10, 10, 10); glRotatef(_modelRot, 0, 1, 0); glCallList(_model); glPopMatrix(); } 

Point, output - these are just the vertices, for example:

enter image description here

How can I change this to at least show a three-dimensional shape between points without adding third-party libraries? This is what I am looking for:

enter image description here

Thanks. If necessary, an additional code may be provided.

+5
source share
1 answer

Your parsers and rendering calls are not complete.

The first part of the .obj file contains vertex data. Including position, texture coordinate, and normal data. The second part contains information on how verticals are interconnected.

  # List of geometric vertices, with (x,y,z[,w]) coordinates, w is optional and defaults to 1.0. v 0.123 0.234 0.345 1.0 # first vertex v ... # second vertex ... # List of texture coordinates, in (u, v [,w]) coordinates, these will vary between 0 and 1, w is optional and defaults to 0. vt 0.500 1 [0] # first texture coordinate vt ... # second ... # List of vertex normals in (x,y,z) form; normals might not be unit vectors. vn 0.707 0.000 0.707 # first normal vn ... # second ... # Parameter space vertices in ( u [,v] [,w] ) form; free form geometry statement ( see below ) vp 0.310000 3.210000 2.100000 vp ... ... # Polygonal face element (see below) f 1 2 3 # face of the first, second and third vertex f 3/1 4/2 5/3 # face of the third, fourth and fifth vertex with the first second and third texture coordinate f 6/4/1 3/5/3 7/6/5 # face of sixth, thrid and seventh vertex, fourth, fifth and sixth texture coordinate and first thrid and fifth normal f 7//1 8//2 9//3 # similar to the line over but without texture coordinates f ... ... 

Listing taken from here: https://en.wikipedia.org/wiki/Wavefront_.obj_file

OpenGl cannot match vertex data together; .obj files allow this. Thus, you must create a data structure with all vertices, one for all texels and one for all normals.

You can then analyze the faces and build your vertex data by collecting the correct position, texel and normals to build a full face.

After this step, you can use this combination to draw your primitives using GL_TRIANGLES or GL_QUADS.

Here is the bootloader that I wrote a while ago for OpenGl 4.x

 #pragma once #include <fstream> #include <string> #include <sstream> #include <vector> #include <map> #include <glm/glm.hpp> #include <GL/glew.h> #include "System/Log.hpp" #include "Graphics/Primitives/Object.hpp" #include "Graphics/Primitives/Material.hpp" namespace Loader { template <class ObjectT = Graphics::Primitives::Object> class ObjectLoader { private: const std::string fullPath; std::vector<Graphics::Primitives::VertexGroup> objects; std::map<std::string, Graphics::Primitives::Material> materials; std::string prefixPath; std::vector<glm::vec3> verticies; std::vector<glm::vec2> texels; std::vector<glm::vec3> normals; std::vector<glm::uvec3> faces; std::vector<glm::vec3> index_verticies; std::vector<glm::vec2> index_texels; std::vector<glm::vec3> index_normals; Graphics::Primitives::Material material; std::map<std::string, GLuint> indexDb; std::string getPrefixPath(); std::vector<std::string> explode(std::string str, char delimiter = ' '); glm::vec3 stringsToVec(const std::vector<std::string> parts, unsigned int begin); void loadMaterial(std::string fileName); Graphics::Primitives::VertexGroup flush(); public: ObjectLoader(std::string fileName); ObjectT load(); }; template <class ObjectT> std::string ObjectLoader<ObjectT>::getPrefixPath() { unsigned int lastSlash = 0; for(int i = fullPath.size(); i > 0; i--) { if(fullPath[i] == '/') { lastSlash = i; break; } } std::string prefixPath = fullPath.substr(0, lastSlash); prefixPath += "/"; return prefixPath; } template <class ObjectT> std::vector<std::string> ObjectLoader<ObjectT>::explode(std::string str, char delimiter) { std::vector<std::string> result; std::stringstream data(str); std::string line; while(std::getline(data,line,delimiter)) { result.push_back(line); } return result; } template <class ObjectT> glm::vec3 ObjectLoader<ObjectT>::stringsToVec(const std::vector<std::string> parts, unsigned int begin) { glm::vec3 result; if(parts.size() > begin + 2) { result.x = std::atof(parts[begin].c_str()); result.y = std::atof(parts[begin+1].c_str()); result.z = std::atof(parts[begin+2].c_str()); } else if(parts.size() > begin) { result.x = std::atof(parts[begin].c_str()); result.y = std::atof(parts[begin].c_str()); result.z = std::atof(parts[begin].c_str()); } return result; } template <class ObjectT> void ObjectLoader<ObjectT>::loadMaterial(std::string fileName) { std::ifstream materialFile(fileName); std::string line; Graphics::Primitives::Material material; std::string materialName; bool initialised = false; while(std::getline(materialFile, line)) { //System::Log::msg << " " << line << std::endl; std::vector<std::string> parts = explode(line); if(parts.size() > 0) { if(parts[0] == "newmtl") { if(initialised) { materials.insert(std::make_pair(materialName, material)); System::Log::msg << "Loaded material: " << materialName << std::endl; } materialName = parts[1]; initialised = true; material = Graphics::Primitives::Material(); } else if(parts[0] == "Ns") { if(parts.size() > 1) { material.specularExponent = std::atof(parts[1].c_str()); } } else if(parts[0] == "Ka") { material.ambientReflectance = stringsToVec(parts,1); } else if(parts[0] == "Kd") { material.diffuseReflectance = stringsToVec(parts,1); } else if(parts[0] == "Ks") { material.specularReflectance = stringsToVec(parts,1); } else //if(parts[0] == "Ke") { //No idea what this value means, maybe transmission filter aka Tf? //} else if(parts[0] == "Ni") { //Optical density ignored for now } else if(parts[0] == "d") { material.dissolve = std::atof(parts[1].c_str()); } else if(parts[0] == "map_Ka") { material.textureStack.push_back(Graphics::Ogl::loadTexture(parts[1])); } else if(parts[0] == "map_Kd") { material.textureStack.push_back(Graphics::Ogl::loadTexture(parts[1])); } else if(parts[0] == "map_Ks") { material.textureStack.push_back(Graphics::Ogl::loadTexture(parts[1])); } } } materials.insert(std::make_pair(materialName, material)); System::Log::msg << "Loaded material: " << materialName << std::endl; } template <class ObjectT> Graphics::Primitives::VertexGroup ObjectLoader<ObjectT>::flush() { Graphics::Ogl::VertexArrayObject vao = Graphics::Ogl::makeVertexArrayObject( std::vector<Graphics::Ogl::ArrayBufferObject>({ Graphics::Ogl::makeArrayBufferObject(index_verticies), Graphics::Ogl::makeArrayBufferObject(index_normals), Graphics::Ogl::makeArrayBufferObject(index_texels), Graphics::Ogl::makeIndexBufferObject(faces) }) ); System::Log::msg << "Flushing buffers: vertecies(" << index_verticies.size() << "), texels(" << index_texels.size() << "), normals(" << index_normals.size() << "), faces(" << faces.size() << ")" << std::endl; Graphics::Primitives::VertexGroup obj(vao, material); glCheckError(); faces.clear(); index_verticies.clear(); index_texels.clear(); index_normals.clear(); indexDb.clear(); return obj; } template <class ObjectT> ObjectLoader<ObjectT>::ObjectLoader(std::string fileName) : fullPath(fileName), prefixPath(getPrefixPath()) {} template <class ObjectT> ObjectT ObjectLoader<ObjectT>::load() { System::Log::msg << "Loading Object from file: " << fullPath << std::endl; std::ifstream objectFile(fullPath); std::string line; while(std::getline(objectFile,line)) { //System::Log::msg << line << std::endl; if(line[0] == '#' || line[0] == 'o' || line[0] == 'g') { continue; } std::vector<std::string> substrs = explode(line); if(substrs.size() == 0) { continue; } if( substrs[0] == "v") { //Add new Vertex to index buffer glm::vec3 vertex; if(substrs.size() > 3) { vertex.x = std::stof(substrs[1]); vertex.y = std::stof(substrs[2]); vertex.z = std::stof(substrs[3]); verticies.push_back(vertex); } else { System::Log::err << "Vertex with less than 3 coordinates." << std::endl; } } else if(substrs[0] == "vt") { //Add new Texel to index buffer glm::vec2 texel; if(substrs.size() > 2) { texel.x= std::stof(substrs[1]); texel.y = std::stof(substrs[2]); texels.push_back(texel); } else { System::Log::err << "Texel with less than 2 coordinates." << std::endl; } } else if(substrs[0] == "vn") { //Add new Normal to index buffer glm::vec3 normal; if(substrs.size() > 3) { normal.x = std::stof(substrs[1]); normal.y = std::stof(substrs[2]); normal.z = std::stof(substrs[3]); normals.push_back(normal); } else { System::Log::err << "Normal with less than 3 coordinates." << std::endl; } } else if(substrs[0] == "f") { if(texels.size() == 0) texels.push_back(glm::vec2(0,0)); if(normals.size() == 0) normals.push_back(glm::vec3(0,0,0)); //Lookup in index db; glm::uvec3 face; for(unsigned int faceIndex = 1; faceIndex < 4; faceIndex++) { std::string vtn = substrs[faceIndex]; try { //Try to find index combination in db face[faceIndex-1] = indexDb.at(substrs[faceIndex]); //Index found } catch (std::exception e) { //Index not found, now to the hard part //Create new index in indexDb GLuint newFace = indexDb.size(); face[faceIndex-1] = newFace; indexDb.insert(std::make_pair(substrs[faceIndex], newFace)); //Create new vtn triple in buffers std::vector<std::string> components = explode(substrs[faceIndex],'/'); if(components[1].size() == 0) components[1] = "0"; if(components[2].size() == 0) components[2] = "0"; auto clipValue = [](std::string& number) -> GLuint { GLuint result = std::atoi(number.c_str()); if(result > 0) result--; return result; }; const GLuint vi = clipValue(components[0]); const GLuint ti = clipValue(components[1]); const GLuint ni = clipValue(components[2]); if(verticies.size() > vi) { index_verticies.push_back(verticies[vi]); } else { System::Log::err << "Error: Invalid vertex index. (Index="<< vi <<", LoadedVerticies=" << verticies.size() << ")" << std::endl; } if(texels.size() > ti) { index_texels.push_back(texels[ti]); } if(normals.size() > ni) { index_normals.push_back(normals[ni]); } } } //Add new Face to Mesh faces.push_back(face); //System::Log::msg << "Face: " << face.x << " " << face.y << " " << face.z << std::endl; } else if(substrs[0] == "usemtl") { //Flush last mesh if(faces.size() > 0) { objects.push_back(flush()); } //Use new material material = materials.at(substrs[1]); System::Log::msg << "Use Material:" << substrs[1] << std::endl; } else if(substrs[0] == "s") { //Smoothing //TODO: } else if(substrs[0] == "mtllib") { //Load new materials loadMaterial(prefixPath+substrs[1]); } else { System::Log::err << "Unknown prefix in file" << std::endl; } } if(faces.size() > 0) { objects.push_back(flush()); } System::Log::msg << "Done loading object." << std::endl; return ObjectT(objects); } } // End of namespace Loader 
+3
source

Source: https://habr.com/ru/post/1265033/


All Articles