MD2 Model Importer Overview

By Justin Klein, 2004

 

 

Overview

This class contains everything needed to load a quake2 (.MD2) model file through the interface described below.  Note that one instance of this class corresponds to one .MD2 file.  Skin image files must be in .PCX format.  The quality of the textures in OpenGL can be altered by changing the following to #defines:

 

#define MINFILTER GL_NEAREST_MIPMAP_NEAREST

#define MAGFILTER GL_LINEAR

 

The remainder of the API is as follows:

 

 

/**

  * Loads an .md2 model file.  Once this has successfully completed, skins can be applied and the model can be drawn.

  *     -modelFile should contain the filename of the .md2 file to load

  *     -generateNormals indicates if lighting normals should be generated for this model; note that doing so

  *      will drastically decrease rendering performance.

  *     -logProc can contain a pointer to a function that will be called to log messages for this model, or NULL

  *      to suppress logging.

  **/

      bool LoadCharacterModel(char *modelFile, bool generateNormals, void (*logProc)(char *));

 

 

/**

  * Frees the memory associated with our Quake2 Model, its textures, and its skins (if any)

  **/

      void UnloadCharacterModel();

 

 

/**

* Loads a skin, applies it to the model, and loads it into OpenGL.   This CANNOT be called until the model has been loaded.

  *         -skinFile must contain the filename of a .PCX image to apply as a texture.

  **/

      bool LoadCharacterSkins(char *skinFile);

 

 

/**

  * Frees this model's materials from OpenGL and removes its associations to them.  Once this has completed,

  * a different skin may be loaded into the same model.

  **/

      void UnloadCharacterSkins();

 

 

/**

  * Loads any materials associated with this model into OpenGL.  This is called automatically when a skin

  * is loaded, but if the textures ever need to be REinitialized, this can be used.  However, you MUST call

  * DeInitCharacterMaterials() before calling this, or it will have no effect (it only loads materials that have been cleared)

  **/

      bool InitCharacterMaterials();

 

 

/**

  * Unloads materials from OpenGL, but leaves them associated with the model.  This may be called if

  * the user wants to re-load the same skins into OpenGL (see InitCharacterMaterials()).

  **/

      void DeInitCharacterMaterials();

 

 

/**

  * Loads a Quake2 Weapon, attatching it to the character's hand (initially without a skin)

  *         -modelFile should be the *.md2 file of the weapon.

  *

  * NOTE that for weapons to work, the weapon-model must have the EXACT same set of animations,

  *      each with the EXACT same names, number of frames, keyframes, and so forth.  Otherwise,

  *      the animations will either not work, or the program will crash.

  **/

      bool LoadWeaponModel(char* modelFile);

 

 

/**

  * Frees the memory associated with our Quake2 weapon, and unloads its textures

  **/

      void UnloadWeaponModel();

 

 

/**

  * Loads a .PCX image and applies it to the Weapon model.

  * Materials are automatically initialized by this function.

  **/

      bool LoadWeaponSkins(char *skinFile);

 

 

/**

  * Frees this model's materials from OpenGL and removes its associations to them.  Once this has completed,

  * a different skin may be loaded into the same model.

  **/

      void UnloadWeaponSkins();

 

 

/**

  * Loads the model's skins into OpenGL.  This is called automatically when a skin is loaded, but if

  * the textures ever need to be REinitialized, this can be used.  However, you MUST call

  * DeInitCharacterMaterials() before calling this, or it will have no effect.

  **/

      bool InitWeaponMaterials();

 

 

/**

  * Unloads materials from OpenGL, but leaves them associated with the model.  This may be called if

  * the user wants to re-load the same skins into OpenGL (see InitWeaponMaterials()).

  **/

      void DeInitWeaponMaterials();

 

 

/**

  * Animates the model!  The animationSpeed parameter can be used to control how quickly each

* animation is displayed (a common value is around 5).  Time-based movement is handled internally

* by the model, using animationSpeed only as a scalar. Returns the number of triangles rendered.

  **/

      int DrawModel(int animationSpeed);

 

 

/**

  * Sets the current animation to the animation specified by the index;

  * If immediate is true, the change takes place instantly; else it switches once the current animation completes.

  **/

      void SetAnimation(int index, bool immediate);

 

 

