parsing .obj files

Started by
7 comments, last by Ximmer 15 years, 1 month ago
Hello, I just want to know, if you use an .obj parser, how long would it take your game to parse a 5mb file. because for me it takes a minute or two, I think this is too slow but I am not sure. I run my game not from IDE, but from the final release folder. I tried to load a 30mb file and after waiting about 20 minutes of hopeless loading black screen I gave up. Is there any way to speed things up? thanks
Advertisement
To parse file, it should take between 100 and 500ms, majority of which time would be spent waiting for disk reads. In memory, it should be well under 100ms.

However - such files are usually used to construct something, and allocating that data may take time.

Quote:Is there any way to speed things up?


First step is to use a profiler to determine where the bottlenecks lie.
thanks for reply, could you maybe provide me with a link for a profiler?
this is a new concept for me, I don't know what people normally use
Sounds like you must be doing something pretty inefficiently for it to take that long. If you post your loading code we might be able to help more.
scottrick49
This is the loading code, its mostly code I read on this forum a while ago, im learning from it. the actual code loads the model very well. but slow :(

bool model::Load(char * objfile){	char buffer[256];	strcpy(filename, objfile);	FILE * file = fopen(filename, "r");	if(file == NULL)	{		return false;	}	while(fscanf(file, "%s", buffer) != EOF)	{		if(!strcmp("#", buffer))skipComment(file);		if(!strcmp("mtllib", buffer))loadMaterialLib(file);		if(!strcmp("v", buffer))loadVertex(file);		if(!strcmp("vt", buffer))loadTexCoord(file);		if(!strcmp("vn", buffer))loadNormal(file);		if(!strcmp("f", buffer))loadFace(file);		if(!strcmp("s", buffer))fscanf(file, "%s", buffer); 		if(!strcmp("usemtl", buffer))useMaterial(file);	}		fclose(file);	loaded = true;	return true;}void model::useMaterial(FILE * file){	char buffer[256];	mnode * cursor = mfirst;	fscanf(file, "%s", buffer);	while(strcmp(buffer, cursor->data.name))		cursor= cursor->next;	mcurrent = cursor;}void model::skipComment(FILE * file){	char buffer[256];	fgets(buffer, 256, file);}bool model::loadMaterialLib(FILE * file){	char * wd = strtok(filename, "/");	char buffer[256];	fscanf(file, "%s", buffer);	sprintf(mtllib, "%s/%s", wd, buffer);	strcpy(directory, wd);	FILE * lib = fopen(mtllib, "r");	if(lib == NULL)	{		return false;	}	else loadMaterials(lib);	fclose(lib);	return true;}void model::loadMaterials(FILE * file){		char parameter[32];	mnode * temp;	while(fscanf(file, "%s", parameter) != EOF)	{				if(!strcmp("newmtl", parameter))		{			temp = new mnode();			fscanf(file, "%s", temp->data.name);		}		if(!strcmp("illum", parameter))			fscanf(file, "%i", &temp->data.illum);		if(!strcmp("map_Kd", parameter))		{			fscanf(file, "%s", temp->data.map_Kd);			if(strstr(temp->data.map_Kd, ".bmp") != NULL)			{				char buffer[256];				sprintf(buffer, "%s/%s", directory, temp->data.map_Kd);				//#ifdef __GL_H__				//temp->data.texture = new unsigned int();				//loadTexture(buffer, temp->data.texture, 1);				//#endif			}		}		if(!strcmp("Ni", parameter))			fscanf(file, "%f", &temp->data.Ni);		if(!strcmp("Kd", parameter))			fscanf(file, "%f %f %f", &temp->data.Kd[0],&temp->data.Kd[1],&temp->data.Kd[2]);		if(!strcmp("Ka", parameter))			fscanf(file, "%f %f %f", &temp->data.Ka[0],&temp->data.Ka[1],&temp->data.Ka[2]);		if(!strcmp("Tf", parameter))		{			fscanf(file, "%f %f %f", &temp->data.Tf[0],&temp->data.Tf[1],&temp->data.Tf[2]);			if(mfirst == NULL)			{				mfirst = temp;				mcurrent = temp;				mfirst->next = NULL;			}			else			{				mcurrent->next = temp;				mcurrent = mcurrent->next;				mcurrent->next = NULL;			}					}	}	}bool model::loadVertex(FILE * file){	vnode * temp = new vnode();	fscanf(file, "%f %f %f", &temp->data.x, &temp->data.y, &temp->data.z);	temp->data.index = vindex;	if(vfirst == NULL)	{		vfirst = temp;		vcurrent = temp;		vfirst->next = NULL;	}	else	{		vcurrent->next = temp;		vcurrent = vcurrent->next;		vcurrent->next = NULL;	}	vindex++;	return true;}bool model::loadTexCoord(FILE * file){	tnode * temp = new tnode();	fscanf(file, "%f %f", &temp->data.u, &temp->data.v);	temp->data.index = tindex;	temp->next = NULL;	if(tfirst == NULL)	{		tfirst = temp;		tcurrent = temp;	}	else	{		tcurrent->next = temp;		tcurrent = tcurrent->next;	}	tindex++;	return true;}bool model::loadNormal(FILE * file){	vnode * temp = new vnode();	fscanf(file, "%f %f %f", &temp->data.x, &temp->data.y, &temp->data.z);	temp->data.index = nindex;	temp->next = NULL;	if(nfirst == NULL)	{		nfirst = temp;		ncurrent = temp;	}	else	{		ncurrent->next = temp;		ncurrent = ncurrent->next;	}	nindex++;	return true;}bool model::loadFace(FILE * file){	fnode * temp = new fnode();	temp->mat = mcurrent;	vnode * vcursor = vfirst;	tnode * tcursor = tfirst;	vnode * ncursor = nfirst;	unsigned int v_index[3], t_index[3], n_index[3];	for(int i = 0; i < 3; i++)	{		vcursor = vfirst;		tcursor = tfirst;		ncursor = nfirst;		fscanf(file, "%i/%i/%i", &v_index, &t_index, &n_index);				for(int v = 1; v != v_index; v++)			vcursor = vcursor->next;		temp->data.x = vcursor->data.x;		temp->data.y = vcursor->data.y;		temp->data.z = vcursor->data.z;		for(int k = 1; k != t_index; k++)			tcursor = tcursor->next;		temp->data.u = tcursor->data.u;		temp->data.v = tcursor->data.v;				for(int j = 1; j != n_index; j++)			ncursor = ncursor->next;		temp->data.a = ncursor->data.x;		temp->data.b = ncursor->data.y;		temp->data.c = ncursor->data.z;	}		temp->next = NULL;	if(ffirst == NULL)	{		ffirst = temp;		fcurrent = temp;		ffirst->next = NULL;	}	else	{		fcurrent->next = temp;		fcurrent = fcurrent->next;		fcurrent->next = NULL;	}	faces++;	return true;}void model::draw(GLuint textureToUse){	if(loaded)	{		fnode * fcursor = ffirst;		glBindTexture(GL_TEXTURE_2D, textureToUse);		glColor4f(1.0f, 1.0f, 1.0f, 1.0f);		glBegin(GL_TRIANGLES);		while(fcursor != NULL)		{			glTexCoord2f(fcursor->data.u[0], fcursor->data.v[0]);			glNormal3f(fcursor->data.a[0], fcursor->data.b[0], fcursor->data.c[0]);			glVertex3f(fcursor->data.x[0], fcursor->data.y[0], fcursor->data.z[0]);			glTexCoord2f(fcursor->data.u[1], fcursor->data.v[1]);			glNormal3f(fcursor->data.a[1], fcursor->data.b[1], fcursor->data.c[1]);			glVertex3f(fcursor->data.x[1], fcursor->data.y[1], fcursor->data.z[1]);			glTexCoord2f(fcursor->data.u[2], fcursor->data.v[2]);			glNormal3f(fcursor->data.a[2], fcursor->data.b[2], fcursor->data.c[2]);			glVertex3f(fcursor->data.x[2], fcursor->data.y[2], fcursor->data.z[2]);			fcursor = fcursor->next;		}		glEnd();	}}
There's nothing in that code that would warrant 1-20 minute loading times.

What kind of hardware are you testing on?
Are you sure it's release build?

Comparable code I used for test on a mid-range dual core AMD - tokenizing 5Mb obj file takes 160ms. Tokenizing a 24Mb obj file takes 1200ms. That means parsing strings, but not allocating structures.

In debug, the times are 430 and 3100 respectively.

I simply don't see anything that could possibly take minutes.
If you want to use a profiler take a look into this: Code Analyst. Its a free profiler which you can use under Windows. It brings also a good documentation with it.

Kimmi
A complicate solution may indicate a not understood problem.


[twitter]KimKulling[/twitter]
hardware is amd athlon single core 64 3700, windows xp 32bit, 2gb ram, 8800 gts 640mb

I tried running the release build from another OS, which doesnt have visual studio installed and has barely anything installed, fastest time to load 2.5mb obj file was 11 seconds. when I tried with a 10mb file it was loading very slow, was 99% cpu usage and process memory was rising slowly
from a quick glance over the code something that sticks out to me as a potential bottleneck is that your storing your vertecies, normals, and tex coords in linked lists.

Then in the loadFace function you have to search for the correct vertex, normal, and uv coord for each face for every vertex.

While the vertex count should be easy to determine from a model, the normal and uv count could baloon into astronomical numbers (worst case count would be faces * 3)

I would recommend just using something like std::vector<vnode *> for temporarily storing the vertecies normals and uv's until your done loading the model and then discard them when your done loading. This would take your face building function from O(n) to O(1) and should greatly help with your loading speeds as well as make the code easier to read.

This topic is closed to new replies.

Advertisement