OBJ Loader Problem

Started by
9 comments, last by V-man 15 years, 9 months ago
I don't know how, but my OBJ loader is giving me problems. I've isolated the main cause to the fact that it doesn't correctly count the number of vertices or indices. The number of vertices and texcoords and normals in the file is said to be some ridiculously large number when i check the output. 382464 or so. The number of vertices in the model I'm using is roughly 180. I'm guessing its a problem with my string comparison, but I'm not sure. Can someone please look this code over and tell me whats wrong?
[source lang ="cpp"]
CModel * OGLDevice::LoadModel(std::string name)
{
		char line[256];
		FILE * fp = fopen(name.c_str(),"rt");
		int numVertices,numTexCoords,numNormals,numIndices = 0;
		unsigned int poscounter,uvcounter,normcounter;
		vector3d * tempPos;
		vector3d * tempNorm;
		vector2d * tempUV;
		unsigned int * tempFace; //To store the texcoords with the / between em
		while(!feof(fp))
		{
			fgets(line,256,fp);
			if(strncmp("v ",line,2) == 0)
			{
				numVertices += 1;
			}
			else if(strncmp("vn",line,2) == 0)
			{
				numNormals += 1;
			}
			else if(strncmp("vt",line,2) == 0)
			{
				numTexCoords += 1;
			}
			else if(strncmp("f",line,1) == 0)
			{
				numIndices += 9;
			}
			else
			{
			};
			memset(line,0,sizeof(char) * 256);
		};
		fclose(fp);
		fp = NULL;
		tempPos = new vector3d[numVertices];
		tempNorm = new vector3d[numNormals];
		tempUV = new vector2d[numTexCoords];
		tempFace = new unsigned int[numIndices];
		float x,y,z;
		memset(line,0,sizeof(char) * 256);
		fp = fopen(name.c_str(),"rt");
		while(!feof(fp))
		{
			fgets(line,256,fp);
			if(strncmp("v ",line,sizeof(char) * 2))
			{
				sscanf(line,"v %f %f %f",&x,&y,&z);
				tempPos[poscounter] = vector3d(x,y,z);
				poscounter++;
			}
			else if(strncmp("vn",line,sizeof(char) * 2))
			{
				sscanf(line,"vn %f %f %f",&x,&y,&z);
				tempNorm[normcounter] = vector3d(x,y,z);
				normcounter++;
			}
			else if(strncmp("vt",line,sizeof(char) * 2))
			{
				sscanf(line,"vt %f %f",&x,&y);
				tempUV[uvcounter] = vector2d(x,y);
				uvcounter++;
			}
			else if(strncmp("f ",line,sizeof(char) * 2))
			{
				sscanf(line,"f %u/%u/%u %u/%u/%u %u/%u/
u",tempFace[numIndices],tempFace[numIndices + 1],tempFace[numIndices +]
,tempFace[numIndices + 3],tempFace[numIndices + 4],tempFace[numIndices+5]
,tempFace[numIndices + 6],tempFace[numIndices + 7],tempFace[numIndices + 8]);
			};
			memset(line,0,sizeof(char) * 256);
		};
		memset(line,0,sizeof(char) * 256);
		fclose(fp);
		MessageBox(NULL,"Read all the data","Info",MB_OK);
		for(unsigned int i = 0;i < numIndices;i++)
		{
			tempFace -= 1;
		};
		CModel * output = new CModel;
		output->positions = new vector3d[numVertices];
		output->texcoords = new vector2d[numTexCoords];
		output->normals = new vector3d[numNormals];
		memcpy(output->positions,tempPos,sizeof(vector3d) * numVertices);
		for(i = 0;i < numIndices;i+=3)
		{
			output->texcoords[tempFace] = tempUV[tempFace];
			output-&gt;normals[tempFace] = tempNorm[tempFace];
		};
		MessageBox(NULL,<span class="cpp-literal">"Copied all the data"</span>,<span class="cpp-literal">"Info"</span>,MB_OK);
		output-&gt;indices = <span class="cpp-keyword">new</span> <span class="cpp-keyword">unsigned</span> <span class="cpp-keyword">int</span>[numIndices / <span class="cpp-number">3</span>];
		output-&gt;count = numIndices / <span class="cpp-number">3</span>;
		<span class="cpp-keyword">for</span>(i = <span class="cpp-number">0</span>;i &lt; numIndices;i += <span class="cpp-number">3</span>)
		{
			output-&gt;indices = tempFace<span style="font-weight:bold;">;
		};
		<span class="cpp-keyword">delete</span> [] tempFace;
		<span class="cpp-keyword">delete</span> [] tempUV;
		<span class="cpp-keyword">delete</span> [] tempNorm;
		<span class="cpp-keyword">delete</span> [] tempPos;
		<span class="cpp-keyword">return</span> output;
	
};