/**

  * Cycles to the next animation.

  * If immediate is true, the change takes place instantly; else it switches once the current animation completes.

  **/

      void NextAnimation(bool immediate);

 

 

/**

  * Sets its parameter to the name of the animation that's currently being displayed,

  * and returns the number (index) of the current animation.

  * -1 is returned if there is no animation.  If the parameter is NULL, nothing will be copied there.

  **/

      int GetCurrentAnimation(char *name);

 

 

 

Low-Level Data Structures (structs):

 

mVector2f, mVector3f

Points in 2D and 3D space, respectively.

 

tFace

Each model, animated or static, contains a fixed number of “faces”, or “surfaces”, or “triangles.”  Each tFace struct represents one such surface; the struct itself contains 3 vertex indices and 3 texture-coordinate indices (for the 3 corners of a triangle) that refer to specific vertices in the model’s vertex arrays (see t3DFrameVerts).

 

t3DFrameVerts

Each still frame of an animated model has an equal-sized set of vertices and lighting normals that make up the model’s “still” figure for that frame.  Each t3DFrameVerts struct contains one of each of these arrays. These are the arrays indexed by the tFace structs (note that one vertex may be shared by multiple faces).  One t3DFrameVerts exists for each of the model’s frames of animation.  NOTE that for each vertex, the texture coordinates do NOT depend on the current frame, so texCoord arrays are stored in the model, not in each frame.

 

t3DMesh

A mesh is a complete 3D object, and contains

-Name

-An array of texture coordinates

-An array of t3DFrameVerts (one for each of the models’ animation frames)

-An integer representing the number of vertices PER t3DFrameVerts (this number is the same for every t3DFrameVert for this model)

-An array of faces in the mesh (indices into textureCoordinate array, and into vertexArray in the CURRENT t3DFrameVerts’ vertices)

 

tAnimationInfo

Holds information for a single model animation.  Since each model contains just a huge set of frames, this says which frames in the model correspond to which animations.  It includes:

-name

-startFrame

-endFrame

-FPS (for this animation, so we can interpolate between its frames)

 

tMaterialInfo

Holds information on one material, which is really one “skin”.  For quake2 models, only one skin may be applied to the entire model (quake3 models support multiple image files).  This struct contains the filename of the skin, and a pointer to where OpenGL will store it.

 

High-Level Data Structures (classes):

 

t3DModel

Contain information on an entire 3D model, including its animations, materials, etc.

-An array of tAnimationInfo’s (one per animation)

-An array of tMaterialInfo’s     (one per texture)

-An array of t3DMeshes (for Quake 2 models, each .md2 contains only one t3DMesh)

-Several indices used for determining the current animation, frame, etc.

-The following functions for manipulating the above data.  Note that this is NOT the top-level interface (see below):

void SetAnimation(int index, bool immediate);

void NextAnimation(bool immediate)

int GetCurrentAnimation(char* name)

bool InitMaterials()

void DeInitMaterials()

void RemoveMaterialsLists()

void UpdateTime(float speed)

void DrawCurrentFrame()

void Destroy()

 

MD2 Model

This is the highest level data structure: our complete model! The only data it contains are two t3DModels: one for a character, and one for a weapon.  Mainly, this is a set of helper functions to render the model, as well as a loader to fill out a t3DModel.  It does contain some intermediate data structures used for loading which won’t be discussed here.  The functions are:

bool LoadCharacterModel(char *modelFile, bool generateNormals, void (*logProc)(char *));

void UnloadCharacterModel();

bool LoadCharacterSkins(char *skinFile);

void UnloadCharacterSkins();

bool InitCharacterMaterials();

void DeInitCharacterMaterials();

 

bool LoadWeaponModel(char* modelFile);

void UnloadWeaponModel();

bool LoadWeaponSkins(char *skinFile);

void UnloadWeaponSkins();

bool InitWeaponMaterials();

void DeInitWeaponMaterials();

 

void DrawModel(int animationSpeed);

void SetAnimation(int index, bool immediate);

void NextAnimation(bool immediate);

int GetCurrentAnimation(char *name);