Sign in to follow this  
Helixirr

OpenGL Wavefront OBJ file reading - Polygons connect incorrectly

Recommended Posts

Helixirr    141

Hi, folks!

I have tried to create a 3D model class called Model3, which will inherit from my base class Model. This Model3-class supports only Wavefront OBJ files at the moment and I'm having trouble setting up correct data for OpenGL to process. I try to get rid of the deprecated OpenGL functions and use vertex array objects and vertex buffers objects instead to accomplish my goal to display a 3D model on screen.

The problem is rather simple to describe: polygons are formed incorrectly.

This is how I see the model in Blender (in which I also did the model):
[url="http://imageshack.us/photo/my-images/838/timg.png/"][url="http://imageshack.us/photo/my-images/838/timg.png/"][url="http://imageshack.us/photo/my-images/838/timg.png/"][url="http://imageshack.us/photo/my-images/838/timg.png/"]http://imageshack.us/photo/my-images/838/timg.png/[/url][/url][/url][/url]

And this is what ends up looking in my program:
[url="http://imageshack.us/photo/my-images/546/tim2i.png/"][url="http://imageshack.us/photo/my-images/546/tim2i.png/"][url="http://imageshack.us/photo/my-images/546/tim2i.png/"][url="http://imageshack.us/photo/my-images/546/tim2i.png/"]http://imageshack.us/photo/my-images/546/tim2i.png/[/url][/url][/url][/url]

 

Here's Model3 class definition:

/// ----------------------------------
    /// @class  	Model3
    /// @brief  	Represents a 3D model.
	/// @typedef	Model3D
    /// ----------------------------------
    typedef class Model3 : public Model{
    public:

		/// Enumerations:
		enum class FormatFile : unsigned int{
			FORMATFILE_3DS,
			FORMATFILE_OBJ
		};

        /// Constructors & destructors:
        Model3(void);
        Model3(Model3 const& model) = default;
        Model3(Model3&& model) = default;
		~Model3(void);

		/// Member functions:
		Model3& set_data(Model3::FormatFile const& format_file, std::string const& name_file);
		Model3& set_data(Model3::FormatFile const& format_file, std::string const&& name_file);
		Model3& set_data(Model3::FormatFile&& format_file, std::string const& name_file);
		Model3& set_data(Model3::FormatFile&& format_file, std::string const&& name_file);
		void show(void) const;

		/// Member functions (overloaded operators):
		Model3& operator=(Model3 const& model);
		Model3& operator=(Model3&& model);

    private:
        /// Member data:
		std::vector<Helixirr::Vector3<float>> _m_voDataFace[3]; // Element 0 contains vertex coordinates, element 1 contains texture coordinates, element 2 contains vertex normals.
		std::vector<Helixirr::Vector3<float>> _m_voDataVertex[3]; // Element 0 contains vertex coordinates, element 1 contains texture coordinates, element 2 contains vertex normals.
		unsigned int _m_uiIdArrayVertex, _m_uiBufferVertex;
	}Model3D;

 

Here are implementations of functions set_data and show:

/// Member functions:
Model3& Model3::set_data(Model3::FormatFile const& format_file, std::string const& name_file){ /* UNFINISHED! */
	std::ifstream __file(name_file);

	// 3D model loading lambdas:
	auto const __load_3ds = [&](void){
		/* UNFINISHED! */
	};
	auto const __load_obj = [&](void){ /* WORKS HALF RIGHT AND MORE FUNCTIONALITY IS REQUIRED! */
		auto const __read_data_vertices = [](std::string const& string, float coordinates[], unsigned int const&& amount_elements, unsigned int&& index_begin){
			// Variables used in the following for loop:
			// index_begin = end index for string substraction operation
			// __i = index of the temporary float array
			// __j = start index for string substraction operation
			for(unsigned int __i = 0, __j = index_begin; __i != amount_elements; ++__j){
				if(string[__j] == ' '){ // If the "end character" for floating number is detected:
					coordinates[__i++] = atof(string.substr(index_begin, ++__j).c_str());
					index_begin = __j;
				}
			}
		};

		std::string __line;
		clock_t init = clock();
		while(std::getline(__file, __line)){ // For each line in the requested file:
			switch(__line[0]){ // Check out the first letter of a line:
				case 'f':{
					// Read through the whole line ignoring all the insignificant characters (like '\n'):
					for(unsigned int __i = 2, __j = 2, __k = 0; __j != __line.size() - 1; ++__j){
						if(__line[__j] == '/'){
							_m_voDataFace[__k].push_back(_m_voDataVertex[__k][atoi(__line.substr(__i, ++__j).c_str())]);
							__i = __j;
							++__k;
						}
						else if(__line[__j] == ' '){
							++__j;
							__i = __j;
							__k = 0;
						}
					}
					continue;
				}
				case 'v': // If a requested line contains some sort of vertex data:
					if(__line[1] == ' '){ // If the requested line contains vertex coordinates:
						float __coordinates[3] = {0.0f}; // A temporary float array to store vertex coordinates.
						__read_data_vertices(__line, __coordinates, 3, 2);
						_m_voDataVertex[0].push_back(Helixirr::Vector3<float>(std::move(__coordinates[0]), std::move(__coordinates[1]), std::move(__coordinates[2])));
					}
					else if(__line[1] == 'n'){ // If the requested line contains vertex normals:
						float __coordinates[3] = {0.0f}; // A temporary float array to store vertex normals.
						__read_data_vertices(__line, __coordinates, 3, 3);
						_m_voDataVertex[2].push_back(Helixirr::Vector3<float>(std::move(__coordinates[0]), std::move(__coordinates[1]), std::move(__coordinates[2])));
					}
					else if(__line[1] == 't'){ // If the requested line contains UV coordinates:
						float __coordinates[2] = {0.0f}; // A temporary float array to store UV coordinates.
						__read_data_vertices(__line, __coordinates, 2, 3);
						_m_voDataVertex[1].push_back(Helixirr::Vector3<float>(std::move(__coordinates[0]), std::move(__coordinates[1]), 0.0f));
					}
					continue;
				default:
					continue;
			}
		}
		printf("OBJ file loading - elapsed time: %f\n", static_cast<double>(clock() - init) / static_cast<double>(CLOCKS_PER_SEC));
	};

	// Detect requested file format:
	switch(format_file){
		case Model3::FormatFile::FORMATFILE_3DS:
			__load_3ds();
			break;
		case Model3::FormatFile::FORMATFILE_OBJ:
			__load_obj();
			break;
		default:
			break;
	}

	// Create and bind a vertex array:
	if(_m_uiIdArrayVertex == 0)
		glGenVertexArrays(1, &_m_uiIdArrayVertex);
	glBindVertexArray(_m_uiIdArrayVertex);

	// Create and bind a vertex buffer object:
	if(_m_uiBufferVertex == 0)
		glGenBuffers(1, &_m_uiBufferVertex);
	glBindBuffer(GL_ARRAY_BUFFER, _m_uiBufferVertex);

	// Insert vertex data:
	glBufferData(GL_ARRAY_BUFFER, sizeof(_m_voDataFace[0][0]) * _m_voDataFace[0].size(), &_m_voDataFace[0][0], GL_STATIC_DRAW);

	return *this;
}
Model3& Model3::set_data(Model3::FormatFile const& format_file, std::string const&& name_file){
	return this->set_data(format_file, name_file);
}
Model3& Model3::set_data(Model3::FormatFile&& format_file, std::string const& name_file){
	return this->set_data(format_file, name_file);
}
Model3& Model3::set_data(Model3::FormatFile&& format_file, std::string const&& name_file){
	return this->set_data(format_file, name_file);
}
void Model3::show(void) const{
	glEnableVertexAttribArray(0);
	glBindBuffer(GL_ARRAY_BUFFER, _m_uiBufferVertex);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

	glEnable(GL_POINT_SMOOTH);
	glPointSize(2.0f);

	glDrawArrays(GL_TRIANGLES, 0, _m_voDataFace[0].size() + 1);
	glDisableVertexAttribArray(0);
}


I don't understand what have I done wrong. Can anybody help me?

Edited by Helixirr

Share this post


Link to post
Share on other sites
Helixirr    141

Looks like you're not converting the vertex indices. The obj-file has 1-based indices but std::vector has 0-based indices.

 

