Home Working with 3D models Details about GLM structure
Details about GLM geometry data

As you might know GLM is a library that reads 3D objects in Wavefront OBJ format (.OBJ) and renders them in opengl. You can read more about it here.

This article will explain the internal structure of GLM for those of you that intend to do complex applications that will inevitably use geometry information about the 3D model for things such as shadows or collision detection.

As you might know reading a model from a file is done simply by calling glmReadOBJ(char *filename) and will return a pointer to a GLMmodel object:

GLMmodel *pmodel1 = glmReadOBJ("data/world.obj",&call);

typedef struct _GLMmodel {

char* pathname; /* path to this model */
char* mtllibname; /* name of the material library */

GLuint numvertices; /* number of vertices in model */
GLfloat* vertices; /* array of vertices */

GLuint numnormals; /* number of normals in model */
GLfloat* normals; /* array of normals */

GLuint numtexcoords; /* number of texcoords in model */
GLfloat* texcoords; /* array of texture coordinates */

GLuint numfacetnorms; /* number of facetnorms in model */
GLfloat* facetnorms; /* array of facetnorms */

GLuint numtriangles; /* number of triangles in model */
GLMtriangle* triangles; /* array of triangles */

GLuint nummaterials; /* number of materials in model */
GLMmaterial* materials; /* array of materials */

GLuint numgroups; /* number of groups in model */
GLMgroup* groups; /* linked list of groups */

// added texture support - does not exist in the original GLM
GLuint numtextures;
GLMtexture* textures;

GLfloat position[3]; /* position of the model */

} GLMmodel;

As you can see it holds every peace of geometry information as an array and a number that tells us how much information is in the array. Let's take vertices for example:

GLuint numvertices; /* number of vertices in model */
GLfloat* vertices; /* array of vertices */

The array holds 3 float (GLfloat is actually float) values for each vertex: the x, y and z coordinates as they were exported from 3d Studio Max or Gmax. So the first vertex would be (vertices[0],vertices[1], vertices[2]), the second would be (vertices[3],vertices[4],vertices[5]) and so on.

The triangles array holds all the data that links vertices to triangles:

typedef struct _GLMtriangle

{

GLuint vindices[3]; /* array of triangle vertex indices */
GLuint nindices[3]; /* array of triangle normal indices */
GLuint tindices[3]; /* array of triangle texcoord indices*/
GLuint findex; /* index of triangle facet normal */

/* the following 2 things do not exist in the original GLM, only in the version modified by me. I added them to make it easier to work with shadow volumes. Vecini holds the indexes of the neighboring triangles (in a valid mesh you can only have 3 neighbors) and a boolean variable that says if the triangle is visible in the light or not. Obviously these aren't used when loading the model as they do not exist in the .OBJ file. They would be calculated later if we need them for shadows. For more information on them look at the shadow volumes tutorial*/

GLuint vecini[3];
bool visible;

} GLMtriangle;

As you can see we have an array of int. vindices[0], vindices[1] and vindices[2] are the indexes of the vertices from the previously explained vertices array in GLMmodel. Say you want the coordinates of the first vertex in the triangle...this is what you would do:

If you had a GLMmodel *model;
int index = model->triangle[number_of_your_triangle]->vindices[0];
float x = model->vindices[3*index];
float y = model->vindices[3*index+1];
float z = model->vindices[3*index+2];

Pretty much the same goes for nindices that represent the indexes for the real normals that are stored in the normals array of the GLMmodel object and the tindices that are indexes for the texcoords array.

You have noticed that vertexes and triangles from the entire scene are all put together in an array. So if you have more then one object in the scene how do you which one belong to who? The answer are smoothing groups.

Let's take a look again at an an .OBJ file generated by 3D Studio Max:

 

# -----------------
# Start of obj file
g Box01

v -62.0579 -41.4791 0.0
v 58.8424 -41.4791 0.0
v -62.0579 22.1865 0.0
v 58.8424 22.1865 0.0
v -62.0579 -41.4791 39.8714
v 58.8424 -41.4791 39.8714
v -62.0579 22.1865 39.8714
v 58.8424 22.1865 39.8714

