Tree branch
Another tree branch
OpenGL Tutorials
Spider web
Facebook Twitter Google Plus
Constructing your own 3D model format is an important step in building 3D programs, especially computer games.
OpenGL Tutorials:
OpenGL Tutorials - Full List Tutorial 0 - OpenGL Compiler Setup Tutorial 1 - 3D Graphics Introduction Tutorial 2 - Creating an OpenGL Window in Win32API Tutorial 3 - OpenGL Primitives, Triangles, Fans, Stripes, Vertices, Vertexes, Arrays Tutorial 4 -Working with OpenGL Color Tutorial 5 - Transforms, Transformations (Rotate, Move, Spin) in 3D Coordinate Space Tutorial 6 - How to Load 3D Model in OpenGL OpenGL Base Code (Starting Point) OpenGL Light: Ambient, Diffuse, Specular Highlight

3D Model Composition. It wouldn't be convenient to create your 3D models by calling glVertex3f again and again from within your code. Unless you have much time on your hands. I'm sure that you don't. Therefore, a model file format of some kind should be created.

The point of this tutorial is not to cover existing 3D object formats (Such as 3Dsmax, Maya, Blender, Z-brush, Unity, etc.) but rather take a raw meat and bones look at how loading a 3D model into an OpenGL world works. In order to do this, we will invent our own 3D model format.

Creating, Loading and Displaying 3D Models In Your Own Model Format

Well, of course simply creating a file format won't save us from all of the tedious work of specifying vertices "by hand" as a model designer program is also desirable, but at least you won't have to recompile your code every time you make changes to your model.

A model editor program can also be further programmed utilizing this file format. However, this is not something we will be doing any time soon.

This tutorial demonstrates what could probably be called the simplest 3D model format. I named it the m model format. A model file in this format would have a name of "myModel.m" on your hard drive. The format simply consists of triangle vertices specified one by one.

The file(which is written in text(translated) mode, as opposed to binary mode) header starts with the tag: beginmodel n. Where n is the number of consequently specified polygons (tringles only). 3 vertices per line define a single polygon/triangle. What's really neat about the m model file format is that you are allowed to define how you want the polygon vertices to be stored.

In other words, it ignores all formatting except the floating point values which actually specify a vertex position parameter. The m model format parser skips all and every characters unless the character is either a digit from 0 to 1, a minus sign or a dot. Every other character is sent right down the toilet. But let me illustrate this functionality:

In the m model format a polygon can be defined as:

-1.0f 0.0f 0.0f , 0.0f -1.0f 0.0f , 1.0f 0.0f 0.0f ;initial polygon, format style #1

or it can also be defined by using any other style of formatting. Consider the same polygon described above but with a different formatting style:

[-1.0f 0.0f 0.0f] [0.0f -1.0f 0.0f] [1.0f 0.0f 0.0f] ;initial polygon, format style #2

The above definition is just as right as it is in the original example. Formatting styles can be also mixed in any way possible, just keep at least a single space between the x, y and z coordinates. Also, try not to specify less triangles than whats defined by beginmodel, or your program will most likely crash during a LoadModel(...); call, but a little later about this. Now, let's see how the model.h header file is defined:

// model.h; typedef struct vertex_s { float x, y, z; } vertex_t; typedef struct polygon_s { vertex_t v[3]; } polygon_t; typedef struct model_s { int nPolygons; // number of polygons polygon_t *pList; // list of polygons } model_t; extern model_t sampleModel; // render a polygon void DrawPolygon (polygon_t *p); // model ops: void LoadModel (char *sFilename, model_t *model); // loads a *.m model file void DrawModel (model_t *model, vertex_t *translate, vertex_t *rotate); void FreeModel (model_t *model);

The vertex_t, polygon_t and model_t structs describe a vertex, a polygon and an m format model. This should be obvious. Note that model_t consists of a number of polygons stored in a model and a polygon list. When we load an m model file with a call to LoadModel(...); a number of polygons is read into this structure and is stored in *pList;

nPolygons is what n defines by the means of the beginmodel n tag in the beginning of the m model file. n is the number of polygons in the entire models of course, as already described above. model_t sampleModel; is just a definition of a model that could be used for experimenting purposes.

As will be seen in this and the following tutorials, we will use sampleModel to load a simple model and display it on the screen. Mouse-based movement will be utilized to see how the light reacts with that model's surface. But that's the topic of the next tutorial. Right now, let's see what the four remaining functions in the header file listing are for:

DrawPolygon (polygon_t *p); simply renders a polygon specified by a polygon_t struct.

LoadModel (char *sFilename, model_t *model); loads a m model file specified by sFilename into the address specified by model_t *model;

DrawModel (model_t *model, vertex_t *translate, vertex_t *rotate); draws the model specified by model_t *model on the screen. Model must be loaded with a call to LoadModel(...) first.

FreeModel (model_t *model); frees memory allocated for model's vertices.

Now, let's take a look at the model.cpp file contents. The first part of this code block includes all necessary include-files ("glbase.h"), defines an alternative wrapper for the return value of fgetc(...), defines a function which skips a file stream line, and more importantly the function that reads a single floating-point parameter from the file stream.