</pre></div><!–ENDSCRIPT–>

Thanks in advance.
Advertisement
Here are the big issues first:

If you enable and pay attention to your compiler's warnings, you'll immediately see that the variables numVertices, numTexCoords, numNormals, poscounter, uvcounter, and normcounter aren't being initialized.

Your code to read the vertex indices is broken because it writes off the end of the tempFace array. You're writing to tempFace[numIndices..numIndices+8] when only tempFace[0..numIndices-1] are defined.

There are also some other problems that aren't the direct cause of your current difficulties:

There's no need to read the file twice; you can do the same thing much more easily with a vector instead of dynamic arrays. By the same token, you should really be using std::string instead of character arrays.

The repeated calls to "memset(line, 0, sizeof (char) * 256);" are completely unnecessary, but if you insist on using them, bear in mind that sizeof (char) is guaranteed to be equal to 1.

You're not checking the return value from fgets, which means you're missing the error condition that occurs when you reach the end of the file. feof only returns true after you try reading past the end of the file. This is an incredibly common mistake (see the C FAQ) and it would be more obvious if you were using the C++ iostream library.

Finally, why is this code associated with a class called "OGLDevice" and not part of CModel itself?
Well, I've made some changes, but it still is not working. Can you please help me out here? As far as I know, it doesn't seem as if it scans the data properly although now it reads the correct count of indices, vertices, normals and texcoords. I'm on the verge of a breakthrough. Any help counts. Thanks in advance, and thanks @ dwhaler. Can I please get some more help?

