Sign in to follow this  

parsing .obj files

This topic is 3194 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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[i], &t_index[i], &n_index[i]);

for(int v = 1; v != v_index[i]; v++)
vcursor = vcursor->next;
temp->data.x[i] = vcursor->data.x;
temp->data.y[i] = vcursor->data.y;
temp->data.z[i] = vcursor->data.z;

for(int k = 1; k != t_index[i]; k++)
tcursor = tcursor->next;
temp->data.u[i] = tcursor->data.u;
temp->data.v[i] = tcursor->data.v;

for(int j = 1; j != n_index[i]; j++)
ncursor = ncursor->next;
temp->data.a[i] = ncursor->data.x;
temp->data.b[i] = ncursor->data.y;
temp->data.c[i] = 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();
}
}

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites

This topic is 3194 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

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