CSCI441 OpenGL Library 5.23.1.1
CS@Mines CSCI441 Computer Graphics Course Library
Loading...
Searching...
No Matches
MD5Model.hpp
Go to the documentation of this file.
1
12#ifndef CSCI441_MD5_MODEL_HPP
13#define CSCI441_MD5_MODEL_HPP
14
15/*
16 * md5mesh model loader + animation
17 * last modification: Dr. Jeffrey Paone
18 * encapsulated into a class
19 * supports texturing
20 *
21 * Doom3's md5mesh viewer with animation. Mesh and Animation declaration
22 * See http://tfc.duke.free.fr/coding/md5-specs-en.html for more details
23 *
24 * Copyright (c) 2005-2007 David HENRY
25 *
26 * Permission is hereby granted, free of charge, to any person
27 * obtaining a copy of this software and associated documentation
28 * files (the "Software"), to deal in the Software without
29 * restriction, including without limitation the rights to use,
30 * copy, modify, merge, publish, distribute, sublicense, and/or
31 * sell copies of the Software, and to permit persons to whom the
32 * Software is furnished to do so, subject to the following conditions:
33 *
34 * The above copyright notice and this permission notice shall be
35 * included in all copies or substantial portions of the Software.
36 *
37 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
38 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
39 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
40 *
41 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
42 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
43 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
44 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
45 *
46 */
47
48#include "constants.h"
49#include "TextureUtils.hpp"
50
51#ifdef CSCI441_USE_GLEW
52 #include <GL/glew.h>
53#else
54 #include <glad/gl.h>
55#endif
56
57#include <glm/exponential.hpp>
58#include <glm/ext/quaternion_common.hpp>
59#include <glm/ext/quaternion_float.hpp>
60
61#define GLM_ENABLE_EXPERIMENTAL
62#include <glm/gtx/quaternion.hpp>
63
64#include <cassert>
65#include <cmath>
66#include <cstdio>
67#include <cstdlib>
68#include <cstring>
69#include <stdexcept>
70
71namespace CSCI441 {
72
77 class [[maybe_unused]] MD5Model {
78 protected:
79 // md5mesh types
83 struct MD5Joint {
87 static constexpr GLint NULL_JOINT = -1;
91 static constexpr GLshort MAX_NAME_LENGTH = 256;
95 char name[MAX_NAME_LENGTH] = "";
99 GLint parent = NULL_JOINT;
103 glm::vec3 position = {0.0f, 0.0f, 0.0f};
107 glm::quat orientation = {0.0f, 0.0f, 0.0f, 0.0f};
108
112 MD5Joint() = default;
117 MD5Joint(const MD5Joint &OTHER) {
118 _copyFromSrc(OTHER);
119 }
125 MD5Joint& operator=(const MD5Joint &OTHER) {
126 if (this != &OTHER) {
127 _copyFromSrc(OTHER);
128 }
129 return *this;
130 }
135 MD5Joint(MD5Joint&& src) noexcept {
136 _moveFromSrc(src);
137 }
143 MD5Joint& operator=(MD5Joint&& src) noexcept {
144 if (this != &src) {
145 _moveFromSrc(src);
146 }
147 return *this;
148 }
149 private:
154 void _copyFromSrc(const MD5Joint& src) {
155 strncpy(this->name, src.name, MAX_NAME_LENGTH);
156 this->parent = src.parent;
157 this->position = src.position;
158 this->orientation = src.orientation;
159 }
165 void _moveFromSrc(MD5Joint& src) {
166 // copy values from source
167 _copyFromSrc(src);
168 // reset source
169 strncpy(src.name, "", MAX_NAME_LENGTH);
170 src.parent = NULL_JOINT;
171 src.position = glm::vec3(0.0f, 0.0f, 0.0f);
172 src.orientation = glm::quat(0.0f, 0.0f, 0.0f, 0.0f);
173 }
174 };
175
179 struct MD5Vertex {
183 glm::vec2 texCoord = {0.0f, 0.0f};
187 GLint start = 0;
191 GLint count = 0;
192
196 MD5Vertex() = default;
201 MD5Vertex(const MD5Vertex &OTHER) {
202 _copyFromSrc(OTHER);
203 }
210 if (this != &OTHER) {
211 _copyFromSrc(OTHER);
212 }
213 return *this;
214 }
219 MD5Vertex(MD5Vertex&& src) noexcept {
220 _moveFromSrc(src);
221 }
227 MD5Vertex& operator=(MD5Vertex&& src) noexcept {
228 if (this != &src) {
229 _moveFromSrc(src);
230 }
231 return *this;
232 }
233 private:
238 void _copyFromSrc(const MD5Vertex &src) {
239 this->texCoord = src.texCoord;
240 this->start = src.start;
241 this->count = src.count;
242 }
247 void _moveFromSrc(MD5Vertex& src) {
248 _copyFromSrc(src);
249
250 src.texCoord = glm::vec2(0.0f, 0.0f);
251 src.start = 0;
252 src.count = 0;
253 }
254 };
255
259 struct MD5Triangle {
263 static constexpr GLshort NUM_VERTICES = 3;
267 GLint index[NUM_VERTICES] = {0};
268
272 MD5Triangle() = default;
277 MD5Triangle(const MD5Triangle &OTHER) {
278 _copyFromSrc(OTHER);
279 }
286 if (this != &OTHER) {
287 _copyFromSrc(OTHER);
288 }
289 return *this;
290 }
295 MD5Triangle(MD5Triangle&& src) noexcept {
296 _moveFromSrc(src);
297 }
304 if (this != &src) {
305 _moveFromSrc(src);
306 }
307 return *this;
308 }
309 private:
314 void _copyFromSrc(const MD5Triangle &src) {
315 for (GLshort i = 0; i < NUM_VERTICES; i++) {
316 this->index[i] = src.index[i];
317 }
318 }
323 void _moveFromSrc(MD5Triangle &src) {
324 _copyFromSrc(src);
325
326 for (GLint & i : src.index) {
327 i = 0;
328 }
329 }
330 };
331
335 struct MD5Weight {
339 GLint joint = MD5Joint::NULL_JOINT;
343 GLfloat bias = 0.f;
347 glm::vec3 position = {0.0f, 0.0f, 0.0f};
348
352 MD5Weight() = default;
357 MD5Weight(const MD5Weight &OTHER) {
358 _copyFromSrc(OTHER);
359 }
366 if (this != &OTHER) {
367 _copyFromSrc(OTHER);
368 }
369 return *this;
370 }
375 MD5Weight(MD5Weight&& src) noexcept {
376 _moveFromSrc(src);
377 }
383 MD5Weight& operator=(MD5Weight&& src) noexcept {
384 if (this != &src) {
385 _moveFromSrc(src);
386 }
387 return *this;
388 }
389 private:
394 void _copyFromSrc(const MD5Weight &src) {
395 this->joint = src.joint;
396 this->bias = src.bias;
397 this->position = src.position;
398 }
403 void _moveFromSrc(MD5Weight &src) {
404 _copyFromSrc(src);
405
406 src.joint = MD5Joint::NULL_JOINT;
407 src.bias = 0.0f;
408 src.position = glm::vec3(0.0f, 0.0f, 0.0f);
409 }
410 };
411
415 struct MD5Texture {
419 static constexpr GLshort MAX_NAME_LENGTH = 512;
423 GLuint texHandle = 0;
427 char filename[MAX_NAME_LENGTH] = "";
428
432 MD5Texture() = default;
437 MD5Texture(const MD5Texture &OTHER) {
438 _copyFromSrc(OTHER);
439 }
446 if (this != &OTHER) {
447 _copyFromSrc(OTHER);
448 }
449 return *this;
450 }
455 MD5Texture(MD5Texture&& src) noexcept {
456 _moveFromSrc(src);
457 }
463 MD5Texture& operator=(MD5Texture&& src) noexcept {
464 if (this != &src) {
465 _moveFromSrc(src);
466 }
467 return *this;
468 }
469 private:
474 void _copyFromSrc(const MD5Texture &src) {
475 this->texHandle = src.texHandle;
476 strncpy(this->filename, src.filename, MAX_NAME_LENGTH);
477 }
482 void _moveFromSrc(MD5Texture& src) {
483 _copyFromSrc(src);
484
485 src.texHandle = 0;
486 strncpy(src.filename, "", MAX_NAME_LENGTH);
487 }
488 };
489
493 struct MD5Mesh {
497 static constexpr GLshort MAX_NAME_LENGTH = 512;
501 static constexpr GLshort NUM_TEXTURES = 4;
505 MD5Vertex* vertices = nullptr;
509 MD5Triangle* triangles = nullptr;
513 MD5Weight* weights = nullptr;
517 MD5Texture textures[NUM_TEXTURES];
537 HEIGHT
538 };
542 GLint numVertices = 0;
546 GLint numTriangles = 0;
550 GLint numWeights = 0;
551
555 char shader[MAX_NAME_LENGTH] = "";
556
560 MD5Mesh() = default;
565 delete[] vertices;
566 vertices = nullptr;
567
568 delete[] triangles;
569 triangles = nullptr;
570
571 delete[] weights;
572 weights = nullptr;
573 }
574
579 MD5Mesh(const MD5Mesh &OTHER) = delete;
584 MD5Mesh& operator=(const MD5Mesh &OTHER) = delete;
585
590 MD5Mesh(MD5Mesh&& src) noexcept {
591 _moveFromSrc(src);
592 }
598 MD5Mesh& operator=(MD5Mesh&& src) noexcept {
599 if (this != &src) {
600 _moveFromSrc(src);
601 }
602 return *this;
603 }
604 private:
609 void _moveFromSrc(MD5Mesh& src) {
610 this->vertices = src.vertices;
611 src.vertices = nullptr;
612
613 this->triangles = src.triangles;
614 src.triangles = nullptr;
615
616 this->weights = src.weights;
617 src.weights = nullptr;
618
619 for (GLshort i = 0; i < NUM_TEXTURES; i++) {
620 this->textures[i] = std::move( src.textures[i] );
621 }
622
623 this->numVertices = src.numVertices;
624 src.numVertices = 0;
625
626 this->numTriangles = src.numTriangles;
627 src.numTriangles = 0;
628
629 this->numWeights = src.numWeights;
630 src.numWeights = 0;
631
632 strncpy(this->shader, src.shader, MAX_NAME_LENGTH);
633 strncpy(src.shader, "", MAX_NAME_LENGTH);
634 }
635 };
636
637 // md5anim types
645 static constexpr GLshort MAX_NAME_LENGTH = 256;
649 char name[MAX_NAME_LENGTH] = "";
653 GLint parent = MD5Joint::NULL_JOINT;
657 GLuint flags = 0;
661 GLint startIndex = 0;
662
666 MD5JointInfo() = default;
672 _copyFromSrc(OTHER);
673 }
680 if (this != &OTHER) {
681 _copyFromSrc(OTHER);
682 }
683 return *this;
684 }
689 MD5JointInfo(MD5JointInfo&& src) noexcept {
690 _moveFromSrc(src);
691 }
698 if (this != &src) {
699 _moveFromSrc(src);
700 }
701 return *this;
702 }
703 private:
708 void _copyFromSrc(const MD5JointInfo &src) {
709 strncpy(this->name, src.name, MAX_NAME_LENGTH);
710 this->parent = src.parent;
711 this->flags = src.flags;
712 this->startIndex = src.startIndex;
713 }
718 void _moveFromSrc(MD5JointInfo &src) {
719 _copyFromSrc(src);
720
721 strncpy(src.name, "", MAX_NAME_LENGTH);
722 src.parent = MD5Joint::NULL_JOINT;
723 src.flags = 0;
724 src.startIndex = 0;
725 }
726 };
727
735 glm::vec3 position = {0.0f, 0.0f, 0.0f};
739 glm::quat orientation = {0.0f, 0.0f, 0.0f, 0.0f};
740
744 MD5BaseFrameJoint() = default;
750 _copyFromSrc(OTHER);
751 }
758 if (this != &OTHER) {
759 _copyFromSrc(OTHER);
760 }
761 return *this;
762 }
768 _moveFromSrc(src);
769 }
776 if (this != &src) {
777 _moveFromSrc(src);
778 }
779 return *this;
780 }
781 private:
786 void _copyFromSrc(const MD5BaseFrameJoint &src) {
787 this->position = src.position;
788 this->orientation = src.orientation;
789 }
794 void _moveFromSrc(MD5BaseFrameJoint &src) {
795 _copyFromSrc(src);
796
797 src.position = glm::vec3(0.0f, 0.0f, 0.0f);
798 src.orientation = glm::quat(0.0f, 0.0f, 0.0f, 0.0f);
799 }
800 };
801
810 glm::vec3 min = {0.0f, 0.0f, 0.0f};
814 glm::vec3 max = {0.0f, 0.0f, 0.0f};
815
819 MD5BoundingBox() = default;
825 _copyFromSrc(OTHER);
826 }
833 if (this != &OTHER) {
834 _copyFromSrc(OTHER);
835 }
836 return *this;
837 }
843 _moveFromSrc(src);
844 }
851 if (this != &src) {
852 _moveFromSrc(src);
853 }
854 return *this;
855 }
856 private:
861 void _copyFromSrc(const MD5BoundingBox& src) {
862 this->min = src.min;
863 this->max = src.max;
864 }
869 void _moveFromSrc(MD5BoundingBox& src) {
870 _copyFromSrc(src);
871
872 src.min = glm::vec3(0.0f, 0.0f, 0.0f);
873 src.max = glm::vec3(0.0f, 0.0f, 0.0f);
874 }
875 };
876
881 public:
886 GLint frameRate = 0;
887
892 [[nodiscard]] GLint getNumberOfFrames() const { return _numFrames; }
899 void setNumberOfFrames(const GLint numFrames) {
900 // if previously set, delete prior allocation to avoid memory leak
901 if(_skeletonFrames != nullptr) {
902 for (GLint i = 0; i < _numFrames; ++i) {
903 delete _skeletonFrames[i];
904 }
905 }
906 delete[] _skeletonFrames;
907 _skeletonFrames = nullptr; // no longer exists, in event new set equals zero
908 delete[] _boundingBoxes;
909 _boundingBoxes = nullptr; // no longer exists, in event new set equals zero
910
911 _numFrames = numFrames;
912 if ( _numFrames > 0 ) {
913 _skeletonFrames = new MD5Joint*[_numFrames];
914 for (GLint i = 0; i < _numFrames; ++i) {
915 _skeletonFrames[i] = nullptr;
916 }
917 _boundingBoxes = new MD5BoundingBox[_numFrames];
918 }
919 }
920
925 [[nodiscard]] GLint getNumberOfJoints() const { return _numJoints; }
933 void setNumberOfJoints(const GLint numJoints) {
934 // if previously set, delete prior allocation to avoid memory leak
935 if(_skeletonFrames != nullptr) {
936 for (GLint i = 0; i < _numFrames; ++i) {
937 delete _skeletonFrames[i];
938 _skeletonFrames[i] = nullptr; // no longer exists, in event new set equals zero
939 }
940 }
941
942 _numJoints = numJoints;
943 if (_numJoints > 0) {
944 if(_skeletonFrames != nullptr) {
945 for(GLint i = 0; i < _numFrames; ++i) {
946 // Allocate memory for joints of each frame
947 _skeletonFrames[i] = new MD5Joint[_numJoints];
948 }
949 }
950 }
951 }
952
960 [[nodiscard]] const MD5Joint* getSkeletonFrame(const GLint frameIndex) const {
961 if ( frameIndex < 0 || frameIndex >= _numFrames ) {
962 throw std::out_of_range("frameIndex out of range");
963 }
964 if (_skeletonFrames == nullptr) {
965 throw std::out_of_range("skeleton frames are null, setNumberOfFrames() may not have been called");
966 }
967 return _skeletonFrames[frameIndex];
968 }
978 [[nodiscard]] MD5Joint& getSkeletonFrameJoint(const GLint frameIndex, const GLint jointIndex) const {
979 if ( frameIndex < 0 || frameIndex >= _numFrames ) {
980 throw std::out_of_range("frameIndex out of range");
981 }
982 if ( jointIndex < 0 || jointIndex >= _numJoints ) {
983 throw std::out_of_range("jointIndex out of range");
984 }
985 if (_skeletonFrames == nullptr) {
986 throw std::out_of_range("skeleton frames are null, setNumberOfFrames() may not have been called");
987 }
988 if (_skeletonFrames[frameIndex] == nullptr) {
989 throw std::out_of_range("skeleton joints are null, setNumberOfJoints() may not have been called");
990 }
991 return _skeletonFrames[frameIndex][jointIndex];
992 }
993
1001 [[nodiscard]] MD5BoundingBox& getBoundingBox(const GLint frameIndex) const {
1002 if(frameIndex < 0 || frameIndex >= _numFrames) {
1003 throw std::out_of_range("frameIndex out of range");
1004 }
1005 if (_boundingBoxes == nullptr) {
1006 throw std::out_of_range("bounding boxes are null, setNumberOfFrames() may not have been called");
1007 }
1008 return _boundingBoxes[frameIndex];
1009 }
1010
1014 MD5Animation() = default;
1019 if(_skeletonFrames != nullptr) {
1020 for (GLint i = 0; i < _numFrames; i++) {
1021 delete[] _skeletonFrames[i];
1022 _skeletonFrames[i] = nullptr;
1023 }
1024 }
1025 delete[] _skeletonFrames;
1026 _skeletonFrames = nullptr;
1027
1028 delete[] _boundingBoxes;
1029 _boundingBoxes = nullptr;
1030 }
1031
1036 MD5Animation(const MD5Animation& OTHER) = delete;
1041 MD5Animation& operator=(const MD5Animation& OTHER) = delete;
1042
1047 MD5Animation(MD5Animation&& src) noexcept {
1048 _moveFromSrc(src);
1049 }
1056 if (this != &src) {
1057 _moveFromSrc(src);
1058 }
1059 return *this;
1060 }
1061 private:
1066 GLint _numFrames = 0;
1071 GLint _numJoints = 0;
1076 MD5Joint** _skeletonFrames = nullptr;
1081 MD5BoundingBox* _boundingBoxes = nullptr;
1082
1087 void _moveFromSrc(MD5Animation &src) {
1088 this->_numFrames = src._numFrames;
1089 src._numFrames = 0;
1090
1091 this->_numJoints = src._numJoints;
1092 src._numJoints = 0;
1093
1094 this->frameRate = src.frameRate;
1095 src.frameRate = 0;
1096
1097 this->_skeletonFrames = src._skeletonFrames;
1098 src._skeletonFrames = nullptr;
1099
1100 this->_boundingBoxes = src._boundingBoxes;
1101 src._boundingBoxes = nullptr;
1102 }
1103 };
1104
1112 GLint currFrame = 0;
1116 GLint nextFrame = 0;
1120 GLfloat lastTime = 0.0f;
1125 GLfloat maxTime = 0.0f;
1126
1136 _copyFromSrc(OTHER);
1137 }
1144 if (this != &OTHER) {
1145 _copyFromSrc(OTHER);
1146 }
1147 return *this;
1148 }
1154 _moveFromSrc(src);
1155 }
1162 if (this != &src) {
1163 _moveFromSrc(src);
1164 }
1165 return *this;
1166 }
1167 private:
1172 void _copyFromSrc(const MD5AnimationState &src) {
1173 this->currFrame = src.currFrame;
1174 this->nextFrame = src.nextFrame;
1175 this->lastTime = src.lastTime;
1176 this->maxTime = src.maxTime;
1177 }
1182 void _moveFromSrc(MD5AnimationState &src) {
1183 _copyFromSrc(src);
1184
1185 src.currFrame = 0;
1186 src.nextFrame = 0;
1187 src.lastTime = 0.0f;
1188 src.maxTime = 0.0f;
1189 }
1190 };
1191
1192 public:
1198 // allocate space for one animation by default
1199 _numAnimations = 1;
1200 // create the initial animation
1201 _animations = new MD5Animation*[_numAnimations];
1202 _animations[0] = new MD5Animation();
1203 // and the associated animation state information
1204 _animationInfos = new MD5AnimationState[_numAnimations];
1205 }
1209 ~MD5Model();
1210
1214 MD5Model(const MD5Model&) = delete;
1218 MD5Model& operator=(const MD5Model&) = delete;
1219
1223 MD5Model(MD5Model&& src) noexcept {
1224 _moveFromSrc(src);
1225 }
1229 MD5Model& operator=(MD5Model&& src) noexcept {
1230 if (this != &src) {
1231 _moveFromSrc(src);
1232 }
1233 return *this;
1234 }
1235
1242 [[maybe_unused]] bool loadMD5Model(const char* MD5_MESH_FILE, const char* MD5_ANIM_FILE = "");
1243
1248 [[nodiscard]] bool isAnimated() const { return _isAnimated; }
1249
1250 // md5mesh prototypes
1256 [[nodiscard]] bool readMD5Model(const char* FILENAME);
1265 [[maybe_unused]] void allocVertexArrays(GLuint vPosAttribLoc, GLuint vColorAttribLoc, GLuint vTexCoordAttribLoc);
1266
1274 [[maybe_unused]] void setActiveTextures(GLint diffuseMapActiveTexture, GLint specularMapActiveTexture, GLint normalMapActiveTexture, GLint heightMapActiveTexture);
1275
1279 [[maybe_unused]] void draw() const;
1283 [[maybe_unused]] void drawSkeleton() const;
1284
1285 // md5anim prototypes
1292 [[nodiscard]] bool readMD5Anim(const char* filename, GLushort targetAnimationIndex = 0);
1298 [[nodiscard]] bool addMD5Anim(const char* filename);
1305 [[nodiscard]] GLushort getNumberOfAnimations() const {
1306 // one slot is always allocated, but if model is not allocated then return 0
1307 if (!_isAnimated) return 0;
1308 // model is animated, return number of animations that were successfully loaded
1309 return _numAnimations;
1310 }
1316 void useTargetAnimationIndex(GLushort targetAnimationIndex);
1321 void animate(GLfloat dt);
1322
1323 private:
1328 MD5Joint* _baseSkeleton = nullptr;
1333 MD5Mesh* _meshes = nullptr;
1338 GLint _numJoints = 0;
1343 GLint _numMeshes = 0;
1344
1345 // vertex array related stuff
1350 GLint _maxVertices = 0;
1355 GLint _maxTriangles = 0;
1360 glm::vec3* _vertexArray = nullptr;
1365 glm::vec2* _texelArray = nullptr;
1370 GLuint* _vertexIndicesArray = nullptr;
1374 GLuint _vao = 0;
1380 GLuint _vbo[2] = {0, 0};
1381
1382 // skeleton related stuff
1386 GLuint _skeletonVAO = 0;
1390 GLuint _skeletonVBO = 0;
1394 MD5Joint* _skeleton = nullptr;
1395
1396 // animation related stuff
1400 MD5Animation** _animations = nullptr;
1404 GLushort _numAnimations = 0;
1408 GLushort _currentAnimationIndex = 0;
1412 bool _isAnimated = false;
1416 MD5AnimationState* _animationInfos = nullptr;
1417
1421 GLint _diffuseActiveTexture = GL_TEXTURE0;
1425 GLint _specularActiveTexture = GL_TEXTURE1;
1429 GLint _normalActiveTexture = GL_TEXTURE2;
1433 GLint _heightActiveTexture = GL_TEXTURE3;
1434
1435 // helper functions
1441 void _prepareMesh(const MD5Mesh* pMESH) const;
1446 void _drawMesh(const MD5Mesh* pMESH) const;
1453 [[nodiscard]] bool _checkAnimValidity(GLushort targetAnimationIndex) const;
1462 static void _buildFrameSkeleton(const MD5JointInfo* pJOINT_INFOS,
1463 const MD5BaseFrameJoint* pBASE_FRAME,
1464 const GLfloat* pANIM_FRAME_DATA,
1465 const MD5Joint* pSkeletonFrame,
1466 GLint NUM_JOINTS);
1471 void _interpolateSkeletons(GLfloat interp);
1475 void _freeModel();
1479 void _freeVertexArrays();
1483 void _freeAnim();
1484
1489 void _moveFromSrc(MD5Model &src);
1490 };
1491}
1492
1493//----------------------------------------------------------------------------------------------------
1494
1495inline
1497{
1498 _freeModel();
1499 _freeVertexArrays();
1500 _freeAnim();
1501}
1502
1503// load our MD5 model
1504[[maybe_unused]]
1505inline bool
1507 const char* MD5_MESH_FILE,
1508 const char* MD5_ANIM_FILE
1509) {
1510 // Load MD5 _model file
1511 if( readMD5Model(MD5_MESH_FILE) ) {
1512 // if MD5 animation file name provided
1513 if(strcmp(MD5_ANIM_FILE, "") != 0 ) {
1514 // Load MD5 animation file
1515 if( !readMD5Anim(MD5_ANIM_FILE) ) {
1516 return false;
1517 }
1518 }
1519 if( !isAnimated() ) {
1520 printf ("[.MD5_ANIM_FILE]: no animation loaded.\n");
1521 }
1522 } else {
1523 return false;
1524 }
1525 return true;
1526}
1527
1528// Load an MD5 model from file.
1529inline bool
1531 const char* FILENAME
1532) {
1533 char buff[512];
1534 GLint version = 0;
1535 GLint currentMesh = 0;
1536
1537 GLint totalVertices = 0;
1538 GLint totalWeights = 0;
1539 GLint totalTriangles = 0;
1540
1541 GLfloat minX = 999999, minY = 999999, minZ = 999999;
1542 GLfloat maxX = -999999, maxY = -999999, maxZ = -999999;
1543
1544 printf("[.md5mesh]: about to read %s\n", FILENAME );
1545
1546 FILE *fp = fopen(FILENAME, "rb" );
1547 if( !fp ) {
1548 fprintf (stderr, "[.md5mesh]: Error: couldn't open \"%s\"!\n", FILENAME);
1549 return false;
1550 }
1551
1552 while( !feof(fp) ) {
1553 // Read whole line
1554 fgets( buff, sizeof(buff), fp );
1555
1556 if( sscanf(buff, " MD5Version %d", &version) == 1 ) {
1557 if( version != 10 ) {
1558 // Bad version
1559 fprintf (stderr, "[.md5mesh]: Error: bad model version\n");
1560 fclose (fp);
1561 return false;
1562 }
1563 } else if( sscanf(buff, " numJoints %d", &_numJoints) == 1 ) {
1564 if( _numJoints > 0 ) {
1565 // Allocate memory for base skeleton joints
1566 _baseSkeleton = new MD5Joint[_numJoints];
1567 }
1568 } else if( sscanf(buff, " numMeshes %d", &_numMeshes) == 1 ) {
1569 if( _numMeshes > 0 ) {
1570 // Allocate memory for meshes
1571 _meshes = new MD5Mesh[_numMeshes];
1572 }
1573 } else if( strncmp(buff, "joints {", 8) == 0 ) {
1574 // Read each joint
1575 for(GLint i = 0; i < _numJoints; ++i) {
1576 MD5Joint *joint = &_baseSkeleton[i];
1577
1578 // Read whole line
1579 fgets( buff, sizeof(buff), fp );
1580
1581 if( sscanf(buff, "%s %d ( %f %f %f ) ( %f %f %f )",
1582 joint->name, &joint->parent,
1583 &joint->position[0], &joint->position[1], &joint->position[2],
1584 &joint->orientation[0],&joint->orientation[1], &joint->orientation[2]) == 8
1585 ) {
1586 // Compute the w component
1587 joint->orientation.w = glm::extractRealComponent(joint->orientation);
1588 }
1589 }
1590 } else if( strncmp(buff, "mesh {", 6) == 0 ) {
1591 MD5Mesh *mesh = &_meshes[currentMesh];
1592 GLint vert_index = 0;
1593 GLint tri_index = 0;
1594 GLint weight_index = 0;
1595 GLfloat floatData[4];
1596 GLint intData[3];
1597
1598 while( buff[0] != '}' && !feof(fp) ) {
1599 // Read whole line
1600 fgets( buff, sizeof(buff), fp );
1601
1602 if( strstr( buff, "shader ") ) {
1603 GLint quote = 0, j = 0;
1604
1605 // Copy the shader name without the quote marks
1606 for(unsigned long uli = 0; uli < sizeof(buff) && (quote < 2); ++uli) {
1607 if( buff[uli] == '\"' )
1608 quote++;
1609
1610 if( (quote == 1) && (buff[uli] != '\"') ) {
1611 mesh->shader[j] = buff[uli];
1612 j++;
1613 }
1614 }
1615 // there was a shader name
1616 if( j > 0 ) {
1617 // diffuse map
1618 strcpy(mesh->textures[MD5Mesh::TextureMap::DIFFUSE].filename, mesh->shader);
1619 strcat(mesh->textures[MD5Mesh::TextureMap::DIFFUSE].filename, ".tga");
1620 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);
1621 if( mesh->textures[MD5Mesh::TextureMap::DIFFUSE].texHandle == 0 ) {
1622 strcpy(mesh->textures[MD5Mesh::TextureMap::DIFFUSE].filename, mesh->shader);
1623 strcat(mesh->textures[MD5Mesh::TextureMap::DIFFUSE].filename, "_d.tga");
1624 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 );
1625 if( mesh->textures[MD5Mesh::TextureMap::DIFFUSE].texHandle == 0 ) {
1626 strcpy(mesh->textures[MD5Mesh::TextureMap::DIFFUSE].filename, mesh->shader);
1627 strcat(mesh->textures[MD5Mesh::TextureMap::DIFFUSE].filename, ".png");
1628 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 );
1629 if( mesh->textures[MD5Mesh::TextureMap::DIFFUSE].texHandle == 0 ) {
1630 fprintf(stderr, "[.md5mesh | ERROR]: Could not load diffuse map for shader %s\n", mesh->shader);
1631 }
1632 }
1633 }
1634
1635 // specular map
1636 strcpy(mesh->textures[MD5Mesh::TextureMap::SPECULAR].filename, mesh->shader);
1637 strcat(mesh->textures[MD5Mesh::TextureMap::SPECULAR].filename, "_s.tga");
1638 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 );
1639 if( mesh->textures[MD5Mesh::TextureMap::SPECULAR].texHandle == 0 ) {
1640 strcpy(mesh->textures[MD5Mesh::TextureMap::SPECULAR].filename, mesh->shader);
1641 strcat(mesh->textures[MD5Mesh::TextureMap::SPECULAR].filename, "_s.png");
1642 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 );
1643 if( mesh->textures[MD5Mesh::TextureMap::SPECULAR].texHandle == 0 ) {
1644 fprintf(stderr, "[.md5mesh | ERROR]: Could not load specular map for shader %s\n", mesh->shader);
1645 }
1646 }
1647
1648 // normal map
1649 strcpy(mesh->textures[MD5Mesh::TextureMap::NORMAL].filename, mesh->shader);
1650 strcat(mesh->textures[MD5Mesh::TextureMap::NORMAL].filename, "_local.tga");
1651 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 );
1652 if( mesh->textures[MD5Mesh::TextureMap::NORMAL].texHandle == 0 ) {
1653 strcpy(mesh->textures[MD5Mesh::TextureMap::NORMAL].filename, mesh->shader);
1654 strcat(mesh->textures[MD5Mesh::TextureMap::NORMAL].filename, "_local.png");
1655 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 );
1656 if( mesh->textures[MD5Mesh::TextureMap::NORMAL].texHandle == 0 ) {
1657 fprintf(stderr, "[.md5mesh | ERROR]: Could not load normal map for shader %s\n", mesh->shader);
1658 }
1659 }
1660
1661 // height map
1662 strcpy(mesh->textures[MD5Mesh::TextureMap::HEIGHT].filename, mesh->shader);
1663 strcat(mesh->textures[MD5Mesh::TextureMap::HEIGHT].filename, "_h.tga");
1664 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 );
1665 if( mesh->textures[MD5Mesh::TextureMap::HEIGHT].texHandle == 0 ) {
1666 strcpy(mesh->textures[MD5Mesh::TextureMap::HEIGHT].filename, mesh->shader);
1667 strcat(mesh->textures[MD5Mesh::TextureMap::HEIGHT].filename, "_h.png");
1668 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 );
1669 if( mesh->textures[MD5Mesh::TextureMap::HEIGHT].texHandle == 0 ) {
1670 fprintf(stderr, "[.md5mesh | ERROR]: Could not load height map for shader %s\n", mesh->shader);
1671 }
1672 }
1673 }
1674 } else if( sscanf(buff, " numverts %d", &mesh->numVertices) == 1 ) {
1675 if( mesh->numVertices > 0 ) {
1676 // Allocate memory for vertices
1677 mesh->vertices = new MD5Vertex[mesh->numVertices];
1678 }
1679
1680 if( mesh->numVertices > _maxVertices )
1681 _maxVertices = mesh->numVertices;
1682
1683 totalVertices += mesh->numVertices;
1684 } else if( sscanf(buff, " numtris %d", &mesh->numTriangles) == 1 ) {
1685 if( mesh->numTriangles > 0 ) {
1686 // Allocate memory for triangles
1687 mesh->triangles = new MD5Triangle[mesh->numTriangles];
1688 }
1689
1690 if( mesh->numTriangles > _maxTriangles )
1691 _maxTriangles = mesh->numTriangles;
1692
1693 totalTriangles += mesh->numTriangles;
1694 } else if( sscanf(buff, " numweights %d", &mesh->numWeights) == 1 ) {
1695 if( mesh->numWeights > 0 ) {
1696 // Allocate memory for vertex weights
1697 mesh->weights = new MD5Weight[mesh->numWeights];
1698 }
1699
1700 totalWeights += mesh->numWeights;
1701 } else if( sscanf(buff, " vert %d ( %f %f ) %d %d",
1702 &vert_index,
1703 &floatData[0], &floatData[1],
1704 &intData[0], &intData[1]) == 5
1705 ) {
1706 // Copy vertex data
1707 mesh->vertices[vert_index].texCoord.s = floatData[0];
1708 mesh->vertices[vert_index].texCoord.t = floatData[1];
1709 mesh->vertices[vert_index].start = intData[0];
1710 mesh->vertices[vert_index].count = intData[1];
1711 } else if( sscanf(buff, " tri %d %d %d %d",
1712 &tri_index,
1713 &intData[0], &intData[1], &intData[2]) == 4
1714 ) {
1715 // Copy triangle data
1716 mesh->triangles[tri_index ].index[0] = intData[0];
1717 mesh->triangles[tri_index ].index[1] = intData[1];
1718 mesh->triangles[tri_index ].index[2] = intData[2];
1719 } else if( sscanf(buff, " weight %d %d %f ( %f %f %f )",
1720 &weight_index, &intData[0], &floatData[3],
1721 &floatData[0], &floatData[1], &floatData[2]) == 6
1722 ) {
1723 // Copy vertex data
1724 mesh->weights[weight_index].joint = intData[0];
1725 mesh->weights[weight_index].bias = floatData[3];
1726 mesh->weights[weight_index].position[0] = floatData[0];
1727 mesh->weights[weight_index].position[1] = floatData[1];
1728 mesh->weights[weight_index].position[2] = floatData[2];
1729
1730 if( floatData[0] < minX ) { minX = floatData[0]; }
1731 if( floatData[0] > maxX ) { maxX = floatData[0]; }
1732 if( floatData[1] < minY ) { minY = floatData[1]; }
1733 if( floatData[1] > maxY ) { maxY = floatData[1]; }
1734 if( floatData[2] < minZ ) { minZ = floatData[2]; }
1735 if( floatData[2] > maxZ ) { maxZ = floatData[2]; }
1736 }
1737 }
1738
1739 currentMesh++;
1740 }
1741 }
1742
1743 fclose(fp);
1744
1745 _skeleton = _baseSkeleton;
1746
1747 printf("[.md5mesh]: finished reading %s\n", FILENAME );
1748 printf("[.md5mesh]: read in %d meshes, %d joints, %d vertices, %d weights, and %d triangles\n", _numMeshes, _numJoints, totalVertices, totalWeights, totalTriangles );
1749 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) );
1750 printf( "\n" );
1751
1752 return true;
1753}
1754
1755inline void
1756CSCI441::MD5Model::_freeModel()
1757{
1758 delete[] _baseSkeleton;
1759 if (_baseSkeleton == _skeleton) _skeleton = nullptr; // if there is no animation, prevent double delete
1760 _baseSkeleton = nullptr;
1761
1762 delete[] _meshes;
1763 _meshes = nullptr;
1764}
1765
1766[[maybe_unused]]
1767inline void
1769{
1770 // Draw each mesh of the model
1771 for(GLint i = 0; i < _numMeshes; ++i) {
1772 MD5Mesh& mesh = _meshes[i]; // get the mesh
1773 _prepareMesh(&mesh); // do some preprocessing on it
1774 _drawMesh(&mesh);
1775 }
1776}
1777
1778inline void
1779CSCI441::MD5Model::_prepareMesh(
1780 const MD5Mesh *pMESH
1781) const {
1782 GLint i, j, k;
1783
1784 // Setup vertex indices
1785 for(k = 0, i = 0; i < pMESH->numTriangles; ++i) {
1786 for(j = 0; j < 3; ++j, ++k)
1787 _vertexIndicesArray[k] = pMESH->triangles[i].index[j];
1788 }
1789
1790 // Setup vertices
1791 for(i = 0; i < pMESH->numVertices; ++i) {
1792 glm::vec3 finalVertex = {0.0f, 0.0f, 0.0f };
1793
1794 // Calculate final vertex to draw with weights
1795 for(j = 0; j < pMESH->vertices[i].count; ++j) {
1796 const MD5Weight *weight = &pMESH->weights[pMESH->vertices[i].start + j];
1797 const MD5Joint *joint = &_skeleton[weight->joint];
1798
1799 // Calculate transformed vertex for this weight
1800 const glm::vec3 weightedVertex = glm::rotate(joint->orientation, glm::vec4(weight->position, 0.0f));
1801
1802 // The sum of all weight->bias should be 1.0
1803 finalVertex.x += (joint->position.x + weightedVertex.x) * weight->bias;
1804 finalVertex.y += (joint->position.y + weightedVertex.y) * weight->bias;
1805 finalVertex.z += (joint->position.z + weightedVertex.z) * weight->bias;
1806 }
1807
1808 _vertexArray[i].x = finalVertex.x;
1809 _vertexArray[i].y = finalVertex.y;
1810 _vertexArray[i].z = finalVertex.z;
1811
1812 _texelArray[i].s = pMESH->vertices[i].texCoord.s;
1813 _texelArray[i].t = pMESH->vertices[i].texCoord.t;
1814 }
1815
1816 glBindVertexArray(_vao );
1817
1818 glBindBuffer(GL_ARRAY_BUFFER, _vbo[0] );
1819 glBufferSubData(GL_ARRAY_BUFFER, 0, static_cast<GLsizeiptr>(sizeof(glm::vec3)) * pMESH->numVertices, &_vertexArray[0] );
1820 glBufferSubData(GL_ARRAY_BUFFER, static_cast<GLsizeiptr>(sizeof(glm::vec3)) * _maxVertices, static_cast<GLsizeiptr>(sizeof(glm::vec2)) * pMESH->numVertices, &_texelArray[0] );
1821
1822 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vbo[1] );
1823 glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, static_cast<GLsizeiptr>(sizeof(GLuint)) * pMESH->numTriangles * 3, _vertexIndicesArray );
1824}
1825
1826inline void
1827CSCI441::MD5Model::_drawMesh(
1828 const MD5Mesh *pMESH
1829) const {
1830 if (pMESH->textures[MD5Mesh::TextureMap::SPECULAR].texHandle != 0) {
1831 // Bind Specular Map if exists
1832 glActiveTexture(_specularActiveTexture);
1833 glBindTexture(GL_TEXTURE_2D, pMESH->textures[MD5Mesh::TextureMap::SPECULAR].texHandle );
1834 }
1835 if (pMESH->textures[MD5Mesh::TextureMap::NORMAL].texHandle != 0) {
1836 // Bind Normal Map if exists
1837 glActiveTexture(_normalActiveTexture);
1838 glBindTexture(GL_TEXTURE_2D, pMESH->textures[MD5Mesh::TextureMap::NORMAL].texHandle );
1839 }
1840 if (pMESH->textures[MD5Mesh::TextureMap::HEIGHT].texHandle != 0) {
1841 // Bind Height Map if exists
1842 glActiveTexture(_heightActiveTexture);
1843 glBindTexture(GL_TEXTURE_2D, pMESH->textures[MD5Mesh::TextureMap::HEIGHT].texHandle );
1844 }
1845 // bind diffuse last because ideally it is texture 0 and will remain for future renderings
1846 if (pMESH->textures[MD5Mesh::TextureMap::DIFFUSE].texHandle != 0) {
1847 // Bind Diffuse Map if exists
1848 glActiveTexture(_diffuseActiveTexture);
1849 glBindTexture(GL_TEXTURE_2D, pMESH->textures[MD5Mesh::TextureMap::DIFFUSE].texHandle );
1850 }
1851 if (_diffuseActiveTexture != GL_TEXTURE0) {
1852 // reset back to active texture being zero
1853 glActiveTexture(GL_TEXTURE0);
1854 }
1855
1856 glBindVertexArray(_vao );
1857 glDrawElements(GL_TRIANGLES, pMESH->numTriangles * 3, GL_UNSIGNED_INT, (void*)nullptr );
1858}
1859
1860[[maybe_unused]]
1861inline void
1863 const GLuint vPosAttribLoc,
1864 const GLuint vColorAttribLoc,
1865 const GLuint vTexCoordAttribLoc
1866) {
1867 _vertexArray = new glm::vec3[_maxVertices];
1868 _texelArray = new glm::vec2[_maxVertices];
1869 _vertexIndicesArray = new GLuint[_maxTriangles * 3];
1870
1871 glGenVertexArrays( 1, &_vao );
1872 glBindVertexArray(_vao );
1873
1874 glGenBuffers(2, _vbo );
1875 glBindBuffer(GL_ARRAY_BUFFER, _vbo[0] );
1876 glBufferData(GL_ARRAY_BUFFER, static_cast<GLsizeiptr>(sizeof(glm::vec3)) * _maxVertices + static_cast<GLsizeiptr>(sizeof(glm::vec2)) * _maxVertices, nullptr, GL_DYNAMIC_DRAW );
1877
1878 glEnableVertexAttribArray( vPosAttribLoc );
1879 glVertexAttribPointer( vPosAttribLoc, 3, GL_FLOAT, GL_FALSE, 0, static_cast<void *>(nullptr) );
1880
1881 glEnableVertexAttribArray( vTexCoordAttribLoc );
1882 glVertexAttribPointer( vTexCoordAttribLoc, 2, GL_FLOAT, GL_FALSE, 0, reinterpret_cast<void *>(sizeof(glm::vec3) * _maxVertices) );
1883
1884 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vbo[1] );
1885 glBufferData(GL_ELEMENT_ARRAY_BUFFER, static_cast<GLsizeiptr>(sizeof(GLuint)) * _maxTriangles * 3, nullptr, GL_DYNAMIC_DRAW );
1886
1887 printf("[.md5mesh]: Model VAO/VBO/IBO registered at %u/%u/%u\n", _vao, _vbo[0], _vbo[1] );
1888
1889 glGenVertexArrays( 1, &_skeletonVAO );
1890 glBindVertexArray(_skeletonVAO );
1891
1892 glGenBuffers( 1, &_skeletonVBO );
1893 glBindBuffer(GL_ARRAY_BUFFER, _skeletonVBO );
1894 glBufferData(GL_ARRAY_BUFFER, static_cast<GLsizeiptr>(sizeof(glm::vec3)) * _numJoints * 3 * 2, nullptr, GL_DYNAMIC_DRAW );
1895
1896 glEnableVertexAttribArray( vPosAttribLoc ); // vPos
1897 glVertexAttribPointer( vPosAttribLoc, 3, GL_FLOAT, GL_FALSE, 0, static_cast<void *>(nullptr) );
1898
1899 glEnableVertexAttribArray( vColorAttribLoc ); // vColor
1900 glVertexAttribPointer( vColorAttribLoc, 3, GL_FLOAT, GL_FALSE, 0, reinterpret_cast<void *>(sizeof(glm::vec3) * _numJoints * 3) );
1901
1902 printf("[.md5mesh]: Skeleton VAO/VBO registered at %u/%u\n", _skeletonVAO, _skeletonVBO );
1903}
1904
1905inline void
1907 const GLint diffuseMapActiveTexture = GL_TEXTURE0,
1908 const GLint specularMapActiveTexture = GL_TEXTURE1,
1909 const GLint normalMapActiveTexture = GL_TEXTURE2,
1910 const GLint heightMapActiveTexture = GL_TEXTURE3
1911) {
1912 _diffuseActiveTexture = diffuseMapActiveTexture;
1913 _specularActiveTexture = specularMapActiveTexture;
1914 _normalActiveTexture = normalMapActiveTexture;
1915 _heightActiveTexture = heightMapActiveTexture;
1916}
1917
1918inline void
1919CSCI441::MD5Model::_freeVertexArrays()
1920{
1921 delete[] _vertexArray;
1922 _vertexArray = nullptr;
1923
1924 delete[] _vertexIndicesArray;
1925 _vertexIndicesArray = nullptr;
1926
1927 delete[] _texelArray;
1928 _texelArray = nullptr;
1929
1930 glDeleteVertexArrays( 1, &_vao );
1931 _vao = 0;
1932
1933 glDeleteBuffers(2, _vbo );
1934 _vbo[0] = 0;
1935 _vbo[1] = 0;
1936
1937 glDeleteVertexArrays( 1, &_skeletonVAO );
1938 _skeletonVAO = 0;
1939
1940 glDeleteBuffers( 1, &_skeletonVBO );
1941 _skeletonVBO = 0;
1942}
1943
1944[[maybe_unused]]
1945inline void
1947{
1948 glBindVertexArray(_skeletonVAO );
1949 glBindBuffer(GL_ARRAY_BUFFER, _skeletonVBO );
1950
1951 constexpr glm::vec3 jointColor = {1.0f, 1.0f, 0.0f };
1952 constexpr glm::vec3 boneColor = {1.0f, 0.0f, 1.0f };
1953
1954 // put in points for joints
1955 for(GLint i = 0; i < _numJoints; ++i ) {
1956 glBufferSubData(GL_ARRAY_BUFFER, i * static_cast<GLsizeiptr>(sizeof(glm::vec3)), sizeof(glm::vec3), &(_skeleton[i].position) );
1957 glBufferSubData(GL_ARRAY_BUFFER, i * static_cast<GLsizeiptr>(sizeof(glm::vec3)) + static_cast<GLsizeiptr>(sizeof(glm::vec3)) * _numJoints * 3, sizeof(glm::vec3), &jointColor[0]);
1958 }
1959
1960 // put in lines for bones
1961 GLint numBones = 0;
1962 for(GLint i = 0; i < _numJoints; ++i ) {
1963 if( _skeleton[i].parent != MD5Joint::NULL_JOINT ) {
1964 glBufferSubData(
1965 GL_ARRAY_BUFFER,
1966 static_cast<GLsizeiptr>(sizeof(glm::vec3)) * _numJoints + (i * 2) * static_cast<GLsizeiptr>(sizeof(glm::vec3)),
1967 static_cast<GLsizeiptr>(sizeof(glm::vec3)),
1968 &(_skeleton[_skeleton[i].parent].position)
1969 );
1970 glBufferSubData(
1971 GL_ARRAY_BUFFER,
1972 static_cast<GLsizeiptr>(sizeof(glm::vec3)) * _numJoints + (i * 2) * static_cast<GLsizeiptr>(sizeof(glm::vec3)) + static_cast<GLsizeiptr>(sizeof(glm::vec3)) * _numJoints * 3,
1973 sizeof(glm::vec3),
1974 &boneColor[0]
1975 );
1976
1977 glBufferSubData(
1978 GL_ARRAY_BUFFER,
1979 static_cast<GLsizeiptr>(sizeof(glm::vec3)) * _numJoints + (i * 2) * static_cast<GLsizeiptr>(sizeof(glm::vec3)) + static_cast<GLsizeiptr>(sizeof(glm::vec3)),
1980 static_cast<GLsizeiptr>(sizeof(glm::vec3)),
1981 &(_skeleton[i].position)
1982 );
1983 glBufferSubData(
1984 GL_ARRAY_BUFFER,
1985 static_cast<GLsizeiptr>(sizeof(glm::vec3)) * _numJoints + (i * 2) * static_cast<GLsizeiptr>(sizeof(glm::vec3)) + static_cast<GLsizeiptr>(sizeof(glm::vec3)) + static_cast<GLsizeiptr>(sizeof(glm::vec3)) * _numJoints * 3,
1986 sizeof(glm::vec3),
1987 &boneColor[0]
1988 );
1989 numBones++;
1990 }
1991 }
1992
1993 glPointSize(5.0f);
1994 glDrawArrays(GL_POINTS, 0, _numJoints );
1995 glPointSize(1.0f);
1996
1997 glLineWidth( 3.0f );
1998 glDrawArrays(GL_LINES, _numJoints, numBones * 2 );
1999 glLineWidth(1.0f);
2000}
2001
2002inline bool
2003CSCI441::MD5Model::_checkAnimValidity(const GLushort targetAnimationIndex) const
2004{
2005 // md5mesh and md5anim must have the same number of joints
2006 if( _numJoints != _animations[targetAnimationIndex]->getNumberOfJoints() ) {
2007 fprintf(stdout, "\n[.md5anim]: skeleton and animation do not have same number of joints. cannot apply animation %u to skeleton\n\n", targetAnimationIndex);
2008 return false;
2009 }
2010 if (_animations[targetAnimationIndex]->getNumberOfJoints() == 0 ) {
2011 fprintf(stdout, "\n[.md5anim]: animation has zero joints. cannot apply animation %u to skeleton\n\n", targetAnimationIndex);
2012 return false;
2013 }
2014
2015 // We just check with frame[0]
2016 for(GLint i = 0; i < _numJoints; ++i) {
2017 // Joints must have the same parent index
2018 if (_baseSkeleton[i].parent != _animations[targetAnimationIndex]->getSkeletonFrameJoint(0, i).parent) {
2019 fprintf(stdout, "\n[.md5anim]: skeleton and animation joints do not have same parent index. cannot apply animation %u to skeleton\n\n", targetAnimationIndex);
2020 return false;
2021 }
2022
2023 // Joints must have the same name
2024 if (strcmp (_baseSkeleton[i].name, _animations[targetAnimationIndex]->getSkeletonFrameJoint(0, i).name) != 0) {
2025 fprintf(stdout, "\n[.md5anim]: skeleton and animation joints do not have same name. cannot apply animation %u to skeleton\n\n", targetAnimationIndex);
2026 return false;
2027 }
2028 }
2029
2030 fprintf(stdout, "\n[.md5anim]: skeleton and animation match. animation %u can be applied to skeleton\n\n", targetAnimationIndex);
2031 return true;
2032}
2033
2034inline void
2035CSCI441::MD5Model::_buildFrameSkeleton(
2036 const MD5JointInfo* pJOINT_INFOS,
2037 const MD5BaseFrameJoint* pBASE_FRAME,
2038 const GLfloat* pANIM_FRAME_DATA,
2039 const MD5Joint* pSkeletonFrame,
2040 const GLint NUM_JOINTS
2041) {
2042 if(pJOINT_INFOS == nullptr
2043 || pBASE_FRAME == nullptr
2044 || pANIM_FRAME_DATA == nullptr
2045 || pSkeletonFrame == nullptr) return;
2046
2047 for(GLint i = 0; i < NUM_JOINTS; ++i) {
2048 const MD5BaseFrameJoint *baseJoint = &pBASE_FRAME[i];
2049 glm::vec3 animatedPosition = baseJoint->position;
2050 glm::quat animatedOrientation = baseJoint->orientation;
2051 GLint j = 0;
2052
2053 // Tx
2054 if(pJOINT_INFOS[i].flags & 1 ) {
2055 animatedPosition.x = pANIM_FRAME_DATA[pJOINT_INFOS[i].startIndex + j];
2056 ++j;
2057 }
2058
2059 // Ty
2060 if(pJOINT_INFOS[i].flags & 2 ) {
2061 animatedPosition.y = pANIM_FRAME_DATA[pJOINT_INFOS[i].startIndex + j];
2062 ++j;
2063 }
2064
2065 // Tz
2066 if(pJOINT_INFOS[i].flags & 4 ) {
2067 animatedPosition.z = pANIM_FRAME_DATA[pJOINT_INFOS[i].startIndex + j];
2068 ++j;
2069 }
2070
2071 // Qx
2072 if(pJOINT_INFOS[i].flags & 8 ) {
2073 animatedOrientation.x = pANIM_FRAME_DATA[pJOINT_INFOS[i].startIndex + j];
2074 ++j;
2075 }
2076
2077 // Qy
2078 if(pJOINT_INFOS[i].flags & 16 ) {
2079 animatedOrientation.y = pANIM_FRAME_DATA[pJOINT_INFOS[i].startIndex + j];
2080 ++j;
2081 }
2082
2083 // Qz
2084 if(pJOINT_INFOS[i].flags & 32 ) {
2085 animatedOrientation.z = pANIM_FRAME_DATA[pJOINT_INFOS[i].startIndex + j];
2086 }
2087
2088 // Compute orientation quaternion's w value
2089 animatedOrientation.w = glm::extractRealComponent(animatedOrientation);
2090
2091 // NOTE: we assume that this joint's parent has
2092 // already been calculated, i.e. joint's ID should
2093 // never be smaller than its parent ID.
2094 const auto thisJoint = const_cast<MD5Joint *>(&pSkeletonFrame[i]);
2095
2096 const GLint parent = pJOINT_INFOS[i].parent;
2097 thisJoint->parent = parent;
2098 strcpy (thisJoint->name, pJOINT_INFOS[i].name);
2099
2100 // Has parent?
2101 if( thisJoint->parent == MD5Joint::NULL_JOINT ) {
2102 thisJoint->position = animatedPosition;
2103 thisJoint->orientation = animatedOrientation;
2104 } else {
2105 const MD5Joint *parentJoint = &pSkeletonFrame[parent];
2106 glm::vec3 rotatedPosition = glm::rotate(parentJoint->orientation, glm::vec4(animatedPosition, 0.0f));
2107
2108 // Add positions
2109 thisJoint->position = parentJoint->position + rotatedPosition;
2110
2111 // Concatenate rotations
2112 thisJoint->orientation = glm::normalize( glm::cross(parentJoint->orientation, animatedOrientation) );
2113 }
2114 }
2115}
2116
2117inline bool
2119 const char *filename,
2120 const GLushort targetAnimationIndex
2121) {
2122 if (targetAnimationIndex >= _numAnimations) {
2123 fprintf (stderr, "[.md5anim]: Error: target animation index %u is out of range for currently allocated animations (which is %u)\n", targetAnimationIndex, _numAnimations);
2124 fprintf (stderr, "[.md5anim]: Hey Developer, if you wish to add an animation to the sequence, use CSCI441::MD5Model::addMD5Anim(const char*)\n");
2125 return false;
2126 }
2127
2128 char buff[512];
2129 MD5JointInfo *jointInfos = nullptr;
2130 MD5BaseFrameJoint *baseFrame = nullptr;
2131 GLfloat *animFrameData = nullptr;
2132 GLint version;
2133 GLint numAnimatedComponents;
2134 GLint frameIndex, numFrames, numJoints;
2135 GLint i;
2136
2137 printf( "[.md5anim]: about to read %s into animation %u\n", filename, targetAnimationIndex );
2138
2139 FILE *fp = fopen( filename, "rb" );
2140 if( !fp ) {
2141 fprintf (stderr, "[.md5anim]: Error: couldn't open \"%s\"!\n", filename);
2142 return false;
2143 }
2144
2145 while( !feof(fp) ) {
2146 // Read whole line
2147 fgets( buff, sizeof(buff), fp );
2148
2149 if( sscanf(buff, " MD5Version %d", &version) == 1 ) {
2150 if( version != 10 ) {
2151 // Bad version
2152 fprintf (stderr, "[.md5anim]: Error: bad animation version\n");
2153 fclose (fp);
2154 return false;
2155 }
2156 } else if( sscanf(buff, " numFrames %d", &numFrames) == 1 ) {
2157 // Allocate memory for skeleton frames and bounding boxes
2158 _animations[targetAnimationIndex]->setNumberOfFrames(numFrames);
2159 } else if( sscanf(buff, " numJoints %d", &numJoints) == 1 ) {
2160 if (jointInfos != nullptr) {
2161 fprintf( stderr, "[.md5anim]: Error: md5anim file malformed. numJoints already specified\n" );
2162 }
2163 if( numJoints > 0 ) {
2164 _animations[targetAnimationIndex]->setNumberOfJoints(numJoints);
2165
2166 // Allocate temporary memory for building skeleton frames
2167 jointInfos = new MD5JointInfo[numJoints];
2168 baseFrame = new MD5BaseFrameJoint[numJoints];
2169 }
2170 } else if( sscanf(buff, " frameRate %d", &_animations[targetAnimationIndex]->frameRate) == 1 ) {
2171
2172 } else if( sscanf(buff, " numAnimatedComponents %d", &numAnimatedComponents) == 1 ) {
2173 if (animFrameData != nullptr) {
2174 fprintf( stderr, "[.md5anim]: Error: md5anim file malformed. numAnimatedComponents already specified\n" );
2175 }
2176 if( numAnimatedComponents > 0 ) {
2177 // Allocate memory for animation frame data
2178 animFrameData = new GLfloat[numAnimatedComponents];
2179 }
2180 } else if( strncmp(buff, "hierarchy {", 11) == 0 ) {
2181 if (jointInfos == nullptr) {
2182 fprintf( stderr, "[.md5anim]: Error: md5anim file malformed. numJoints not specified prior to hierarchy\n" );
2183 } else {
2184 for(i = 0; i < numJoints; ++i) {
2185 // Read whole line
2186 fgets( buff, sizeof(buff), fp );
2187
2188 // Read joint info
2189 sscanf(buff, " %s %d %d %d",
2190 jointInfos[i].name, &jointInfos[i].parent,
2191 &jointInfos[i].flags, &jointInfos[i].startIndex);
2192 }
2193 }
2194 } else if( strncmp(buff, "bounds {", 8) == 0 ) {
2195 if (_animations[targetAnimationIndex]->getNumberOfFrames() == 0) {
2196 fprintf( stderr, "[.md5anim]: Error: md5anim file malformed. numFrames not specified prior to bounds\n" );
2197 } else {
2198 for(i = 0; i < _animations[targetAnimationIndex]->getNumberOfFrames(); ++i) {
2199 // Read whole line
2200 fgets( buff, sizeof(buff), fp );
2201
2202 // Read bounding box
2203 sscanf(buff, " ( %f %f %f ) ( %f %f %f )",
2204 &_animations[targetAnimationIndex]->getBoundingBox(i).min[0], &_animations[targetAnimationIndex]->getBoundingBox(i).min[1], &_animations[targetAnimationIndex]->getBoundingBox(i).min[2],
2205 &_animations[targetAnimationIndex]->getBoundingBox(i).max[0], &_animations[targetAnimationIndex]->getBoundingBox(i).max[1], &_animations[targetAnimationIndex]->getBoundingBox(i).max[2]);
2206 }
2207 }
2208 } else if( strncmp(buff, "baseframe {", 10) == 0 ) {
2209 if (baseFrame == nullptr) {
2210 fprintf( stderr, "[.md5anim]: Error: md5anim file malformed. numJoints not specified prior to baseframe\n" );
2211 } else {
2212 for(i = 0; i < numJoints; ++i) {
2213 // Read whole line
2214 fgets( buff, sizeof(buff), fp );
2215
2216 // Read base frame joint
2217 if( sscanf(buff, " ( %f %f %f ) ( %f %f %f )",
2218 &baseFrame[i].position[0], &baseFrame[i].position[1], &baseFrame[i].position[2],
2219 &baseFrame[i].orientation[0], &baseFrame[i].orientation[1], &baseFrame[i].orientation[2]) == 6 ) {
2220 // Compute the w component
2221 baseFrame[i].orientation.w = glm::extractRealComponent(baseFrame[i].orientation);
2222 }
2223 }
2224 }
2225 } else if(sscanf(buff, " frame %d", &frameIndex) == 1 ) {
2226 if (animFrameData == nullptr) {
2227 fprintf( stderr, "[.md5anim]: Error: md5anim file malformed. numAnimatedComponents not specified prior to frame\n" );
2228 } else if (_animations[targetAnimationIndex]->getNumberOfFrames() == 0) {
2229 fprintf( stderr, "[.md5anim]: Error: md5anim file malformed. numFrames not specified prior to frame\n" );
2230 } else if (baseFrame == nullptr) {
2231 fprintf( stderr, "[.md5anim]: Error: md5anim file malformed. baseframe not specified prior to frame\n" );
2232 } else if (jointInfos == nullptr) {
2233 fprintf( stderr, "[.md5anim]: Error: md5anim file malformed. numJoints not specified prior to frame\n" );
2234 } else {
2235 // Read frame data
2236 for(i = 0; i < numAnimatedComponents; ++i)
2237 fscanf( fp, "%f", &animFrameData[i] );
2238
2239 // Build frame _skeleton from the collected data
2240 _buildFrameSkeleton(jointInfos, baseFrame, animFrameData,
2241 _animations[targetAnimationIndex]->getSkeletonFrame(frameIndex),
2242 numJoints);
2243 }
2244 }
2245 }
2246
2247 fclose( fp );
2248
2249 printf( "[.md5anim]: finished reading %s into animation %u\n", filename, targetAnimationIndex );
2250 printf( "[.md5anim]: read in %d frames of %d joints with %d animated components\n", _animations[targetAnimationIndex]->getNumberOfFrames(), _animations[targetAnimationIndex]->getNumberOfJoints(), numAnimatedComponents );
2251 printf( "[.md5anim]: animation's frame rate is %d\n", _animations[targetAnimationIndex]->frameRate );
2252
2253 // Free temporary data allocated
2254 delete[] animFrameData;
2255 delete[] baseFrame;
2256 delete[] jointInfos;
2257
2258 // successful loading...set up animation parameters
2259 _animationInfos[targetAnimationIndex].currFrame = 0;
2260 _animationInfos[targetAnimationIndex].nextFrame = 1;
2261
2262 _animationInfos[targetAnimationIndex].lastTime = 0.0f;
2263 _animationInfos[targetAnimationIndex].maxTime = 1.0f / static_cast<GLfloat>(_animations[targetAnimationIndex]->frameRate);
2264
2265 // Allocate memory for animated _skeleton
2266 if (_animations[targetAnimationIndex]->getNumberOfJoints() == 0) {
2267 fprintf( stderr, "[.md5anim]: Error: md5anim file malformed. numJoints never specified\n" );
2268 } else {
2269 _skeleton = new MD5Joint[_animations[targetAnimationIndex]->getNumberOfJoints()];
2270 }
2271
2272 if( _checkAnimValidity(targetAnimationIndex) ) {
2273 _isAnimated = true;
2274 // compute initial pose
2275 animate(0.0);
2276 }
2277
2278 return true;
2279}
2280
2281inline bool
2283 const char* filename
2284) {
2285 // extend animation array
2286 auto newAnimations = new MD5Animation*[_numAnimations + 1];
2287 auto newAnimationInfos = new MD5AnimationState[_numAnimations + 1];
2288
2289 for( int i = 0; i < _numAnimations; ++i ) {
2290 newAnimations[i] = _animations[i];
2291 newAnimationInfos[i] = _animationInfos[i];
2292 }
2293 newAnimations[_numAnimations] = new MD5Animation();
2294
2295 delete[] _animations;
2296 delete[] _animationInfos;
2297
2298 _animations = newAnimations;
2299 _animationInfos = newAnimationInfos;
2300
2301 _numAnimations++;
2302
2303 // read animation into new array position
2304 fprintf( stdout, "[.md5anim]: preparing to read %s into new animation %u\n", filename, (_numAnimations-1) );
2305 const bool readSuccess = readMD5Anim(filename, _numAnimations - 1);
2306
2307 if (readSuccess) {
2308 fprintf( stdout, "[.md5anim]: successfully read %s into new animation %u\n", filename, (_numAnimations-1) );
2309 } else {
2310 fprintf( stderr, "[.md5anim]: Error: could not read %s into new animation %u\n", filename, (_numAnimations-1) );
2311
2312 // undo array growth
2313 newAnimations = new MD5Animation*[_numAnimations-1];
2314 newAnimationInfos = new MD5AnimationState[_numAnimations-1];
2315
2316 for (int i = 0; i < _numAnimations - 1; ++i) {
2317 newAnimations[i] = _animations[i];
2318 newAnimationInfos[i] = _animationInfos[i];
2319 }
2320 delete _animations[_numAnimations-1];
2321
2322 delete[] _animations;
2323 delete[] _animationInfos;
2324
2325 _animations = newAnimations;
2326 _animationInfos = newAnimationInfos;
2327
2328 _numAnimations--;
2329 }
2330
2331 return readSuccess;
2332}
2333
2334inline void
2336 const GLushort targetAnimationIndex
2337) {
2338 // if target index is within range
2339 if (targetAnimationIndex < _numAnimations) {
2340 // update animation to run through
2341 _currentAnimationIndex = targetAnimationIndex;
2342
2343 // set to base animation
2344 _animationInfos[targetAnimationIndex].currFrame = 0;
2345 _animationInfos[targetAnimationIndex].nextFrame = 1;
2346
2347 _animationInfos[targetAnimationIndex].lastTime = 0.0f;
2348 _animationInfos[targetAnimationIndex].maxTime = 1.0f / static_cast<GLfloat>(_animations[targetAnimationIndex]->frameRate);
2349
2350 animate(0.f);
2351 }
2352}
2353
2354inline void
2355CSCI441::MD5Model::_freeAnim()
2356{
2357 delete[] _skeleton;
2358 _skeleton = nullptr;
2359
2360 for (int i = 0; i < _numAnimations; i++) {
2361 delete _animations[i];
2362 }
2363 delete[] _animations;
2364 _animations = nullptr;
2365
2366 delete[] _animationInfos;
2367 _animationInfos = nullptr;
2368}
2369
2370inline void CSCI441::MD5Model::_moveFromSrc(MD5Model &src) {
2371 this->_baseSkeleton = src._baseSkeleton;
2372 src._baseSkeleton = nullptr;
2373
2374 this->_meshes = src._meshes;
2375 src._meshes = nullptr;
2376
2377 this->_maxVertices = src._maxVertices;
2378 src._maxVertices = 0;
2379
2380 this->_maxTriangles = src._maxTriangles;
2381 src._maxTriangles = 0;
2382
2383 this->_vertexArray = src._vertexArray;
2384 src._vertexArray = nullptr;
2385
2386 this->_texelArray = src._texelArray;
2387 src._texelArray = nullptr;
2388
2389 this->_vertexIndicesArray = src._vertexIndicesArray;
2390 src._vertexIndicesArray = nullptr;
2391
2392 this->_vao = src._vao;
2393 src._vao = 0;
2394
2395 this->_vbo[0] = src._vbo[0];
2396 this->_vbo[1] = src._vbo[1];
2397 src._vbo[0] = 0;
2398 src._vbo[1] = 0;
2399
2400 this->_skeletonVAO = src._skeletonVAO;
2401 src._skeletonVAO = 0;
2402
2403 this->_skeletonVBO = src._skeletonVBO;
2404 src._skeletonVBO = 0;
2405
2406 this->_skeleton = src._skeleton;
2407 src._skeleton = nullptr;
2408
2409 this->_animations = src._animations;
2410 src._animations = nullptr;
2411
2412 this->_numAnimations = src._numAnimations;
2413 src._numAnimations = 0;
2414
2415 this->_currentAnimationIndex = src._currentAnimationIndex;
2416 src._currentAnimationIndex = 0;
2417
2418 this->_isAnimated = src._isAnimated;
2419 src._isAnimated = false;
2420
2421 this->_animationInfos = src._animationInfos;
2422 src._animationInfos = nullptr;
2423}
2424
2425inline void
2426CSCI441::MD5Model::_interpolateSkeletons(const GLfloat interp)
2427{
2428 const MD5Joint *skeletonA = _animations[_currentAnimationIndex]->getSkeletonFrame(_animationInfos[_currentAnimationIndex].currFrame);
2429 const MD5Joint *skeletonB = _animations[_currentAnimationIndex]->getSkeletonFrame(_animationInfos[_currentAnimationIndex].nextFrame);
2430
2431 for(GLint i = 0; i < _animations[_currentAnimationIndex]->getNumberOfJoints(); ++i) {
2432 // Copy parent index
2433 _skeleton[i].parent = skeletonA[i].parent;
2434
2435 // Linear interpolation for position
2436 _skeleton[i].position = glm::mix(skeletonA[i].position, skeletonB[i].position, interp);
2437
2438 // Spherical linear interpolation for orientation
2439 _skeleton[i].orientation = glm::slerp(skeletonA[i].orientation, skeletonB[i].orientation, interp);
2440 }
2441}
2442
2443// Perform animation related computations. Calculate the current and
2444// next frames, given a delta time.
2445inline void
2447{
2448 const GLint maxFrames = _animations[_currentAnimationIndex]->getNumberOfFrames() - 1;
2449 if (maxFrames <= 0) return;
2450
2451 _animationInfos[_currentAnimationIndex].lastTime += dt;
2452
2453 // move to next frame
2454 if( _animationInfos[_currentAnimationIndex].lastTime >= _animationInfos[_currentAnimationIndex].maxTime ) {
2455 _animationInfos[_currentAnimationIndex].currFrame++;
2456 _animationInfos[_currentAnimationIndex].nextFrame++;
2457 _animationInfos[_currentAnimationIndex].lastTime = 0.0;
2458
2459 if( _animationInfos[_currentAnimationIndex].currFrame > maxFrames )
2460 _animationInfos[_currentAnimationIndex].currFrame = 0;
2461
2462 if( _animationInfos[_currentAnimationIndex].nextFrame > maxFrames )
2463 _animationInfos[_currentAnimationIndex].nextFrame = 0;
2464 }
2465
2466 // Interpolate skeletons between two frames
2467 _interpolateSkeletons( _animationInfos[_currentAnimationIndex].lastTime * static_cast<GLfloat>(_animations[_currentAnimationIndex]->frameRate) );
2468}
2469
2470#endif//CSCI441_MD5_MODEL_HPP
Helper functions to work with OpenGL Textures.
stores an entire animation sequence for a given MD5 Model
Definition: MD5Model.hpp:880
MD5Animation & operator=(MD5Animation &&src) noexcept
reassign object by moving an existing object
Definition: MD5Model.hpp:1055
MD5Animation()=default
construct a default animation object
MD5Animation & operator=(const MD5Animation &OTHER)=delete
do not allow animation objects to be copied
MD5Animation(MD5Animation &&src) noexcept
construct an animation object by moving an existing object
Definition: MD5Model.hpp:1047
MD5Animation(const MD5Animation &OTHER)=delete
do not allow animation objects to be copied
GLint getNumberOfFrames() const
getter to retrieve number of frames within the animation
Definition: MD5Model.hpp:892
void setNumberOfFrames(const GLint numFrames)
sets the number of frames and allocates skeletonFrames and boundingBoxes to the associated size
Definition: MD5Model.hpp:899
const MD5Joint * getSkeletonFrame(const GLint frameIndex) const
get the skeleton for a specific animation frame
Definition: MD5Model.hpp:960
GLint frameRate
number of frames per second to draw for the animation
Definition: MD5Model.hpp:886
~MD5Animation()
deallocate animation arrays
Definition: MD5Model.hpp:1018
MD5Joint & getSkeletonFrameJoint(const GLint frameIndex, const GLint jointIndex) const
get the specific joint from a skeleton for a specific animation frame
Definition: MD5Model.hpp:978
MD5BoundingBox & getBoundingBox(const GLint frameIndex) const
get the specific bounding box for a target frame
Definition: MD5Model.hpp:1001
void setNumberOfJoints(const GLint numJoints)
set the number of joints in the skeleton of each animation frame and allocates each frame of skeleton...
Definition: MD5Model.hpp:933
GLint getNumberOfJoints() const
get the number of joints in each skeleton frame
Definition: MD5Model.hpp:925
stores a Doom3 MD5 Mesh + Animation
Definition: MD5Model.hpp:77
bool addMD5Anim(const char *filename)
adds another MD5 Animation sequence to the model's set of animation
Definition: MD5Model.hpp:2282
void draw() const
draws all the meshes that make up the model
Definition: MD5Model.hpp:1768
GLushort getNumberOfAnimations() const
returns the number of animations that were successfully loaded against the model
Definition: MD5Model.hpp:1305
bool readMD5Model(const char *FILENAME)
parses md5mesh file and allocates corresponding mesh data
Definition: MD5Model.hpp:1530
bool readMD5Anim(const char *filename, GLushort targetAnimationIndex=0)
reads in an animation sequence from an external file
Definition: MD5Model.hpp:2118
bool isAnimated() const
returns if the MD5 Model has an accompanying animation
Definition: MD5Model.hpp:1248
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:1506
void setActiveTextures(GLint diffuseMapActiveTexture, GLint specularMapActiveTexture, GLint normalMapActiveTexture, GLint heightMapActiveTexture)
specify which texture targets each texture map should be bound to when rendering
Definition: MD5Model.hpp:1906
MD5Model()
initializes an empty MD5 Model
Definition: MD5Model.hpp:1197
MD5Model & operator=(const MD5Model &)=delete
do not allow MD5 models to be copied
void allocVertexArrays(GLuint vPosAttribLoc, GLuint vColorAttribLoc, GLuint vTexCoordAttribLoc)
binds model VBOs to attribute pointer locations
Definition: MD5Model.hpp:1862
void animate(GLfloat dt)
advances the model forward in its animation sequence the corresponding amount of time based on frame ...
Definition: MD5Model.hpp:2446
MD5Model & operator=(MD5Model &&src) noexcept
do not allow MD5 models to be moved
Definition: MD5Model.hpp:1229
void useTargetAnimationIndex(GLushort targetAnimationIndex)
update current animation to be running through
Definition: MD5Model.hpp:2335
MD5Model(const MD5Model &)=delete
do not allow MD5 models to be copied
MD5Model(MD5Model &&src) noexcept
do not allow MD5 models to be moved
Definition: MD5Model.hpp:1223
~MD5Model()
deallocates any used memory on the CPU and GPU
Definition: MD5Model.hpp:1496
void drawSkeleton() const
draws the skeleton joints (as points) and bones (as lines)
Definition: MD5Model.hpp:1946
CSCI441 Helper Functions for OpenGL.
Definition: ArcballCam.hpp:17
stores state of current animation frame
Definition: MD5Model.hpp:1108
GLint currFrame
index of current frame model is in
Definition: MD5Model.hpp:1112
MD5AnimationState(MD5AnimationState &&src) noexcept
construct an object by moving an existing object
Definition: MD5Model.hpp:1153
MD5AnimationState(const MD5AnimationState &OTHER)
construct an object by copying an existing object
Definition: MD5Model.hpp:1135
MD5AnimationState()=default
construct a default object
GLint nextFrame
index of next frame model will move to
Definition: MD5Model.hpp:1116
MD5AnimationState & operator=(MD5AnimationState &&src) noexcept
reassign object by moving an existing object
Definition: MD5Model.hpp:1161
MD5AnimationState & operator=(const MD5AnimationState &OTHER)
reassign object by copying an existing object
Definition: MD5Model.hpp:1143
GLfloat lastTime
time of last frame interpolation
Definition: MD5Model.hpp:1120
GLfloat maxTime
duration of a single frame
Definition: MD5Model.hpp:1125
base frame joint
Definition: MD5Model.hpp:731
glm::quat orientation
joint orientation expressed as a quaternion in object space
Definition: MD5Model.hpp:739
MD5BaseFrameJoint & operator=(const MD5BaseFrameJoint &OTHER)
reassign object by copying an existing object
Definition: MD5Model.hpp:757
MD5BaseFrameJoint()=default
construct a default object
MD5BaseFrameJoint(MD5BaseFrameJoint &&src) noexcept
construct object by moving an existing object
Definition: MD5Model.hpp:767
glm::vec3 position
position of the joint in object space
Definition: MD5Model.hpp:735
MD5BaseFrameJoint(const MD5BaseFrameJoint &OTHER)
construct an object by copying an existing object
Definition: MD5Model.hpp:749
MD5BaseFrameJoint & operator=(MD5BaseFrameJoint &&src) noexcept
reassign object by moving an existing object
Definition: MD5Model.hpp:775
bounding box containing the model during animation
Definition: MD5Model.hpp:806
MD5BoundingBox(const MD5BoundingBox &OTHER)
construct an object by copying an existing object
Definition: MD5Model.hpp:824
MD5BoundingBox & operator=(MD5BoundingBox &&src) noexcept
reassign object by moving an existing object
Definition: MD5Model.hpp:850
glm::vec3 min
minimum dimension bound
Definition: MD5Model.hpp:810
MD5BoundingBox(MD5BoundingBox &&src) noexcept
construct an object by moving an existing object
Definition: MD5Model.hpp:842
glm::vec3 max
maximum dimension bound
Definition: MD5Model.hpp:814
MD5BoundingBox & operator=(const MD5BoundingBox &OTHER)
reassign object by copying an existing object
Definition: MD5Model.hpp:832
MD5BoundingBox()=default
construct a default object
a joint of the MD5 Skeleton
Definition: MD5Model.hpp:83
MD5Joint(const MD5Joint &OTHER)
construct a joint object by copying an existing joint
Definition: MD5Model.hpp:117
char name[MAX_NAME_LENGTH]
joint identifier
Definition: MD5Model.hpp:95
MD5Joint(MD5Joint &&src) noexcept
construct a joint object by moving an existing object
Definition: MD5Model.hpp:135
glm::vec3 position
position of the joint in object space
Definition: MD5Model.hpp:103
MD5Joint & operator=(const MD5Joint &OTHER)
reassign an existing joint object by copying another joint object
Definition: MD5Model.hpp:125
glm::quat orientation
joint orientation expressed as a quaternion in object space
Definition: MD5Model.hpp:107
MD5Joint()=default
construct a default joint object
MD5Joint & operator=(MD5Joint &&src) noexcept
reassign an existing joint object by moving another joint object
Definition: MD5Model.hpp:143
GLint parent
index of the parent joint on skeletal tree
Definition: MD5Model.hpp:99
information pertaining to each animation joint
Definition: MD5Model.hpp:641
MD5JointInfo & operator=(MD5JointInfo &&src) noexcept
reassign object by moving an existing object
Definition: MD5Model.hpp:697
GLint startIndex
index of starting parameter
Definition: MD5Model.hpp:661
MD5JointInfo(const MD5JointInfo &OTHER)
construct a joint info object by copying an existing object
Definition: MD5Model.hpp:671
MD5JointInfo(MD5JointInfo &&src) noexcept
construct joint info object by moving an existing object
Definition: MD5Model.hpp:689
GLint parent
index of parent joint on skeletal tree
Definition: MD5Model.hpp:653
char name[MAX_NAME_LENGTH]
joint identifier
Definition: MD5Model.hpp:649
MD5JointInfo & operator=(const MD5JointInfo &OTHER)
reassign object by copying an existing object
Definition: MD5Model.hpp:679
MD5JointInfo()=default
construct a default joint info object
GLuint flags
bit flags denoted how to compute the skeleton of a frame for this joint
Definition: MD5Model.hpp:657
mesh that comprises the model's skin
Definition: MD5Model.hpp:493
GLint numTriangles
number of triangles in the mesh triangle array
Definition: MD5Model.hpp:546
MD5Texture textures[NUM_TEXTURES]
texture map array
Definition: MD5Model.hpp:517
TextureMap
named entities for different texture maps applied to the model
Definition: MD5Model.hpp:521
@ DIFFUSE
diffuse map
Definition: MD5Model.hpp:525
@ SPECULAR
specular map
Definition: MD5Model.hpp:529
@ NORMAL
normal map
Definition: MD5Model.hpp:533
MD5Mesh(MD5Mesh &&src) noexcept
construct a new mesh by moving an existing object
Definition: MD5Model.hpp:590
MD5Mesh()=default
construct a default mesh object
GLint numVertices
number of vertices in the mesh vertex array
Definition: MD5Model.hpp:542
MD5Mesh & operator=(MD5Mesh &&src) noexcept
reassign mesh object by moving an existing object
Definition: MD5Model.hpp:598
MD5Triangle * triangles
array triangles comprising the mesh
Definition: MD5Model.hpp:509
char shader[MAX_NAME_LENGTH]
base filename for all textures applied to mesh
Definition: MD5Model.hpp:555
MD5Vertex * vertices
array of vertices comprising the mesh
Definition: MD5Model.hpp:505
GLint numWeights
number of weights in the mesh weight array
Definition: MD5Model.hpp:550
~MD5Mesh()
deallocate member arrays
Definition: MD5Model.hpp:564
MD5Mesh & operator=(const MD5Mesh &OTHER)=delete
do not allow meshes to be copied
MD5Weight * weights
array of weights to determine vertex position based on joint positions
Definition: MD5Model.hpp:513
MD5Mesh(const MD5Mesh &OTHER)=delete
do not allow meshes to be copied
texture handle for the model
Definition: MD5Model.hpp:415
char filename[MAX_NAME_LENGTH]
filename texture was loaded from
Definition: MD5Model.hpp:427
MD5Texture & operator=(const MD5Texture &OTHER)
reassign texture object by copying an existing object
Definition: MD5Model.hpp:445
MD5Texture & operator=(MD5Texture &&src) noexcept
reassign texture object by moving an existing object
Definition: MD5Model.hpp:463
MD5Texture(const MD5Texture &OTHER)
construct a texture object by copying an existing object
Definition: MD5Model.hpp:437
GLuint texHandle
handle of texture stored on the GPU
Definition: MD5Model.hpp:423
MD5Texture()=default
constructs a default texture object
MD5Texture(MD5Texture &&src) noexcept
construct a texture object by moving an existing object
Definition: MD5Model.hpp:455
a triangle on the mesh
Definition: MD5Model.hpp:259
MD5Triangle & operator=(const MD5Triangle &OTHER)
reassign triangle object by copying an existing triangle
Definition: MD5Model.hpp:285
MD5Triangle(MD5Triangle &&src) noexcept
construct a triangle object by moving an existing triangle
Definition: MD5Model.hpp:295
MD5Triangle & operator=(MD5Triangle &&src) noexcept
reassign triangle object by moving an existing triangle
Definition: MD5Model.hpp:303
MD5Triangle(const MD5Triangle &OTHER)
construct a triangle by copying an existing triangle
Definition: MD5Model.hpp:277
MD5Triangle()=default
construct a default triangle object
GLint index[NUM_VERTICES]
vertex indices that make up triangle
Definition: MD5Model.hpp:267
a vertex on the mesh
Definition: MD5Model.hpp:179
MD5Vertex & operator=(const MD5Vertex &OTHER)
reassign existing vertex object by copying an existing vertex object
Definition: MD5Model.hpp:209
MD5Vertex & operator=(MD5Vertex &&src) noexcept
reassign existing vertex object by moving an existing vertex object
Definition: MD5Model.hpp:227
MD5Vertex(MD5Vertex &&src) noexcept
construct a vertex object by moving an existing vertex
Definition: MD5Model.hpp:219
glm::vec2 texCoord
texture coordinate for vertex
Definition: MD5Model.hpp:183
MD5Vertex(const MD5Vertex &OTHER)
construct a vertex object by copying an existing vertex
Definition: MD5Model.hpp:201
GLint start
index of starting weight
Definition: MD5Model.hpp:187
GLint count
number of weights that determine vertex's position
Definition: MD5Model.hpp:191
MD5Vertex()=default
construct a default vertex object
the weight for a mesh vertex
Definition: MD5Model.hpp:335
MD5Weight(MD5Weight &&src) noexcept
construct a weight object by moving an existing object
Definition: MD5Model.hpp:375
MD5Weight & operator=(MD5Weight &&src) noexcept
reassign an existing weight object by moving another
Definition: MD5Model.hpp:383
MD5Weight & operator=(const MD5Weight &OTHER)
reassign an existing weight object by copying another
Definition: MD5Model.hpp:365
MD5Weight(const MD5Weight &OTHER)
construct a weight object by copying an existing weight
Definition: MD5Model.hpp:357
GLfloat bias
contribution of the weight
Definition: MD5Model.hpp:343
GLint joint
index of joint the weight depends on
Definition: MD5Model.hpp:339
MD5Weight()=default
construct a default weight object
glm::vec3 position
weight's position in object space
Definition: MD5Model.hpp:347