MDL file format
how to parse Quake 1 models (.MDL) and sample C source code for rendering in 3D
This is an archival mirror of the page "MDL file format (Quake's models)" by David Henry, published on 20 December 2004 (last accessed: 13 January 2021). Licensed under GNU Free Documentation License.
This is a technical file format specification article for programmers.
For 3D artist tutorials and files, see Quake resources # Modeling instead.

Introduction

The MDL file format is the model format used in Quake (June 1996). MDL model files' characteristics are these:
  • Model's geometric data (triangles);
  • 8 bits texture data;
  • Frame-by-frame animations;
A MDL file can hold multiple textures.
MDL model file's extension is .mdl. A MDL file is a binary file divided in two part: the header dans the data. The header contains all information needed to use and manipulate the data.

Variable sizes

Variable types used in this document have those sizes:
  • char: 1 byte
  • short: 2 bytes
  • int: 4 bytes
  • float: 4 bytes
  • ubyte: 1 unsigned byte
They correspond to C type sizes on the x86 architecture. Ensure that type sizes correspond to these ones if you're compiling for another architecture.

Endianess issues

Since the MDL file format is a binary format, you'll have to deal with endianess. MDL files are stored in little-endian (x86). If you're targetting a big-endian architecture (PowerPC, SPARC, ...), or simply want your program to be portable, you'll have to perform proper conversions for each word or double word read from the file.

Header

The header is a structure which comes at the beginning of the file:
1
/* MDL header */
2
struct mdl_header_t
3
{
4
int ident; /* magic number: "IDPO" */
5
int version; /* version: 6 */
6
7
vec3_t scale; /* scale factor */
8
vec3_t translate; /* translation vector */
9
float boundingradius;
10
vec3_t eyeposition; /* eyes' position */
11
12
int num_skins; /* number of textures */
13
int skinwidth; /* texture width */
14
int skinheight; /* texture height */
15
16
int num_verts; /* number of vertices */
17
int num_tris; /* number of triangles */
18
int num_frames; /* number of frames */
19
20
int synctype; /* 0 = synchron, 1 = random */
21
int flags; /* state flag */
22
float size;
23
};
Copied!
ident is the magic number of the file. It is used to identify the file type. ident must be equal to 1330660425 or to the string “IDPO”. We can obtain this number with the expression (('2'<<24) + ('P'<<16) + ('D'<<8) + 'I').
version is the version number of the file format and must be equal to 6.
scale and translate are needed to obtain the real vertex coordinates of the model. scale is a scale factor and translate a translation vector (or the origin of the model). You have to first multiply the respective value of scale with the vertex coordinate and then, add the respective value of translate to the result:
1
vreal[i] = (scale[i] * vertex[i]) + translate[i];
Copied!
where i ranges from 0 ou 2 (x, y and z coordinates).
boundingradius is the radius of a sphere in which the whole model can fit (used for collision detection for exemple).
eyeposition is... eyes' position (if the model is for a monster or other NPC). Make what you want of it.
num_skins is the number of textures present in the file. skinwidth and skinheight are respectively the with and height of the textures. All textures must have the same size.
num_verts is the number of vertices of one frame. num_tris is the number of triangles of the model. num_frames is the number of frames of the model.

Data types

Vector

The vector, composed of three floating coordinates (x, y, z):
1
/* Vector */
2
typedef float vec3_t[3];
Copied!

Texture information

Texture data come right after the header in the file. It can be a texture composed of a single picture or a group of pictures (animated texture).
1
/* Skin */
2
struct mdl_skin_t
3
{
4
int group; /* 0 = single, 1 = group */
5
GLubyte *data; /* texture data */
6
};
Copied!
or:
1
/* Group of pictures */
2
struct mdl_groupskin_t
3
{
4
int group; /* 1 = group */
5
int nb; /* number of pics */
6
float *time; /* time duration for each pic */
7
ubyte **data; /* texture data */
8
};
Copied!
time is an array of nb elements and data an array of nb arrays of skinwidth * skinheight elements (picture size).
Data pictures are contained in the data array and are in 8 bits color index mode. The colormap is generally in a separate LMP file (*.lmp). LMP files are binary files which contain only 768 bytes (256 colors in 24 bits). They are easy to use: just read the whole file in a buffer and it's done. (see colormap)
There are num_skins objects of mdl_skin_t type or mdl_groupskin_t type.

Texture coordinates

