Sign in to follow this  
gosper

.OBJ rendering

Recommended Posts

gosper    100
Hello every one. Please excuse me, I know all the questions about .obj files must get annoying. I've read every forum posting on the subject and googled extensively. Still I've found no solution to the problem. I'm working on a c++ class to load and render .obj files. My .obj file is loaded fine but the problem is when I try to render it. It comes out really messed up. It looks like I'm doing every thing correctly. I'm subtracting - 1 from my indices. To render I loop through all faces get the index for each vertex and normal then pass them to glVertex3f and glNormal3f. This is really driving me crazy. I'm developing under debian linux and using blender to export .obj files. If anyone can help me I would be forevor thankfull. Here is my code:
 // deals with loading .obj (lightwave) model format

#include "debug.h"
#include "objloader.h"
#include "common.h"

extern debug debug_object;

//TODO
//add material support
//add texture stack support
//scale and translatef model when drawing


//
// opens the file and calls parse_file()
//
void objloader::load(char *file){

	//
	// load our file
	//
	
	filep.open(file, ios::in);
	
	if(filep == NULL){
		debug_object.debug_message(ERROR, "error opening .obj file %s", file);
		
		return;
	}
	
	//
	// parse the file
	//
	parse_file();
	
	filep.close();
	
	dump_model_info();
	
	return;

}

//
// entry point into parsing mechanism
//
void objloader::parse_file(){
	char buffer[500], line[500], *arg;

	//
	//read the whole file line by line until we hit a null
	//
	while(filep != NULL){
		
		//
		// get a new line
		//
		filep.getline(buffer, 400);
	
		//
		// backup the line we just read because strtok will frag the buffer
		// its parsing
		//
		
		snprintf(line, 400, "%s", buffer);
		
		//
		// okay pop off the first part of this line
		//

		arg = strtok(line, " ");
		
		//
		// first make sure we dont have a NULL, then determine what kind of line we have
		//
		
		if(arg != NULL){
		
			//
			// if we have a v , then its a vertex
			//
			if(strcmp(arg, "v") == 0)
				read_vertex(buffer);
		
			//
			// if we have a vt then we have a texture cord, - vt u v
			//
			if(strcmp(arg, "vt") == 0)
				read_texture_cord(buffer);
		
			//
			// load normals
			//
			if(strcmp(arg, "vn") == 0)
				read_normals(buffer);
		
			//
			// if we have an f then we have a faceheader
			//
		
			if(strcmp(arg, "f") == 0)
				read_faces(buffer);
		
			
		
		} // end of null check
	} // end of while
	
	fill_object_info();
}

//
//read in a vertex
//
void objloader::read_vertex(char *buffer){
	Vector NewVector;
	char *arg;
	
	if(readface == true)
		fill_object_info();
		
	//
	// read in the x vertice by poping it off
	//
			
	arg = strtok(buffer, " ");
		
	NewVector.x = strtof(arg, NULL);
			
	//
	// read in the y vertice by popping off the next arg
	//
			
	arg = strtok(NULL, " ");	
			
	NewVector.y = strtof(arg, NULL);
			
	//
	// finally read in the z vertice
	//
			
	arg = strtok(NULL, " ");
			
	NewVector.z = strtof(arg, NULL);
				
	//
	// okay push this data onto our temprorary storage 
	//
				
	tmp_vertices.push_back(NewVector);
}

//
// read texture cord
//
void objloader::read_texture_cord(char *buffer){
	char *arg;
	Vector2d TexCord;
		
	//
	// read in the first value
	//
				
	arg = strtok(buffer, " ");
				
	TexCord.x = strtof(arg, NULL);
				
	//
	// read in the next value
	//
				
	arg = strtok(NULL, " ");
				
	TexCord.y = strtof(arg, NULL);
				
	//
	// okay now we push this information onto our tmptexture cord stack
	//
				
	tmp_texcords.push_back(TexCord);
				
	//
	// this object has an uv
	//
				
	uvflag = true;
	
}

//
// read normals
//
void objloader::read_normals(char *buffer){
	Vector NewNormals;
	char *arg;

	//
	// read in the first value
	//
	arg = strtok(buffer, " ");
				
	NewNormals.x = strtof(arg, NULL);
				
	//
	// read in next value
	//
				
	arg = strtok(NULL, " ");
				
	NewNormals.y = strtof(arg, NULL);
				
	//
	// read in final value
	//
				
	arg = strtok(NULL, " ");
				
	NewNormals.z = strtof(arg, NULL);
				
	tmp_normals.push_back(NewNormals);

}

//
// read in faces
//
void objloader::read_faces(char *buffer){
	char *arg;
	face_info NewFace;
	char one[500], two[500], three[500];
	
	//
	// if the object is texture cords
	//

	//skip over f
	arg = strtok(buffer, " ");
		
	if(uvflag == true){
				
		//
		// split each entry up v/u v/u v/u
		//
					
		arg = strtok(NULL, " ");
					
		snprintf(one, 400, "%s", arg);
				
		arg = strtok(NULL, " ");
				
		snprintf(two, 400, "%s", arg);
					
		arg = strtok(NULL, " ");
					
		snprintf(three, 400, "%s", arg);			
					
		//
		// now we finish parsing these v/u
		//
					
		//
		// first one
		//
				
		arg = strtok(one, "/");
					
		NewFace.vert_index[0] = (atoi(arg));
					
		arg = strtok(NULL, "/");
					
		NewFace.cord_index[0] = (atoi(arg));
					
		arg = strtok(NULL, " ");
					
		NewFace.normal[0] = (atoi(arg));
					
		//
		// second one
		//
					
		arg = strtok(two, "/");
					
		NewFace.vert_index[1] = (atoi(arg));
					
		arg = strtok(NULL, "/");
					
		NewFace.cord_index[1] = (atoi(arg));
					
		arg = strtok(NULL, " ");
					
		NewFace.normal[1] = (atoi(arg));
					
		//
		// third one
		//
					
		arg = strtok(three, "/");
					
		NewFace.vert_index[2] = (atoi(arg));
					
		arg = strtok(NULL, "/");
					
		NewFace.cord_index[2] = (atoi(arg));
					
		arg = strtok(NULL, " ");
					
		NewFace.normal[2] = (atoi(arg));
					
		tmp_faces.push_back(NewFace);		
	}
				
	//
	// read them in normally
	//
	else{
				
				
		arg = strtok(NULL, " ");
					
		NewFace.vert_index[0] = atoi(arg);
					
		arg = strtok(NULL, " ");
					
		NewFace.vert_index[1] = atoi(arg);
					
		arg = strtok(NULL, " ");
					
		NewFace.vert_index[2] = atoi(arg);
					
		tmp_faces.push_back(NewFace);
				
	}
				
	readface = true;
}


void objloader::fill_object_info(){
	object_info NewObject;
	int texture_offset, vertex_offset;


	//
	// increase the number of objects in our model
	//

	Model.object_num++;
	
	//
	// fill in some basic information for this object
	//
	 NewObject.faces_num = tmp_faces.size();
	 NewObject.vertices_num = tmp_vertices.size();
	 NewObject.texture_cords_num = tmp_texcords.size();
	 NewObject.normals_num = tmp_normals.size();
		
	if(NewObject.texture_cords_num > 0)
		NewObject.textured = 1;
	
	 //
	 // loop through all the faces in this object
	 //
	 for(int i = 0; i < NewObject.faces_num; i++){
	 	
		NewObject.faces.push_back(tmp_faces[i]);
	
	 }
	 
	 //
	 // copy all vertices
	 //
	 
	 for(int i = 0; i < NewObject.vertices_num; i++){
	 
	 	NewObject.verts.push_back(tmp_vertices[i]);
	 
	 }
	 
	 //
	 // copy all texture cordinates
	 //
	 
	 for(int i = 0; i < NewObject.texture_cords_num; i++){
	 
	 	NewObject.textverts.push_back(tmp_texcords[i]);
	 
	 }
	 
	 //
	 // copy all normals 
	 //
	 for(int i = 0; i < NewObject.normals_num; i++){
	 
	 	NewObject.normals.push_back(tmp_normals[i]);
	 
	 }
	 
	 //
	 // here we should call add material 
	 //
	 
	 //NewObject.material_id = id;
	 
	 
	 //
	 // push this object onto our model structure
	 //
	 
	 Model.objects.push_back(NewObject);
	 
	 
	 
	 //
	 // clear all fields
	 //
	 
	 tmp_vertices.clear();
	 
	 tmp_texcords.clear();
	 
	 tmp_faces.clear();
	 
	 uvflag = false;
	 
	 readface = false;
	 
}

//
// draw the model
//
void objloader::draw(){

	//
	// loop through each object
	//
	
	for(int i = 0; i < Model.object_num; i++){
	
		glBegin(GL_TRIANGLES);
		
		//
		// loop through all faces
		//
		
		for(int j = 0; j < Model.objects[i].faces_num; i++){
		
			//
			// go through all vertexs
			//
			for(int v = 0; v < 3; v++){
			
				int vertindex = Model.objects[i].faces[j].vert_index[v];
				int normali = Model.objects[i].faces[j].normal[v];
				
				vertindex -= 1;
				normali -= 1;
				
				glNormal3f(Model.objects[i].normals[normali].x, Model.objects[i].normals[normali].y , Model.objects[i].normals[normali].z);
				
				glVertex3f(Model.objects[i].verts[vertindex].x, Model.objects[i].verts[vertindex].y, Model.objects[i].verts[vertindex].z);
				
				
				
			
			}
		
		}
		
		glEnd();
	
	}
}



void objloader::dump_model_info(){

	//
	// print out initial info
	//
	
	cout << "dumping objstruct ------------" << endl;
	
	cout << "number of objects " << Model.object_num << endl;
	cout << "number of materials " << Model.material_num << endl;

	//
	// loop through all objects in this model
	//
	for(int i = 0; i < Model.object_num; i++){
	
		cout << "number of vertices " << Model.objects[i].vertices_num << endl;
		
		cout << "number of faces " << Model.objects[i].faces.size() << endl;
		
		cout << "number of texture cords " << Model.objects[i].texture_cords_num << endl;
	
		if(Model.objects[i].textured == 1)
			cout << "Material is textured" << endl;
			
		cout << "outputing vertices:--------" << endl;
		
		for(int j = 0; j < Model.objects[i].vertices_num; j++){
		
			cout << Model.objects[i].verts[j].x << " " << 
				Model.objects[i].verts[j].y << " " << 
				Model.objects[i].verts[j].z << endl;
		
		}
		
		cout << "outputting UV cords:------" << endl;
		
		for(int j = 0; j < Model.objects[i].texture_cords_num; j++){
		
			cout << Model.objects[i].textverts[j].x << " " 
	 		     <<  Model.objects[i].textverts[j].y << endl;
			
		
		}
		
		cout << "outputing faces:-------" << endl;
		
		int test = 0;
		
		for(int j = 0; j < Model.objects[i].faces.size(); j++){
		
			cout << "-------------------------" << endl;
		
			test++;
			cout << "looped " << test << endl;
			
			for(int k = 0; k < 3; k++){
		
				cout << "f " << Model.objects[i].faces[j].vert_index[k] <<
			    	 " " << Model.objects[i].faces[j].cord_index[k] << " " <<
			     	Model.objects[i].faces[j].normal[k] << endl;
			     
			 }
		
		}
		
		cout << "outputing normals:--------" << endl;
		
		for(int j = 0; j < Model.objects[i].normals.size(); j++){
		
				cout << Model.objects[i].normals[j].x << " " << 
				Model.objects[i].normals[j].y << " " << 
				Model.objects[i].normals[j].z << endl;
		
		}
		
	
	}

}



//
// remember to add scaling, position and rotation...
//

#include "Vector.h"

class objloader{

	public:

	// for 3d texture cordinates should put this in our vector class
	struct Vector2d {
		float x;
		float y;
		float z;
	};
	
