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);