Выбрать главу

For more information look at the source code. I tried to comment it as best as I could. Once the collision detection and response logic is understood, the source should become very clear. For any more info don't hesitate to contact me.

As I stated at the beginning of this tutorial, the subject of collision detection is a very difficult subject to cover in one tutorial. You will learn a lot in this tutorial, enough to create some pretty impressive demos of your own, but there is still alot more to learn on this subject. Now that you have the basics, all the other sources on Collision Detection and Physically Based Modeling out there should become easier to understand. With this said, I send you on your way and wish you happy collisions!!!

Some information about Dimitrios Christopoulos: He is currently working as a Virtual Reality software engineer at the Foundation of the Hellenic World in Athens/Greece (www.fhw.gr). Although Born in Germany, he studied in Greece at the University of Patras for a B.Sc. in Computer Engineering and Informatics. He holds also a MSc degree (honours) from the University of Hull (UK) in Computer Graphics and Virtual Environments. He did his first steps in game programming using Basic on an Commodore 64, and switched to C/C++/Assembly on the PC platform after the start of his studium. During the last few years OpenGL has become his graphics API of choice. For more information visit his site at: http://members.xoom.com/D_Christop.

Dimitrios Christopoulos
Jeff Molofee (NeHe)

* DOWNLOAD Visual C++ Code For This Lesson.

* DOWNLOAD Linux/GLX Code For This Lesson. (Conversion by Rodolphe Suescun

Lesson 31

Model Rendering Tutorial by Brett Porter (brettporter@yahoo.com)

The source for this project has been extracted from PortaLib3D, a library I have written to enable users to do things like displaying models with very little extra code. But so that you can trust such a library, you should understand what it is doing, so this tutorial aims to help with that.

The portions of PortaLib3D included here retain my copyright notices. This doesn't mean they can't be used by you – it means that if you cut-and-paste the code into your project, you have to give me proper credit. That's all. If you choose to read, understand, and re-implement the code yourself (and it is what you are encouraged to do if you are not actually using the library. You don't learn anything with cut-and-paste!), then you free yourself of that obligation. Let's face it, the code is nothing special. Ok, let's get onto something more interesting!

OpenGL Base Code

The OpenGL base code is in Lesson32.cpp. Mostly it came from Lesson 6, with a small modification to the loading of textures and the drawing routine. The changes will be discussed later.

Milkshape 3D

The model I use in this example is from Milkshape 3D. The reason I use this is because it is a damn fine modelling package, and it includes its file-format so it is easy to parse and understand. My next plan is to implement an Anim8or (http://www.anim8or.com) file reader because it is free and of course a 3DS reader.

However, the file format, while it will be described briefly here, is not the major concern for loading a model. You must create your own structures that are suitable to store the data, and then read the file into that. So first, let's describe the structures required for a model.

Model Data Structures

These model data structures come from the class Model in Model.h. First, and most important, we need vertices:

// Vertex Structure

struct Vertex {

 char m_boneID; // For Skeletal Animation

 float m_location[3];

};

// Vertices Used

int m_numVertices;

Vertex *m_pVertices;

For now, you can ignore the m_boneID variable — that will come in a future tutorial! The m_location array represents the coordinate of the vertex (X,Y,Z). The two variables store the number of vertices and the actual vertices in a dynamic array which is allocated by the loader.

Next we need to group these vertices into triangles:

// Triangle Structure

struct Triangle {

 float m_vertexNormals[3][3];

 float m_s[3], m_t[3];

 int m_vertexIndices[3];

};

// Triangles Used

int m_numTriangles;

Triangle *m_pTriangles;

Now, the 3 vertices that make up the triangle are stored in m_vertexIndices. These are offsets into the array of m_pVertices. This way each vertex need only be listed once, saving memory (and calculations when it comes to animating later). m_s and m_t are the (s,t) texture coordinates for each of the 3 vertices. The texture used is the one applied to this mesh (which is described next). Finally we have the m_vertexNormals member which stores the normal to each of the 3 vertices. Each normal has 3 float coordinates describing the vector.

The next structure we have in a model is a mesh. A mesh is a group of triangles that all have the same material applied to them. The collection of meshes make up the entire model. The mesh structure is as follows:

// Mesh s

truct Mesh {

 int m_materialIndex;

 int m_numTriangles;

 int *m_pTriangleIndices;

};

// Meshes Used

int m_numMeshes;

Mesh *m_pMeshes;

This time you have m_pTriangleIndices storing the triangles in the mesh in the same way as the triangle stored indicies to its vertices. It will be dynamically allocated because the number of triangles in a mesh is not known in advance, and is specified by m_numTriangles. Finally, m_materialIndex is the index of the material (texture and lighting coeffecients) to use for the mesh. I'll show you the material structure below:

// Material Properties

struct Material {

 float m_ambient[4], m_diffuse[4], m_specular[4], m_emissive[4];

 float m_shininess;

 GLuint m_texture;

 char *m_pTextureFilename;

};

// Materials Used

int m_numMaterials;

Material *m_pMaterials;

Here we have all the standard lighting coeffecients in the same format as OpenGL: ambient, diffuse, specular, emissive and shininess. We also have the texture object m_texture and the filename (dynamically allocated) of the texture so that it can be reloaded if the OpenGL context is lost.

The Code — Loading the Model

Now, on to loading the model. You will notice there is a pure virtual function called loadModelData, which takes the filename of the model as an argument. What happens is we create a derived class, MilkshapeModel, which implements this function, filling in the protected data structures mentioned above. Lets look at that function now:

bool MilkshapeModeclass="underline" :loadModelData(const char *filename) {

 ifstream inputFile(filename, ios::in | ios::binary | ios::nocreate);

 if (inputFile.fail()) return false; // "Couldn't Open The Model File."

First, the file is opened. It is a binary file, hence the ios::binary qualifier. If it is not found, the function returns false to indicate an error.

 inputFile.seekg(0, ios::end);