	// used to index into texture and vertex arrays, keeps track of which vertices go to which face
	// along with texture cordinates as well
	struct face_info{
		int vert_index[3];	//verts that make up the triangle
		int cord_index[3];	//texture cords
		int normal[3];
	};
	
	// this keeps track of materials, materials contain textures and uv cords
	struct material_info{
		char texture_name[500];		//texture name 
		char texture_path[500];		//path to texture
		float color[3];			//objects color
		int texture_id;			//offset into texture stack for this texture
		float u_tile;			// u tiling for object
		float v_tile;			// v tiling for object
		float u_offset;			// u offset for texture
		float v_offset;			// v offset for texture
		
	};
	
	
	// contains all information for an object
	// models can have more than one object
	struct object_info{
		int vertices_num;	//number of verts for this object
		int faces_num;		//number of faces for this object
		int normals_num;	//number of normals
		int texture_cords_num;  //numer of texture cords
		int material_id;	//offset for material
		int textured;		//flag if object is textured
		char object_name[500];  //this objects name
		vector &lt;Vector&gt; verts;		//the objects vertices
		vector &lt;Vector&gt; normals;	//the objects normals
		vector &lt;Vector2d&gt; textverts;	//the objects UV cords
		vector &lt;face_info&gt; faces;	//face information for objects
	};
	
	//contains all the information for the model
	struct model{
		int object_num;		//number of objects
		int material_num;	//number of materials
		vector &lt;material_info&gt; materials; // list of material info
		vector &lt;object_info&gt; objects;	//list of objects in this model
	
	};
	
	fstream filep;	//our file stream
	model Model;	//or main model structure
	
	//the following are for temporary storage
	vector &lt;Vector&gt; tmp_vertices;
	vector &lt;Vector2d&gt; tmp_texcords;
	vector &lt;Vector&gt; tmp_normals;
	vector &lt;face_info&gt; tmp_faces;
	bool uvflag;
	bool readface;
	
	//loads the file
	void load(char *filename);

	//parses the obj file
	void parse_file();
	
	void read_vertex(char *buffer);
	
	void read_texture_cord(char *buffer);
	
	void read_normals(char *buffer);
	
	void read_faces(char *buffer);
	
	//fill in all information for this object
	void fill_object_info();
	
	void compute_normals();
	
	//render the model
	void draw();
	
	//dump model info
	void dump_model_info();
	
};






Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
First, did you mean to post the same code? Second, you should glBegin() inside your face loop, not outside it. Also, are you sure that all your faces are triangles? Here's some sample code:


// Loop through the model's data and render the object.
for(int i = 0; i < Model.totalFaces; i++)
{
// The current face might be a quad, polygon, or a triangle so we must
// begin rendering based on the number of vertex points there are.
if(Model.polyList[i].numPoints == 3) glBegin(GL_TRIANGLES);
else if(Model.polyList[i].numPoints == 4) glBegin(GL_QUADS);
else glBegin(GL_POLYGON);

// Loop through all the points and draw.
for(int j = 0; j < Model.polyList[i].numPoints; j++)
{
glNormal3f(Model.polyList[i].normal.x,
Model.polyList[i].normal.y,
Model.polyList[i].normal.z);

glVertex3f(Model.vertices[Model.polyList[i].Indices[j]].x,
Model.vertices[Model.polyList[i].Indices[j]].y,
Model.vertices[Model.polyList[i].Indices[j]].z);
}

glEnd();
}

SwapBuffers(...); // Display the new frame.

Share this post


Link to post
Share on other sites
gosper    100
I posted my .cpp file twice my accident. I fixed it now. No I'm not sure that all my faces are triangles. I never thought about that. I was wondering why in my .obj file there was 4 fields in the face header -> f 1/1/1 2/2/2 3/3/3 4/4/4 . Does this mean that this is a quad and not a triangle? If so are you using numPoints to count how many there are? E.G. 3 or 4?

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
Yes, the f 1/1/1 2/2/2 3/3/3 4/4/4 means it's a quad. It's a good idea to make your loader deal with more than triangles to you would need to keep track of the number of points for each face.

Go to http://www.ultimategameprogramming.com/ and check out the OpenGL .obj loading tutorial on page #8.

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