Keep in mind that because the file model.cpp is a part of a bigger structure - the opengl base code we are developing, all required include files are included within "glmain.h". You will see this file in the source code download and you will also understand how include files are connected to the OpenGL code base. For now, your mission is to just understand this code.

// model.cpp - *.m file loading and displaying functions // from: www.falloutsoftware.com // by: Greg Sidelnikov (greg.sidelnikov@gmail.com) // --------------------------------------------- #include "glbase.h" model_t sampleModel; int mmReadCh (FILE **file) { return fgetc(*file); } void mmSkipLine (FILE **file) { fgets(sysBuffer, 128, *file); } // mmReadNextParam: reads the next parameter from a *.m file stream, // skips all unneeded characters float mmReadNextParam (FILE **file) { int ch = 0; float f = 0; bool legal = false; while (true) { ch = mmReadCh(file); // EOF or error: set err to 1 and break out of the loop if (ch == EOF) SysShutdownMSG("mmReadNextParam: unexpected EOF or parsing" "error\r\ncheck your *.m model file syntax"); // skip spaces or any other misc. characters (, . etc) // also; a vertex param. could be negative // so look out for '-' (0x2d) while parsing ((ch >= 0x30 && ch <= 0x39) || ch == 0x2d) ? legal = true : legal = false; if (legal) { // back up by one character if (fseek(*file, (ftell(*file)- 1), SEEK_SET) != 0) SysShutdownMSG("mmReadNextParam: fseek failed, " "sorry; check your *.m model file syntax"); // read the floating-point parameter and return it fscanf(*file, "%f", &f); return f; } } return 0; // EOF or error at this point }

The most significant part of this block of code is the mmReadNextParam (FILE **file); command. What it does is, it takes the passed file stream FILE **file (the stream should be a valid file stream) and parses it until it finds the next floating-point parameter in it. Once it does the parameter is returned. The function is well-commented so it should be obvious for you to see what's going on.

I should probably mention that some commands seen in this block of code come from other files #included within "glbase.h"(and "it is the main file that includes all required files for the base code to operate). Such one is the SysShutdownMSG command.

When an error or EOF (end of file) while parsing is encountered, the program exits out with an error description within a message box. If you want to use this code in your own program, that has nothing to do with the OpenGL base code described in these tutorials, then you would mostly likely want to change the calls to SysShutdownMSG by your own error-displaying functions and also get rid of the "glbase.h" include file. But that's something I wouldn't recommend doing at this point.

So let's see what else is contained within model.cpp, after the definition of the mmReadNextParam(...) function:

// LoadModel(); loads an m file format model into model_t *model void LoadModel (char *sFilename, model_t *model) { FILE *file = fopen(sFilename, "r"); if (file) { // find the "beginmodel" tag while (true) if (strncmp(fgets(sysBuffer, 128, file), "beginmodel", 10) == 0) break; // read the number of polygons in this model sscanf(sysBuffer, "beginmodel %d", &model->nPolygons); // allocate enough memory for this model model->pList = (polygon_t *)malloc(model->nPolygons * sizeof(polygon_t)); // read all polygon vertices for (int i=0; i < model->nPolygons * 3; i++) { model->pList->v[i].x = mmReadNextParam(&file); model->pList->v[i].y = mmReadNextParam(&file); model->pList->v[i].z = mmReadNextParam(&file); } fclose(file); } else { sprintf(sysBuffer, "LoadModelM(): Cannot open file \"%s\"", sFilename); SysShutdownMSG(sysBuffer); } }

The LoadModel(...); function loads an m file format model from file passed as the sFilename parameter into the specified address of an existing model_t structure. Note for convenience, that as a sample model we will use the model defined as model_t sampleModel; for most OpenGL tutorials. For example if we had an m-format file "model.m" and we wanted to load it into the sampleModel struct, we would make the following call:

LoadModel("model.m", &sampleModel); // ... at this point we're ready to display the model by calling DrawModel(&sampleModel);

There is nothing complex about the three remaining functions from model.cpp. These functions are DrawPolygon, DrawModel and FreeModel:

// DrawPolygon(); draws an opengl polygon defined by polygon_t struct void DrawPolygon (polygon_t *p) { glBegin(GL_TRIANGLES); glColor3f(1.0f, 1.0f, 1.0f); glVertex3f(p->v[0].x, p->v[0].y, p->v[0].z); glVertex3f(p->v[1].x, p->v[1].y, p->v[1].z); glVertex3f(p->v[2].x, p->v[2].y, p->v[2].z); glEnd(); }

DrawPolygon simply draws a polygon of type GL_TRIANGLE (remember, we are only using triangles in these tutorials, so whenever DrawPolygon is called, it is implied that we are drawing a triangle and not any other type of a polygon). For this purpose, the polygon_t struct actually defines only 3 vertices.