Texture coordinates are stored in a structure as short integers.
1
/* Texture coords */
2
struct mdl_texcoord_t
3
{
4
int onseam;
5
int s;
6
int t;
7
};
Copied!
Texture are generally divided in two pieces: one for the frontface of the model, and one for the backface. The backface piece must be translated of skinwidth/2 from the frontface piece.
onseam indicates if the vertex is on the boundary of two pieces.
To obtain real (s, t) coordinates (ranging from 0.0 to 1.0), you have to add 0.5 to the coordinates and then divide the result by skinwidth for s and skinheight for t.
There are num_verts (s, t) texture coordinates in a MDL model. Texture coordinate data come after texture data.

Triangles

Each triangle has an array of vertex indices and a flag to indicate if it is a frontface or a backface triangle.
1
/* Triangle info */
2
struct mdl_triangle_t
3
{
4
int facesfront; /* 0 = backface, 1 = frontface */
5
int vertex[3]; /* vertex indices */
6
};
Copied!
If a vertex which belong to a backface triangle is on the boundary of two pieces (onseam is true), you have to add skinwidth/2 to s in order to correct texture coordinates.
There are num_tris triangles in a MDL model. Triangle data follow texture coord. data in the file.

Vertices

Vertices are composed of “compressed” 3D coordinates, which are stored in one byte for each coordinate, and of a normal vector index. The normal vector array is stored in the anorms.h file of Quake and hold 162 vectors in floating point (3 float). (see anorms.h)
1
/* Compressed vertex */
2
struct mdl_vertex_t
3
{
4
unsigned char v[3];
5
unsigned char normalIndex;
6
};
Copied!

Frames

Each frames has its vertex list and some other specific informations.
1
/* Simple frame */
2
struct mdl_simpleframe_t
3
{
4
struct mdl_vertex_t bboxmin; /* bouding box min */
5
struct mdl_vertex_t bboxmax; /* bouding box max */
6
char name[16];
7
struct mdl_vertex_t *verts; /* vertex list of the frame */
8
};
Copied!
bboxmin and bboxmax define a box in which the model can fit. name is the name of the frame. verts is the vertex list of the frame.
Frames can be simple frames or groups of frames. We can know if it's a simple frame or a group with a flag:
1
/* Model frame */
2
struct mdl_frame_t
3
{
4
int type; /* 0 = simple, !0 = group */
5
struct mdl_simpleframe_t frame; /* this program can't read models
6
composed of group frames! */
7
};
Copied!
or:
1
/* Group of simple frames */
2
struct mdl_groupframe_t
3
{
4
int type; /* !0 = group */
5
struct mdl_vertex_t min; /* min pos in all simple frames */
6
struct mdl_vertex_t max; /* max pos in all simple frames */
7
float *time; /* time duration for each frame */
8
struct mdl_simpleframe_t *frames; /* simple frame list */
9
};
Copied!
time and frames are arrays of nb dimension. min and max correspond to the min and max positions in all simple frames of the frame group. time is the duration of each simple frame.
There are num_frames frames in a MDL model. Frames come after triangle data in the MDL file.

Reading a MDL file

