12#ifndef CSCI441_MD5_MODEL_HPP
13#define CSCI441_MD5_MODEL_HPP
50#ifdef CSCI441_USE_GLEW
56#include <glm/exponential.hpp>
57#include <glm/ext/quaternion_common.hpp>
58#include <glm/ext/quaternion_float.hpp>
60#define GLM_ENABLE_EXPERIMENTAL
61#include <glm/gtx/quaternion.hpp>
85 static constexpr GLint NULL_JOINT = -1;
93 GLint parent = NULL_JOINT;
97 glm::vec3 position = {0.0f, 0.0f, 0.0f};
101 glm::quat orientation = {0.0f, 0.0f, 0.0f, 0.0f};
111 glm::vec2 texCoord = {0.0f, 0.0f};
129 GLint index[3] = {0};
139 GLint joint = MD5Joint::NULL_JOINT;
147 glm::vec3 position = {0.0f, 0.0f, 0.0f};
157 GLuint texHandle = 0;
161 char filename[512] =
"";
209 GLint numVertices = 0;
213 GLint numTriangles = 0;
217 GLint numWeights = 0;
222 char shader[512] =
"";
237 GLint parent = MD5Joint::NULL_JOINT;
245 GLint startIndex = 0;
255 glm::vec3 position = {0.0f, 0.0f, 0.0f};
259 glm::quat orientation = {0.0f, 0.0f, 0.0f, 0.0f};
270 glm::vec3 min = {0.0f, 0.0f, 0.0f};
274 glm::vec3 max = {0.0f, 0.0f, 0.0f};
322 GLfloat lastTime = 0.0f;
327 GLfloat maxTime = 0.0f;
365 [[maybe_unused]]
bool loadMD5Model(
const char* MD5_MESH_FILE,
const char* MD5_ANIM_FILE =
"");
371 [[nodiscard]]
bool isAnimated()
const {
return _isAnimated; }
379 [[nodiscard]]
bool readMD5Model(
const char* FILENAME);
388 [[maybe_unused]]
void allocVertexArrays(GLuint vPosAttribLoc, GLuint vColorAttribLoc, GLuint vTexCoordAttribLoc);
392 [[maybe_unused]]
void draw()
const;
396 [[maybe_unused]]
void drawSkeleton()
const;
404 [[nodiscard]]
bool readMD5Anim(
const char* filename);
409 void animate(GLfloat dt);
412 MD5Joint* _baseSkeleton;
422 glm::vec3* _vertexArray;
423 glm::vec2* _texelArray;
424 GLuint* _vertexIndicesArray;
439 MD5Animation _animation;
447 MD5AnimationState _animationInfo;
449 void _prepareMesh(
const MD5Mesh* pMESH)
const;
450 void _drawMesh(
const MD5Mesh* pMESH)
const;
451 [[nodiscard]]
bool _checkAnimValidity()
const;
452 static void _buildFrameSkeleton(
const MD5JointInfo* pJOINT_INFOS,
453 const MD5BaseFrameJoint* pBASE_FRAME,
454 const GLfloat* pANIM_FRAME_DATA,
455 MD5Joint* pSkeletonFrame,
457 void _interpolateSkeletons(GLfloat interp);
459 void _freeVertexArrays();
468 _baseSkeleton =
nullptr;
474 _vertexArray =
nullptr;
475 _texelArray =
nullptr;
476 _vertexIndicesArray =
nullptr;
499 const char* MD5_MESH_FILE,
500 const char* MD5_ANIM_FILE
503 if( readMD5Model(MD5_MESH_FILE) ) {
505 if(strcmp(MD5_ANIM_FILE,
"") != 0 ) {
507 if( !readMD5Anim(MD5_ANIM_FILE) ) {
511 if( !isAnimated() ) {
512 printf (
"[.MD5_ANIM_FILE]: no animation loaded.\n");
528 GLint currentMesh = 0;
532 GLint totalVertices = 0;
533 GLint totalWeights = 0;
534 GLint totalTriangles = 0;
536 GLfloat minX = 999999, minY = 999999, minZ = 999999;
537 GLfloat maxX = -999999, maxY = -999999, maxZ = -999999;
539 printf(
"[.md5mesh]: about to read %s\n", FILENAME );
541 fp = fopen(FILENAME,
"rb" );
543 fprintf (stderr,
"[.md5mesh]: Error: couldn't open \"%s\"!\n", FILENAME);
549 fgets( buff,
sizeof(buff), fp );
551 if( sscanf(buff,
" MD5Version %d", &version) == 1 ) {
552 if( version != 10 ) {
554 fprintf (stderr,
"[.md5mesh]: Error: bad model version\n");
558 }
else if( sscanf(buff,
" numJoints %d", &_numJoints) == 1 ) {
559 if( _numJoints > 0 ) {
561 _baseSkeleton =
new MD5Joint[_numJoints];
563 }
else if( sscanf(buff,
" numMeshes %d", &_numMeshes) == 1 ) {
564 if( _numMeshes > 0 ) {
566 _meshes =
new MD5Mesh[_numMeshes];
568 }
else if( strncmp(buff,
"joints {", 8) == 0 ) {
570 for(i = 0; i < _numJoints; ++i) {
571 MD5Joint *joint = &_baseSkeleton[i];
574 fgets( buff,
sizeof(buff), fp );
576 if( sscanf(buff,
"%s %d ( %f %f %f ) ( %f %f %f )",
585 }
else if( strncmp(buff,
"mesh {", 6) == 0 ) {
586 MD5Mesh *mesh = &_meshes[currentMesh];
587 GLint vert_index = 0;
589 GLint weight_index = 0;
593 while( buff[0] !=
'}' && !feof(fp) ) {
595 fgets( buff,
sizeof(buff), fp );
597 if( strstr( buff,
"shader ") ) {
598 GLint quote = 0, j = 0;
601 for(uli = 0; uli <
sizeof(buff) && (quote < 2); ++uli) {
602 if( buff[uli] ==
'\"' )
605 if( (quote == 1) && (buff[uli] !=
'\"') ) {
606 mesh->
shader[j] = buff[uli];
615 mesh->
textures[MD5Mesh::TextureMap::DIFFUSE].
texHandle = CSCI441::TextureUtils::loadAndRegisterTexture( mesh->
textures[MD5Mesh::TextureMap::DIFFUSE].
filename, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, GL_REPEAT, GL_REPEAT, GL_FALSE, GL_FALSE);
619 mesh->
textures[MD5Mesh::TextureMap::DIFFUSE].
texHandle = CSCI441::TextureUtils::loadAndRegisterTexture( mesh->
textures[MD5Mesh::TextureMap::DIFFUSE].
filename, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, GL_REPEAT, GL_REPEAT, GL_FALSE, GL_FALSE );
623 mesh->
textures[MD5Mesh::TextureMap::DIFFUSE].
texHandle = CSCI441::TextureUtils::loadAndRegisterTexture(mesh->
textures[MD5Mesh::TextureMap::DIFFUSE].
filename, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, GL_REPEAT, GL_REPEAT, GL_FALSE, GL_FALSE );
625 printf(
"[.md5mesh | ERROR]: Could not load diffuse map %s\n", mesh->
shader);
633 mesh->
textures[MD5Mesh::TextureMap::SPECULAR].
texHandle = CSCI441::TextureUtils::loadAndRegisterTexture( mesh->
textures[MD5Mesh::TextureMap::SPECULAR].
filename, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, GL_REPEAT, GL_REPEAT, GL_FALSE, GL_FALSE );
637 mesh->
textures[MD5Mesh::TextureMap::SPECULAR].
texHandle = CSCI441::TextureUtils::loadAndRegisterTexture( mesh->
textures[MD5Mesh::TextureMap::SPECULAR].
filename, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, GL_REPEAT, GL_REPEAT, GL_FALSE, GL_FALSE );
639 printf(
"[.md5mesh | ERROR]: Could not load specular map %s\n", mesh->
shader);
646 mesh->
textures[MD5Mesh::TextureMap::NORMAL].
texHandle = CSCI441::TextureUtils::loadAndRegisterTexture( mesh->
textures[MD5Mesh::TextureMap::NORMAL].
filename, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, GL_REPEAT, GL_REPEAT, GL_FALSE, GL_FALSE );
650 mesh->
textures[MD5Mesh::TextureMap::NORMAL].
texHandle = CSCI441::TextureUtils::loadAndRegisterTexture( mesh->
textures[MD5Mesh::TextureMap::NORMAL].
filename, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, GL_REPEAT, GL_REPEAT, GL_FALSE, GL_FALSE );
652 printf(
"[.md5mesh | ERROR]: Could not load normal map %s\n", mesh->
shader);
659 mesh->
textures[MD5Mesh::TextureMap::HEIGHT].
texHandle = CSCI441::TextureUtils::loadAndRegisterTexture( mesh->
textures[MD5Mesh::TextureMap::HEIGHT].
filename, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, GL_REPEAT, GL_REPEAT, GL_FALSE, GL_FALSE );
663 mesh->
textures[MD5Mesh::TextureMap::HEIGHT].
texHandle = CSCI441::TextureUtils::loadAndRegisterTexture( mesh->
textures[MD5Mesh::TextureMap::HEIGHT].
filename, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, GL_REPEAT, GL_REPEAT, GL_FALSE, GL_FALSE );
665 printf(
"[.md5mesh | ERROR]: Could not load height map %s\n", mesh->
shader);
669 }
else if( sscanf(buff,
" numverts %d", &mesh->
numVertices) == 1 ) {
679 }
else if( sscanf(buff,
" numtris %d", &mesh->
numTriangles) == 1 ) {
689 }
else if( sscanf(buff,
" numweights %d", &mesh->
numWeights) == 1 ) {
696 }
else if( sscanf(buff,
" vert %d ( %f %f ) %d %d",
698 &fdata[0], &fdata[1],
699 &idata[0], &idata[1]) == 5
706 }
else if( sscanf(buff,
" tri %d %d %d %d",
708 &idata[0], &idata[1], &idata[2]) == 4
714 }
else if( sscanf(buff,
" weight %d %d %f ( %f %f %f )",
715 &weight_index, &idata[0], &fdata[3],
716 &fdata[0], &fdata[1], &fdata[2]) == 6
725 if( fdata[0] < minX ) { minX = fdata[0]; }
726 if( fdata[0] > maxX ) { maxX = fdata[0]; }
727 if( fdata[1] < minY ) { minY = fdata[1]; }
728 if( fdata[1] > maxY ) { maxY = fdata[1]; }
729 if( fdata[2] < minZ ) { minZ = fdata[2]; }
730 if( fdata[2] > maxZ ) { maxZ = fdata[2]; }
740 _skeleton = _baseSkeleton;
742 printf(
"[.md5mesh]: finished reading %s\n", FILENAME );
743 printf(
"[.md5mesh]: read in %d meshes, %d joints, %d vertices, %d weights, and %d triangles\n", _numMeshes, _numJoints, totalVertices, totalWeights, totalTriangles );
744 printf(
"[.md5mesh]: base pose %f units across in X, %f units across in Y, %f units across in Z\n", (maxX - minX), (maxY-minY), (maxZ - minZ) );
752CSCI441::MD5Model::_freeModel()
754 delete _baseSkeleton;
755 _baseSkeleton =
nullptr;
758 for(GLint i = 0; i < _numMeshes; ++i) {
759 delete _meshes[i].vertices;
760 _meshes[i].vertices =
nullptr;
762 delete _meshes[i].triangles;
763 _meshes[i].triangles =
nullptr;
765 delete _meshes[i].weights;
766 _meshes[i].weights =
nullptr;
778 for(GLint i = 0; i < _numMeshes; ++i) {
788CSCI441::MD5Model::_prepareMesh(
794 for(k = 0, i = 0; i < pMESH->numTriangles; ++i) {
795 for(j = 0; j < 3; ++j, ++k)
796 _vertexIndicesArray[k] = pMESH->triangles[i].index[j];
800 for(i = 0; i < pMESH->numVertices; ++i) {
801 glm::vec3 finalVertex = {0.0f, 0.0f, 0.0f };
804 for(j = 0; j < pMESH->vertices[i].count; ++j) {
805 const MD5Weight *weight = &pMESH->weights[pMESH->vertices[i].start + j];
806 const MD5Joint *joint = &_skeleton[weight->joint];
809 glm::vec3 weightedVertex;
810 weightedVertex = glm::rotate(joint->orientation, glm::vec4(weight->position, 0.0f));
813 finalVertex.x += (joint->position.x + weightedVertex.x) * weight->bias;
814 finalVertex.y += (joint->position.y + weightedVertex.y) * weight->bias;
815 finalVertex.z += (joint->position.z + weightedVertex.z) * weight->bias;
818 _vertexArray[i].x = finalVertex.x;
819 _vertexArray[i].y = finalVertex.y;
820 _vertexArray[i].z = finalVertex.z;
822 _texelArray[i].s = pMESH->vertices[i].texCoord.s;
823 _texelArray[i].t = pMESH->vertices[i].texCoord.t;
826 glBindVertexArray(_vao );
827 glBindBuffer(GL_ARRAY_BUFFER, _vbo[0] );
828 glBufferSubData(GL_ARRAY_BUFFER, 0,
sizeof(glm::vec3) * pMESH->numVertices, &_vertexArray[0] );
829 glBufferSubData(GL_ARRAY_BUFFER,
sizeof(glm::vec3) * _maxVertices,
sizeof(glm::vec2) * pMESH->numVertices, &_texelArray[0] );
830 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vbo[1] );
831 glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0,
sizeof(GLuint) * pMESH->numTriangles * 3, _vertexIndicesArray );
835CSCI441::MD5Model::_drawMesh(
839 glBindTexture(GL_TEXTURE_2D, pMESH->textures[MD5Mesh::TextureMap::DIFFUSE].texHandle );
841 glBindVertexArray(_vao );
842 glDrawElements(GL_TRIANGLES, pMESH->numTriangles * 3, GL_UNSIGNED_INT, (
void*)
nullptr );
848 GLuint vPosAttribLoc,
849 GLuint vColorAttribLoc,
850 GLuint vTexCoordAttribLoc
852 _vertexArray =
new glm::vec3[_maxVertices];
853 _texelArray =
new glm::vec2[_maxVertices];
854 _vertexIndicesArray =
new GLuint[_maxTriangles * 3];
856 glGenVertexArrays( 1, &_vao );
857 glBindVertexArray(_vao );
859 glGenBuffers(2, _vbo );
860 glBindBuffer(GL_ARRAY_BUFFER, _vbo[0] );
861 glBufferData(GL_ARRAY_BUFFER,
sizeof(glm::vec3) * _maxVertices +
sizeof(glm::vec2) * _maxVertices,
nullptr, GL_DYNAMIC_DRAW );
863 glEnableVertexAttribArray( vPosAttribLoc );
864 glVertexAttribPointer( vPosAttribLoc, 3, GL_FLOAT, GL_FALSE, 0, (
void*)
nullptr );
866 glEnableVertexAttribArray( vTexCoordAttribLoc );
867 glVertexAttribPointer( vTexCoordAttribLoc, 2, GL_FLOAT, GL_FALSE, 0, (
void*)(
sizeof(glm::vec3) * _maxVertices) );
869 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vbo[1] );
870 glBufferData(GL_ELEMENT_ARRAY_BUFFER,
sizeof(GLuint) * _maxTriangles * 3,
nullptr, GL_DYNAMIC_DRAW );
872 printf(
"[.md5mesh]: Model VAO/VBO/IBO registered at %u/%u/%u\n", _vao, _vbo[0], _vbo[1] );
874 glGenVertexArrays( 1, &_skeletonVAO );
875 glBindVertexArray(_skeletonVAO );
877 glGenBuffers( 1, &_skeletonVBO );
878 glBindBuffer(GL_ARRAY_BUFFER, _skeletonVBO );
879 glBufferData(GL_ARRAY_BUFFER,
sizeof(glm::vec3) * _numJoints * 3 * 2,
nullptr, GL_DYNAMIC_DRAW );
881 glEnableVertexAttribArray( vPosAttribLoc );
882 glVertexAttribPointer( vPosAttribLoc, 3, GL_FLOAT, GL_FALSE, 0, (
void*)
nullptr );
884 glEnableVertexAttribArray( vColorAttribLoc );
885 glVertexAttribPointer( vColorAttribLoc, 3, GL_FLOAT, GL_FALSE, 0, (
void*)(
sizeof(glm::vec3) * _numJoints * 3) );
887 printf(
"[.md5mesh]: Skeleton VAO/VBO registered at %u/%u\n", _skeletonVAO, _skeletonVBO );
891CSCI441::MD5Model::_freeVertexArrays()
893 delete[] _vertexArray;
894 _vertexArray =
nullptr;
896 delete[] _vertexIndicesArray;
897 _vertexIndicesArray =
nullptr;
899 delete[] _texelArray;
900 _texelArray =
nullptr;
902 glDeleteVertexArrays( 1, &_vao );
903 glDeleteBuffers(2, _vbo );
904 glDeleteVertexArrays( 1, &_skeletonVAO );
905 glDeleteBuffers( 1, &_skeletonVBO );
913 glBindVertexArray(_skeletonVAO );
914 glBindBuffer(GL_ARRAY_BUFFER, _skeletonVBO );
916 glm::vec3 jointColor = {1.0f, 1.0f, 0.0f };
917 glm::vec3 boneColor = {1.0f, 0.0f, 1.0f };
920 for(GLint i = 0; i < _numJoints; ++i ) {
921 glBufferSubData(GL_ARRAY_BUFFER, i *
sizeof(glm::vec3),
sizeof(glm::vec3), &(_skeleton[i].position) );
922 glBufferSubData(GL_ARRAY_BUFFER, i *
sizeof(glm::vec3) +
sizeof(glm::vec3) * _numJoints * 3,
sizeof(glm::vec3), &jointColor[0]);
927 for(GLint i = 0; i < _numJoints; ++i ) {
928 if( _skeleton[i].parent != MD5Joint::NULL_JOINT ) {
929 glBufferSubData(GL_ARRAY_BUFFER,
sizeof(glm::vec3) * _numJoints + (i * 2) *
sizeof(glm::vec3),
sizeof(glm::vec3), &(_skeleton[_skeleton[i].parent].position) );
930 glBufferSubData(GL_ARRAY_BUFFER,
sizeof(glm::vec3) * _numJoints + (i * 2) *
sizeof(glm::vec3) +
sizeof(glm::vec3) * _numJoints * 3,
sizeof(glm::vec3), &boneColor[0]);
932 glBufferSubData(GL_ARRAY_BUFFER,
sizeof(glm::vec3) * _numJoints + (i * 2) *
sizeof(glm::vec3) +
sizeof(glm::vec3),
sizeof(glm::vec3), &(_skeleton[i].position) );
933 glBufferSubData(GL_ARRAY_BUFFER,
sizeof(glm::vec3) * _numJoints + (i * 2) *
sizeof(glm::vec3) +
sizeof(glm::vec3) +
sizeof(glm::vec3) * _numJoints * 3,
sizeof(glm::vec3), &boneColor[0]);
939 glDrawArrays(GL_POINTS, 0, _numJoints );
943 glDrawArrays(GL_LINES, _numJoints, numBones * 2 );
950CSCI441::MD5Model::_checkAnimValidity()
const
953 if( _numJoints != _animation.numJoints ) {
954 printf(
"\n[.md5anim]: skeleton and animation do not have same number of joints. cannot apply animation to skeleton\n\n");
959 for(GLint i = 0; i < _numJoints; ++i) {
961 if (_baseSkeleton[i].parent != _animation.skeletonFrames[0][i].parent) {
962 printf(
"\n[.md5anim]: skeleton and animation joints do not have same parent index. cannot apply animation to skeleton\n\n");
967 if (strcmp (_baseSkeleton[i].name, _animation.skeletonFrames[0][i].name) != 0) {
968 printf(
"\n[.md5anim]: skeleton and animation joints do not have same name. cannot apply animation to skeleton\n\n");
973 printf(
"\n[.md5anim]: skeleton and animation match. animation can be applied to skeleton\n\n");
979CSCI441::MD5Model::_buildFrameSkeleton(
980 const MD5JointInfo* pJOINT_INFOS,
981 const MD5BaseFrameJoint* pBASE_FRAME,
982 const GLfloat* pANIM_FRAME_DATA,
983 MD5Joint* pSkeletonFrame,
984 const GLint NUM_JOINTS
986 if(pJOINT_INFOS ==
nullptr
987 || pBASE_FRAME ==
nullptr
988 || pANIM_FRAME_DATA ==
nullptr
989 || pSkeletonFrame ==
nullptr)
return;
993 for(i = 0; i < NUM_JOINTS; ++i) {
994 const MD5BaseFrameJoint *baseJoint = &pBASE_FRAME[i];
995 glm::vec3 animatedPosition = baseJoint->position;
996 glm::quat animatedOrientation = baseJoint->orientation;
1000 if(pJOINT_INFOS[i].flags & 1 ) {
1001 animatedPosition.x = pANIM_FRAME_DATA[pJOINT_INFOS[i].startIndex + j];
1006 if(pJOINT_INFOS[i].flags & 2 ) {
1007 animatedPosition.y = pANIM_FRAME_DATA[pJOINT_INFOS[i].startIndex + j];
1012 if(pJOINT_INFOS[i].flags & 4 ) {
1013 animatedPosition.z = pANIM_FRAME_DATA[pJOINT_INFOS[i].startIndex + j];
1018 if(pJOINT_INFOS[i].flags & 8 ) {
1019 animatedOrientation.x = pANIM_FRAME_DATA[pJOINT_INFOS[i].startIndex + j];
1024 if(pJOINT_INFOS[i].flags & 16 ) {
1025 animatedOrientation.y = pANIM_FRAME_DATA[pJOINT_INFOS[i].startIndex + j];
1030 if(pJOINT_INFOS[i].flags & 32 ) {
1031 animatedOrientation.z = pANIM_FRAME_DATA[pJOINT_INFOS[i].startIndex + j];
1035 animatedOrientation.w = glm::extractRealComponent(animatedOrientation);
1040 MD5Joint *thisJoint = &pSkeletonFrame[i];
1042 GLint parent = pJOINT_INFOS[i].parent;
1043 thisJoint->parent = parent;
1044 strcpy (thisJoint->name, pJOINT_INFOS[i].name);
1047 if( thisJoint->parent == MD5Joint::NULL_JOINT ) {
1048 thisJoint->position = animatedPosition;
1049 thisJoint->orientation = animatedOrientation;
1051 MD5Joint *parentJoint = &pSkeletonFrame[parent];
1052 glm::vec3 rotatedPosition = glm::rotate(parentJoint->orientation, glm::vec4(animatedPosition, 0.0f));
1055 thisJoint->position = parentJoint->position + rotatedPosition;
1058 thisJoint->orientation = glm::normalize( glm::cross(parentJoint->orientation, animatedOrientation) );
1066 const char *filename
1071 GLfloat *animFrameData =
nullptr;
1073 GLint numAnimatedComponents;
1077 printf(
"[.md5anim]: about to read %s\n", filename );
1079 FILE *fp = fopen( filename,
"rb" );
1081 fprintf (stderr,
"[.md5anim]: Error: couldn't open \"%s\"!\n", filename);
1085 while( !feof(fp) ) {
1087 fgets( buff,
sizeof(buff), fp );
1089 if( sscanf(buff,
" MD5Version %d", &version) == 1 ) {
1090 if( version != 10 ) {
1092 fprintf (stderr,
"[.md5anim]: Error: bad animation version\n");
1096 }
else if( sscanf(buff,
" numFrames %d", &_animation.numFrames) == 1 ) {
1098 if( _animation.numFrames > 0 ) {
1099 _animation.skeletonFrames =
new MD5Joint*[_animation.numFrames];
1100 _animation.boundingBoxes =
new MD5BoundingBox[_animation.numFrames];
1102 }
else if( sscanf(buff,
" numJoints %d", &_animation.numJoints) == 1 ) {
1103 if( _animation.numJoints > 0 ) {
1104 for(i = 0; i < _animation.numFrames; ++i) {
1106 _animation.skeletonFrames[i] =
new MD5Joint[_animation.numJoints];
1114 }
else if( sscanf(buff,
" frameRate %d", &_animation.frameRate) == 1 ) {
1116 }
else if( sscanf(buff,
" numAnimatedComponents %d", &numAnimatedComponents) == 1 ) {
1117 if( numAnimatedComponents > 0 ) {
1119 animFrameData =
new GLfloat[numAnimatedComponents];
1121 }
else if( strncmp(buff,
"hierarchy {", 11) == 0 ) {
1122 for(i = 0; i < _animation.numJoints; ++i) {
1124 fgets( buff,
sizeof(buff), fp );
1127 sscanf(buff,
" %s %d %d %d",
1128 jointInfos[i].name, &jointInfos[i].parent,
1129 &jointInfos[i].flags, &jointInfos[i].startIndex);
1131 }
else if( strncmp(buff,
"bounds {", 8) == 0 ) {
1132 for(i = 0; i < _animation.numFrames; ++i) {
1134 fgets( buff,
sizeof(buff), fp );
1137 sscanf(buff,
" ( %f %f %f ) ( %f %f %f )",
1138 &_animation.boundingBoxes[i].min[0], &_animation.boundingBoxes[i].min[1], &_animation.boundingBoxes[i].min[2],
1139 &_animation.boundingBoxes[i].max[0], &_animation.boundingBoxes[i].max[1], &_animation.boundingBoxes[i].max[2]);
1141 }
else if( strncmp(buff,
"baseframe {", 10) == 0 ) {
1142 for(i = 0; i < _animation.numJoints; ++i) {
1144 fgets( buff,
sizeof(buff), fp );
1147 if( sscanf(buff,
" ( %f %f %f ) ( %f %f %f )",
1148 &baseFrame[i].position[0], &baseFrame[i].position[1], &baseFrame[i].position[2],
1149 &baseFrame[i].orientation[0], &baseFrame[i].orientation[1], &baseFrame[i].orientation[2]) == 6 ) {
1151 baseFrame[i].
orientation.w = glm::extractRealComponent(baseFrame[i].orientation);
1154 }
else if(sscanf(buff,
" frame %d", &frameIndex) == 1 ) {
1156 for(i = 0; i < numAnimatedComponents; ++i)
1157 fscanf( fp,
"%f", &animFrameData[i] );
1160 _buildFrameSkeleton(jointInfos, baseFrame, animFrameData,
1161 _animation.skeletonFrames[frameIndex],
1162 _animation.numJoints);
1168 printf(
"[.md5anim]: finished reading %s\n", filename );
1169 printf(
"[.md5anim]: read in %d frames of %d joints with %d animated components\n", _animation.numFrames, _animation.numJoints, numAnimatedComponents );
1170 printf(
"[.md5anim]: animation's frame rate is %d\n", _animation.frameRate );
1174 free( animFrameData );
1183 _animationInfo.currFrame = 0;
1184 _animationInfo.nextFrame = 1;
1186 _animationInfo.lastTime = 0.0f;
1187 _animationInfo.maxTime = 1.0f / (GLfloat)_animation.frameRate;
1190 _skeleton =
new MD5Joint[_animation.numJoints];
1192 if( _checkAnimValidity() ) {
1203CSCI441::MD5Model::_freeAnim()
1207 for(i = 0; i < _animation.numFrames; ++i) {
1208 delete _animation.skeletonFrames[i];
1209 _animation.skeletonFrames[i] =
nullptr;
1212 delete[] _animation.skeletonFrames;
1213 _animation.skeletonFrames =
nullptr;
1215 delete _animation.boundingBoxes;
1216 _animation.boundingBoxes =
nullptr;
1219 _skeleton =
nullptr;
1224CSCI441::MD5Model::_interpolateSkeletons(GLfloat interp)
1226 const MD5Joint *skeletonA = _animation.skeletonFrames[_animationInfo.currFrame];
1227 const MD5Joint *skeletonB = _animation.skeletonFrames[_animationInfo.nextFrame];
1231 for(i = 0; i < _animation.numJoints; ++i) {
1233 _skeleton[i].parent = skeletonA[i].parent;
1236 _skeleton[i].position[0] = skeletonA[i].position[0] + interp * (skeletonB[i].position[0] - skeletonA[i].position[0]);
1237 _skeleton[i].position[1] = skeletonA[i].position[1] + interp * (skeletonB[i].position[1] - skeletonA[i].position[1]);
1238 _skeleton[i].position[2] = skeletonA[i].position[2] + interp * (skeletonB[i].position[2] - skeletonA[i].position[2]);
1241 _skeleton[i].orientation = glm::slerp(skeletonA[i].orientation, skeletonB[i].orientation, interp);
1250 GLint maxFrames = _animation.numFrames - 1;
1252 _animationInfo.lastTime += dt;
1255 if( _animationInfo.lastTime >= _animationInfo.maxTime ) {
1256 _animationInfo.currFrame++;
1257 _animationInfo.nextFrame++;
1258 _animationInfo.lastTime = 0.0;
1260 if( _animationInfo.currFrame > maxFrames )
1261 _animationInfo.currFrame = 0;
1263 if( _animationInfo.nextFrame > maxFrames )
1264 _animationInfo.nextFrame = 0;
1268 _interpolateSkeletons( _animationInfo.lastTime * _animation.frameRate );
Helper functions to work with OpenGL Textures.
stores a Doom3 MD5 Mesh + Animation
Definition: MD5Model.hpp:75
MD5Model(MD5Model &&)=delete
do not allow MD5 models to be moved
void draw() const
draws all the meshes that make up the model
Definition: MD5Model.hpp:775
MD5Model & operator=(MD5Model &&)=delete
do not allow MD5 models to be moved
bool readMD5Model(const char *FILENAME)
parses md5mesh file and allocates corresponding mesh data
Definition: MD5Model.hpp:522
bool isAnimated() const
returns if the MD5 Model has an accompanying animation
Definition: MD5Model.hpp:371
bool loadMD5Model(const char *MD5_MESH_FILE, const char *MD5_ANIM_FILE="")
loads a corresponding md5mesh and md5anim file to the object
Definition: MD5Model.hpp:498
MD5Model()
initializes an empty MD5 Model
Definition: MD5Model.hpp:466
MD5Model & operator=(const MD5Model &)=delete
do not allow MD5 models to be copied
bool readMD5Anim(const char *filename)
reads in an animation sequence from an external file
Definition: MD5Model.hpp:1065
void allocVertexArrays(GLuint vPosAttribLoc, GLuint vColorAttribLoc, GLuint vTexCoordAttribLoc)
binds model VBOs to attribute pointer locations
Definition: MD5Model.hpp:847
void animate(GLfloat dt)
advances the model forward in its animation sequence the corresponding amount of time based on frame ...
Definition: MD5Model.hpp:1248
MD5Model(const MD5Model &)=delete
do not allow MD5 models to be copied
~MD5Model()
deallocates any used memory on the CPU and GPU
Definition: MD5Model.hpp:488
void drawSkeleton() const
draws the skeleton joints (as points) and bones (as lines)
Definition: MD5Model.hpp:911
CSCI441 Helper Functions for OpenGL.
Definition: ArcballCam.hpp:17
stores an entire animation sequence for a given MD5 Model
Definition: MD5Model.hpp:280
stores state of current animation frame
Definition: MD5Model.hpp:309
base frame joint
Definition: MD5Model.hpp:251
glm::quat orientation
joint orientation expressed as a quaternion in object space
Definition: MD5Model.hpp:259
bounding box containing the model during animation
Definition: MD5Model.hpp:266
a joint of the MD5 Skeleton
Definition: MD5Model.hpp:81
glm::vec3 position
position of the joint in object space
Definition: MD5Model.hpp:97
glm::quat orientation
joint orientation expressed as a quaternion in object space
Definition: MD5Model.hpp:101
char name[256]
joint identifier
Definition: MD5Model.hpp:89
GLint parent
index of the parent joint on skeletal tree
Definition: MD5Model.hpp:93
information pertaining to each animation joint
Definition: MD5Model.hpp:229
mesh that comprises the model's skin
Definition: MD5Model.hpp:167
GLint numTriangles
number of triangles in the mesh triangle array
Definition: MD5Model.hpp:213
TextureMap
named entities for different texture maps applied to the model
Definition: MD5Model.hpp:187
@ DIFFUSE
diffuse map
Definition: MD5Model.hpp:191
@ SPECULAR
specular map
Definition: MD5Model.hpp:195
@ NORMAL
normal map
Definition: MD5Model.hpp:199
MD5Texture textures[4]
texture map array
Definition: MD5Model.hpp:183
GLint numVertices
number of vertices in the mesh vertex array
Definition: MD5Model.hpp:209
MD5Triangle * triangles
array triangles comprising the mesh
Definition: MD5Model.hpp:175
MD5Vertex * vertices
array of vertices comprising the mesh
Definition: MD5Model.hpp:171
GLint numWeights
number of weights in the mesh weight array
Definition: MD5Model.hpp:217
MD5Weight * weights
array of weights to determine vertex position based on joint positions
Definition: MD5Model.hpp:179
char shader[512]
base filename for all textures applied to mesh
Definition: MD5Model.hpp:222
texture handle for the model
Definition: MD5Model.hpp:153
GLuint texHandle
handle of texture stored on the GPU
Definition: MD5Model.hpp:157
char filename[512]
filename texture was loaded from
Definition: MD5Model.hpp:161
a triangle on the mesh
Definition: MD5Model.hpp:125
GLint index[3]
vertex indices that make up triangle
Definition: MD5Model.hpp:129
a vertex on the mesh
Definition: MD5Model.hpp:107
glm::vec2 texCoord
texture coordinate for vertex
Definition: MD5Model.hpp:111
GLint start
index of starting weight
Definition: MD5Model.hpp:115
GLint count
number of weights that determine vertex's position
Definition: MD5Model.hpp:119
the weight for a mesh vertex
Definition: MD5Model.hpp:135
GLfloat bias
contribution of the weight
Definition: MD5Model.hpp:143
GLint joint
index of joint the weight depends on
Definition: MD5Model.hpp:139
glm::vec3 position
weight's position in object space
Definition: MD5Model.hpp:147