Advertisement

loading obj files help

Started by April 06, 2005 06:32 PM
18 comments, last by RobTheBloke 19 years, 4 months ago
ok im trying to load obj files and display them in opengl. exporting from lightwave gives them a format like this.

####
#
#	OBJ File Generated by LightWave 3D(TM)
#	LightWave 3D OBJ Export v2.1
#
####
#	Object: 2
#
#	                Vertices: 8
#	                  Points: 0
#	                   Lines: 0
#	                   Faces: 12
#	               Materials: 1
#
####

o 2

#	             Vertex list

v -0.60960 -0.60960 0.63500
v 0.60960 -0.60960 0.63500
v 0.60960 -0.60960 -0.63500
v -0.60960 -0.60960 -0.63500
v -0.60960 0.60960 0.63500
v 0.60960 0.60960 0.63500
v 0.60960 0.60960 -0.63500
v -0.60960 0.60960 -0.63500

#	               Face list

usemtl Default
f 4 2 1
f 4 3 2
f 5 1 2
f 6 5 2
f 3 6 2
f 7 6 3
f 5 4 1
f 5 8 4
f 8 5 6
f 7 8 6
f 8 3 4
f 8 7 3

#	             End of file


seems pretty easy, there are a few problems. i have gotten it to read separate files that are like this:

#	                Vertices: 8
v -0.60960 -0.60960 0.63500
v 0.60960 -0.60960 0.63500
v 0.60960 -0.60960 -0.63500
v -0.60960 -0.60960 -0.63500
v -0.60960 0.60960 0.63500
v 0.60960 0.60960 0.63500
v 0.60960 0.60960 -0.63500
v -0.60960 0.60960 -0.63500


and this:

#  Faces: 12
f 4 2 1
f 4 3 2
f 5 1 2
f 6 5 2
f 3 6 2
f 7 6 3
f 5 4 1
f 5 8 4
f 8 5 6
f 7 8 6
f 8 3 4
f 8 7 3


but if any lines are out of order or if i combine these into the same file like above nothing will load. i kinda based my code on lesson 25. here are the two loading functions for vertices and faces. it only loads all triangles.

void readstr(FILE *f,char *string)						// Reads A String From File (f)
{
	do													// Do This
	{
		fgets(string, 255, f);							// Gets A String Of 255 Chars Max From f (File)
	} while ((string[0] == '/') || (string[0] == '\n'));// Until End Of Line Is Reached
	return;												// Return
}

void objload(char *name,OBJECT *k)						// Loads Object From File (name)
{
	int		ver;										// Will Hold Vertice Count
	float	rx,ry,rz;									// Hold Vertex X, Y & Z Position
	FILE	*filein;									// Filename To Open
	char	oneline[255];								// Holds One Line Of Text (255 Chars Max)

	filein = fopen(name, "rt");							// Opens The File For Reading Text In Translated Mode
														
	readstr(filein,oneline);							// Jumps To Code That Reads One Line Of Text From The File
	sscanf(oneline, "# Vertices: %d\n", &ver);			// Scans Text For "Vertices: ".  Number After Is Stored In ver
	k->verts=ver;										// Sets Objects verts Variable To Equal The Value Of ver
	objallocate(k,ver);									// Jumps To Code That Allocates Ram To Hold The Object

	for (int i=0;i<ver;i++)								// Loops Through The Vertices
	{
		readstr(filein,oneline);						// Reads In The Next Line Of Text
		sscanf(oneline, "v %f %f %f", &rx, &ry, &rz);		// Searches For 3 verteces, Floating Point Numbers, Store In rx,ry & rz
		k->points.x = rx;							// Sets Objects (k) points.x Value To rx
		k->points.y = ry;							// Sets Objects (k) points.y Value To ry
		k->points.z = rz;							// Sets Objects (k) points.z Value To rz
	}
	fclose(filein);										// Close The File

	if(ver>maxver) maxver=ver;							// If ver Is Greater Than maxver Set maxver Equal To ver

}														// Keeps Track Of Highest Number Of Vertices Used In Any Of The