// DrawModel(); draws a model void DrawModel (model_t *model, vertex_t *translate, float r_anglex, float r_angley, float r_anglez) { glLoadIdentity(); glTranslatef(translate->x, translate->y, translate->z); glRotatef(r_anglex, 1.0f, 0.0f, 0.0f); glRotatef(r_angley, 0.0f, 1.0f, 0.0f); glRotatef(r_anglez, 0.0f, 0.0f, 1.0f); for (int i=0; inPolygons; i++) DrawPolygon(&model->pList[i]); }

DrawModel renders the model specified by the *model parameter to the screen. The translate vertex (here is actually used as a VECTOR, not a vertex, the struct definition is just identical to that of a vector; as both a vector and a vertex contain only the x,y,z coordinates and nothing else) specifies the position of the model in the global world space. In other words, the translate vector specifies which location [x,y,z] the model will be translated to before displaying it.

The r_anglex, r_angley and r_anglez parameters specify the rotation angles of the model around all of the 3 axis. To simply display the model in its original, non-rotated state, and slightly away from the camera, you would want to assign a value of [0.0f, 0.0f, -6.0f] to the translate vector and assign 0.0f to all of the 3 r_anglen parameters.

// FreeModel(); frees model memory void FreeModel (model_t *model) { if (model->pList) { free(model->pList); model->pList = NULL; } }

FreeModel simply frees the previously allocated memory of a specified model. This function should be called during the shutdown process of your application. To sum it all up here are the main steps to load, display and free an m file format model assuming the model we're working with is defined as model_t sampleModel:

// define the model model_t sampleModel; // ...somewhere before the main rendering loop begins // load "model.h" into sampleModel... LoadModel("model.m", &sampleModel); // ... within the main rendering loop // render the model to the screen... float modelAnglex = 0.0f; float modelAngley = 0.0f; float modelAnglez = 0.0f; float modelTranslate[3] = { 0.0f, 0.0f, -6.0f }; DrawModel(&sampleModel, &modelTranslate, modelAnglex, modelAngley, 0.0f); // ... during program shutdown, free memory... FreeModel(&sampleModel);

The source code combines all of the little pieces of information we've been previously talking about in the OpenGL tutorial series. Note, we're not dealing with a single cpp file anymore. The base code is split into a number of files. These files are:

glbase.h All important #includes. defined.h misc. definitions, such as word, dword, etc. dierr.h | dierr.cpp DirectInput error codes. directinput.h | directinput.cpp DirectInput implementation for mouse and keyboard user control (taken from my DirectInput Tutorial). glmain.h | glmain.cpp The main rendering loop. log.h | log.cpp System file logging as explained in c/misc. tutorial 1. model.h | model.cpp *.m file format model definition as explained in this tutorial. system.h some 'system' crap. winmain.h 'windows' crap.

Note the usage of directinput.h and directinput.cpp. These files are taken straight from my DirectInput tutorial, so that's already been explained. Just to let you know.

This is the defining point of the fallout OpenGL base code development so the next tutorial shows you the code structure, how everything fits together and how all files are linked to each other.

Take your time to look at the actual source code of this tutorial and read the following one. After you're done, you'll be ready to progress to the next quite important stage in the series of OpenGL tutorials which describes the OpenGL lighting system.

When you compile and run the source code, use the mouse to move the loaded object. By pressing and holding down the left mouse button you will be able to drag the object around its x and y axis.

By dragging the object fast enough and releasing the mouse button you will set the object in spinning motion defined by the direction of however you were dragging it. And that functionality's utilized by using the following function:

void GetInput (void) { I_GetMouse(); // rotate the model only when the left mouse button is pressed if (mse.state.rgbButtons[0]) { float prevx = modelAnglex; float prevy = modelAngley; modelAnglex += mse.state.lY; modelAngley += mse.state.lX; addAnglex = modelAnglex - prevx; addAngley = modelAngley - prevy; } else { modelAnglex += addAnglex; modelAngley += addAngley; } }

The model I'm using in this tutorial is a simple quad made out of two polygons. Sorry but I actually don't have time to create something more advanced right now. However, I'm kindly asking everyone who's reading my tutorials to contribute to this tutorial by designing something more complex than what is described by "model.m" in this tutorial.

So, open up "model.m" in notepad and hack something up if you have time. I will credit you right on these lines and will update this tutorial with your model. Send it to greg.sidelnikov@falloutsoftware.com.

OpenGL Tutorials:
OpenGL Tutorials - Full List Tutorial 0 - OpenGL Compiler Setup Tutorial 1 - 3D Graphics Introduction Tutorial 2 - Creating an OpenGL Window in Win32API Tutorial 3 - OpenGL Primitives, Triangles, Fans, Stripes, Vertices, Vertexes, Arrays Tutorial 4 -Working with OpenGL Color Tutorial 5 - Transforms, Transformations (Rotate, Move, Spin) in 3D Coordinate Space Tutorial 6 - How to Load 3D Model in OpenGL OpenGL Base Code (Starting Point) OpenGL Light: Ambient, Diffuse, Specular Highlight

© 2014 OpenGL Tutorials.

About | Privacy | Contact

Built with Tornado PHP FrameworkTornado PHP Framework with Wooden PHP Template template | Web design by Web Design by Greg.