Assuming that mdl_model_t is a structure holding all your model's data and *mdl a pointer on a mdl_model_t object, this code show how to load a MDL model file:
1
int
2
ReadMDLModel (const char *filename, struct mdl_model_t *mdl)
3
{
4
FILE *fp;
5
int i;
6
7
fp = fopen (filename, "rb");
8
if (!fp)
9
{
10
fprintf (stderr, "error: couldn't open \"%s\"!\n", filename);
11
return 0;
12
}
13
14
/* Read header */
15
fread (&mdl->header, 1, sizeof (struct mdl_header_t), fp);
16
17
if ((mdl->header.ident != 1330660425) ||
18
(mdl->header.version != 6))
19
{
20
/* Error! */
21
fprintf (stderr, "Error: bad version or identifier\n");
22
fclose (fp);
23
return 0;
24
}
25
26
/* Memory allocations */
27
mdl->skins = (struct mdl_skin_t *)
28
malloc (sizeof (struct mdl_skin_t) * mdl->header.num_skins);
29
mdl->texcoords = (struct mdl_texcoord_t *)
30
malloc (sizeof (struct mdl_texcoord_t) * mdl->header.num_verts);
31
mdl->triangles = (struct mdl_triangle_t *)
32
malloc (sizeof (struct mdl_triangle_t) * mdl->header.num_tris);
33
mdl->frames = (struct mdl_frame_t *)
34
malloc (sizeof (struct mdl_frame_t) * mdl->header.num_frames);
35
mdl->tex_id = (GLuint *)
36
malloc (sizeof (GLuint) * mdl->header.num_skins);
37
38
mdl->iskin = 0;
39
40
/* Read texture data */
41
for (i = 0; i < mdl->header.num_skins; ++i)
42
{
43
mdl->skins[i].data = (GLubyte *)malloc (sizeof (GLubyte)
44
* mdl->header.skinwidth * mdl->header.skinheight);
45
46
fread (&mdl->skins[i].group, sizeof (int), 1, fp);
47
fread (mdl->skins[i].data, sizeof (GLubyte),
48
mdl->header.skinwidth * mdl->header.skinheight, fp);
49
50
mdl->tex_id[i] = MakeTextureFromSkin (i, mdl);
51
52
free (mdl->skins[i].data);
53
mdl->skins[i].data = NULL;
54
}
55
56
fread (mdl->texcoords, sizeof (struct mdl_texcoord_t),
57
mdl->header.num_verts, fp);
58
fread (mdl->triangles, sizeof (struct mdl_triangle_t),
59
mdl->header.num_tris, fp);
60
61
/* Read frames */
62
for (i = 0; i < mdl->header.num_frames; ++i)
63
{
64
/* Memory allocation for vertices of this frame */
65
mdl->frames[i].frame.verts = (struct mdl_vertex_t *)
66
malloc (sizeof (struct mdl_vertex_t) * mdl->header.num_verts);
67
68
/* Read frame data */
69
fread (&mdl->frames[i].type, sizeof (int), 1, fp);
70
fread (&mdl->frames[i].frame.bboxmin,
71
sizeof (struct mdl_vertex_t), 1, fp);
72
fread (&mdl->frames[i].frame.bboxmax,
73
sizeof (struct mdl_vertex_t), 1, fp);
74
fread (mdl->frames[i].frame.name, sizeof (char), 16, fp);
75
fread (mdl->frames[i].frame.verts, sizeof (struct mdl_vertex_t),
76
mdl->header.num_verts, fp);
77
}
78
79
fclose (fp);
80
return 1;
81
}
Copied!
Note: this code can't handle MDL files with group frames.

Rendering the model

Here is an example of how to draw a frame n of a model mdl:
1
void
2
RenderFrame (int n, const struct mdl_model_t *mdl)
3
{
4
int i, j;
5
GLfloat s, t;
6
vec3_t v;
7
struct mdl_vertex_t *pvert;
8
9
/* Check if n is in a valid range */
10
if ((n < 0) || (n > mdl->header.num_frames - 1))
11
return;
12
13
/* Enable model's texture */
14
glBindTexture (GL_TEXTURE_2D, mdl->tex_id[mdl->iskin]);
15
16
/* Draw the model */
17
glBegin (GL_TRIANGLES);
18
/* Draw each triangle */
19
for (i = 0; i < mdl->header.num_tris; ++i)
20
{
21
/* Draw each vertex */
22
for (j = 0; j < 3; ++j)
23
{
24
pvert = &mdl->frames[n].frame.verts[mdl->triangles[i].vertex[j]];
25
26
/* Compute texture coordinates */
27
s = (GLfloat)mdl->texcoords[mdl->triangles[i].vertex[j]].s;
28
t = (GLfloat)mdl->texcoords[mdl->triangles[i].vertex[j]].t;
29
30
if (!mdl->triangles[i].facesfront &&
31
mdl->texcoords[mdl->triangles[i].vertex[j]].onseam)
32
{
33
s += mdl->header.skinwidth * 0.5f; /* Backface */
34
}
35
36
/* Scale s and t to range from 0.0 to 1.0 */
37
s = (s + 0.5) / mdl->header.skinwidth;
38
t = (t + 0.5) / mdl->header.skinheight;
39
40
/* Pass texture coordinates to OpenGL */
41
glTexCoord2f (s, t);
42
43
/* Normal vector */
44
glNormal3fv (anorms_table[pvert->normalIndex]);
45
46
/* Calculate real vertex position */
47
v[0] = (mdl->header.scale[0] * pvert->v[0]) + mdl->header.translate[0];
48
v[1] = (mdl->header.scale[1] * pvert->v[1]) + mdl->header.translate[1];
49
v[2] = (mdl->header.scale[2] * pvert->v[2]) + mdl->header.translate[2];
50
51
glVertex3fv (v);
52
}
53
}
54
glEnd ();
55
}
Copied!