Thanks for the reply. Now, what does this mean? What are these vertex indices anyway? Am I reading OBJ file incorrectly? How do I practically do this conversion?

Share this post


Link to post
Share on other sites
Brother Bob    10344

In C++, an index is 0-based which means that the first element of an array has index 0, the second element has index 1, the third element has index 2, and so on. The OBJ file is 1-based, which means that the first vertex has index 1, the second vertex has index 2, the third vertex has index 3, and so on. Therefore, you cannot read an index from the OBJ file and use it directly to reference a C++ array.

 

The indices are what you read from the f-lines. For example, "f 1 2 3" means: the face is made up of three vertices (because there are three values), and the vertices are the vertex with index 1, the vertex with index 2, and the vertex with index 3.

Share this post


Link to post
Share on other sites
L. Spiro    25622

It means read the index from the .OBJ file, subtract one, and use that value in your std::vector.

 

 

L. Spiro

 

 

PS: Heh, and here I am always being so vocal against spoon-feeding. tongue.png

Edited by L. Spiro

Share this post


Link to post
Share on other sites
Helixirr    141

In C++, an index is 0-based which means that the first element of an array has index 0, the second element has index 1, the third element has index 2, and so on. The OBJ file is 1-based, which means that the first vertex has index 1, the second vertex has index 2, the third vertex has index 3, and so on. Therefore, you cannot read an index from the OBJ file and use it directly to reference a C++ array.

 

The indices are what you read from the f-lines. For example, "f 1 2 3" means: the face is made up of three vertices (because there are three values), and the vertices are the vertex with index 1, the vertex with index 2, and the vertex with index 3.

 

It means read the index from the .OBJ file, subtract one, and use that value in your std::vector.

 

 

L. Spiro

 

 

PS: Heh, and here I am always being so vocal against spoon-feeding. tongue.png

 

Now, after reading all this, I feel so stupid not to have realized, what I did wrong. Oh boy... rolleyes.gif

 

Fortunately, it works now. Thank you, God and thank you all. smile.png

 

Here is the working piece of code with a slight modification dedicated for those who are also interested in reading an OBJ file and putting OBJ file data into OpenGL. Please, have a look:

 

/// ----------------------------------
    /// @class  	Model3
    /// @brief  	Represents a 3D model.
	/// @typedef	Model3D
    /// ----------------------------------
    typedef class Model3 : public Model{
    public:

		/// Enumerations:
		enum class FormatFile : unsigned int{
			FORMATFILE_3DS,
			FORMATFILE_OBJ
		};

        /// Constructors & destructors:
        Model3(void);
        Model3(Model3 const& model) = default;
        Model3(Model3&& model) = default;
		~Model3(void);

		/// Member functions:
		Model3& set_data(Model3::FormatFile const& format_file, std::string const& name_file);
		Model3& set_data(Model3::FormatFile const& format_file, std::string const&& name_file);
		Model3& set_data(Model3::FormatFile&& format_file, std::string const& name_file);
		Model3& set_data(Model3::FormatFile&& format_file, std::string const&& name_file);
		void show(void) const;

		/// Member functions (overloaded operators):
		Model3& operator=(Model3 const& model);
		Model3& operator=(Model3&& model);

    private:
        /// Member data:
		std::vector<Helixirr::Vector3<float>> _m_voDataFace[3]; // Element 0 contains vertex coordinates, element 1 contains texture coordinates, element 2 contains vertex normals.
		std::vector<Helixirr::Vector3<float>> _m_voDataVertex[3]; // Element 0 contains vertex coordinates, element 1 contains texture coordinates, element 2 contains vertex normals.
		unsigned int _m_uiIdArrayVertex, _m_uiBufferVertex;
	}Model3D;
