Part of the Khronos Group
OpenGL.org

The Industry's Foundation for High Performance Graphics

from games to virtual reality, mobile phones to supercomputers

Page 1 of 2 12 LastLast
Results 1 to 10 of 15

Thread: Strange problem when drawing wavefront OBJ

Hybrid View

  1. #1
    Intern Contributor
    Join Date
    Jul 2014
    Location
    Italy, Lissone
    Posts
    80

    Strange problem when drawing wavefront OBJ

    hi,
    i'm new in the forum so i hope i'm not making mistakes posting this help request.

    i wrote some code to load an OBJ file into my program but the result is very strange, here it is:

    Click image for larger version. 

Name:	MyProblem.jpg 
Views:	160 
Size:	17.5 KB 
ID:	1371

    and this is my code

    main.cpp

    Code :
    #include <GL\glew.h>
    #include <GL\freeglut.h>
    #include <iostream>
     
    #include "ShaderManager.h"
    #include "ContentManager.h"
    #include "DebugManager.h"
     
    #pragma comment (lib, "glew32.lib")
     
    const int WIDTH = 800;
    const int HEIGHT = 600;
    const char TITLE[] = "Spark Engine";
     
    GLuint program;
    ShaderManager shader;
    DebugManager debug;
    ContentManager content;
     
    void init()
    {	
     
    	glMatrixMode(GL_PROJECTION);
    	glLoadIdentity();
    	glOrtho(-2.0, 2.0, -2.0, 2.0, -2.0, 2.0);
    	debug.openGlInfoLog();
    	program = shader.loadFromStrings(); //load vertex and fragment shader from strings	
    	debug.modelInfoLog("../cube.obj"); // print information about model that you want to load		
    	content.load("../cube.obj");	
    }
     
     
    void display()
    {
    	 // background color	
    	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    	glClearColor(1, 0, 0, 1);
    	glEnable(GL_LIGHTING);
    	glEnable(GL_LIGHT0);	
     
    	content.draw();
    	//glUseProgram(program);	
    	glDrawArrays(GL_TRIANGLES, 0, 3);
     
    	//glRotatef(1.0, 0.0, 360.0, 0.0);
     
    	glutSwapBuffers();
    }
     
     
    void reshape(GLsizei width, GLsizei height)
    {
    }
     
    void close()
    {
    	//exit(1);
    }
     
    int main(int argc, char** argv)
    {
    	glutInit(&argc, argv);
    	glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
    	glutInitContextVersion(4, 0);
    	glutInitContextFlags(GLUT_CORE_PROFILE | GLUT_DEBUG);
    	glutInitContextProfile(GLUT_FORWARD_COMPATIBLE);
     
    	glutInitWindowSize(WIDTH, HEIGHT);
    	glutCreateWindow(TITLE);
     
    	GLenum status = glewInit();
     
    	if (status != GLEW_OK)
    	{
    		std::cerr << "Glew Initialization failed: " << status << std::endl;
    		exit(1);
    	}
     
    	init();	
    	glutDisplayFunc(display);
    	glutIdleFunc(display);
    	glutReshapeFunc(reshape);
    	glutCloseFunc(close);
    	glutMainLoop();
     
    	return 0;
    }

    contentManager.h
    Code :
    #ifndef CONTENTMANAGER_H
    #define CONTENTMANAGER_H
     
    #include <string>
    #include <vector>
     
    typedef struct Model
    {
    	int vertices;	
    	int texels;
    	int normals;
    	int faces;
     
    }Model;
     
    typedef struct
    {
    	float x;
    	float y;
    	float z;
     
    }Vertex;
     
    typedef struct
    {
    	float x;
    	float y;
    	float z;
     
    }Normal;
     
    typedef struct
    {
    	int vx;
    	int vy;
    	int vz;
     
    	int vnx;
    	int vny;
    	int vnz;
     
    }Face;
     
    class ContentManager
    {
    public:
    	ContentManager();
    	~ContentManager();
     
    	Model m_load(std::string file_path);
     
    	bool load(char *file_path);
    	void draw();
     
     
    private:
    	std::vector <Vertex> vertices;
    	std::vector <Normal> normals;
    	std::vector <Face> faces;
    };
     
     
    #endif // CONTENTMANAGER_H

    ContentManager.cpp
    Code :
    #include <GL\freeglut.h>
    #include <fstream>
    #include <iostream>
    #include <string>
     
    #include "ContentManager.h"
     
    ContentManager::ContentManager()
    {
    }
     
     
    ContentManager::~ContentManager()
    {
     
    }
     
    Model ContentManager::m_load(std::string file_path)
    {
    	Model model = { 0 };
     
    	std::ifstream file;
    	file.open(file_path);
     
    	if (!file.good())
    	{
    		std::cerr << "Error opening obj file" << std::endl;
    		exit(1);
    	}
     
    	while (!file.eof())
    	{
    		std::string line;
    		getline(file, line);
     
    		std::string type = line.substr(0, 2);
     
    		if (type.compare("v ") == 0)
    			model.vertices++;
    		else if (type.compare("vt") == 0)
    			model.texels++;
    		else if (type.compare("vn") == 0)
    			model.normals++;
    		else if (type.compare("f ") == 0)
    			model.faces++;
    	}	
     
    	file.close();
     
    	return model;
    }
     
    bool ContentManager::load(char *file_path)
    {
    	std::string line;
    	std::ifstream file(file_path);
     
    	if (file.fail())
    	{
    		std::cerr << "Could not open file " << file_path << " file is illegible or doesn' t exist." << std::endl;
    		return false;
    	}
     
    	int vertex_count = 0;
    	int normal_count = 0;
    	int face_count = 0;
     
    	float fill;
     
    	while (!file.eof())
    	{
    		std::getline(file, line);
     
    		if (line.c_str()[0] == 'v')		{
     
    			if (line.c_str()[1] == ' ')
    			{
    				vertices.resize(vertex_count + 1);
     
    				sscanf(line.c_str(), "v %f %f %f", &vertices[vertex_count].x, &vertices[vertex_count].y, &vertices[vertex_count].z);
    				//std::cout << "vertex " << vertices[vertex_count].x << "," << vertices[vertex_count].y << "," << vertices[vertex_count].z << std::endl;
    				vertex_count += 1;
    			}
    			else if (line.c_str()[1] == 'n')
    			{
    				normals.resize(normal_count + 1);
     
    				sscanf(line.c_str(), "vn %f %f %f", &normals[normal_count].x, &normals[normal_count].y, &normals[normal_count].z);
    				//std::cout << "normals " << normals[normal_count].x << "," << normals[normal_count].y << "," << normals[normal_count].z << std::endl;
    				normal_count += 1;
    			}			
    		}
    		else if (line.c_str()[0] == 'f')
    		{
    			faces.resize(face_count + 1);
     
    			sscanf(line.c_str(), "f %d//%d %d//%d %d//%d", &faces[face_count].vx, &faces[face_count].vnx, &faces[face_count].vy, &faces[face_count].vny, &faces[face_count].vz, &faces[face_count].vnz);
    			//std::cout << "faces " << faces[face_count].vx << "//" << faces[face_count].vnx << "," << faces[face_count].vy << "//" << faces[face_count].vny << "," << faces[face_count].vz << "//" << faces[face_count].vnz << std::endl;
    			face_count += 1;
    		}
    	}
     
    	return true;
    }
     
    void ContentManager::draw()
    {
    	for (int i = 0; i < faces.size(); i++)
    	{
    		glBegin(GL_TRIANGLE_FAN);	
     
    		glNormal3f(normals[faces[i].vnx - 1].x, normals[faces[i].vnx - 1].y, normals[faces[i].vnx - 1].z);
    		glVertex3f(vertices[faces[i].vx - 1].x, vertices[faces[i].vx - 1].y, vertices[faces[i].vx - 1].z);
     
    		glNormal3f(normals[faces[i].vny - 1].x, normals[faces[i].vny - 1].y, normals[faces[i].vny - 1].z);
    		glVertex3f(vertices[faces[i].vy - 1].x, vertices[faces[i].vy - 1].y, vertices[faces[i].vy - 1].z);
     
    		glNormal3f(normals[faces[i].vnz - 1].x, normals[faces[i].vnz - 1].y, normals[faces[i].vnz - 1].z);
    		glVertex3f(vertices[faces[i].vz - 1].x, vertices[faces[i].vz - 1].y, vertices[faces[i].vz - 1].z);
     
    		glEnd();		
    	}	
    }

    in your opinion why have i this strange result?

  2. #2
    Intern Newbie
    Join Date
    Apr 2014
    Posts
    47
    It looks like you clear the depth buffer, but remember that depth testing is disabled by default. I'd try changing your code before your draw to:

    // background color
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glClearColor(1, 0, 0, 1);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_DEPTH_TEST); //<<<<<

    In your future programs try to not change state every time you draw if you don't need to. Those glEnable commands would be better put in your init() function since all your code here is using lighting and depth testing.

    On a different note, why are you drawing using GL_TRIANGLE_FAN? Most models are created using triangles, quads or triangle strips. If you are using models exported from blender, they are most likely quads or triangles. From the way your .OBJ model loader reads faces, you are only readings faces with 3 vertices (which would be triangles), so you should use GL_TRIANGLES instead. This is likely another source of the problem. Be careful with your model loader because .OBJ also supports quads.
    Last edited by MtRoad; 07-21-2014 at 10:02 PM.

  3. #3
    Intern Contributor
    Join Date
    Jul 2014
    Location
    Italy, Lissone
    Posts
    80
    Quote Originally Posted by MtRoad View Post
    It looks like you clear the depth buffer, but remember that depth testing is disabled by default. I'd try changing your code before your draw to:

    // background color
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glClearColor(1, 0, 0, 1);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_DEPTH_TEST); //<<<<<

    In your future programs try to not change state every time you draw if you don't need to. Those glEnable commands would be better put in your init() function since all your code here is using lighting and depth testing.

    On a different note, why are you drawing using GL_TRIANGLE_FAN? Most models are created using triangles, quads or triangle strips. If you are using models exported from blender, they are most likely quads or triangles. From the way your .OBJ model loader reads faces, you are only readings faces with 3 vertices (which would be triangles), so you should use GL_TRIANGLES instead. This is likely another source of the problem. Be careful with your model loader because .OBJ also supports quads.

    i'm sorry, i was drawing TRIANGLES_FAN because i was experimenting and i forgot to give you the correct code, for the others notes i made everything as you say so now work perfectly and i put
    in the init() function all the functions that don't need to be in the draw() function, thanks a lot

  4. #4
    Intern Newbie
    Join Date
    Apr 2014
    Posts
    47
    Quote Originally Posted by Ruggero Visitnin View Post
    i'm sorry, i was drawing TRIANGLES_FAN because i was experimenting and i forgot to give you the correct code, for the others notes i made everything as you say so now work perfectly and i put
    in the init() function all the functions that don't need to be in the draw() function, thanks a lot
    You're using a lot of older fixed-function style OpenGL. Here are some tutorials http://www.opengl-tutorial.org/ to help you out. Also, instead of writing your own loader you may want to try a free loader such as FBX by AutoDesk which supports animation (which obj doesnt).
    .

  5. #5
    Junior Member Regular Contributor
    Join Date
    Apr 2012
    Location
    Los Angeles
    Posts
    208
    Quote Originally Posted by Ruggero Visitnin View Post
    in your opinion why have i this strange result?
    I can make some debugging suggestions if you are still interested in taking the same approach. Make a copy of the obj file you are trying to render, and edit it with a text editor. Cut out all of the face (polygon) definitions except for the first 5 or so. Try to render it in wireframe. If that doesn't make things clear, use OpenGL to display the vertex numbers referred to in the 'f' lines. Good luck.

  6. #6
    Intern Newbie
    Join Date
    Apr 2014
    Posts
    47
    If I'm needing to write a model loader, I'll always check my model data by importing it in Blender to ensure it's valid, made of triangles (or strips), normals point the right way, and the textures look correct. This step is especially important to me if you are exporting something from a 3D modeler to your preferred format to ensure you exported the data correctly. I've felt really stupid a few times when I forgot to triangulate meshes from blender before export.

    I use a shader similar to the following to ensure the models normals look correct.
    Code :
    //vertex shader
    attribute vec4 position;
    attribute vec3 normal;
     
    varying lowp vec4 colorVarying;
     
    uniform mat4 modelViewProjectionMatrix;
    uniform mat3 normalMatrix;
     
    void main() {
        // normals can be from (-1,-1,-1) to (1,1,1) so bound to (0,0,0) to (1,1,1).
        vec3 boundedNormal = 0.5 * ( normal + vec3( 1 ) );
        colorVarying = vec4( boundedNormal, 1 );
        gl_Position = modelViewProjectionMatrix * position;
    }
     
    // fragment shader:
    varying lowp vec4 colorVarying;
     
    void main() {
        gl_FragColor = colorVarying;
    }

  7. #7
    Intern Contributor
    Join Date
    Jul 2014
    Location
    Italy, Lissone
    Posts
    80
    hi, it's me again
    i started working on modern opengl and i was stopped by the OBJ loader and, again, i can't really understand why this doesn't work.
    i know that the following codes aren't well written but before do things well i want to understand how to make this stuff works and if it doesn't work i want to understand why.

    thanks for your time.

    this is my hedaer for OBJ loader file
    Code :
    #ifndef CONTENTMANAGER_H
    #define CONTENTMANAGER_H
     
    #include <GL\glew.h>
    #include <GL\freeglut.h>
    #include <string>
    #include <vector>
     
     
    class ContentManager
    {
    public:
    	ContentManager(); // constructor 
    	~ContentManager(); // destructor
     
    	GLboolean load(const char *file_path);	
    	GLvoid draw();
     
     
    private:
    	typedef struct
    	{
    		GLfloat x;
    		GLfloat y;
    		GLfloat z;
     
    	}Vertex;
     
    	typedef struct
    	{
    		GLfloat x;
    		GLfloat y;
    		GLfloat z;
     
    	}Normal;
     
    	typedef struct
    	{
    		GLfloat x;
    		GLfloat y;
    	}Uv;
     
    	typedef struct
    	{
    		GLint vx;
    		GLint vy;
    		GLint vz;
     
    		GLint vnx;
    		GLint vny;
    		GLint vnz;
     
    		GLint uvx;
    		GLint uvy;
    		GLint uvz;
     
    	}Face;
     
    private:
    	std::vector <Vertex> vertices;
    	std::vector <Normal> normals;
    	std::vector <Uv> uvs;
    	std::vector <Face> faces;
     
    	std::vector <GLfloat> data;
     
    	GLboolean is_uv;
     
    	GLuint vao_id;
    	GLuint vbo_id;
    };
     
     
    #endif // CONTENTMANAGER_H

    and this is my code for OBJ loader cpp file
    Code :
    #include <fstream>
    #include <iostream>
     
    #include "ContentManager.h"
     
    ContentManager::ContentManager()
    {
    }
     
    ContentManager::~ContentManager()
    {
    }
     
    GLboolean ContentManager::load(const char *file_path)
    {
    	std::string line;
    	std::ifstream file(file_path);
     
    	is_uv = false;
     
    	if (file.fail())
    	{
    		std::cerr << "Could not open file " << file_path << " file is illegible or doesn' t exist." << std::endl;
    		return false;
    	}
     
    	GLint vertex_count = 0;
    	GLint normal_count = 0;
    	GLint face_count = 0;
    	GLint uv_count = 0;
     
    	while (!file.eof())
    	{
    		std::getline(file, line);
     
    		if (line.c_str()[0] == 'v')
    		{
    			if (line.c_str()[1] == ' ')
    			{
    				vertices.resize(vertex_count + 1);
     
    				sscanf(line.c_str(), "v %f %f %f", &vertices[vertex_count].x, &vertices[vertex_count].y, &vertices[vertex_count].z);
    				//std::cout << line << std::endl;
     
    				vertex_count += 1;
    			}
    			else if (line.c_str()[1] == 'n')
    			{
    				normals.resize(normal_count + 1);
     
    				sscanf(line.c_str(), "vn %f %f %f", &normals[normal_count].x, &normals[normal_count].y, &normals[normal_count].z);
    				//std::cout << line << std::endl;
     
    				normal_count += 1;
    			}
    			else if (line.c_str()[1] == 't')
    			{
    				is_uv = true;
     
    				uvs.resize(uv_count + 1);
     
    				sscanf(line.c_str(), "vt %f %f", &uvs[uv_count].x, &uvs[uv_count].y);
    				//std::cout << line << std::endl;
    				uv_count += 1;
    			}
    		}
    		else if (line.c_str()[0] == 'f')
    		{
    			faces.resize(face_count + 1);
     
    			if (is_uv)
    			{
    				sscanf(line.c_str(), "f %d/%d/%d %d/%d/%d %d/%d/%d", &faces[face_count].vx, &faces[face_count].uvx, &faces[face_count].vnx, &faces[face_count].vy, &faces[face_count].uvy, &faces[face_count].vny, &faces[face_count].vz, &faces[face_count].uvz, &faces[face_count].vnz);
    				//std::cout << line << std::endl;
     
    				face_count += 1;
     
    			}
    			else if (!is_uv)
    			{
    				sscanf(line.c_str(), "f %d//%d %d//%d %d//%d", &faces[face_count].vx, &faces[face_count].vnx, &faces[face_count].vy, &faces[face_count].vny, &faces[face_count].vz, &faces[face_count].vnz);
    				//std::cout << line << std::endl;
     
    				face_count += 1;
    			}
    		}
    	}
     
    	data.resize(faces.size() * 9);
    	std::cout << data.size() << std::endl;
     
    	GLint j = 0;
    	for (GLint i = 0; i < data.size(); i++)
    	{	
    		// first vertex
    		data[i] = vertices[faces[j].vx - 1].x;	std::cout << data[i] << " ";	i++;
    		data[i] = vertices[faces[j].vx - 1].y;	std::cout << data[i] << " ";	i++;
    		data[i] = vertices[faces[j].vx - 1].z;	std::cout << data[i] << " // ";	i++;
     
    		// second vertex
    		data[i] = vertices[faces[j].vy - 1].x; std::cout << data[i] << " "; i++;
    		data[i] = vertices[faces[j].vy - 1].y; std::cout << data[i] << " "; i++;
    		data[i] = vertices[faces[j].vy - 1].z; std::cout << data[i] << " // "; i++;
     
    		// third vertex
    		data[i] = vertices[faces[j].vz - 1].x; std::cout << data[i] << " "; i++;
    		data[i] = vertices[faces[j].vz - 1].y; std::cout << data[i] << " "; i++;
    		data[i] = vertices[faces[j].vz - 1].z; std::cout << data[i] << std::endl;
     
    		j++;				
    	}	
     
    	glGenVertexArrays(1, &vao_id);
     
    	glBindVertexArray(vao_id);
     
    	glGenBuffers(1, &vbo_id);
     
    	glBindBuffer(GL_ARRAY_BUFFER, vbo_id);
     
    	glBufferData(GL_ARRAY_BUFFER, sizeof(data), &data, GL_STATIC_DRAW);
     
    	return true;
    }
     
    GLvoid ContentManager::draw()
    {	
    	glEnableVertexAttribArray(0);
    	glBindBuffer(GL_ARRAY_BUFFER, vbo_id);
    	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
     
    	glDrawArrays(GL_TRIANGLES, 0, data.size() / 3);
     
    	glDisableVertexAttribArray(0);
    }

  8. #8
    Intern Newbie
    Join Date
    Apr 2014
    Posts
    47
    Don't post to the same thread for additional questions, use a different thread.


    I think you need to use data.data instead of &data. Since data is an object, &data points to the start of the object, which is not necessarily where your data is stored.

    As a side note, you don't need to keep your own counts. You can use vertices.size() to give you the number of elements in the vector.

    I'm going to try something and post back to see my version of this will work for you.

  9. #9
    Intern Contributor
    Join Date
    Jul 2014
    Location
    Italy, Lissone
    Posts
    80
    thanks a lot

  10. #10
    Junior Member Regular Contributor
    Join Date
    Apr 2012
    Location
    Los Angeles
    Posts
    208
    Quote Originally Posted by Ruggero Visitnin View Post
    hi, it's me again ...
    Hello. It's me again too. I still say you should draw the first 5 triangles in your obj file in wireframe to find your error. It looks like a topology/logic error, i.e. you're connecting the wrong vertices to make the triangles. Perhaps you are misinterpreting how the 'f' records in an OBJ file work? 'f 5 7 8', means to connect vertex 5 to vertex 7 to vertex 8 back to vertex 5 to make a triangle. 5, 7, 8 are indices into the vertex array you've read in. Keep in mind that Wavefront vertex indices start with '1', not '0'. Good luck.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •