What to know in order to write a mesh parser?

Started by
5 comments, last by lauris71 11 years, 6 months ago
I'd like to learn how to write a Mesh Parser, using features in OpenGL from 3.3 and up (my drivers should support 4.3, I'm currently running 4.2).

The question is, I'm not sure what techniques/concepts would be good to understand first. I know how to write a binary search tree, if that helps - but maybe a QuadTree or even an OcTree would be more useful?

I've been studying from Learning Modern 3D Graphics Programming, and am currently at the Objects At Depth section.

Also what would be good to know is what kind of file formats are most common in the OpenGL world. Should I use blender to draw meshes, export the files, and render them in OpenGL? Is it possible and pragmatic to write a single mesh parser which would just render whatever kind of mesh I throw at it?

I'm aware of prewritten libraries such as GLMesh which help with this (though I'm not sure to what extent they do), but alas my goal is to learn so I'd like to avoid using something like that :3.

Thanks.
Advertisement
People don't do this. Work out what you need from your mesh data. Design a file format to hold it. It can be in denormalised formats, laid out in a nice easy to handle way for your renderer.

You then do one of two things -- you write an exporter from something like Blender which saves to your files. Or you write a translator which goes from a well known file format into your file format. Which can be quick, hacky and written in something like Perl or Python.

I'd like to learn how to write a Mesh Parser, using features in OpenGL from 3.3 and up (my drivers should support 4.3, I'm currently running 4.2).


Normally you shouldn't tie a mesh parser to your rendering subsystem. Write a parser that can extract all relevant information (usually vertex atributes, triangles and material definitions) from your mesh format into clear in-memory objects. Then write a rendering system, that generates VBO-s etc. from your in-memory data.
You can save some memory bandwidth by going directly from file to OpenGL buffers but normally it is not worth it.


The question is, I'm not sure what techniques/concepts would be good to understand first. I know how to write a binary search tree, if that helps - but maybe a QuadTree or even an OcTree would be more useful?


What sorts of meshes are you trying to parse? Terrain/large static structures should be handled differently from character models and small props. You probably need octree for the first but not for the others.
Start simple - write loader that generates single object/buffer. Once this works you may try octrees.


Also what would be good to know is what kind of file formats are most common in the OpenGL world. Should I use blender to draw meshes, export the files, and render them in OpenGL? Is it possible and pragmatic to write a single mesh parser which would just render whatever kind of mesh I throw at it?


What is OpenGL world? As you are here, you are probably keeping OpenGL game development in mind.
I'd suggest starting with OBJ for static objects, then MD5 for skinned characters. In that way you learn the basics of mesh composition - most file formats are very similar in basic logic, only layouts differ.
FBX is very common format in 3D pipeline. Most engines (include indie ones) use custom formats that will be exported from game editor.


I'm aware of prewritten libraries such as GLMesh which help with this (though I'm not sure to what extent they do), but alas my goal is to learn so I'd like to avoid using something like that :3.


Write parsers for some mesh formats to learn the basics. Once you know how this can be done, throw your code away and use library wink.png
Lauris Kaplinski

First technology demo of my game Shinya is out: http://lauris.kaplinski.com/shinya
Khayyam 3D - a freeware poser and scene builder application: http://khayyam.kaplinski.com/

What sorts of meshes are you trying to parse? Terrain/large static structures should be handled differently from character models and small props. You probably need octree for the first but not for the others.
Start simple - write loader that generates single object/buffer. Once this works you may try octrees.


I haven't even gotten to that point yet. I guess I've been thinking character models as well as terrain and large static structures. I'll probably continue just working through these tutorials until I've at least grasped the basics, and then work towards mesh parsing from there.

Just so I understand this correctly:

The file format will contain the vertex/geometry/fragment data, as well as, probably, which primitives (likely triangles, I'd imagine) to use as a basis for rendering the mesh. Once I have the mesh data structure taken care of in my C++ code, I could simply parse the file, store the vertex data in c-style structures or something like glm::vec4, and then pass those to my renderer in a loop - correct?

If that's the case, I assume that this wouldn't really take much math at all apart from a good understanding of the matrices, etc involved (unless, of course, I was using physics to do this, which will likely be a while from now).

I appreciate the advice.
Ok I was in you shoes not long ago. It can be incredibly hard to find the legit answer online to this parsing question. The best thing you could do is learn how to open files for reading in and out information which is purely going to be with your C++ code. I only say C++ vs some other language because that's what you just wrote you where using. Now you can learn how to do this in almost any C++ book you can find or even online in any C++ programing tutorial site. Once you understand how to open and write to and read from like .txt files you will know that you can open and read and write to a plethora of file types using the exact same methodology, whether it be .DAE .obj .ASE. these are file types often used in the parsing process often because they are the easiest read by human eyes before parsing.

You also have to understand that there are only up to like 5 things you need to be able to extract at the beginning to get your objects on the screen and that's if you want to include info to do bump mapping. SO the most important info you need is Vertex Data. So POSITION AND NORMALS is the minimum to get something to render and this is with out textures or bump mapping. If you want textures then you will read in the POSITION, NORMALS, and TextureCOORDS. If you want to do bumpMapping then you will read in POSITIONS, NORMALS, TEXTRCOORDS, AND Binormals with BiTangent information. I would just try rendering Position and Normals to start out.
Unless you want to do animations in your game that where created in 3dsmax or blender then this is all the info you need to get objects to renderd in your world. And you can then just extract the info from one of the above file formats and place them into a vector(container) of vertex structs. such as this
[source lang="java"]

#define FIELD_OFFSET(StructType, field) ((GLvoid*)(offsetof(StructType, field)))

struct YourVec3 {
float x,y,z;
};

struct Vertex {
YourVec3 VertsPos;
YourVec3 Norms;
YourVec3 Tangents;
YourVec3 Bitangents;
YourVec3 texCoords;
};

struct Face {
unsigned short v[3];
};

class Mesh
{
public:
Mesh(void);
~Mesh(void);



std::wstring name;
std::vector<Vertex> v;
std::vector<Face> f;
Vertex Vertz;
Material *mat;


// Vertex Buffer Object Names // Buffer objects are handles to OpenGL-managed memory
unsigned int VBO; ///--------------------------------- Vertex VBO Name
unsigned int IBO; ///--------------------------------- Index VBO Name



//----------------------------------// VBO Build Function
void BuildVBOs()
{
if (f.size()==0 || v.size()==0) return;

/// VERTEX BUFFER
glGenBuffers( 1, &VBO );///--------------------------- Get A Valid object Name
glBindBuffer( GL_ARRAY_BUFFER, VBO );///-------------- Bind The Buffer to the target GL_ARRAY_BUFFER
glBufferData( GL_ARRAY_BUFFER, v.size()*sizeof(Vertex), v.data(), GL_STATIC_DRAW );///--Loading data


/// INDEX BUFFER
glGenBuffers( 1, &IBO );///--------------------------- Get A Valid object Name
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);///-------- Bind The Buffer to the target GL_ELEMENT_ARRAY_BUFFER
glBufferData(GL_ELEMENT_ARRAY_BUFFER, f.size()*sizeof(Face), f.data(), GL_STATIC_DRAW);///--Loading data



}[/source]

[source lang="cpp"] void Render(){

if (jVBO==0) BuildVBOs(); ///-------------------------- called once at beginning to initalize the VBOs


jgVertex verts;
enum {ATTRIBUTE_POSITION, ATTRIBUTE_NORMAL, ATTRIBUTE_TANGENT, ATTRIBUTE_BINORMAL, ATTRIBUTE_TEXTURE, NUM_ATTRIBUTES };

glEnableVertexAttribArray( ATTRIBUTE_POSITION);
glEnableVertexAttribArray( ATTRIBUTE_NORMAL);
glEnableVertexAttribArray( ATTRIBUTE_TANGENT);
glEnableVertexAttribArray( ATTRIBUTE_BINORMAL);
glEnableVertexAttribArray( ATTRIBUTE_TEXTURE);

glBindBuffer(GL_ARRAY_BUFFER, jg_VBO);
glVertexAttribPointer(ATTRIBUTE_POSITION, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), FIELD_OFFSET(Vertex, VertsPos));
glVertexAttribPointer(ATTRIBUTE_NORMAL, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), FIELD_OFFSET(Vertex, Norms));
glVertexAttribPointer(ATTRIBUTE_TANGENT, 3, GL_FLOAT, GL_FALSE, sizeof(jgVertex), FIELD_OFFSET(Vertex, Tangents));
glVertexAttribPointer(ATTRIBUTE_BINORMAL, 3, GL_FLOAT, GL_FALSE, sizeof(jgVertex), FIELD_OFFSET(Vertex, Bitangents));
glVertexAttribPointer(ATTRIBUTE_TEXTURE, 2, GL_FLOAT, GL_FALSE, sizeof(jgVertex), FIELD_OFFSET(Vertex, texCoords));

