.obj MESH loader

Started by
11 comments, last by SolDirix 7 years, 9 months ago

Im programming in C++ Directx 11 win32 and was trying to make a mesh loader from a .OBJ file.

My .obj looks like


v 1.000000 -1.000000 -1.000000

v 1.000000 -1.000000 1.000000

v -1.000000 -1.000000 1.000000

v -1.000000 -1.000000 -1.000000

v 1.000000 1.000000 -0.999999

v 0.999999 1.000000 1.000001

v -1.000000 1.000000 1.000000

v -1.000000 1.000000 -1.000000

vn 0.0000 -1.0000 0.0000

vn 0.0000 1.0000 0.0000

vn 1.0000 -0.0000 0.0000

vn 0.0000 -0.0000 1.0000

vn -1.0000 -0.0000 -0.0000

vn 0.0000 0.0000 -1.0000

f 2//1 4//1 1//1

f 8//2 6//2 5//2

f 5//3 2//3 1//3

f 6//4 3//4 2//4

f 3//5 8//5 4//5

f 1//6 8//6 5//6

f 2//1 3//1 4//1

f 8//2 7//2 6//2

f 5//3 6//3 2//3

f 6//4 7//4 3//4

f 3//5 7//5 8//5

f 1//6 4//6 8//6

*was done in blender.

For each indices, I'm making a Vertice. So I have 36 total vertices with 36 normal that I'm generating from the faces part "f".

My mesh is not coming out right and I wanted to know is this the proper way? creating 36 verticies?

need some advice..

Advertisement
Well, since you did not post any codes or described the problem in any detail this will be difficult to debug.

However, this is about Wavefront .obj files, so the obvious question first: did you account for the fact that Wavefront .obj files assume indices to be one-based?

Edit: Also, .obj files allow different indices for different attributes. Neither OpenGL nor DirectX allow that, so you will have to generate a different vertex for every distinct (vertex index, normal index) pair.

Yeah i know that .obj start at 1 for their indices/texture/normal in the face area

I subtracted 1 from every value and loaded those into the Position and normals

Yes, allowing face vertices to index normals, texture coordinates, and positions separately pretty much forces you to create a different vertex for each face vertex for rendering. If you can, you can make it a prerequisite that all meshes be triangulated when exported. That should make writing the loader easier. I wrote such a loader but for D3D9 a few years ago.

That said, obj is a bad choice of mesh exchange format in most cases. Switch to FBX.


for(;;)
    {
	

	   fin >> strCommand;
	   if(!fin)
		   break;
  
   strcpy(str1v1, "v");
   strcpy(str2v1, strCommand);

   ret = strcmp(str1v1, str2v1);

   if(ret == 0)
   {
	   
	   fin >> vertices[test].sPos.x >> vertices[test].sPos.y >> vertices[test].sPos.z;
	   test++;
   }

  
   strcpy(str1v2, "vn");
   strcpy(str2v2, strCommand);

   ret4 = strcmp(str2v2, str2v2);

   if(ret4 == 0)
   {
	   fin >> vertices[test2].sNor.x >> vertices[test2].sNor.y >> vertices[test2].sNor.z;
	   test2++;
   }
 

   strcpy(str1v3, "f");
   strcpy(str2v3, strCommand);
   
  
   ret5 = strcmp(str1v3, str2v3);
  
   if(ret5 == 0)
   {
	   for(int i = 0;i<3;i++)
			{
				
            fin >> ind[in];
			 if( '/' == fin.peek() )
                {
                    fin.ignore();

                    if( '/' != fin.peek() )
                    {
                        fin.ignore();
                    }

                    if( '/' == fin.peek() )
                    {
                        fin.ignore();

                     }
					 if( '/' != fin.peek() )
                    {
                        fin >> nor2[in];
                    }
					
	         }
			
		in++;
                

  }
  }
  }
	for(int i = 0;i<36;i++)
  {
		indices[i] = (ind[i] - 1);
  }
	
	 for(int i = 0;i<36;i++)
   {
	  
	   vertices[i].Pos.x = vertices[indices[i]].sPos.x;
	   vertices[i].Pos.y = vertices[indices[i]].sPos.y;
	   vertices[i].Pos.z = vertices[indices[i]].sPos.z;
   } 

   ret4 = strcmp(str2v2, str2v2);

ret4 will always equal 0 here.

Hello to all my stalkers.

bad mistake when cleaning up code to readable. but still not working

You could use https://github.com/syoyo/tinyobjloader, will save you a bunch of time.

I haven't tried OBJ files. I built my own first thing. But you can look at it and tell what it is. Your model is a cube. You've defined 8 vertices. The number of normals is 6, which would be one per cube face. So, it's lying when it says those are vertex normals. They are face normals, which explains a bit about how Blender makes cubes and why they are flat shaded. With 12 faces, that must mean that we are talking about triangle faces, not polygon faces, which is a switch from the normals. So, that's a bit schizophrenic. But it's easy enough. Not sure exactly why they did it that way, but it is what it is.

Oh. When you look at the faces they are actually indices although the number after the // gives the face number it belongs to. So, you can assign the face normal to all vertices in that face. This is going to be bad when you have a model more complicated than a cube because it will result in a flat shaded model instead of a smooth shaded model. There's a smooth shading setting in the menu you get when you press the T hotkey in Blender. For a cube this is perfect. For something you don't want faceted, you will want it smooth shaded.

Anyway, The "face" values define polygons although they "should" have grouped all the "faces" that belong to one polygon together. You can see that all 12 are numbered 1 to 6 to assign that index to a face. The first number before the // is the vertex number.