void objfindfaces(char *name,FACES *f)						// Loads Object From File (name)
{
	int		faces;										// Will Hold face Count
	float	one,two,three;									// Hold Vertices
	FILE	*filein;									// Filename To Open
	char	oneline[255];								// Holds One Line Of Text (255 Chars Max)

	filein = fopen(name, "rt");							// Opens The File For Reading Text In Translated Mode
														
	readstr(filein,oneline);							// Jumps To Code That Reads One Line Of Text From The File
	sscanf(oneline, "# Faces: %d\n", &faces);			// Scans Text For "Vertices: ".  Number After Is Stored In ver
	f->faces=faces;										// Sets FACES faces Variable To Equal The Value Of faces
	faceallocate(f,faces);									// Jumps To Code That Allocates Ram To Hold The Object

	for (int i=0;i<faces;i++)								// Loops Through The faces
	{
		readstr(filein,oneline);						// Reads In The Next Line Of Text
		sscanf(oneline, "f %f %f %f", &one, &two, &three);		// Searches For 3 vertices, Floating Point Numbers, Store In one,two & three
		f->facenumber.one = one-1;							
		f->facenumber.two = two-1;							
		f->facenumber.three = three-1;							
	}
	fclose(filein);										// Close The File

						

}	

what is my problem? shouldn't i be able to read from the original file? oh and my other questions. what do i do about normals? and textures? thanks
ok now i have changed it now it loads files in this format.
#	                Vertices: 8#	                   Faces: 12v -0.60960 -0.60960 0.63500v 0.60960 -0.60960 0.63500v 0.60960 -0.60960 -0.63500v -0.60960 -0.60960 -0.63500v -0.60960 0.60960 0.63500v 0.60960 0.60960 0.63500v 0.60960 0.60960 -0.63500v -0.60960 0.60960 -0.63500f 4 2 1f 4 3 2f 5 1 2f 6 5 2f 3 6 2f 7 6 3f 5 4 1f 5 8 4f 8 5 6f 7 8 6f 8 3 4f 8 7 3

,but i still can't load normal files. which i guess isn't too bad since it is just a few lines to be removed, but it's the principle

here is the load code:
void readstr(FILE *f,char *string)						// Reads A String From File (f){	do													// Do This	{		fgets(string, 255, f);							// Gets A String Of 255 Chars Max From f (File)	} while ((string[0] == '/') || (string[0] == '\n'));// Until End Of Line Is Reached	return;												// Return}void objload(char *name,OBJECT *k, FACES *f)						// Loads Object From File (name){	int		ver;										// Will Hold Vertice Count	float	rx,ry,rz;									// Hold Vertex X, Y & Z Position	FILE	*filein;									// Filename To Open	char	oneline[255];								// Holds One Line Of Text (255 Chars Max)	int		faces;										// Will Hold Vertice Count	float	one,two,three;	filein = fopen(name, "rt");							// Opens The File For Reading Text In Translated Mode														// CTRL Z Symbolizes End Of File In Translated Mode	readstr(filein,oneline);							// Jumps To Code That Reads One Line Of Text From The File	sscanf(oneline, "# Vertices: %d\n", &ver);			// Scans Text For "Vertices: ".  Number After Is Stored In ver	k->verts=ver;										// Sets Objects verts Variable To Equal The Value Of ver	objallocate(k,ver);									// Jumps To Code That Allocates Ram To Hold The Object	readstr(filein,oneline);							// Jumps To Code That Reads One Line Of Text From The File	sscanf(oneline, "# Faces: %d\n", &faces);			// Scans Text For "Vertices: ".  Number After Is Stored In ver	f->faces=faces;										// Sets Objects verts Variable To Equal The Value Of ver	faceallocate(f,faces);		for (int i=0;i<ver;i++)								// Loops Through The Vertices	{		readstr(filein,oneline);						// Reads In The Next Line Of Text		sscanf(oneline, "v %f %f %f", &rx, &ry, &rz);		// Searches For 3 verteces, Floating Point Numbers, Store In rx,ry & rz		k->points.x = rx;							// Sets Objects (k) points.x Value To rx		k->points.y = ry;							// Sets Objects (k) points.y Value To ry		k->points.z = rz;							// Sets Objects (k) points.z Value To rz	}			for (int i=0;i<faces;i++)								// Loops Through The faces	{		readstr(filein,oneline);						// Reads In The Next Line Of Text		sscanf(oneline, "f %f %f %f", &one, &two, &three);		// Searches For 3 vertices		f->facenumber.one = one-1;									f->facenumber.two = two-1;									f->facenumber.three = three-1;								}		fclose(filein);										// Close The File	if(ver>maxver) maxver=ver;							// If ver Is Greater Than maxver Set maxver Equal To ver}
Advertisement
i am stuck here do i need to make the program skip the other lines or what? if so, how do i do this? and about normals and texturing? im really lost, can i get some help here? i really don't know what to do next.

thanks in advance.
FILE* fp = fopen(filename,"r");int faceCount=0,vertexCount=0,normalCount=0,uvcount=0;char buffer[512];while(!feof(fp)) {    fscanf(fp,"%s",buffer);    if(buffer[0] == '#')       fgets(buffer,512,fp);    else    if(buffer[0] == 'f')       ++faceCount;        else    if(buffer[0] == 'v') {       if(buffer[1]=='t')           ++uvcount;       else       if(buffer[1]=='n')          ++normalCount;       else          ++vertexCount;    }}// return to start of filerewind(fp);// allocate the data for the vers, normals etc.// Then re-read the data from the file.ie, if vertex found,    fscanf(fp,"%f%f%f",&x,&y,&z);