Animation

MDL models are frame-by-frame animated. A frame is a screenshot of an animation. To avoid jerked and ugly animations, we use linear interpolation between vertex coordinates of two consecutive frames (the current frame we are drawing and the next frame). We do the same for the normal vector:
1
struct mdl_vertex_t *pvert1, *pvert2;
2
vec3_t v;
3
4
for (/* ... */)
5
{
6
pvert1 = &mdl->frames[current].frame.verts[mdl->triangles[i].vertex[j]];
7
pvert2 = &mdl->frames[current + 1].frame.verts[mdl->triangles[i].vertex[j]];
8
9
/* ... */
10
11
v[0] = mdl->header.scale[0] * (pvert1->v[0] + interp * (pvert2->v[0] - pvert1->v[0])) + mdl->header.translate[0];
12
v[1] = mdl->header.scale[1] * (pvert1->v[1] + interp * (pvert2->v[1] - pvert1->v[1])) + mdl->header.translate[1];
13
v[2] = mdl->header.scale[2] * (pvert1->v[2] + interp * (pvert2->v[2] - pvert1->v[2])) + mdl->header.translate[2];
14
15
/* ... */
16
}
Copied!
v is the final vertex to draw. interp is the interpolation percent between the two frames. It's a float which ranges from 0.0 to 1.0. When it is equal to 1.0, current is incremented by 1 and interp is reinitialized at 0.0. It is useless to interpolate texture coordinates because they are the same for all the model frames. It is preferable that interp is related to the program's number of rendering frame per second (fps).
1
void
2
Animate (int start, int end, int *frame, float *interp)
3
{
4
if ((*frame < start) || (*frame > end))
5
*frame = start;
6
7
if (*interp >= 1.0f)
8
{
9
/* Move to next frame */
10
*interp = 0.0f;
11
(*frame)++;
12
13
if (*frame >= end)
14
*frame = start;
15
}
16
}
Copied!

Constants

Here are some constant values defining maximal dimensions:
  • Maximum number of triangles: 2048
  • Maximum number of vertices: 1024
  • Maximum number of texture coordinates: 1024
  • Maximum number of frames: 256
  • Number of precalculated normal vectors: 162

Sample C code

mdl.c