So, you don't have color and you don't have texture info. Your models are not going to have any color by the way. (Part of the reason I rolled my own right off the bat rather than messing with these formats.)

But interpreting it your vertex buffer will go something like this if you don't use an index buffer:

First face:

v 1.000000 -1.000000 1.000000 vn 0.0000 -1.0000 0.0000 v -1.000000 -1.000000 -1.000000 vn 0.0000 -1.0000 0.0000 v 1.000000 -1.000000 -1.000000 vn 0.0000 -1.0000 0.0000

v 1.000000 -1.000000 1.000000 vn 0.0000 -1.0000 0.0000 v -1.000000 -1.000000 1.000000 vn 0.0000 -1.0000 0.0000 v -1.000000 -1.000000 -1.000000 vn 0.0000 -1.0000 0.0000

Second face:

v -1.000000 1.000000 -1.000000 vn 0.0000 1.0000 0.0000 v 0.999999 1.000000 1.000001 vn 0.0000 1.0000 0.0000 v 1.000000 1.000000 -0.999999 vn 0.0000 1.0000 0.0000

v -1.000000 1.000000 -1.000000 vn 0.0000 1.0000 0.0000 v -1.000000 1.000000 1.000000 vn 0.0000 1.0000 0.0000 v 0.999999 1.000000 1.000001 vn 0.0000 1.0000 0.0000

So, that's just two of the 6 faces. To use an index buffer, you are going to have to go through almost as much. First, you have to expand the 8 vertices to 36. It actually tells you that, although it's difficult to read that from it. But the f values are indices. All 36 of those entries must be a separate vertex because it only gave you 6 normals. If it had of given you 8 normals, that would have matched the number of vertices.

A vertex can only have one normal, but a vertex belongs to 3 faces in this cube. So, it will have 3 normals, which is not possible. That's not a thing. I mean, I suppose you could write a special shader in GLSL or HLSL that has a vertex buffer with a vertex definition with normal1, normal2, and normal3, but that would mean you could basically only draw cubes with it pretty much and it would be for faceted shading only. Forget smooth shading.

Maybe the cube was a bad model to use for this. Maybe a UV sphere would be better and make it smooth shaded so that you have one normal per vertex. That would make this easier and will be what you want 99.9% of the time anyway. Although, the way it is doing this makes cubes and boxes come out very nicely.

Anyway, you have 36 vertices here. So you have to start by building all 36 vertices for your vertex buffer. So, that would look something like this:

Vertex 2 gets normal 1, Vertex 4 gets normal 1, Vertex 1 gets normal 1, Vertex 8 gets normal 2, Vertex 6 gets normal 2, Vertex 5 gets normal 2, Vertex 5 gets normal 3...

Wait, go back and read those last two. Now you have a problem. Vertex 5 just got two completely different normals. That's not possible. To make it possible the second vertex 5 has to become a new vertex. (What a mess.)

Probably what I would do would be to start by cleaning these up first thing. Read them in and reassign them to your own vertex numbers. Forget about the vertices it gave you: build your own.

So, make a list with 36 spots, one for each "face" (that's not really a face but an index). Then reinterpret the whole thing. This is probably a good time to start numbering from zero too. So f 2//1 4//1 1//1 becomes:

Vertex 0 v 1.000000 -1.000000 1.000000 vn 0.0000 -1.0000 0.0000

Vertex 1 v -1.000000 -1.000000 -1.000000 vn 0.0000 -1.0000 0.0000

Vertex 2 v 1.000000 -1.000000 1.000000 vn 0.0000 -1.0000 0.0000

and so on.

Or (obviously substitute in the vertex positions and normal values),

V0 f2//1 V1 4//1 V2 1//1
V3 f 8//2 V4 6//2 V55//2
V6 f 5//3 V7 2//3 V8 1//3
V9 f 6//4 V10 3//4 V11 2//4
V12 f 3//5 V13 8//5 V14 4//5
V15 f 1//6 V16 8//6 V17 5//6
V18 f 2//1 V19 3//1 V20 4//1
V21 f 8//2 V22 7//2 V23 6//2
V24 f 5//3 V25 6//3 V26 2//3
V27 f 6//4 V28 7//4 V29 3//4
V30 f 3//5 V31 7//5 V32 8//5
V33 f 1//6 V34 4//6 V35 8//6

And your index buffer is easy at that point and largely pointless because it is:

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35

But there's no way 8 vertices can share 6 normals. You need at least 8 normals and these 6 are not them. You need vertex normals, not face normals. It gave you face normals. You would also get smooth shading which is not what you want with a cube. So, this is basically right. It just takes 36 vertices to do it. Although, if it had of defined it as smooth shaded with 8 normals, you can tell the shader "no interpolate" and get the faceted effect with vertex normals even though they are not face normals. So, thumbs down to the OBJ format. ;-) lol

You might try playing with the smooth shading and flat shading setting in Blender and see if it gives different results.

Of course, the biggest problem is there's no color data. No vertex color and no UV textures. So, all your models are colorless. Maybe UV coordinates are supposed to go between the slashes? Or a vertex color.

Alright thx. Got it working.

Instead of doing below, I just loaded the Positions normally...


vertices[i].Pos.x = vertices[indices[i]].sPos.x
vertices[i].Pos.y = vertices[indices[i]].sPos.y;
vertices[i].Pos.z = vertices[indices[i]].sPos.z;

This topic is closed to new replies.

Advertisement