To read the individual face indices you'll have to parse the line data. I do something like this to handle the f 1 2 3 types and the f 1/1/1 2/2/2 3/3/3 style.

/// this stores all face indices for a face in an obj filestruct Face {	// number of indices	int nVerts;		// array of vertex indices	int* pVerts;		// array of normal indices of NULL	int* pNorms;		// array of uv indices of NULL	int* pUvs;};/// reads an indexchar* ReadInt(char* p,int* val) {	if(isdigit(*p)) {		val=0;		while(isdigit(*p)) {			val *= 10;			val += (*p) - '0';			++p;		}	}	else {		*val=-1;	}}/// assumes you just read 'f' and want the rest of the data read in.void ReadFace(FILE* fp,Face* outface) {	char buffer[512];	char* ptr = buffer;	int vi=-1;	int ni=-1;	int ti=-1;		// read the line data		fgets(buffer,512,fp);		while(1) {			// if at end of line, finish		if(*ptr =='\n')			return;			// skip white spaces		while(*ptr==' ' || *ptr=='\t') {			++ptr;						// if at end of line, finish			if(*ptr =='\n')				return;		}				// read vertex index		ptr = ReadInt(ptr,&vi);				// increase number of verts		++outface->nVerts;				// reallocate the vertex indices		outface->pVerts = (int*)realloc( outface->pVerts, sizeof(int)*outface->nVerts );		outface->pVerts[outface->nVerts-1] = vi;				// if / found, we have uv and/or normal indices to read		if(*ptr =='/') {						// skip slash			++ptr;						// grab uv index			ptr = ReadInt(ptr,&ti);						// skip slash			++ptr						// read normal index			ptr = ReadInt(ptr,&ni);						// reallocate uv indices and assign new UV value			if(ti != -1) {				outface->pUvs = (int*)realloc( outface->pUvs, sizeof(int)*outface->nVerts );				outface->pUvs[outface->nVerts-1] = ti;			}						// reallocate normal indices and assign new value			if(ni != -1) {				outface->pNorms = (int*)realloc( outface->pNorms, sizeof(int)*outface->nVerts );				outface->pNorms[outface->nVerts-1] = ni;			}		}		}}


Hey dude!!

Look like we are doing the same thing..Im up to reading in .objs too.

I'll have a look thru your code soon and see what Im doing.


How are you planning to store your model format once its read in (see my thread about "Storing model format"

-LW
-------------------LordsWarrior-------------------
Probably useless but here's a BASIC program I wrote to load L3d OBJ files.

http://rel.betterwebber.com/junk.php?id=20

Hi.
Advertisement
TO ROb the bloke

I wondered about this .

I see that you scan the file first to count the vertices and face..then make the arrays to hold them with the correct length.

I know a number of programs that take 2 passes at reading in an obj so I guess this is why.

I might try this too.

-LW
-------------------LordsWarrior-------------------
Good thing. now I can learn how to convert .obj file as well. keep up the good work
Quote: Original post by LordsWarrior
TO ROb the bloke

I wondered about this .

I see that you scan the file first to count the vertices and face..then make the arrays to hold them with the correct length.

I know a number of programs that take 2 passes at reading in an obj so I guess this is why......


Well, in actual fact, i don't do things like that at all :|

I only bother counting the number of elements if i'm writing C-code, since re-allocating memory in C is a pain. Normally i just use C++ std::vectors to hold the data and just push_back() for each new element. I also don't bother storing faces, instead all faces are triangulated as they are read in, and pushed to the back of a triangles array.

i've got a step-by-step C code version of an objloader on my website (final version uses VBO's, tangent+bi-normal calculation plus a few other goodies). I doubt the code is the most readable ever, but errr....

link

I'll hunt down a C++ version and post that up.
i wasn't planning on saving the files as another format, but i just store them as vertexes and faces. i was just going to use them as they are. i need to do something different because this method is a pain in the ass. i have to edit the files before i can load them. i think i'm going to go with something closer to robs method so it will skip the unneeded lines, but i'm not sure. anyway does anyone know how to get the texture coordinates. i can't save textures with the files so i'm probably going to add an extra line to my obj file that has the adress of the texture and get the coordinates within the program, but how do i do this? i'm pretty new so if im asking an obvious question i'm sorry.

This topic is closed to new replies.

Advertisement