/// Member functions:
Model3& Model3::set_data(Model3::FormatFile const& format_file, std::string const& name_file){ /* UNFINISHED! */
	std::ifstream __file(name_file);

	// 3D model loading lambdas:
	auto const __load_3ds = [&](void){
		/* UNFINISHED! */
	};
	auto const __load_obj = [&](void){ /* WORKS HALF RIGHT AND MORE FUNCTIONALITY IS REQUIRED! */
		auto const __read_data_vertices = [](std::string const& string, float coordinates[], unsigned int const&& amount_elements, unsigned int&& index_begin){
			// Variables used in the following for loop:
			// index_begin = end index for string substraction operation
			// __i = index of the temporary float array
			// __j = start index for string substraction operation
			for(unsigned int __i = 0, __j = index_begin; __i != amount_elements; ++__j){
				if(string[__j] == ' '){ // If the "end character" for floating number is detected:
					coordinates[__i++] = atof(string.substr(index_begin, ++__j).c_str());
					index_begin = __j;
				}
			}
		};

		std::string __line;
		clock_t init = clock();
		while(std::getline(__file, __line)){ // For each line in the requested file:
			switch(__line[0]){ // Check out the first letter of a line:
				case 'f':{
					// Read through the whole line ignoring all the insignificant characters (like '\n'):
					for(unsigned int __i = 2, __j = 2, __k = 0; __j != __line.size() - 1; ++__j){
						if(__line[__j] == '/'){
							_m_voDataFace[__k].push_back(_m_voDataVertex[__k][atoi(__line.substr(__i, ++__j).c_str()) - 1]);
							__i = __j;
							++__k;
						}
						else if(__line[__j] == ' '){
							++__j;
							__i = __j;
							__k = 0;
						}
					}
					continue;
				}
				case 'v': // If a requested line contains some sort of vertex data:
					if(__line[1] == ' '){ // If the requested line contains vertex coordinates:
						float __coordinates[3] = {0.0f}; // A temporary float array to store vertex coordinates.
						__read_data_vertices(__line, __coordinates, 3, 2);
						_m_voDataVertex[0].push_back(Helixirr::Vector3<float>(std::move(__coordinates[0]), std::move(__coordinates[1]), std::move(__coordinates[2])));
					}
					else if(__line[1] == 'n'){ // If the requested line contains vertex normals:
						float __coordinates[3] = {0.0f}; // A temporary float array to store vertex normals.
						__read_data_vertices(__line, __coordinates, 3, 3);
						_m_voDataVertex[2].push_back(Helixirr::Vector3<float>(std::move(__coordinates[0]), std::move(__coordinates[1]), std::move(__coordinates[2])));
					}
					else if(__line[1] == 't'){ // If the requested line contains UV coordinates:
						float __coordinates[2] = {0.0f}; // A temporary float array to store UV coordinates.
						__read_data_vertices(__line, __coordinates, 2, 3);
						_m_voDataVertex[1].push_back(Helixirr::Vector3<float>(std::move(__coordinates[0]), std::move(__coordinates[1]), 0.0f));
					}
					continue;
				default:
					continue;
			}
		}
		printf("OBJ file loading - elapsed time: %f\n", static_cast<double>(clock() - init) / static_cast<double>(CLOCKS_PER_SEC));
	};

	// Detect requested file format:
	switch(format_file){
		case Model3::FormatFile::FORMATFILE_3DS:
			__load_3ds();
			break;
		case Model3::FormatFile::FORMATFILE_OBJ:
			__load_obj();
			break;
		default:
			break;
	}

	// Create and bind a vertex array:
	if(_m_uiIdArrayVertex == 0)
		glGenVertexArrays(1, &_m_uiIdArrayVertex);
	glBindVertexArray(_m_uiIdArrayVertex);

	// Create and bind a vertex buffer object:
	if(_m_uiBufferVertex == 0)
		glGenBuffers(1, &_m_uiBufferVertex);
	glBindBuffer(GL_ARRAY_BUFFER, _m_uiBufferVertex);

	// Insert vertex data:
	glBufferData(GL_ARRAY_BUFFER, sizeof(_m_voDataFace[0][0]) * _m_voDataFace[0].size(), &_m_voDataFace[0][0], GL_STATIC_DRAW);

	return *this;
}
Model3& Model3::set_data(Model3::FormatFile const& format_file, std::string const&& name_file){
	return this->set_data(format_file, name_file);
}
Model3& Model3::set_data(Model3::FormatFile&& format_file, std::string const& name_file){
	return this->set_data(format_file, name_file);
}
Model3& Model3::set_data(Model3::FormatFile&& format_file, std::string const&& name_file){
	return this->set_data(format_file, name_file);
}
void Model3::show(void) const{
	glEnableVertexAttribArray(0);
	glBindBuffer(GL_ARRAY_BUFFER, _m_uiBufferVertex);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

	glEnable(GL_POINT_SMOOTH);
	glPointSize(2.0f);

	glDrawArrays(GL_TRIANGLES, 0, _m_voDataFace[0].size() + 1);
	glDisableVertexAttribArray(0);
}

 


 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

  • Similar Content

    • By povilaslt2
      Hello. I'm Programmer who is in search of 2D game project who preferably uses OpenGL and C++. You can see my projects in GitHub. Project genre doesn't matter (except MMO's :D).
    • By ZeldaFan555
      Hello, My name is Matt. I am a programmer. I mostly use Java, but can use C++ and various other languages. I'm looking for someone to partner up with for random projects, preferably using OpenGL, though I'd be open to just about anything. If you're interested you can contact me on Skype or on here, thank you!
      Skype: Mangodoor408
    • By tyhender
      Hello, my name is Mark. I'm hobby programmer. 
      So recently,I thought that it's good idea to find people to create a full 3D engine. I'm looking for people experienced in scripting 3D shaders and implementing physics into engine(game)(we are going to use the React physics engine). 
      And,ye,no money =D I'm just looking for hobbyists that will be proud of their work. If engine(or game) will have financial succes,well,then maybe =D
      Sorry for late replies.
      I mostly give more information when people PM me,but this post is REALLY short,even for me =D
      So here's few more points:
      Engine will use openGL and SDL for graphics. It will use React3D physics library for physics simulation. Engine(most probably,atleast for the first part) won't have graphical fron-end,it will be a framework . I think final engine should be enough to set up an FPS in a couple of minutes. A bit about my self:
      I've been programming for 7 years total. I learned very slowly it as "secondary interesting thing" for like 3 years, but then began to script more seriously.  My primary language is C++,which we are going to use for the engine. Yes,I did 3D graphics with physics simulation before. No, my portfolio isn't very impressive. I'm working on that No,I wasn't employed officially. If anybody need to know more PM me. 
       
    • By Zaphyk
      I am developing my engine using the OpenGL 3.3 compatibility profile. It runs as expected on my NVIDIA card and on my Intel Card however when I tried it on an AMD setup it ran 3 times worse than on the other setups. Could this be a AMD driver thing or is this probably a problem with my OGL code? Could a different code standard create such bad performance?
    • By Kjell Andersson
      I'm trying to get some legacy OpenGL code to run with a shader pipeline,
      The legacy code uses glVertexPointer(), glColorPointer(), glNormalPointer() and glTexCoordPointer() to supply the vertex information.
      I know that it should be using setVertexAttribPointer() etc to clearly define the layout but that is not an option right now since the legacy code can't be modified to that extent.
      I've got a version 330 vertex shader to somewhat work:
      #version 330 uniform mat4 osg_ModelViewProjectionMatrix; uniform mat4 osg_ModelViewMatrix; layout(location = 0) in vec4 Vertex; layout(location = 2) in vec4 Normal; // Velocity layout(location = 3) in vec3 TexCoord; // TODO: is this the right layout location? out VertexData { vec4 color; vec3 velocity; float size; } VertexOut; void main(void) { vec4 p0 = Vertex; vec4 p1 = Vertex + vec4(Normal.x, Normal.y, Normal.z, 0.0f); vec3 velocity = (osg_ModelViewProjectionMatrix * p1 - osg_ModelViewProjectionMatrix * p0).xyz; VertexOut.velocity = velocity; VertexOut.size = TexCoord.y; gl_Position = osg_ModelViewMatrix * Vertex; } What works is the Vertex and Normal information that the legacy C++ OpenGL code seem to provide in layout location 0 and 2. This is fine.
      What I'm not getting to work is the TexCoord information that is supplied by a glTexCoordPointer() call in C++.
      Question:
      What layout location is the old standard pipeline using for glTexCoordPointer()? Or is this undefined?
       
      Side note: I'm trying to get an OpenSceneGraph 3.4.0 particle system to use custom vertex, geometry and fragment shaders for rendering the particles.
  • Popular Now