// bind ibo
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);

// draw!
if(Globals::exterior)
glDrawElements(GL_TRIANGLES, 3*f.size(), GL_UNSIGNED_SHORT, 0);
if(!Globals::exterior)
glDrawElements(GL_LINES, 3*f.size(), GL_UNSIGNED_SHORT, 0);


glDisableVertexAttribArray( ATTRIBUTE_POSITION);
glDisableVertexAttribArray( ATTRIBUTE_NORMAL);
glDisableVertexAttribArray( ATTRIBUTE_TANGENT);
glDisableVertexAttribArray( ATTRIBUTE_BINORMAL);
glDisableVertexAttribArray( ATTRIBUTE_TEXTURE);

glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);



}[/source]

This is a typical set up for reading in that geometry data into your openGL environment.



Now learn the ins and outs of FILE I/O with C++. This will help you out but you should go deeper then this tutorial.

Then Open one of the file type above such as .ASE .DAE or .obj and look at them and the way they store thier info. .ASE is by far the easiest to understand just looking at it. Here is a link to help understand the .DAE file type.

Then once you have your mind wrapped around that then you can begin writing your own parser to extract the info you need to store so you can then, Hand your stored info to openGL to render.

I like creating my own file types. So i made a small program that reads from obj or DAE and then stores the info in my own file type then i read from my smaller more efficent file type into my program. Once you fully learn file in and out you can do all kinds of cool things like that and all of this will seem less difficult to understand.

Also I agree with what was said above about libraries. just wait until you understand more before using them. but they can help ones you understand.

Hope any of that helps
J-GREEN

Greenpanoply
As far as I know you'd benefit from using a common format for your parser since you won't need to write export/import plugins for Blender/Maya/3DMax/etc because more common formats have those already.

If you go with your own format, you'd have to write import/export plugins for your preferred 3D editor, or a translator as Katie said. Point is, you'll have to create a tool chain around your format to make it usable both for you and other possible users.

But, learning to use an existing library for an existing format might give you some ideas for writing your own (you'd know what kind of functionality you need from the start) so that could help too.

Though I have no experience in these things, I'm exactly in the same section of the arcsynthesis.org book.

"I AM ZE EMPRAH OPENGL 3.3 THE CORE, I DEMAND FROM THEE ZE SHADERZ AND MATRIXEZ"

My journals: dustArtemis ECS framework and Making a Terrain Generator


The file format will contain the vertex/geometry/fragment data, as well as, probably, which primitives (likely triangles, I'd imagine) to use as a basis for rendering the mesh. Once I have the mesh data structure taken care of in my C++ code, I could simply parse the file, store the vertex data in c-style structures or something like glm::vec4, and then pass those to my renderer in a loop - correct?


It is the simplest way. But the problem with this approach is that rendering primitives from in-memory structures is (relatively) slow.
Better approach is to parse file to in-memory structures and then use these structures to create VBO-s. Those will then be passed to renderer in a loop.
But you can implement the first method first - VBO-s are somewhat orthogonal to mesh parsing code.


If that's the case, I assume that this wouldn't really take much math at all apart from a good understanding of the matrices, etc involved (unless, of course, I was using physics to do this, which will likely be a while from now).


I think the most important is to understand matrix multiplication (including vector-matrix multiplication) and inversion - especially the geometric meaning of those operations. Once you'll start working with skinned character meshes and skeletons the amount of matrices involved grows uncomfortably large wink.png
Lauris Kaplinski

First technology demo of my game Shinya is out: http://lauris.kaplinski.com/shinya
Khayyam 3D - a freeware poser and scene builder application: http://khayyam.kaplinski.com/

This topic is closed to new replies.

Advertisement