Created
February 28, 2014 17:46
-
-
Save gszauer/9275857 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* md5.c -- md5mesh model loader | |
* last modification: aug. 14, 2007 | |
* | |
* Copyright (c) 2005-2007 David HENRY | |
* | |
* Permission is hereby granted, free of charge, to any person | |
* obtaining a copy of this software and associated documentation | |
* files (the "Software"), to deal in the Software without | |
* restriction, including without limitation the rights to use, | |
* copy, modify, merge, publish, distribute, sublicense, and/or | |
* sell copies of the Software, and to permit persons to whom the | |
* Software is furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be | |
* included in all copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
* NONINFRINGEMENT. | |
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR | |
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
* | |
* gcc -Wall -ansi -lGL -lGLU -lglut md5.c -o md5 | |
*/ | |
#include <GL/glut.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <math.h> | |
/* Vectors */ | |
typedef float vec2_t[2]; | |
typedef float vec3_t[3]; | |
/* Quaternion (x, y, z, w) */ | |
typedef float quat4_t[4]; | |
enum { | |
X, Y, Z, W | |
}; | |
/* Joint */ | |
struct md5_joint_t | |
{ | |
char name[64]; | |
int parent; | |
vec3_t pos; | |
quat4_t orient; | |
}; | |
/* Vertex */ | |
struct md5_vertex_t | |
{ | |
vec2_t st; | |
int start; /* start weight */ | |
int count; /* weight count */ | |
}; | |
/* Triangle */ | |
struct md5_triangle_t | |
{ | |
int index[3]; | |
}; | |
/* Weight */ | |
struct md5_weight_t | |
{ | |
int joint; | |
float bias; | |
vec3_t pos; | |
}; | |
/* MD5 mesh */ | |
struct md5_mesh_t | |
{ | |
struct md5_vertex_t *vertices; | |
struct md5_triangle_t *triangles; | |
struct md5_weight_t *weights; | |
int num_verts; | |
int num_tris; | |
int num_weights; | |
char shader[256]; | |
}; | |
/* MD5 model structure */ | |
struct md5_model_t | |
{ | |
struct md5_joint_t *baseSkel; | |
struct md5_mesh_t *meshes; | |
int num_joints; | |
int num_meshes; | |
}; | |
/*** An MD5 model ***/ | |
struct md5_model_t md5file; | |
/* vertex array related stuff */ | |
int max_verts = 0; | |
int max_tris = 0; | |
vec3_t *vertexArray = NULL; | |
GLuint *vertexIndices = NULL; | |
/** | |
* Basic quaternion operations. | |
*/ | |
void | |
Quat_computeW (quat4_t q) | |
{ | |
float t = 1.0f - (q[X] * q[X]) - (q[Y] * q[Y]) - (q[Z] * q[Z]); | |
if (t < 0.0f) | |
q[W] = 0.0f; | |
else | |
q[W] = -sqrt (t); | |
} | |
void | |
Quat_normalize (quat4_t q) | |
{ | |
/* Compute magnitude of the quaternion */ | |
float mag = sqrt ((q[X] * q[X]) + (q[Y] * q[Y]) | |
+ (q[Z] * q[Z]) + (q[W] * q[W])); | |
/* Check for bogus length, to protect against divide by zero */ | |
if (mag > 0.0f) | |
{ | |
/* Normalize it */ | |
float oneOverMag = 1.0f / mag; | |
q[W] *= oneOverMag; | |
q[X] *= oneOverMag; | |
q[Y] *= oneOverMag; | |
q[Z] *= oneOverMag; | |
} | |
} | |
void | |
Quat_multQuat (const quat4_t qa, const quat4_t qb, quat4_t out) | |
{ | |
out[W] = (qa[W] * qb[W]) - (qa[X] * qb[X]) - (qa[Y] * qb[Y]) - (qa[Z] * qb[Z]); | |
out[X] = (qa[X] * qb[W]) + (qa[W] * qb[X]) + (qa[Y] * qb[Z]) - (qa[Z] * qb[Y]); | |
out[Y] = (qa[Y] * qb[W]) + (qa[W] * qb[Y]) + (qa[Z] * qb[X]) - (qa[X] * qb[Z]); | |
out[Z] = (qa[Z] * qb[W]) + (qa[W] * qb[Z]) + (qa[X] * qb[Y]) - (qa[Y] * qb[X]); | |
} | |
void | |
Quat_multVec (const quat4_t q, const vec3_t v, quat4_t out) | |
{ | |
out[W] = - (q[X] * v[X]) - (q[Y] * v[Y]) - (q[Z] * v[Z]); | |
out[X] = (q[W] * v[X]) + (q[Y] * v[Z]) - (q[Z] * v[Y]); | |
out[Y] = (q[W] * v[Y]) + (q[Z] * v[X]) - (q[X] * v[Z]); | |
out[Z] = (q[W] * v[Z]) + (q[X] * v[Y]) - (q[Y] * v[X]); | |
} | |
void | |
Quat_rotatePoint (const quat4_t q, const vec3_t in, vec3_t out) | |
{ | |
quat4_t tmp, inv, final; | |
inv[X] = -q[X]; inv[Y] = -q[Y]; | |
inv[Z] = -q[Z]; inv[W] = q[W]; | |
Quat_normalize (inv); | |
Quat_multVec (q, in, tmp); | |
Quat_multQuat (tmp, inv, final); | |
out[X] = final[X]; | |
out[Y] = final[Y]; | |
out[Z] = final[Z]; | |
} | |
/** | |
* Load an MD5 model from file. | |
*/ | |
int | |
ReadMD5Model (const char *filename, struct md5_model_t *mdl) | |
{ | |
FILE *fp; | |
char buff[512]; | |
int version; | |
int curr_mesh = 0; | |
int i; | |
fp = fopen (filename, "rb"); | |
if (!fp) | |
{ | |
fprintf (stderr, "Error: couldn't open \"%s\"!\n", filename); | |
return 0; | |
} | |
while (!feof (fp)) | |
{ | |
/* Read whole line */ | |
fgets (buff, sizeof (buff), fp); | |
if (sscanf (buff, " MD5Version %d", &version) == 1) | |
{ | |
if (version != 10) | |
{ | |
/* Bad version */ | |
fprintf (stderr, "Error: bad model version\n"); | |
fclose (fp); | |
return 0; | |
} | |
} | |
else if (sscanf (buff, " numJoints %d", &mdl->num_joints) == 1) | |
{ | |
if (mdl->num_joints > 0) | |
{ | |
/* Allocate memory for base skeleton joints */ | |
mdl->baseSkel = (struct md5_joint_t *) | |
calloc (mdl->num_joints, sizeof (struct md5_joint_t)); | |
} | |
} | |
else if (sscanf (buff, " numMeshes %d", &mdl->num_meshes) == 1) | |
{ | |
if (mdl->num_meshes > 0) | |
{ | |
/* Allocate memory for meshes */ | |
mdl->meshes = (struct md5_mesh_t *) | |
calloc (mdl->num_meshes, sizeof (struct md5_mesh_t)); | |
} | |
} | |
else if (strncmp (buff, "joints {", 8) == 0) | |
{ | |
/* Read each joint */ | |
for (i = 0; i < mdl->num_joints; ++i) | |
{ | |
struct md5_joint_t *joint = &mdl->baseSkel[i]; | |
/* Read whole line */ | |
fgets (buff, sizeof (buff), fp); | |
if (sscanf (buff, "%s %d ( %f %f %f ) ( %f %f %f )", | |
joint->name, &joint->parent, &joint->pos[0], | |
&joint->pos[1], &joint->pos[2], &joint->orient[0], | |
&joint->orient[1], &joint->orient[2]) == 8) | |
{ | |
/* Compute the w component */ | |
Quat_computeW (joint->orient); | |
} | |
} | |
} | |
else if (strncmp (buff, "mesh {", 6) == 0) | |
{ | |
struct md5_mesh_t *mesh = &mdl->meshes[curr_mesh]; | |
int vert_index = 0; | |
int tri_index = 0; | |
int weight_index = 0; | |
float fdata[4]; | |
int idata[3]; | |
while ((buff[0] != '}') && !feof (fp)) | |
{ | |
/* Read whole line */ | |
fgets (buff, sizeof (buff), fp); | |
if (strstr (buff, "shader ")) | |
{ | |
int quote = 0, j = 0; | |
/* Copy the shader name whithout the quote marks */ | |
for (i = 0; i < sizeof (buff) && (quote < 2); ++i) | |
{ | |
if (buff[i] == '\"') | |
quote++; | |
if ((quote == 1) && (buff[i] != '\"')) | |
{ | |
mesh->shader[j] = buff[i]; | |
j++; | |
} | |
} | |
} | |
else if (sscanf (buff, " numverts %d", &mesh->num_verts) == 1) | |
{ | |
if (mesh->num_verts > 0) | |
{ | |
/* Allocate memory for vertices */ | |
mesh->vertices = (struct md5_vertex_t *) | |
malloc (sizeof (struct md5_vertex_t) * mesh->num_verts); | |
} | |
if (mesh->num_verts > max_verts) | |
max_verts = mesh->num_verts; | |
} | |
else if (sscanf (buff, " numtris %d", &mesh->num_tris) == 1) | |
{ | |
if (mesh->num_tris > 0) | |
{ | |
/* Allocate memory for triangles */ | |
mesh->triangles = (struct md5_triangle_t *) | |
malloc (sizeof (struct md5_triangle_t) * mesh->num_tris); | |
} | |
if (mesh->num_tris > max_tris) | |
max_tris = mesh->num_tris; | |
} | |
else if (sscanf (buff, " numweights %d", &mesh->num_weights) == 1) | |
{ | |
if (mesh->num_weights > 0) | |
{ | |
/* Allocate memory for vertex weights */ | |
mesh->weights = (struct md5_weight_t *) | |
malloc (sizeof (struct md5_weight_t) * mesh->num_weights); | |
} | |
} | |
else if (sscanf (buff, " vert %d ( %f %f ) %d %d", &vert_index, | |
&fdata[0], &fdata[1], &idata[0], &idata[1]) == 5) | |
{ | |
/* Copy vertex data */ | |
mesh->vertices[vert_index].st[0] = fdata[0]; | |
mesh->vertices[vert_index].st[1] = fdata[1]; | |
mesh->vertices[vert_index].start = idata[0]; | |
mesh->vertices[vert_index].count = idata[1]; | |
} | |
else if (sscanf (buff, " tri %d %d %d %d", &tri_index, | |
&idata[0], &idata[1], &idata[2]) == 4) | |
{ | |
/* Copy triangle data */ | |
mesh->triangles[tri_index ].index[0] = idata[0]; | |
mesh->triangles[tri_index ].index[1] = idata[1]; | |
mesh->triangles[tri_index ].index[2] = idata[2]; | |
} | |
else if (sscanf (buff, " weight %d %d %f ( %f %f %f )", | |
&weight_index, &idata[0], &fdata[3], | |
&fdata[0], &fdata[1], &fdata[2]) == 6) | |
{ | |
/* Copy vertex data */ | |
mesh->weights[weight_index].joint = idata[0]; | |
mesh->weights[weight_index].bias = fdata[3]; | |
mesh->weights[weight_index].pos[0] = fdata[0]; | |
mesh->weights[weight_index].pos[1] = fdata[1]; | |
mesh->weights[weight_index].pos[2] = fdata[2]; | |
} | |
} | |
curr_mesh++; | |
} | |
} | |
fclose (fp); | |
return 1; | |
} | |
/** | |
* Free resources allocated for the model. | |
*/ | |
void | |
FreeModel (struct md5_model_t *mdl) | |
{ | |
int i; | |
if (mdl->baseSkel) | |
{ | |
free (mdl->baseSkel); | |
mdl->baseSkel = NULL; | |
} | |
if (mdl->meshes) | |
{ | |
/* Free mesh data */ | |
for (i = 0; i < mdl->num_meshes; ++i) | |
{ | |
if (mdl->meshes[i].vertices) | |
{ | |
free (mdl->meshes[i].vertices); | |
mdl->meshes[i].vertices = NULL; | |
} | |
if (mdl->meshes[i].triangles) | |
{ | |
free (mdl->meshes[i].triangles); | |
mdl->meshes[i].triangles = NULL; | |
} | |
if (mdl->meshes[i].weights) | |
{ | |
free (mdl->meshes[i].weights); | |
mdl->meshes[i].weights = NULL; | |
} | |
} | |
free (mdl->meshes); | |
mdl->meshes = NULL; | |
} | |
} | |
/** | |
* Prepare a mesh for drawing. Compute mesh's final vertex positions | |
* given a skeleton. Put the vertices in vertex arrays. | |
*/ | |
void | |
PrepareMesh (const struct md5_mesh_t *mesh, | |
const struct md5_joint_t *skeleton) | |
{ | |
int i, j, k; | |
/* Setup vertex indices */ | |
for (k = 0, i = 0; i < mesh->num_tris; ++i) | |
{ | |
for (j = 0; j < 3; ++j, ++k) | |
vertexIndices[k] = mesh->triangles[i].index[j]; | |
} | |
/* Setup vertices */ | |
for (i = 0; i < mesh->num_verts; ++i) | |
{ | |
vec3_t finalVertex = { 0.0f, 0.0f, 0.0f }; | |
/* Calculate final vertex to draw with weights */ | |
for (j = 0; j < mesh->vertices[i].count; ++j) | |
{ | |
const struct md5_weight_t *weight | |
= &mesh->weights[mesh->vertices[i].start + j]; | |
const struct md5_joint_t *joint | |
= &skeleton[weight->joint]; | |
/* Calculate transformed vertex for this weight */ | |
vec3_t wv; | |
Quat_rotatePoint (joint->orient, weight->pos, wv); | |
/* The sum of all weight->bias should be 1.0 */ | |
finalVertex[0] += (joint->pos[0] + wv[0]) * weight->bias; | |
finalVertex[1] += (joint->pos[1] + wv[1]) * weight->bias; | |
finalVertex[2] += (joint->pos[2] + wv[2]) * weight->bias; | |
} | |
vertexArray[i][0] = finalVertex[0]; | |
vertexArray[i][1] = finalVertex[1]; | |
vertexArray[i][2] = finalVertex[2]; | |
} | |
} | |
void | |
AllocVertexArrays () | |
{ | |
vertexArray = (vec3_t *)malloc (sizeof (vec3_t) * max_verts); | |
vertexIndices = (GLuint *)malloc (sizeof (GLuint) * max_tris * 3); | |
} | |
void | |
FreeVertexArrays () | |
{ | |
if (vertexArray) | |
{ | |
free (vertexArray); | |
vertexArray = NULL; | |
} | |
if (vertexIndices) | |
{ | |
free (vertexIndices); | |
vertexIndices = NULL; | |
} | |
} | |
/** | |
* Draw the skeleton as lines and points (for joints). | |
*/ | |
void | |
DrawSkeleton (const struct md5_joint_t *skeleton, int num_joints) | |
{ | |
int i; | |
/* Draw each joint */ | |
glPointSize (5.0f); | |
glColor3f (1.0f, 0.0f, 0.0f); | |
glBegin (GL_POINTS); | |
for (i = 0; i < num_joints; ++i) | |
glVertex3fv (skeleton[i].pos); | |
glEnd (); | |
glPointSize (1.0f); | |
/* Draw each bone */ | |
glColor3f (0.0f, 1.0f, 0.0f); | |
glBegin (GL_LINES); | |
for (i = 0; i < num_joints; ++i) | |
{ | |
if (skeleton[i].parent != -1) | |
{ | |
glVertex3fv (skeleton[skeleton[i].parent].pos); | |
glVertex3fv (skeleton[i].pos); | |
} | |
} | |
glEnd(); | |
} | |
void | |
init (const char *filename) | |
{ | |
/* Initialize OpenGL context */ | |
glClearColor (0.5f, 0.5f, 0.5f, 1.0f); | |
glShadeModel (GL_SMOOTH); | |
glEnable (GL_DEPTH_TEST); | |
/* Load MD5 model file */ | |
if (!ReadMD5Model (filename, &md5file)) | |
exit(EXIT_FAILURE); | |
AllocVertexArrays (); | |
} | |
void | |
cleanup () | |
{ | |
FreeModel (&md5file); | |
FreeVertexArrays (); | |
} | |
void | |
reshape (int w, int h) | |
{ | |
if (h == 0) | |
h = 1; | |
glViewport (0, 0, (GLsizei)w, (GLsizei)h); | |
glMatrixMode (GL_PROJECTION); | |
glLoadIdentity (); | |
gluPerspective (45.0, w/(GLdouble)h, 0.1, 1000.0); | |
glMatrixMode (GL_MODELVIEW); | |
glLoadIdentity (); | |
} | |
void | |
display () | |
{ | |
int i; | |
static float angle = 0; | |
static double curent_time = 0; | |
static double last_time = 0; | |
last_time = curent_time; | |
curent_time = (double)glutGet (GLUT_ELAPSED_TIME) / 1000.0; | |
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
glLoadIdentity (); | |
glPolygonMode (GL_FRONT_AND_BACK, GL_LINE); | |
glTranslatef (0.0f, -35.0f, -150.0f); | |
glRotatef (-90.0f, 1.0, 0.0, 0.0); | |
glRotatef (angle, 0.0, 0.0, 1.0); | |
angle += 25 * (curent_time - last_time); | |
if (angle > 360.0f) | |
angle -= 360.0f; | |
/* Draw skeleton */ | |
DrawSkeleton (md5file.baseSkel, md5file.num_joints); | |
glColor3f (1.0f, 1.0f, 1.0f); | |
glEnableClientState (GL_VERTEX_ARRAY); | |
/* Draw each mesh of the model */ | |
for (i = 0; i < md5file.num_meshes; ++i) | |
{ | |
PrepareMesh (&md5file.meshes[i], md5file.baseSkel); | |
glVertexPointer (3, GL_FLOAT, 0, vertexArray); | |
glDrawElements (GL_TRIANGLES, md5file.meshes[i].num_tris * 3, | |
GL_UNSIGNED_INT, vertexIndices); | |
} | |
glDisableClientState (GL_VERTEX_ARRAY); | |
glutSwapBuffers (); | |
glutPostRedisplay (); | |
} | |
void | |
keyboard (unsigned char key, int x, int y) | |
{ | |
/* Escape */ | |
if (key == 27) | |
exit (0); | |
} | |
int | |
main (int argc, char *argv[]) | |
{ | |
if (argc < 2) | |
{ | |
fprintf (stderr, "usage: %s <filename.md5mesh>\n", argv[0]); | |
return 0; | |
} | |
glutInit (&argc, argv); | |
glutInitDisplayMode (GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH); | |
glutInitWindowSize (640, 480); | |
glutCreateWindow ("MD5 Model"); | |
atexit (cleanup); | |
init (argv[1]); | |
glutReshapeFunc (reshape); | |
glutDisplayFunc (display); | |
glutKeyboardFunc (keyboard); | |
glutMainLoop (); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* md5anim.c -- md5mesh model loader + animation | |
* last modification: aug. 14, 2007 | |
* | |
* Doom3's md5mesh viewer with animation. Animation portion. | |
* Dependences: md5model.h, md5mesh.c. | |
* | |
* Copyright (c) 2005-2007 David HENRY | |
* | |
* Permission is hereby granted, free of charge, to any person | |
* obtaining a copy of this software and associated documentation | |
* files (the "Software"), to deal in the Software without | |
* restriction, including without limitation the rights to use, | |
* copy, modify, merge, publish, distribute, sublicense, and/or | |
* sell copies of the Software, and to permit persons to whom the | |
* Software is furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be | |
* included in all copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
* NONINFRINGEMENT. | |
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR | |
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
* | |
* gcc -Wall -ansi -lGL -lGLU -lglut md5anim.c md5anim.c -o md5model | |
*/ | |
#include <GL/glut.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <math.h> | |
#include <assert.h> | |
#include "md5model.h" | |
/* Joint info */ | |
struct joint_info_t | |
{ | |
char name[64]; | |
int parent; | |
int flags; | |
int startIndex; | |
}; | |
/* Base frame joint */ | |
struct baseframe_joint_t | |
{ | |
vec3_t pos; | |
quat4_t orient; | |
}; | |
/** | |
* More quaternion operations for skeletal animation. | |
*/ | |
float | |
Quat_dotProduct (const quat4_t qa, const quat4_t qb) | |
{ | |
return ((qa[X] * qb[X]) + (qa[Y] * qb[Y]) + (qa[Z] * qb[Z]) + (qa[W] * qb[W])); | |
} | |
void | |
Quat_slerp (const quat4_t qa, const quat4_t qb, float t, quat4_t out) | |
{ | |
/* Check for out-of range parameter and return edge points if so */ | |
if (t <= 0.0) | |
{ | |
memcpy (out, qa, sizeof(quat4_t)); | |
return; | |
} | |
if (t >= 1.0) | |
{ | |
memcpy (out, qb, sizeof (quat4_t)); | |
return; | |
} | |
/* Compute "cosine of angle between quaternions" using dot product */ | |
float cosOmega = Quat_dotProduct (qa, qb); | |
/* If negative dot, use -q1. Two quaternions q and -q | |
represent the same rotation, but may produce | |
different slerp. We chose q or -q to rotate using | |
the acute angle. */ | |
float q1w = qb[W]; | |
float q1x = qb[X]; | |
float q1y = qb[Y]; | |
float q1z = qb[Z]; | |
if (cosOmega < 0.0f) | |
{ | |
q1w = -q1w; | |
q1x = -q1x; | |
q1y = -q1y; | |
q1z = -q1z; | |
cosOmega = -cosOmega; | |
} | |
/* We should have two unit quaternions, so dot should be <= 1.0 */ | |
assert (cosOmega < 1.1f); | |
/* Compute interpolation fraction, checking for quaternions | |
almost exactly the same */ | |
float k0, k1; | |
if (cosOmega > 0.9999f) | |
{ | |
/* Very close - just use linear interpolation, | |
which will protect againt a divide by zero */ | |
k0 = 1.0f - t; | |
k1 = t; | |
} | |
else | |
{ | |
/* Compute the sin of the angle using the | |
trig identity sin^2(omega) + cos^2(omega) = 1 */ | |
float sinOmega = sqrt (1.0f - (cosOmega * cosOmega)); | |
/* Compute the angle from its sin and cosine */ | |
float omega = atan2 (sinOmega, cosOmega); | |
/* Compute inverse of denominator, so we only have | |
to divide once */ | |
float oneOverSinOmega = 1.0f / sinOmega; | |
/* Compute interpolation parameters */ | |
k0 = sin ((1.0f - t) * omega) * oneOverSinOmega; | |
k1 = sin (t * omega) * oneOverSinOmega; | |
} | |
/* Interpolate and return new quaternion */ | |
out[W] = (k0 * qa[3]) + (k1 * q1w); | |
out[X] = (k0 * qa[0]) + (k1 * q1x); | |
out[Y] = (k0 * qa[1]) + (k1 * q1y); | |
out[Z] = (k0 * qa[2]) + (k1 * q1z); | |
} | |
/** | |
* Check if an animation can be used for a given model. Model's | |
* skeleton and animation's skeleton must match. | |
*/ | |
int | |
CheckAnimValidity (const struct md5_model_t *mdl, | |
const struct md5_anim_t *anim) | |
{ | |
int i; | |
/* md5mesh and md5anim must have the same number of joints */ | |
if (mdl->num_joints != anim->num_joints) | |
return 0; | |
/* We just check with frame[0] */ | |
for (i = 0; i < mdl->num_joints; ++i) | |
{ | |
/* Joints must have the same parent index */ | |
if (mdl->baseSkel[i].parent != anim->skelFrames[0][i].parent) | |
return 0; | |
/* Joints must have the same name */ | |
if (strcmp (mdl->baseSkel[i].name, anim->skelFrames[0][i].name) != 0) | |
return 0; | |
} | |
return 1; | |
} | |
/** | |
* Build skeleton for a given frame data. | |
*/ | |
static void | |
BuildFrameSkeleton (const struct joint_info_t *jointInfos, | |
const struct baseframe_joint_t *baseFrame, | |
const float *animFrameData, | |
struct md5_joint_t *skelFrame, | |
int num_joints) | |
{ | |
int i; | |
for (i = 0; i < num_joints; ++i) | |
{ | |
const struct baseframe_joint_t *baseJoint = &baseFrame[i]; | |
vec3_t animatedPos; | |
quat4_t animatedOrient; | |
int j = 0; | |
memcpy (animatedPos, baseJoint->pos, sizeof (vec3_t)); | |
memcpy (animatedOrient, baseJoint->orient, sizeof (quat4_t)); | |
if (jointInfos[i].flags & 1) /* Tx */ | |
{ | |
animatedPos[0] = animFrameData[jointInfos[i].startIndex + j]; | |
++j; | |
} | |
if (jointInfos[i].flags & 2) /* Ty */ | |
{ | |
animatedPos[1] = animFrameData[jointInfos[i].startIndex + j]; | |
++j; | |
} | |
if (jointInfos[i].flags & 4) /* Tz */ | |
{ | |
animatedPos[2] = animFrameData[jointInfos[i].startIndex + j]; | |
++j; | |
} | |
if (jointInfos[i].flags & 8) /* Qx */ | |
{ | |
animatedOrient[0] = animFrameData[jointInfos[i].startIndex + j]; | |
++j; | |
} | |
if (jointInfos[i].flags & 16) /* Qy */ | |
{ | |
animatedOrient[1] = animFrameData[jointInfos[i].startIndex + j]; | |
++j; | |
} | |
if (jointInfos[i].flags & 32) /* Qz */ | |
{ | |
animatedOrient[2] = animFrameData[jointInfos[i].startIndex + j]; | |
++j; | |
} | |
/* Compute orient quaternion's w value */ | |
Quat_computeW (animatedOrient); | |
/* NOTE: we assume that this joint's parent has | |
already been calculated, i.e. joint's ID should | |
never be smaller than its parent ID. */ | |
struct md5_joint_t *thisJoint = &skelFrame[i]; | |
int parent = jointInfos[i].parent; | |
thisJoint->parent = parent; | |
strcpy (thisJoint->name, jointInfos[i].name); | |
/* Has parent? */ | |
if (thisJoint->parent < 0) | |
{ | |
memcpy (thisJoint->pos, animatedPos, sizeof (vec3_t)); | |
memcpy (thisJoint->orient, animatedOrient, sizeof (quat4_t)); | |
} | |
else | |
{ | |
struct md5_joint_t *parentJoint = &skelFrame[parent]; | |
vec3_t rpos; /* Rotated position */ | |
/* Add positions */ | |
Quat_rotatePoint (parentJoint->orient, animatedPos, rpos); | |
thisJoint->pos[0] = rpos[0] + parentJoint->pos[0]; | |
thisJoint->pos[1] = rpos[1] + parentJoint->pos[1]; | |
thisJoint->pos[2] = rpos[2] + parentJoint->pos[2]; | |
/* Concatenate rotations */ | |
Quat_multQuat (parentJoint->orient, animatedOrient, thisJoint->orient); | |
Quat_normalize (thisJoint->orient); | |
} | |
} | |
} | |
/** | |
* Load an MD5 animation from file. | |
*/ | |
int | |
ReadMD5Anim (const char *filename, struct md5_anim_t *anim) | |
{ | |
FILE *fp = NULL; | |
char buff[512]; | |
struct joint_info_t *jointInfos = NULL; | |
struct baseframe_joint_t *baseFrame = NULL; | |
float *animFrameData = NULL; | |
int version; | |
int numAnimatedComponents; | |
int frame_index; | |
int i; | |
fp = fopen (filename, "rb"); | |
if (!fp) | |
{ | |
fprintf (stderr, "error: couldn't open \"%s\"!\n", filename); | |
return 0; | |
} | |
while (!feof (fp)) | |
{ | |
/* Read whole line */ | |
fgets (buff, sizeof (buff), fp); | |
if (sscanf (buff, " MD5Version %d", &version) == 1) | |
{ | |
if (version != 10) | |
{ | |
/* Bad version */ | |
fprintf (stderr, "Error: bad animation version\n"); | |
fclose (fp); | |
return 0; | |
} | |
} | |
else if (sscanf (buff, " numFrames %d", &anim->num_frames) == 1) | |
{ | |
/* Allocate memory for skeleton frames and bounding boxes */ | |
if (anim->num_frames > 0) | |
{ | |
anim->skelFrames = (struct md5_joint_t **) | |
malloc (sizeof (struct md5_joint_t*) * anim->num_frames); | |
anim->bboxes = (struct md5_bbox_t *) | |
malloc (sizeof (struct md5_bbox_t) * anim->num_frames); | |
} | |
} | |
else if (sscanf (buff, " numJoints %d", &anim->num_joints) == 1) | |
{ | |
if (anim->num_joints > 0) | |
{ | |
for (i = 0; i < anim->num_frames; ++i) | |
{ | |
/* Allocate memory for joints of each frame */ | |
anim->skelFrames[i] = (struct md5_joint_t *) | |
malloc (sizeof (struct md5_joint_t) * anim->num_joints); | |
} | |
/* Allocate temporary memory for building skeleton frames */ | |
jointInfos = (struct joint_info_t *) | |
malloc (sizeof (struct joint_info_t) * anim->num_joints); | |
baseFrame = (struct baseframe_joint_t *) | |
malloc (sizeof (struct baseframe_joint_t) * anim->num_joints); | |
} | |
} | |
else if (sscanf (buff, " frameRate %d", &anim->frameRate) == 1) | |
{ | |
/* | |
printf ("md5anim: animation's frame rate is %d\n", anim->frameRate); | |
*/ | |
} | |
else if (sscanf (buff, " numAnimatedComponents %d", &numAnimatedComponents) == 1) | |
{ | |
if (numAnimatedComponents > 0) | |
{ | |
/* Allocate memory for animation frame data */ | |
animFrameData = (float *)malloc (sizeof (float) * numAnimatedComponents); | |
} | |
} | |
else if (strncmp (buff, "hierarchy {", 11) == 0) | |
{ | |
for (i = 0; i < anim->num_joints; ++i) | |
{ | |
/* Read whole line */ | |
fgets (buff, sizeof (buff), fp); | |
/* Read joint info */ | |
sscanf (buff, " %s %d %d %d", jointInfos[i].name, &jointInfos[i].parent, | |
&jointInfos[i].flags, &jointInfos[i].startIndex); | |
} | |
} | |
else if (strncmp (buff, "bounds {", 8) == 0) | |
{ | |
for (i = 0; i < anim->num_frames; ++i) | |
{ | |
/* Read whole line */ | |
fgets (buff, sizeof (buff), fp); | |
/* Read bounding box */ | |
sscanf (buff, " ( %f %f %f ) ( %f %f %f )", | |
&anim->bboxes[i].min[0], &anim->bboxes[i].min[1], | |
&anim->bboxes[i].min[2], &anim->bboxes[i].max[0], | |
&anim->bboxes[i].max[1], &anim->bboxes[i].max[2]); | |
} | |
} | |
else if (strncmp (buff, "baseframe {", 10) == 0) | |
{ | |
for (i = 0; i < anim->num_joints; ++i) | |
{ | |
/* Read whole line */ | |
fgets (buff, sizeof (buff), fp); | |
/* Read base frame joint */ | |
if (sscanf (buff, " ( %f %f %f ) ( %f %f %f )", | |
&baseFrame[i].pos[0], &baseFrame[i].pos[1], | |
&baseFrame[i].pos[2], &baseFrame[i].orient[0], | |
&baseFrame[i].orient[1], &baseFrame[i].orient[2]) == 6) | |
{ | |
/* Compute the w component */ | |
Quat_computeW (baseFrame[i].orient); | |
} | |
} | |
} | |
else if (sscanf (buff, " frame %d", &frame_index) == 1) | |
{ | |
/* Read frame data */ | |
for (i = 0; i < numAnimatedComponents; ++i) | |
fscanf (fp, "%f", &animFrameData[i]); | |
/* Build frame skeleton from the collected data */ | |
BuildFrameSkeleton (jointInfos, baseFrame, animFrameData, | |
anim->skelFrames[frame_index], anim->num_joints); | |
} | |
} | |
fclose (fp); | |
/* Free temporary data allocated */ | |
if (animFrameData) | |
free (animFrameData); | |
if (baseFrame) | |
free (baseFrame); | |
if (jointInfos) | |
free (jointInfos); | |
return 1; | |
} | |
/** | |
* Free resources allocated for the animation. | |
*/ | |
void | |
FreeAnim (struct md5_anim_t *anim) | |
{ | |
int i; | |
if (anim->skelFrames) | |
{ | |
for (i = 0; i < anim->num_frames; ++i) | |
{ | |
if (anim->skelFrames[i]) | |
{ | |
free (anim->skelFrames[i]); | |
anim->skelFrames[i] = NULL; | |
} | |
} | |
free (anim->skelFrames); | |
anim->skelFrames = NULL; | |
} | |
if (anim->bboxes) | |
{ | |
free (anim->bboxes); | |
anim->bboxes = NULL; | |
} | |
} | |
/** | |
* Smoothly interpolate two skeletons | |
*/ | |
void | |
InterpolateSkeletons (const struct md5_joint_t *skelA, | |
const struct md5_joint_t *skelB, | |
int num_joints, float interp, | |
struct md5_joint_t *out) | |
{ | |
int i; | |
for (i = 0; i < num_joints; ++i) | |
{ | |
/* Copy parent index */ | |
out[i].parent = skelA[i].parent; | |
/* Linear interpolation for position */ | |
out[i].pos[0] = skelA[i].pos[0] + interp * (skelB[i].pos[0] - skelA[i].pos[0]); | |
out[i].pos[1] = skelA[i].pos[1] + interp * (skelB[i].pos[1] - skelA[i].pos[1]); | |
out[i].pos[2] = skelA[i].pos[2] + interp * (skelB[i].pos[2] - skelA[i].pos[2]); | |
/* Spherical linear interpolation for orientation */ | |
Quat_slerp (skelA[i].orient, skelB[i].orient, interp, out[i].orient); | |
} | |
} | |
/** | |
* Perform animation related computations. Calculate the current and | |
* next frames, given a delta time. | |
*/ | |
void | |
Animate (const struct md5_anim_t *anim, | |
struct anim_info_t *animInfo, double dt) | |
{ | |
int maxFrames = anim->num_frames - 1; | |
animInfo->last_time += dt; | |
/* move to next frame */ | |
if (animInfo->last_time >= animInfo->max_time) | |
{ | |
animInfo->curr_frame++; | |
animInfo->next_frame++; | |
animInfo->last_time = 0.0; | |
if (animInfo->curr_frame > maxFrames) | |
animInfo->curr_frame = 0; | |
if (animInfo->next_frame > maxFrames) | |
animInfo->next_frame = 0; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* md5mesh.c -- md5mesh model loader + animation | |
* last modification: aug. 14, 2007 | |
* | |
* Doom3's md5mesh viewer with animation. Mesh portion. | |
* Dependences: md5model.h, md5anim.c. | |
* | |
* Copyright (c) 2005-2007 David HENRY | |
* | |
* Permission is hereby granted, free of charge, to any person | |
* obtaining a copy of this software and associated documentation | |
* files (the "Software"), to deal in the Software without | |
* restriction, including without limitation the rights to use, | |
* copy, modify, merge, publish, distribute, sublicense, and/or | |
* sell copies of the Software, and to permit persons to whom the | |
* Software is furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be | |
* included in all copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
* NONINFRINGEMENT. | |
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR | |
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
* | |
* gcc -Wall -ansi -lGL -lGLU -lglut md5anim.c md5anim.c -o md5model | |
*/ | |
#include <GL/glut.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <math.h> | |
#include "md5model.h" | |
struct md5_model_t md5file; | |
struct md5_anim_t md5anim; | |
int animated = 0; | |
struct md5_joint_t *skeleton = NULL; | |
struct anim_info_t animInfo; | |
/* vertex array related stuff */ | |
int max_verts = 0; | |
int max_tris = 0; | |
vec3_t *vertexArray = NULL; | |
GLuint *vertexIndices = NULL; | |
/** | |
* Basic quaternion operations. | |
*/ | |
void | |
Quat_computeW (quat4_t q) | |
{ | |
float t = 1.0f - (q[X] * q[X]) - (q[Y] * q[Y]) - (q[Z] * q[Z]); | |
if (t < 0.0f) | |
q[W] = 0.0f; | |
else | |
q[W] = -sqrt (t); | |
} | |
void | |
Quat_normalize (quat4_t q) | |
{ | |
/* compute magnitude of the quaternion */ | |
float mag = sqrt ((q[X] * q[X]) + (q[Y] * q[Y]) | |
+ (q[Z] * q[Z]) + (q[W] * q[W])); | |
/* check for bogus length, to protect against divide by zero */ | |
if (mag > 0.0f) | |
{ | |
/* normalize it */ | |
float oneOverMag = 1.0f / mag; | |
q[X] *= oneOverMag; | |
q[Y] *= oneOverMag; | |
q[Z] *= oneOverMag; | |
q[W] *= oneOverMag; | |
} | |
} | |
void | |
Quat_multQuat (const quat4_t qa, const quat4_t qb, quat4_t out) | |
{ | |
out[W] = (qa[W] * qb[W]) - (qa[X] * qb[X]) - (qa[Y] * qb[Y]) - (qa[Z] * qb[Z]); | |
out[X] = (qa[X] * qb[W]) + (qa[W] * qb[X]) + (qa[Y] * qb[Z]) - (qa[Z] * qb[Y]); | |
out[Y] = (qa[Y] * qb[W]) + (qa[W] * qb[Y]) + (qa[Z] * qb[X]) - (qa[X] * qb[Z]); | |
out[Z] = (qa[Z] * qb[W]) + (qa[W] * qb[Z]) + (qa[X] * qb[Y]) - (qa[Y] * qb[X]); | |
} | |
void | |
Quat_multVec (const quat4_t q, const vec3_t v, quat4_t out) | |
{ | |
out[W] = - (q[X] * v[X]) - (q[Y] * v[Y]) - (q[Z] * v[Z]); | |
out[X] = (q[W] * v[X]) + (q[Y] * v[Z]) - (q[Z] * v[Y]); | |
out[Y] = (q[W] * v[Y]) + (q[Z] * v[X]) - (q[X] * v[Z]); | |
out[Z] = (q[W] * v[Z]) + (q[X] * v[Y]) - (q[Y] * v[X]); | |
} | |
void | |
Quat_rotatePoint (const quat4_t q, const vec3_t in, vec3_t out) | |
{ | |
quat4_t tmp, inv, final; | |
inv[X] = -q[X]; inv[Y] = -q[Y]; | |
inv[Z] = -q[Z]; inv[W] = q[W]; | |
Quat_normalize (inv); | |
Quat_multVec (q, in, tmp); | |
Quat_multQuat (tmp, inv, final); | |
out[X] = final[X]; | |
out[Y] = final[Y]; | |
out[Z] = final[Z]; | |
} | |
/** | |
* Load an MD5 model from file. | |
*/ | |
int | |
ReadMD5Model (const char *filename, struct md5_model_t *mdl) | |
{ | |
FILE *fp; | |
char buff[512]; | |
int version; | |
int curr_mesh = 0; | |
int i; | |
fp = fopen (filename, "rb"); | |
if (!fp) | |
{ | |
fprintf (stderr, "Error: couldn't open \"%s\"!\n", filename); | |
return 0; | |
} | |
while (!feof (fp)) | |
{ | |
/* Read whole line */ | |
fgets (buff, sizeof (buff), fp); | |
if (sscanf (buff, " MD5Version %d", &version) == 1) | |
{ | |
if (version != 10) | |
{ | |
/* Bad version */ | |
fprintf (stderr, "Error: bad model version\n"); | |
fclose (fp); | |
return 0; | |
} | |
} | |
else if (sscanf (buff, " numJoints %d", &mdl->num_joints) == 1) | |
{ | |
if (mdl->num_joints > 0) | |
{ | |
/* Allocate memory for base skeleton joints */ | |
mdl->baseSkel = (struct md5_joint_t *) | |
calloc (mdl->num_joints, sizeof (struct md5_joint_t)); | |
} | |
} | |
else if (sscanf (buff, " numMeshes %d", &mdl->num_meshes) == 1) | |
{ | |
if (mdl->num_meshes > 0) | |
{ | |
/* Allocate memory for meshes */ | |
mdl->meshes = (struct md5_mesh_t *) | |
calloc (mdl->num_meshes, sizeof (struct md5_mesh_t)); | |
} | |
} | |
else if (strncmp (buff, "joints {", 8) == 0) | |
{ | |
/* Read each joint */ | |
for (i = 0; i < mdl->num_joints; ++i) | |
{ | |
struct md5_joint_t *joint = &mdl->baseSkel[i]; | |
/* Read whole line */ | |
fgets (buff, sizeof (buff), fp); | |
if (sscanf (buff, "%s %d ( %f %f %f ) ( %f %f %f )", | |
joint->name, &joint->parent, &joint->pos[0], | |
&joint->pos[1], &joint->pos[2], &joint->orient[0], | |
&joint->orient[1], &joint->orient[2]) == 8) | |
{ | |
/* Compute the w component */ | |
Quat_computeW (joint->orient); | |
} | |
} | |
} | |
else if (strncmp (buff, "mesh {", 6) == 0) | |
{ | |
struct md5_mesh_t *mesh = &mdl->meshes[curr_mesh]; | |
int vert_index = 0; | |
int tri_index = 0; | |
int weight_index = 0; | |
float fdata[4]; | |
int idata[3]; | |
while ((buff[0] != '}') && !feof (fp)) | |
{ | |
/* Read whole line */ | |
fgets (buff, sizeof (buff), fp); | |
if (strstr (buff, "shader ")) | |
{ | |
int quote = 0, j = 0; | |
/* Copy the shader name whithout the quote marks */ | |
for (i = 0; i < sizeof (buff) && (quote < 2); ++i) | |
{ | |
if (buff[i] == '\"') | |
quote++; | |
if ((quote == 1) && (buff[i] != '\"')) | |
{ | |
mesh->shader[j] = buff[i]; | |
j++; | |
} | |
} | |
} | |
else if (sscanf (buff, " numverts %d", &mesh->num_verts) == 1) | |
{ | |
if (mesh->num_verts > 0) | |
{ | |
/* Allocate memory for vertices */ | |
mesh->vertices = (struct md5_vertex_t *) | |
malloc (sizeof (struct md5_vertex_t) * mesh->num_verts); | |
} | |
if (mesh->num_verts > max_verts) | |
max_verts = mesh->num_verts; | |
} | |
else if (sscanf (buff, " numtris %d", &mesh->num_tris) == 1) | |
{ | |
if (mesh->num_tris > 0) | |
{ | |
/* Allocate memory for triangles */ | |
mesh->triangles = (struct md5_triangle_t *) | |
malloc (sizeof (struct md5_triangle_t) * mesh->num_tris); | |
} | |
if (mesh->num_tris > max_tris) | |
max_tris = mesh->num_tris; | |
} | |
else if (sscanf (buff, " numweights %d", &mesh->num_weights) == 1) | |
{ | |
if (mesh->num_weights > 0) | |
{ | |
/* Allocate memory for vertex weights */ | |
mesh->weights = (struct md5_weight_t *) | |
malloc (sizeof (struct md5_weight_t) * mesh->num_weights); | |
} | |
} | |
else if (sscanf (buff, " vert %d ( %f %f ) %d %d", &vert_index, | |
&fdata[0], &fdata[1], &idata[0], &idata[1]) == 5) | |
{ | |
/* Copy vertex data */ | |
mesh->vertices[vert_index].st[0] = fdata[0]; | |
mesh->vertices[vert_index].st[1] = fdata[1]; | |
mesh->vertices[vert_index].start = idata[0]; | |
mesh->vertices[vert_index].count = idata[1]; | |
} | |
else if (sscanf (buff, " tri %d %d %d %d", &tri_index, | |
&idata[0], &idata[1], &idata[2]) == 4) | |
{ | |
/* Copy triangle data */ | |
mesh->triangles[tri_index ].index[0] = idata[0]; | |
mesh->triangles[tri_index ].index[1] = idata[1]; | |
mesh->triangles[tri_index ].index[2] = idata[2]; | |
} | |
else if (sscanf (buff, " weight %d %d %f ( %f %f %f )", | |
&weight_index, &idata[0], &fdata[3], | |
&fdata[0], &fdata[1], &fdata[2]) == 6) | |
{ | |
/* Copy vertex data */ | |
mesh->weights[weight_index].joint = idata[0]; | |
mesh->weights[weight_index].bias = fdata[3]; | |
mesh->weights[weight_index].pos[0] = fdata[0]; | |
mesh->weights[weight_index].pos[1] = fdata[1]; | |
mesh->weights[weight_index].pos[2] = fdata[2]; | |
} | |
} | |
curr_mesh++; | |
} | |
} | |
fclose (fp); | |
return 1; | |
} | |
/** | |
* Free resources allocated for the model. | |
*/ | |
void | |
FreeModel (struct md5_model_t *mdl) | |
{ | |
int i; | |
if (mdl->baseSkel) | |
{ | |
free (mdl->baseSkel); | |
mdl->baseSkel = NULL; | |
} | |
if (mdl->meshes) | |
{ | |
/* Free mesh data */ | |
for (i = 0; i < mdl->num_meshes; ++i) | |
{ | |
if (mdl->meshes[i].vertices) | |
{ | |
free (mdl->meshes[i].vertices); | |
mdl->meshes[i].vertices = NULL; | |
} | |
if (mdl->meshes[i].triangles) | |
{ | |
free (mdl->meshes[i].triangles); | |
mdl->meshes[i].triangles = NULL; | |
} | |
if (mdl->meshes[i].weights) | |
{ | |
free (mdl->meshes[i].weights); | |
mdl->meshes[i].weights = NULL; | |
} | |
} | |
free (mdl->meshes); | |
mdl->meshes = NULL; | |
} | |
} | |
/** | |
* Prepare a mesh for drawing. Compute mesh's final vertex positions | |
* given a skeleton. Put the vertices in vertex arrays. | |
*/ | |
void | |
PrepareMesh (const struct md5_mesh_t *mesh, | |
const struct md5_joint_t *skeleton) | |
{ | |
int i, j, k; | |
/* Setup vertex indices */ | |
for (k = 0, i = 0; i < mesh->num_tris; ++i) | |
{ | |
for (j = 0; j < 3; ++j, ++k) | |
vertexIndices[k] = mesh->triangles[i].index[j]; | |
} | |
/* Setup vertices */ | |
for (i = 0; i < mesh->num_verts; ++i) | |
{ | |
vec3_t finalVertex = { 0.0f, 0.0f, 0.0f }; | |
/* Calculate final vertex to draw with weights */ | |
for (j = 0; j < mesh->vertices[i].count; ++j) | |
{ | |
const struct md5_weight_t *weight | |
= &mesh->weights[mesh->vertices[i].start + j]; | |
const struct md5_joint_t *joint | |
= &skeleton[weight->joint]; | |
/* Calculate transformed vertex for this weight */ | |
vec3_t wv; | |
Quat_rotatePoint (joint->orient, weight->pos, wv); | |
/* The sum of all weight->bias should be 1.0 */ | |
finalVertex[0] += (joint->pos[0] + wv[0]) * weight->bias; | |
finalVertex[1] += (joint->pos[1] + wv[1]) * weight->bias; | |
finalVertex[2] += (joint->pos[2] + wv[2]) * weight->bias; | |
} | |
vertexArray[i][0] = finalVertex[0]; | |
vertexArray[i][1] = finalVertex[1]; | |
vertexArray[i][2] = finalVertex[2]; | |
} | |
} | |
void | |
AllocVertexArrays () | |
{ | |
vertexArray = (vec3_t *)malloc (sizeof (vec3_t) * max_verts); | |
vertexIndices = (GLuint *)malloc (sizeof (GLuint) * max_tris * 3); | |
} | |
void | |
FreeVertexArrays () | |
{ | |
if (vertexArray) | |
{ | |
free (vertexArray); | |
vertexArray = NULL; | |
} | |
if (vertexIndices) | |
{ | |
free (vertexIndices); | |
vertexIndices = NULL; | |
} | |
} | |
/** | |
* Draw the skeleton as lines and points (for joints). | |
*/ | |
void | |
DrawSkeleton (const struct md5_joint_t *skeleton, int num_joints) | |
{ | |
int i; | |
/* Draw each joint */ | |
glPointSize (5.0f); | |
glColor3f (1.0f, 0.0f, 0.0f); | |
glBegin (GL_POINTS); | |
for (i = 0; i < num_joints; ++i) | |
glVertex3fv (skeleton[i].pos); | |
glEnd (); | |
glPointSize (1.0f); | |
/* Draw each bone */ | |
glColor3f (0.0f, 1.0f, 0.0f); | |
glBegin (GL_LINES); | |
for (i = 0; i < num_joints; ++i) | |
{ | |
if (skeleton[i].parent != -1) | |
{ | |
glVertex3fv (skeleton[skeleton[i].parent].pos); | |
glVertex3fv (skeleton[i].pos); | |
} | |
} | |
glEnd(); | |
} | |
void | |
init (const char *filename, const char *animfile) | |
{ | |
/* Initialize OpenGL context */ | |
glClearColor (0.5f, 0.5f, 0.5f, 1.0f); | |
glShadeModel (GL_SMOOTH); | |
glEnable (GL_DEPTH_TEST); | |
/* Load MD5 model file */ | |
if (!ReadMD5Model (filename, &md5file)) | |
exit (EXIT_FAILURE); | |
AllocVertexArrays (); | |
/* Load MD5 animation file */ | |
if (animfile) | |
{ | |
if (!ReadMD5Anim (animfile, &md5anim)) | |
{ | |
FreeAnim (&md5anim); | |
} | |
else | |
{ | |
animInfo.curr_frame = 0; | |
animInfo.next_frame = 1; | |
animInfo.last_time = 0; | |
animInfo.max_time = 1.0 / md5anim.frameRate; | |
/* Allocate memory for animated skeleton */ | |
skeleton = (struct md5_joint_t *) | |
malloc (sizeof (struct md5_joint_t) * md5anim.num_joints); | |
animated = 1; | |
} | |
} | |
if (!animated) | |
printf ("init: no animation loaded.\n"); | |
} | |
void | |
cleanup () | |
{ | |
FreeModel (&md5file); | |
FreeAnim (&md5anim); | |
if (animated && skeleton) | |
{ | |
free (skeleton); | |
skeleton = NULL; | |
} | |
FreeVertexArrays (); | |
} | |
void | |
reshape (int w, int h) | |
{ | |
if (h == 0) | |
h = 1; | |
glViewport (0, 0, (GLsizei)w, (GLsizei)h); | |
glMatrixMode (GL_PROJECTION); | |
glLoadIdentity (); | |
gluPerspective (45.0, w/(GLdouble)h, 0.1, 1000.0); | |
glMatrixMode (GL_MODELVIEW); | |
glLoadIdentity (); | |
} | |
void | |
display () | |
{ | |
int i; | |
static float angle = 0; | |
static double curent_time = 0; | |
static double last_time = 0; | |
last_time = curent_time; | |
curent_time = (double)glutGet (GLUT_ELAPSED_TIME) / 1000.0; | |
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
glLoadIdentity (); | |
glPolygonMode (GL_FRONT_AND_BACK, GL_LINE); | |
glTranslatef (0.0f, -35.0f, -150.0f); | |
glRotatef (-90.0f, 1.0, 0.0, 0.0); | |
glRotatef (angle, 0.0, 0.0, 1.0); | |
angle += 25 * (curent_time - last_time); | |
if (angle > 360.0f) | |
angle -= 360.0f; | |
if (animated) | |
{ | |
/* Calculate current and next frames */ | |
Animate (&md5anim, &animInfo, curent_time - last_time); | |
/* Interpolate skeletons between two frames */ | |
InterpolateSkeletons (md5anim.skelFrames[animInfo.curr_frame], | |
md5anim.skelFrames[animInfo.next_frame], | |
md5anim.num_joints, | |
animInfo.last_time * md5anim.frameRate, | |
skeleton); | |
} | |
else | |
{ | |
/* No animation, use bind-pose skeleton */ | |
skeleton = md5file.baseSkel; | |
} | |
/* Draw skeleton */ | |
DrawSkeleton (skeleton, md5file.num_joints); | |
glColor3f (1.0f, 1.0f, 1.0f); | |
glEnableClientState (GL_VERTEX_ARRAY); | |
/* Draw each mesh of the model */ | |
for (i = 0; i < md5file.num_meshes; ++i) | |
{ | |
PrepareMesh (&md5file.meshes[i], skeleton); | |
glVertexPointer (3, GL_FLOAT, 0, vertexArray); | |
glDrawElements (GL_TRIANGLES, md5file.meshes[i].num_tris * 3, | |
GL_UNSIGNED_INT, vertexIndices); | |
} | |
glDisableClientState (GL_VERTEX_ARRAY); | |
glutSwapBuffers (); | |
glutPostRedisplay (); | |
} | |
void | |
keyboard (unsigned char key, int x, int y) | |
{ | |
/* Escape */ | |
if (key == 27) | |
exit (0); | |
} | |
int | |
main (int argc, char *argv[]) | |
{ | |
if (argc < 2) | |
{ | |
fprintf (stderr, "usage: %s <filename.md5mesh> " | |
"[<filename.md5anim>]\n", argv[0]); | |
return 0; | |
} | |
glutInit (&argc, argv); | |
glutInitDisplayMode (GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH); | |
glutInitWindowSize (640, 480); | |
glutCreateWindow ("MD5 Model"); | |
atexit (cleanup); | |
init (argv[1], (argc > 2) ? argv[2] : NULL); | |
glutReshapeFunc (reshape); | |
glutDisplayFunc (display); | |
glutKeyboardFunc (keyboard); | |
glutMainLoop (); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* md5model.h -- md5mesh model loader + animation | |
* last modification: aug. 14, 2007 | |
* | |
* Copyright (c) 2005-2007 David HENRY | |
* | |
* Permission is hereby granted, free of charge, to any person | |
* obtaining a copy of this software and associated documentation | |
* files (the "Software"), to deal in the Software without | |
* restriction, including without limitation the rights to use, | |
* copy, modify, merge, publish, distribute, sublicense, and/or | |
* sell copies of the Software, and to permit persons to whom the | |
* Software is furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be | |
* included in all copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
* NONINFRINGEMENT. | |
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR | |
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
* | |
* gcc -Wall -ansi -lGL -lGLU -lglut md5anim.c md5anim.c -o md5model | |
*/ | |
#ifndef __MD5MODEL_H__ | |
#define __MD5MODEL_H__ | |
/* Vectors */ | |
typedef float vec2_t[2]; | |
typedef float vec3_t[3]; | |
/* Quaternion (x, y, z, w) */ | |
typedef float quat4_t[4]; | |
enum { | |
X, Y, Z, W | |
}; | |
/* Joint */ | |
struct md5_joint_t | |
{ | |
char name[64]; | |
int parent; | |
vec3_t pos; | |
quat4_t orient; | |
}; | |
/* Vertex */ | |
struct md5_vertex_t | |
{ | |
vec2_t st; | |
int start; /* start weight */ | |
int count; /* weight count */ | |
}; | |
/* Triangle */ | |
struct md5_triangle_t | |
{ | |
int index[3]; | |
}; | |
/* Weight */ | |
struct md5_weight_t | |
{ | |
int joint; | |
float bias; | |
vec3_t pos; | |
}; | |
/* Bounding box */ | |
struct md5_bbox_t | |
{ | |
vec3_t min; | |
vec3_t max; | |
}; | |
/* MD5 mesh */ | |
struct md5_mesh_t | |
{ | |
struct md5_vertex_t *vertices; | |
struct md5_triangle_t *triangles; | |
struct md5_weight_t *weights; | |
int num_verts; | |
int num_tris; | |
int num_weights; | |
char shader[256]; | |
}; | |
/* MD5 model structure */ | |
struct md5_model_t | |
{ | |
struct md5_joint_t *baseSkel; | |
struct md5_mesh_t *meshes; | |
int num_joints; | |
int num_meshes; | |
}; | |
/* Animation data */ | |
struct md5_anim_t | |
{ | |
int num_frames; | |
int num_joints; | |
int frameRate; | |
struct md5_joint_t **skelFrames; | |
struct md5_bbox_t *bboxes; | |
}; | |
/* Animation info */ | |
struct anim_info_t | |
{ | |
int curr_frame; | |
int next_frame; | |
double last_time; | |
double max_time; | |
}; | |
/** | |
* Quaternion prototypes | |
*/ | |
void Quat_computeW (quat4_t q); | |
void Quat_normalize (quat4_t q); | |
void Quat_multQuat (const quat4_t qa, const quat4_t qb, quat4_t out); | |
void Quat_multVec (const quat4_t q, const vec3_t v, quat4_t out); | |
void Quat_rotatePoint (const quat4_t q, const vec3_t in, vec3_t out); | |
float Quat_dotProduct (const quat4_t qa, const quat4_t qb); | |
void Quat_slerp (const quat4_t qa, const quat4_t qb, float t, quat4_t out); | |
/** | |
* md5mesh prototypes | |
*/ | |
int ReadMD5Model (const char *filename, struct md5_model_t *mdl); | |
void FreeModel (struct md5_model_t *mdl); | |
void PrepareMesh (const struct md5_mesh_t *mesh, | |
const struct md5_joint_t *skeleton); | |
void AllocVertexArrays (); | |
void FreeVertexArrays (); | |
void DrawSkeleton (const struct md5_joint_t *skeleton, int num_joints); | |
/** | |
* md5anim prototypes | |
*/ | |
int CheckAnimValidity (const struct md5_model_t *mdl, | |
const struct md5_anim_t *anim); | |
int ReadMD5Anim (const char *filename, struct md5_anim_t *anim); | |
void FreeAnim (struct md5_anim_t *anim); | |
void InterpolateSkeletons (const struct md5_joint_t *skelA, | |
const struct md5_joint_t *skelB, | |
int num_joints, float interp, | |
struct md5_joint_t *out); | |
void Animate (const struct md5_anim_t *anim, | |
struct anim_info_t *animInfo, double dt); | |
#endif /* __MD5MODEL_H__ */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment