Opinions on 3D model File Formats

Hello everyone!

Now that I’m more comfortable with OpenGl and programming in general, I find that I need to be able to work with models that are stored external from my engine, instead of trying to create objects and scenes by piecing together vertices manually.

What I’m curious about is your opinions on file formats that are simple to learn to parse, or that have simple libraries available to simplify the process. I did my research on the MD2 model format and built my own parser, but I’ve come to find that the format isn’t very wide-spread, and finding editors can be difficult.

I don’t need anything too fancy - just something that can help me render models more easily, and in the future be used for simple animation.

I spent some time looking for a modeling application that could export to the MD2 format as well, I didn’t find anything so I ended up just using the .obj file format because its the best format that blender exports to and since nothing else exported to MD2 I simply didn’t look for a better format. I did learn that blender used to export to MD2 however, if you could find the right version. I it was version 2.42 that exported to it. I can’t vouch for the older versions of blender in terms of how good they are in general, but they do export to MD2 so you might want to poke around for a download for that.

If you can’t find a download for it however, .obj works fairly well. It has no animation support, but if you can get by without animations then its fairly easy to understand and to load and it can work as a decent substitute for MD2.

http://en.wikipedia.org/wiki/List_of_file_formats#3D_graphics

This is a link to wikipedia’s list of all file formats, it has a section on 3d graphics which may be useful to you as well.

Hope I was able to help.

.obj

How about collada?

If you are creating your own models then .OBJ is the simplest and portable format. Tools like Blender can be used to create the models which are then easily imported into your own engine.
Big software companies end up using 3ds files because they end up writting plug-ins so that they can export extra vertex attributes for their engine’s specific requirements, eg 4th Vertex attribute, extra texture coords, etc.

Personally I use a variety of formats because I don’t create models but do import others, eg Doom 3’s MD5 models.

I personally like 3Ds fileformat if i want bin, but also OBJ files if i want text based info.

Do not really like md2 i but its just a matter of taste, nothing else against it.

i did a bit research about my suggested ASE,OBJ text based and 3DS file formats.

Found that the ASE is more detailed and saves the vertex data optimized, OBJ is simpler but doesnt save everything.
3Ds file has almost everything but as i checked it does not optimize the vertex number.

For example in 3D Studio Max i create one box, it shows it has 8 verticies, but if i export it to 3Ds file it shows 26 verticies there.
A cube has 6 sides made up 2 tri/side, so its
623 = 36 so some verticies are shared and some are not,
but 8 verticies should be just enough for a cube.

OBJ is simpler but doesnt save everything

Yes, you’re right there. .OBJ won’t save a second set of texture coordinates for example.

saves the vertex data optimized

Is file size an important factor for you?
Although a bin format would be faster to read and more compact than a text format, personally I don’t think it matters too much. You see at the end of the day you have to render these models and that means converting the data into an OpenGL vertex array structure. So, if your engine loads native models then it must convert the model format on-the-fly. This will always be slow (binary native models formats will be faster than text based). However, once the conversion has been done, it would actually make sense to persist that work to disk, so next time you just load a binary blob which can get uploaded directly to a vertex buffer.
Therefore, IMHO, I don’t think the choice comes down to compact vertex structures or whether its binary or text-based. It comes down to the tool set you have and what authoring features you need - so long as your tools can export the data in some way or another. The key to the export is that your engine needs to be able to parse it again to allow you blob the data.

eobj can be a good choice for text format. It’s simple obj with added attributes (using common obj comments to achieve this).

Also, if you need double precision for your data (I know OpenGL doesn’t really need it, but you might need it internally), text formats are not really suited for this since reading/writting a floating point value with the default way (fstreams or C equivalent) will have 5 digits precision.

At last, since you just start with files I greatly suggest to start with obj format since it’s simple, text-based (so you can easily modify the data in any text editor) and is common (many modelers export to this format).

arts, do you have some links to resources about eobj ? I can not seem to find anything.

Well, you’re right, I couldn’t find any documentation on Internet about it. Don’t really know why since we use this almost everywhere here.

Anyway here is a little example. It’s a simple cube. Added attributes are cubemap coordinates (at the bottom).

The lines of attributes (definitions):


# attribute lock vertex boolean
# attribute uvw vertex Point3d

just says that there are 2 attributes.
The first is named “lock”, is attached to vertices, and is a boolean. The second, named “uvw”, which is attached to any vertex, is a 3D point (so expecting 3 components).

Then, for each attribute line such as:


# attrs v 1 0 -1 -1 -1 

We know that it’s an attribute (line starts with “attrs”), which corresponds to the vertex 1 (v 1). The next value here is the lock boolean value defined above, and last, the uvw coordinates, here (-1,-1,-1).

Very simple :slight_smile:

Here is the full cube with cubemap cordinates:


v -1 -1 -1
v -1 -1 1
v -1 1 -1
v -1 1 1
v 1 -1 -1
v 1 -1 1
v 1 1 -1
v 1 1 1
v 0 1 1
v 1 1 0
v 0 1 -1
v -1 1 0
v -1 -1 0
v -1 0 1
v -1 0 -1
v 0 -1 1
v 1 0 1
v 1 -1 0
v 1 0 -1
v 0 -1 -1
v 0 1 0
v -1 0 0
v 0 0 1
v 1 0 0
v 0 -1 0
v 0 0 -1
vt 0 0
vt 0 0
vt 0 0
vt 0 0
vt 0 0
vt 0 0
vt 0 0
vt 0 0
vt 0 0
vt 0 0
vt 0 0
vt 0 0
vt 0 0
vt 0 0
vt 0 0
vt 0 0
vt 0 0
vt 0 0
vt 0 0
vt 0 0
vt 0 0
vt 0 0
vt 0 0
vt 0 0
vt 0 0
vt 0 0
f 8/1 21/2 9/3 
f 10/4 21/2 8/1 
f 7/5 21/2 10/4 
f 11/6 21/2 7/5 
f 3/7 21/2 11/6 
f 12/8 21/2 3/7 
f 4/9 21/2 12/8 
f 9/3 21/2 4/9 
f 2/10 22/11 13/12 
f 14/13 22/11 2/10 
f 4/9 22/11 14/13 
f 12/8 22/11 4/9 
f 3/7 22/11 12/8 
f 15/14 22/11 3/7 
f 1/15 22/11 15/14 
f 13/12 22/11 1/15 
f 6/16 23/17 16/18 
f 17/19 23/17 6/16 
f 8/1 23/17 17/19 
f 9/3 23/17 8/1 
f 4/9 23/17 9/3 
f 14/13 23/17 4/9 
f 2/10 23/17 14/13 
f 16/18 23/17 2/10 
f 5/20 24/21 18/22 
f 19/23 24/21 5/20 
f 7/5 24/21 19/23 
f 10/4 24/21 7/5 
f 8/1 24/21 10/4 
f 17/19 24/21 8/1 
f 6/16 24/21 17/19 
f 18/22 24/21 6/16 
f 5/20 25/24 20/25 
f 18/22 25/24 5/20 
f 6/16 25/24 18/22 
f 16/18 25/24 6/16 
f 2/10 25/24 16/18 
f 13/12 25/24 2/10 
f 1/15 25/24 13/12 
f 20/25 25/24 1/15 
f 19/23 26/26 7/5 
f 5/20 26/26 19/23 
f 20/25 26/26 5/20 
f 1/15 26/26 20/25 
f 15/14 26/26 1/15 
f 3/7 26/26 15/14 
f 11/6 26/26 3/7 
f 7/5 26/26 11/6 
# attribute lock vertex boolean
# attribute uvw vertex Point3d
# attrs v 1 0 -1 -1 -1 
# attrs v 2 0 -1 -1 1 
# attrs v 3 0 -1 1 -1 
# attrs v 4 0 -1 1 1 
# attrs v 5 0 1 -1 -1 
# attrs v 6 0 1 -1 1 
# attrs v 7 0 1 1 -1 
# attrs v 8 0 1 1 1 
# attrs v 9 0 0 1 1 
# attrs v 10 0 1 1 0 
# attrs v 11 0 0 1 -1 
# attrs v 12 0 -1 1 0 
# attrs v 13 0 -1 -1 0 
# attrs v 14 0 -1 0 1 
# attrs v 15 0 -1 0 -1 
# attrs v 16 0 0 -1 1 
# attrs v 17 0 1 0 1 
# attrs v 18 0 1 -1 0 
# attrs v 19 0 1 0 -1 
# attrs v 20 0 0 -1 -1 
# attrs v 21 0 0 1 0 
# attrs v 22 0 -1 0 0 
# attrs v 23 0 0 0 1 
# attrs v 24 0 1 0 0 
# attrs v 25 0 0 -1 0 
# attrs v 26 0 0 0 -1 
# END

One extra info, maybe it matters for somebody :slight_smile:

If you have texture coordinates, also the files behave differently.
Lets say you UVMap the a cube with 8 verticies:
-ASE file keeps the number of verticies 8 and but the will have more than 8 texture coordinates.
I dont think its good for OpenGL
-3Ds file will split up and duplicate verticies as many as needed (see my previous post)
but the number of verticies will equal with the number texture coordinates.
This is more suitable for OpenGL i think.

Iam not that familiar what OBJ does in that case.

What do you think ? Which is better for OpenGL ?

  1. When the number of verticies lower than the number of texture coords.
  2. When the number of verticies equals to the number of texture coords.

You need an array of N objects of :
struct PerVertexData{
vec3 position;
vec3 normal;
vec2 uv0;
// maybe more uvs and stuff
};

and M*3 indices into that array, per sub-mesh. Each of the N objects should have unique value, ideally. So generally you’ll be preprocessing OBJ and whatever to merge or split vertices.
In a shader-centric world, I find it easier to store uvs and such as general-purpose vtx-attrib data in a bloated mesh:


struct BloatedSubMesh{
	const char* ShaderMaterialName; // different from materials in modelers
	
	int NumVertices;
	
	int NumPoints;
	int NumLines;
	int NumTris;
	int NumQuads;
	
	int* Points; // int[NumPoints]
	int* Lines; // int[NumLines*2]
	int* Tris; // int[NumTris*3]
	int* Quads; // int[NumQuads*4]
	
	
	vec3* positions;// vec3[NumVertices];
	vec3* normals; // vec3[NumVertices];
	
	int NumNamedAttribs;
	struct{
		int type; // i.e "COLR", "TEXC"
		int idx; // for texcoord0..15, and/or color0...15
		
		vec4* data; // vec4[NumVertices]
	} *NamedAttribs; //   _struct[NumNamedAttribs]
	
	int NumTextures;
	char** Textures; // char*[NumTextures]
	
	int BlendingType;
	vec3 K_ambient;
	vec3 K_diffuse;
	vec3 K_specular;
	float K_transparency;
	float K_specExponent;
	
	
	void ConvertQuadsToTris();
	void RecomputeNormals();
	void RecomputeTNBs();
	void OptimizeForVCache();
	
};

-ASE file keeps the number of verticies 8 and but the will have more than 8 texture coordinates.
I dont think its good for OpenGL

but the number of verticies will equal with the number texture coordinates.
This is more suitable for OpenGL i think.

What do you think ? Which is better for OpenGL ?

  1. When the number of verticies lower than the number of texture coords.
  2. When the number of verticies equals to the number of texture coords.

I tried to explain this before. I does not matter how many texture coords or verticies/how compact the model is on export.

Neither case 1 or 2 matter to OpenGL.

When you have a model (in what ever format) you don’t render it in immediate mode (using glVertex3f commands). You have to parse the model data structure first and build an OpenGL freindly vertex array by reading what ever vertex/Tex coord/face information is available. In some cases that could mean duplicating texture coordinates so that each vertex has a corresponding tex coordinate; in other cases it may mean dropping an entire stream (tangent space coordinates for example) becuase you don’t implement that.

llian, in his post above, is saying just that. Look at his structure - it contains arrays to hold all kinds of data for the model (vertex, texture, normals, tangents) and metadata too. He also includes methods so that the raw data can be converted into OpenGL arrays:


	void ConvertQuadsToTris();
	void RecomputeNormals();
	void RecomputeTNBs();
	void OptimizeForVCache();

Only when all of these methods have been executed, is the model ready to be used and efficiently rendered via OpenGL.
At this point, as I suggested earlier, you should persist the vertex array(s) and metadata to disk to save having to repeat the process again. This is part of your tool-set, otherwise the engine will have to perform this task each time.