vt 0.843206 0.405444 0.000499517
vt 0.482802 0.71377 0.9995
vt 0.478066 0.404023 0.000499636
vt 0.482802 0.716612 0.9995
vt 0.841627 0.688332 0.000499517
vt 0.482013 0.981029 0.9995
vt 0.480434 0.688332 0.000499636
vt 0.485959 0.978188 0.9995
vt 0.450102 0.00618343 0.000499547
vt 0.45247 0.509304 0.000499547
vt 0.000499517 0.512146 0.000499547
vt 0.000499517 0.512146 0.000499547
vt -0.0010791 0.00618302 0.000499547
vt 0.450102 0.00618343 0.000499547
vt 0.000499517 0.512009 0.9995
vt 0.450891 0.510588 0.9995
vt 0.45247 0.995237 0.9995
vt 0.45247 0.996658 0.9995
vt 0.000499636 0.9995 0.9995
vt 0.000499517 0.51343 0.9995
vt 0.478855 0.405444 0.000500023
vt 0.841627 0.408286 0.000499576
vt 0.83847 0.688332 0.000499576
vt 0.83847 0.688332 0.000499576
vt 0.477276 0.694016 0.000500023
vt 0.478855 0.405444 0.000500023
vt 0.482802 0.71377 0.9995
vt 0.845574 0.71377 0.999501
vt 0.844784 0.976767 0.999501
vt 0.844784 0.976767 0.999501
vt 0.482802 0.716612 0.9995
vt 0.842417 0.710929 0.9995
vt 0.843995 0.975346 0.9995
vt 0.843995 0.975346 0.9995
vt 0.478066 0.404023 0.000499636
vt 0.841627 0.688332 0.000499517

vn 0.0 0.0 -1.0
vn 0.0 0.0 -1.0
vn 0.0 0.0 1.0
vn 0.0 0.0 1.0
vn 0.0 -1.0 0.0
vn 0.0 -1.0 0.0
vn 1.0 0.0 0.0
vn 1.0 0.0 0.0
vn 0.0 1.0 0.0
vn 0.0 1.0 0.0
vn -1.0 0.0 0.0
vn -1.0 0.0 0.0

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

# end of obj file
# ---------------

Before the object began there was a tag:

g Box01

Box01 was the original name of the object in 3D Studio Max. Every face (f tag) after the "g Box01" command belongs to the Box01 object. If we had another object named "car" we'd put "g Car" after we were finished with Box01 and every face defined from that point until a new g command would belong to the Car. All this data about who belongs to who is loaded into the groups array of the GLMmodel object. The structure of a group is defined as follows:

typedef struct _GLMgroup
{

char* name; /* name of this group */
GLuint numtriangles; /* number of triangles in this group */
GLuint* triangles; /* array of triangle indices */
GLuint material; /* index to material for group */
struct _GLMgroup* next; /* pointer to next group in model */

} GLMgroup;

As you might notice a triangles belong to a group, but not vertices. The triangles array contains indices for triangles that are actualy store in the triangles array of the GLMmodel object. You could say these are just references :) The name variable contains that name of the object as it was in 3D Studio max. If you wanted to enumerate the poligons (triangles) in an object you know is called "Car" you would do something like:

GLMgroup *g = glmFindGroup(model, " Car");
for (int i=0;i<g->numtriangles;i++)

DoSomethingWithTheTriangle(model->triangles[g->triangles[i]]);

 

GLMgroup is a chained list (look at the next pointer that points towards the next GLMgroup in the list or null if it's the last) so instead of using the glmFindGroup function provided in the library you could enumerate the groups your self. Perhaps you would like to process each object at a time like in the following example:

GLMgroup *g = model->groups;
while (g)
{

for (int i=0;i<g->numtriangles;i++)
DoSomethingWithTheTriangle(model->triangles[g->triangles[i]]);
g = g->next;

}

Each group also contains information about the materials used by the current object (represented by the group):

typedef struct _GLMmaterial
{

char* name; /* name of material */
GLfloat diffuse[4]; /* diffuse component */
GLfloat ambient[4]; /* ambient component */
GLfloat specular[4]; /* specular component */
GLfloat emmissive[4]; /* emmissive component */
GLfloat shininess; /* specular exponent */
//I added support for textures (this doesn't exist in the original GLM
GLuint IDTextura; // the id of the texture

} GLMmaterial;

This structure defines the properties of a the material by the definition required by Phong lighting. The values are taken from the material library file (.MTL). If you need to remember the structure of the .OBJ and .MTL files exported by 3D Studio Max read here

 
Copyright © 2012 Tudor Carean. All Rights Reserved.