[source lang = "cpp"]CModel * OGLDevice::LoadModel(std::string name){		char line[256];		FILE * fp = fopen(name.c_str(),"rt");		unsigned int numVertices = 0;		unsigned int numTexCoords = 0;		unsigned int numNormals = 0;		unsigned int numIndices = 0;		unsigned int poscounter = 0;		unsigned int uvcounter = 0;		unsigned int normcounter = 0;		unsigned int indcounter = 0;		vector3d * tempPos;		vector3d * tempNorm;		vector2d * tempUV;		unsigned int * tempFace; //To store the texcoords with the / between em		while(!feof(fp))		{			if(fgets(line,256,fp) == NULL)			{					continue;			};			if(strncmp("v ",line,2) == 0)			{				numVertices += 1;			}			else if(strncmp("vn",line,2) == 0)			{				numNormals += 1;			}			else if(strncmp("vt",line,2) == 0)			{				numTexCoords += 1;			}			else if(strncmp("f",line,1) == 0)			{				numIndices += 9;			}			else			{			};			memset(line,0,256);		};		rewind(fp);		tempPos = new vector3d[numVertices];		tempNorm = new vector3d[numNormals];		tempUV = new vector2d[numTexCoords];		tempFace = new unsigned int[numIndices];		float x = 0.0f;		float y = 0.0f;		float z = 0.0f;		memset(line,0,256);		fp = fopen(name.c_str(),"rt");		while(!feof(fp))		{			if(fgets(line,256,fp) == NULL)			{					continue;			};			if(strncmp("v ",line,2))			{				sscanf(line,"v %f %f %f",&x,&y,&z);				tempPos[poscounter] = vector3d(x,y,z);				poscounter++;			}			else if(strncmp("vn",line,2))			{				sscanf(line,"vn %f %f %f",&x,&y,&z);				tempNorm[normcounter] = vector3d(x,y,z);				normcounter++;			}			else if(strncmp("vt",line,2))			{				sscanf(line,"vt %f %f",&x,&y);				tempUV[uvcounter] = vector2d(x,y);				uvcounter++;			}			else if(strncmp("f",line,2))			{				sscanf(line,"f %u/%u/%u %u/%u/%u %u/%u/%u",tempFace[indcounter],tempFace[indcounter + 1],tempFace[indcounter+2],tempFace[indcounter + 3],tempFace[indcounter + 4],tempFace[indcounter+5],tempFace[indcounter + 6],tempFace[indcounter + 7],tempFace[indcounter + 8]);				indcounter += 9;			};			memset(line,0,256);		};		fclose(fp);		for(unsigned int i = 0;i < numIndices;i++)		{			tempFace -= 1;		};		CModel * output = new CModel;		output->positions = new vector3d[numVertices];		output->texcoords = new vector2d[numTexCoords];		output->normals = new vector3d[numNormals];		memcpy(output->positions,tempPos,sizeof(vector3d) * numVertices);		for(i = 0;i < numIndices;i+=3)		{			output->texcoords[tempFace] = tempUV[tempFace];<br>			output-&gt;normals[tempFace] = tempNorm[tempFace];<br>		};<br>		output-&gt;indices = <span class="cpp-keyword">new</span> <span class="cpp-keyword">unsigned</span> <span class="cpp-keyword">int</span>[numIndices / <span class="cpp-number">3</span>];<br>		output-&gt;count = numIndices / <span class="cpp-number">3</span>;<br>		<span class="cpp-keyword">for</span>(i = <span class="cpp-number">0</span>;i &lt; numIndices;i += <span class="cpp-number">3</span>)<br>		{<br>			output-&gt;indices = tempFace<span style="font-weight:bold;">;<br>		};<br>		<span class="cpp-keyword">delete</span> [] tempFace;<br>		<span class="cpp-keyword">delete</span> [] tempUV;<br>		<span class="cpp-keyword">delete</span> [] tempNorm;<br>		<span class="cpp-keyword">delete</span> [] tempPos;<br>		<span class="cpp-keyword">return</span> output;<br>};<br><br><br></pre></div><!–ENDSCRIPT–> 
Not sure how I missed this the first time around, but when you read the values into tempFace, you need to pass the addresses of the array elements to sscanf, not the array elements themselves. Again, this is something your compiler can warn you about.

Bear in mind that people will be able to help you more easily if you explain exactly what's going wrong with the code. For example, it doesn't correctly handle the case where a vertex has different normals and texture coordinates for different faces, but I'm not sure whether that's an omission or a deliberate design limitation.
It now scans the file and determines the correct number of elements though. Thanks dwhaler.

But it still isn't working. Now, I've finally managed to isolate the problem to the fact that sscanf isn't reading the values of the file data as I expected. When I do some sample output, I get lines like:

Vertex 0 0 0

Meaning that sscanf(line,"v %f %f %f",&x,&y,&z), doesn't read in the floating point data properly.Can someone please just help me pin down this problem?

And yes, I've deliberately omitted support for the cases where one vertex has different normals and texcoords.

[source lang = "cpp"]CModel * OGLDevice::LoadModel(std::string name){		char line[256];		FILE * fp = fopen(name.c_str(),"rt");		unsigned int numVertices = 0;		unsigned int numTexCoords = 0;		unsigned int numNormals = 0;		unsigned int numIndices = 0;		unsigned int poscounter = 0;		unsigned int uvcounter = 0;		unsigned int normcounter = 0;		unsigned int indcounter = 0;		vector3d * tempPos;		vector3d * tempNorm;		vector2d * tempUV;		unsigned int * tempFace; /*To store the texcoords with the / between em*/		while(!feof(fp))		{			if(fgets(line,256,fp) == NULL)			{					continue;			};			if(strncmp("v ",line,2) == 0)			{				numVertices += 1;			}			else if(strncmp("vn",line,2) == 0)			{				numNormals += 1;			}			else if(strncmp("vt",line,2) == 0)			{				numTexCoords += 1;			}			else if(strncmp("f",line,1) == 0)			{				numIndices += 9;			}			else			{			};			memset(line,0,256);		};		rewind(fp);		tempPos = new vector3d[numVertices];		tempNorm = new vector3d[numNormals];		tempUV = new vector2d[numTexCoords];		tempFace = new unsigned int[numIndices];		float x = 0.0f;		float y = 0.0f;		float z = 0.0f;		memset(line,0,256);		while(!feof(fp))		{			if(fgets(line,256,fp) == NULL)			{					continue;			};			if(strncmp("v ",line,2))			{				sscanf(line,"v %f %f %f",&x,&y,&z);				tempPos[poscounter] = vector3d(x,y,z);				poscounter++;			}			else if(strncmp("vn",line,2))			{				sscanf(line,"vn %f %f %f",&x,&y,&z);				tempNorm[normcounter] = vector3d(x,y,z);				normcounter++;			}			else if(strncmp("vt",line,2))			{				sscanf(line,"vt %f %f",&x,&y);				tempUV[uvcounter] = vector2d(x,y);				uvcounter++;			}			else if(strncmp("f",line,1))			{				sscanf(line,"f %u/%u/%u %u/%u/%u %u/%u/u",&tempFace[indcounter],&tempFace[indcounter + 1],&tempFace[indcounter+2],&tempFace[indcounter + 3],&tempFace[indcounter+4],&tempFace[indcounter+5],&tempFace[indcounter+6],&tempFace[indcounter + 7],&tempFace[indcounter + 8]);				indcounter += 9;			};			memset(line,0,256);		};		fclose(fp);		fp = NULL;		for(unsigned int i = 0;i < numIndices;i++)		{			tempFace -= 1;		};		CModel * output = new CModel;		output->positions = new vector3d[numVertices];		output->texcoords = new vector2d[numTexCoords];		output->normals = new vector3d[numNormals];		memcpy(output->positions,tempPos,sizeof(vector3d) * numVertices);		for(i = 0;i < numIndices;i+=3)		{			output->texcoords[tempFace] = tempUV[tempFace];<br>			output-&gt;normals[tempFace] = tempNorm[tempFace];<br>		};<br>		output-&gt;indices = <span class="cpp-keyword">new</span> <span class="cpp-keyword">unsigned</span> <span class="cpp-keyword">int</span>[numIndices / <span class="cpp-number">3</span>];<br>		output-&gt;count = numIndices / <span class="cpp-number">3</span>;<br>		<span class="cpp-keyword">for</span>(i = <span class="cpp-number">0</span>;i &lt; numIndices;i += <span class="cpp-number">3</span>)<br>		{<br>			output-&gt;indices = tempFace<span style="font-weight:bold;">;<br>		};<br>		<span class="cpp-keyword">delete</span> [] tempFace;<br>		<span class="cpp-keyword">delete</span> [] tempUV;<br>		<span class="cpp-keyword">delete</span> [] tempNorm;<br>		<span class="cpp-keyword">delete</span> [] tempPos;<br>		<span class="cpp-keyword">return</span> output;<br>};<br><br></pre></div><!–ENDSCRIPT–><br><br>I don't think I've ever been this stumped. I hate text based files. I prefer reading binary files.<br><br>Thanks in advance for the help.
Well, I stayed up for most of the night, and I still don't have a plausible solution. A little assistance would be great..
Note that your OBJ loader only allows triangulated models

OBJ has full support for polygon faces

f 1/1/1 2/2/2 3/3/3 4/4/4 5/5/5
I'm only going to use triangulated faces. It makes a lot of things simpler.I can ensure that all faces supplied are triangulated but my problem is that it is not reading the vertex data properly.
I've solved it. Thanks to everyone who has made criticisms. Here's the final thing.

[source lang = "cpp"]CModel * OGLDevice::LoadModel(std::string name){		char line[128];		FILE * fp = fopen(name.c_str(),"rt");		unsigned int numVertices = 0;		unsigned int numTexCoords = 0;		unsigned int numNormals = 0;		unsigned int numIndices = 0;		unsigned int poscounter = 0;		unsigned int uvcounter = 0;		unsigned int normcounter = 0;		unsigned int indcounter = 0;		vector3d * tempPos;		vector3d * tempNorm;		vector2d * tempUV;		unsigned int * tempFace; /*To store the texcoords with the / between em*/		while(!feof(fp))		{			if(fgets(line,128,fp) == NULL)			{					continue;			};			if(strncmp("v ",line,2) == 0)			{				numVertices += 1;			}			else if(strncmp("vn",line,2) == 0)			{				numNormals += 1;			}			else if(strncmp("vt",line,2) == 0)			{				numTexCoords += 1;			}			else if(strncmp("f",line,1) == 0)			{				numIndices += 9;			}			else			{			};			memset(line,0,128);		};		rewind(fp);		tempPos = new vector3d[numVertices];		tempNorm = new vector3d[numNormals];		tempUV = new vector2d[numTexCoords];		tempFace = new unsigned int[numIndices];		float x = 0.0f;		float y = 0.0f;		float z = 0.0f;		char bdata[512];		while(!feof(fp))		{			if(fgets(line,128,fp) == NULL)			{					continue;			};			if(strncmp("v",line,1) == 0)			{				sscanf(line,"v %f %f %f",&x,&y,&z);				tempPos[poscounter] = vector3d(x,y,z);				poscounter++;				sprintf(bdata,"Vertex: %f %f %f",x,y,z);				MessageBox(NULL,bdata,"Info",MB_OK);			}			else if(strncmp("vn",line,2) == 0)			{				sscanf(line,"vn %f %f %f",&x,&y,&z);				tempNorm[normcounter] = vector3d(x,y,z);				normcounter++;			}			else if(strncmp("vt",line,2) == 0)			{				sscanf(line,"vt %f %f",&x,&y);				tempUV[uvcounter] = vector2d(x,y);				uvcounter++;			}			else if(strncmp("f",line,1) == 0)			{				sscanf(line,"f %u/%u/%u %u/%u/%u %u/%u/%u",&tempFace[indcounter],&tempFace[indcounter+1],&tempFace[indcounter+2],&tempFace[indcounter+3],&tempFace[indcounter+4],&tempFace[indcounter+5],&tempFace[indcounter+6],&tempFace[indcounter + 7],&tempFace[indcounter + 8]);				indcounter += 9;			}			else			{			};			memset(line,0,128);		};		fclose(fp);		fp = NULL;		for(unsigned int i = 0;i < numIndices;i++)		{			tempFace -= 1;		};		CModel * output = new CModel;		output->positions = new vector3d[numVertices];		output->texcoords = new vector2d[numTexCoords];		output->normals = new vector3d[numNormals];		memcpy(output->positions,tempPos,sizeof(vector3d) * numVertices);		for(i = 0;i < numIndices;i+=3)		{			output->texcoords[tempFace] = tempUV[tempFace];<br>			output-&gt;normals[tempFace] = tempNorm[tempFace];<br>		};<br>		output-&gt;indices = <span class="cpp-keyword">new</span> <span class="cpp-keyword">unsigned</span> <span class="cpp-keyword">int</span>[numIndices / <span class="cpp-number">3</span>];<br>		output-&gt;count = numIndices / <span class="cpp-number">3</span>;<br>		<span class="cpp-keyword">for</span>(i = <span class="cpp-number">0</span>;i &lt; numIndices;i += <span class="cpp-number">3</span>)<br>		{<br>			output-&gt;indices = tempFace<span style="font-weight:bold;">;<br>		};<br>		<span class="cpp-keyword">delete</span> [] tempFace;<br>		<span class="cpp-keyword">delete</span> [] tempUV;<br>		<span class="cpp-keyword">delete</span> [] tempNorm;<br>		<span class="cpp-keyword">delete</span> [] tempPos;<br>		<span class="cpp-keyword">return</span> output;<br>};<br></pre></div><!–ENDSCRIPT–> 
Quote:Original post by GavRobbs
I've solved it. Thanks to everyone who has made criticisms. Here's the final thing.

*** Source Snippet Removed ***


Where are you OBJ's going to be coming from?
We also only use tri faces; however, the obj format doesn't require there to be vertex normal and texture.

faces can be in the formats
f 1/1/1 2/2/2 3/3/3 4/4/4 ... [v/vt/vn]
f 1//1 2//2 3//3 4//4 ... [v//vn]
f 1 2 4 4 ... [v]

Art of Illusion and 3DSMax6 are notorious for not saving out either normals and or texcoords. In these cases we compute our own. Do you plan on detecting these different formats?

This topic is closed to new replies.

Advertisement