Full example C code for loading MDL files.
mdl.c
1
/*
2
* mdl.c -- mdl model loader
3
* last modification: mar. 21, 2015
4
*
5
* Copyright (c) 2005-2015 David HENRY
6
*
7
* Permission is hereby granted, free of charge, to any person
8
* obtaining a copy of this software and associated documentation
9
* files (the "Software"), to deal in the Software without
10
* restriction, including without limitation the rights to use,
11
* copy, modify, merge, publish, distribute, sublicense, and/or
12
* sell copies of the Software, and to permit persons to whom the
13
* Software is furnished to do so, subject to the following conditions:
14
*
15
* The above copyright notice and this permission notice shall be
16
* included in all copies or substantial portions of the Software.
17
*
18
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
* NONINFRINGEMENT.
22
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
23
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
24
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
*
27
* gcc -Wall -ansi -lGL -lGLU -lglut mdl.c -o mdl
28
*/
29
30
#include <GL/glut.h>
31
#include <stdio.h>
32
#include <stdlib.h>
33
#include <string.h>
34
35
36
/* Vector */
37
typedef float vec3_t[3];
38
39
/* MDL header */
40
struct mdl_header_t
41
{
42
int ident; /* magic number: "IDPO" */
43
int version; /* version: 6 */
44
45
vec3_t scale; /* scale factor */
46
vec3_t translate; /* translation vector */
47
float boundingradius;
48
vec3_t eyeposition; /* eyes' position */
49
50
int num_skins; /* number of textures */
51
int skinwidth; /* texture width */
52
int skinheight; /* texture height */
53
54
int num_verts; /* number of vertices */
55
int num_tris; /* number of triangles */
56
int num_frames; /* number of frames */
57
58
int synctype; /* 0 = synchron, 1 = random */
59
int flags; /* state flag */
60
float size;
61
};
62
63
/* Skin */
64
struct mdl_skin_t
65
{
66
int group; /* 0 = single, 1 = group */
67
GLubyte *data; /* texture data */
68
};
69
70
/* Texture coords */
71
struct mdl_texcoord_t
72
{
73
int onseam;
74
int s;
75
int t;
76
};
77
78
/* Triangle info */
79
struct mdl_triangle_t
80
{
81
int facesfront; /* 0 = backface, 1 = frontface */
82
int vertex[3]; /* vertex indices */
83
};
84
85
/* Compressed vertex */
86
struct mdl_vertex_t
87
{
88
unsigned char v[3];
89
unsigned char normalIndex;
90
};
91
92
/* Simple frame */
93
struct mdl_simpleframe_t
94
{
95
struct mdl_vertex_t bboxmin; /* bouding box min */
96
struct mdl_vertex_t bboxmax; /* bouding box max */
97
char name[16];
98
struct mdl_vertex_t *verts; /* vertex list of the frame */
99
};
100
101
/* Model frame */
102
struct mdl_frame_t
103
{
104
int type; /* 0 = simple, !0 = group */
105
struct mdl_simpleframe_t frame; /* this program can't read models
106
composed of group frames! */
107
};
108
109
/* MDL model structure */
110
struct mdl_model_t
111
{
112
struct mdl_header_t header;
113
114
struct mdl_skin_t *skins;
115
struct mdl_texcoord_t *texcoords;
116
struct mdl_triangle_t *triangles;
117
struct mdl_frame_t *frames;
118
119
GLuint *tex_id;
120
int iskin;
121
};
122
123
/* Table of precalculated normals */
124
vec3_t anorms_table[162] = {
125
#include "anorms.h"
126
};
127
128
/* Palette */
129
unsigned char colormap[256][3] = {
130
#include "colormap.h"
131
};
132
133
/*** An MDL model ***/
134
struct mdl_model_t mdlfile;
135
136
137
/**
138
* Make a texture given a skin index 'n'.
139
*/
140
GLuint
141
MakeTextureFromSkin (int n, const struct mdl_model_t *mdl)
142
{
143
int i;
144
GLuint id;
145
146
GLubyte *pixels = (GLubyte *)
147
malloc (mdl->header.skinwidth * mdl->header.skinheight * 3);
148
149
/* Convert indexed 8 bits texture to RGB 24 bits */
150
for (i = 0; i < mdl->header.skinwidth * mdl->header.skinheight; ++i)
151
{
152
pixels[(i * 3) + 0] = colormap[mdl->skins[n].data[i]][0];
153
pixels[(i * 3) + 1] = colormap[mdl->skins[n].data[i]][1];
154
pixels[(i * 3) + 2] = colormap[mdl->skins[n].data[i]][2];
155
}
156
157
/* Generate OpenGL texture */
158
glGenTextures (1, &id);
159
glBindTexture (GL_TEXTURE_2D, id);
160
161
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
162
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
163
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
164
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
165
166
gluBuild2DMipmaps (GL_TEXTURE_2D, GL_RGB, mdl->header.skinwidth,
167
mdl->header.skinheight, GL_RGB, GL_UNSIGNED_BYTE,
168
pixels);
169
170
/* OpenGL has its own copy of image data */
171
free (pixels);
172
return id;
173
}
174
175
/**
176
* Load an MDL model from file.
177
*
178
* Note: MDL format stores model's data in little-endian ordering. On
179
* big-endian machines, you'll have to perform proper conversions.
180
*/
181
int
182
ReadMDLModel (const char *filename, struct mdl_model_t *mdl)
183
{
184
FILE *fp;
185
int i;
186
187
fp = fopen (filename, "rb");
188
if (!fp)
189
{
190
fprintf (stderr, "error: couldn't open \"%s\"!\n", filename);
191
return 0;
192
}
193
194
/* Read header */
195
fread (&mdl->header, 1, sizeof (struct mdl_header_t), fp);
196
197
if ((mdl->header.ident != 1330660425) ||
198
(mdl->header.version != 6))
199
{
200
/* Error! */
201
fprintf (stderr, "Error: bad version or identifier\n");
202
fclose (fp);
203
return 0;
204
}
205
206
/* Memory allocations */
207
mdl->skins = (struct mdl_skin_t *)
208
malloc (sizeof (struct mdl_skin_t) * mdl->header.num_skins);
209
mdl->texcoords = (struct mdl_texcoord_t *)
210
malloc (sizeof (struct mdl_texcoord_t) * mdl->header.num_verts);
211
mdl->triangles = (struct mdl_triangle_t *)
212
malloc (sizeof (struct mdl_triangle_t) * mdl->header.num_tris);
213
mdl->frames = (struct mdl_frame_t *)
214
malloc (sizeof (struct mdl_frame_t) * mdl->header.num_frames);
215
mdl->tex_id = (GLuint *)
216
malloc (sizeof (GLuint) * mdl->header.num_skins);
217
218
mdl->iskin = 0;
219
220
/* Read texture data */
221
for (i = 0; i < mdl->header.num_skins; ++i)
222
{
223
mdl->skins[i].data = (GLubyte *)malloc (sizeof (GLubyte)
224
* mdl->header.skinwidth * mdl->header.skinheight);
225
226
fread (&mdl->skins[i].group, sizeof (int), 1, fp);
227
fread (mdl->skins[i].data, sizeof (GLubyte),
228
mdl->header.skinwidth * mdl->header.skinheight, fp);
229
230
mdl->tex_id[i] = MakeTextureFromSkin (i, mdl);
231
232
free (mdl->skins[i].data);
233
mdl->skins[i].data = NULL;
234
}
235
236
fread (mdl->texcoords, sizeof (struct mdl_texcoord_t),
237
mdl->header.num_verts, fp);
238
fread (mdl->triangles, sizeof (struct mdl_triangle_t),
239
mdl->header.num_tris, fp);
240
241
/* Read frames */
242
for (i = 0; i < mdl->header.num_frames; ++i)
243
{
244
/* Memory allocation for vertices of this frame */
245
mdl->frames[i].frame.verts = (struct mdl_vertex_t *)
246
malloc (sizeof (struct mdl_vertex_t) * mdl->header.num_verts);
247
248
/* Read frame data */
249
fread (&mdl->frames[i].type, sizeof (int), 1, fp);
250
fread (&mdl->frames[i].frame.bboxmin,
251
sizeof (struct mdl_vertex_t), 1, fp);
252
fread (&mdl->frames[i].frame.bboxmax,
253
sizeof (struct mdl_vertex_t), 1, fp);
254
fread (mdl->frames[i].frame.name, sizeof (char), 16, fp);
255
fread (mdl->frames[i].frame.verts, sizeof (struct mdl_vertex_t),
256
mdl->header.num_verts, fp);
257
}
258
259
fclose (fp);
260
return 1;
261
}
262
263
/**
264
* Free resources allocated for the model.
265
*/
266
void
267
FreeModel (struct mdl_model_t *mdl)
268
{
269
int i;
270
271
if (mdl->skins)
272
{
273
free (mdl->skins);
274
mdl->skins = NULL;
275
}
276
277
if (mdl->texcoords)
278
{
279
free (mdl->texcoords);
280
mdl->texcoords = NULL;
281
}
282
283
if (mdl->triangles)
284
{
285
free (mdl->triangles);
286
mdl->triangles = NULL;
287
}
288
289
if (mdl->tex_id)
290
{
291
/* Delete OpenGL textures */
292
glDeleteTextures (mdl->header.num_skins, mdl->tex_id);
293
294
free (mdl->tex_id);
295
mdl->tex_id = NULL;
296
}
297
298
if (mdl->frames)
299
{
300
for (i = 0; i < mdl->header.num_frames; ++i)
301
{
302
free (mdl->frames[i].frame.verts);
303
mdl->frames[i].frame.verts = NULL;
304
}
305
306
free (mdl->frames);
307
mdl->frames = NULL;
308
}
309
}
310
311
/**
312
* Render the model at frame n.
313
*/
314
void
315
RenderFrame (int n, const struct mdl_model_t *mdl)
316
{
317
int i, j;
318
GLfloat s, t;
319
vec3_t v;
320
struct mdl_vertex_t *pvert;
321
322
/* Check if n is in a valid range */
323
if ((n < 0) || (n > mdl->header.num_frames - 1))
324
return;
325
326
/* Enable model's texture */
327
glBindTexture (GL_TEXTURE_2D, mdl->tex_id[mdl->iskin]);
328
329
/* Draw the model */
330
glBegin (GL_TRIANGLES);
331
/* Draw each triangle */
332
for (i = 0; i < mdl->header.num_tris; ++i)
333
{
334
/* Draw each vertex */
335
for (j = 0; j < 3; ++j)
336
{
337
pvert = &mdl->frames[n].frame.verts[mdl->triangles[i].vertex[j]];
338
339
/* Compute texture coordinates */
340
s = (GLfloat)mdl->texcoords[mdl->triangles[i].vertex[j]].s;
341
t = (GLfloat)mdl->texcoords[mdl->triangles[i].vertex[j]].t;
342
343
if (!mdl->triangles[i].facesfront &&
344
mdl->texcoords[mdl->triangles[i].vertex[j]].onseam)
345
{
346
s += mdl->header.skinwidth * 0.5f; /* Backface */
347
}
348
349
/* Scale s and t to range from 0.0 to 1.0 */
350
s = (s + 0.5) / mdl->header.skinwidth;
351
t = (t + 0.5) / mdl->header.skinheight;
352
353
/* Pass texture coordinates to OpenGL */
354
glTexCoord2f (s, t);
355
356
/* Normal vector */
357
glNormal3fv (anorms_table[pvert->normalIndex]);
358
359
/* Calculate real vertex position */
360
v[0] = (mdl->header.scale[0] * pvert->v[0]) + mdl->header.translate[0];
361
v[1] = (mdl->header.scale[1] * pvert->v[1]) + mdl->header.translate[1];
362
v[2] = (mdl->header.scale[2] * pvert->v[2]) + mdl->header.translate[2];
363
364
glVertex3fv (v);
365
}
366
}
367
glEnd ();
368
}
369
370
/**
371
* Render the model with interpolation between frame n and n+1.
372
* interp is the interpolation percent. (from 0.0 to 1.0)
373
*/
374
void
375
RenderFrameItp (int n, float interp, const struct mdl_model_t *mdl)
376
{
377
int i, j;
378
GLfloat s, t;
379
vec3_t norm, v;
380
GLfloat *n_curr, *n_next;
381
struct mdl_vertex_t *pvert1, *pvert2;
382
383
/* Check if n is in a valid range */
384
if ((n < 0) || (n > mdl->header.num_frames))
385
return;
386
387
/* Enable model's texture */
388
glBindTexture (GL_TEXTURE_2D, mdl->tex_id[mdl->iskin]);
389
390
/* Draw the model */
391
glBegin (GL_TRIANGLES);
392
/* Draw each triangle */
393
for (i = 0; i < mdl->header.num_tris; ++i)
394
{
395
/* Draw each vertex */
396
for (j = 0; j < 3; ++j)
397
{
398
pvert1 = &mdl->frames[n].frame.verts[mdl->triangles[i].vertex[j]];
399
pvert2 = &mdl->frames[n + 1].frame.verts[mdl->triangles[i].vertex[j]];
400
401
/* Compute texture coordinates */
402
s = (GLfloat)mdl->texcoords[mdl->triangles[i].vertex[j]].s;
403
t = (GLfloat)mdl->texcoords[mdl->triangles[i].vertex[j]].t;
404
405
if (!mdl->triangles[i].facesfront &&
406
mdl->texcoords[mdl->triangles[i].vertex[j]].onseam)
407
{
408
s += mdl->header.skinwidth * 0.5f; /* Backface */
409
}
410
411
/* Scale s and t to range from 0.0 to 1.0 */
412
s = (s + 0.5) / mdl->header.skinwidth;
413
t = (t + 0.5) / mdl->header.skinheight;
414
415
/* Pass texture coordinates to OpenGL */
416
glTexCoord2f (s, t);
417
418
/* Interpolate normals */
419
n_curr = anorms_table[pvert1->normalIndex];
420
n_next = anorms_table[pvert2->normalIndex];
421
422
norm[0] = n_curr[0] + interp * (n_next[0] - n_curr[0]);
423
norm[1] = n_curr[1] + interp * (n_next[1] - n_curr[1]);
424
norm[2] = n_curr[2] + interp * (n_next[2] - n_curr[2]);
425
426
glNormal3fv (norm);
427
428
/* Interpolate vertices */
429
v[0] = mdl->header.scale[0] * (pvert1->v[0] + interp
430
* (pvert2->v[0] - pvert1->v[0])) + mdl->header.translate[0];
431
v[1] = mdl->header.scale[1] * (pvert1->v[1] + interp
432
* (pvert2->v[1] - pvert1->v[1])) + mdl->header.translate[1];
433
v[2] = mdl->header.scale[2] * (pvert1->v[2] + interp
434
* (pvert2->v[2] - pvert1->v[2]))