CSCI441 OpenGL Library 5.17.0
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
70namespace CSCI441 {
71
76 class [[maybe_unused]] MD5Model {
77 protected:
78 // md5mesh types
82 struct MD5Joint {
86 static constexpr GLint NULL_JOINT = -1;
90 static constexpr GLshort MAX_NAME_LENGTH = 256;
94 char name[MAX_NAME_LENGTH] = "";
98 GLint parent = NULL_JOINT;
102 glm::vec3 position = {0.0f, 0.0f, 0.0f};
106 glm::quat orientation = {0.0f, 0.0f, 0.0f, 0.0f};
107
111 MD5Joint() = default;
116 MD5Joint(const MD5Joint &OTHER) {
117 _copyFromSrc(OTHER);
118 }
124 MD5Joint& operator=(const MD5Joint &OTHER) {
125 if (this != &OTHER) {
126 _copyFromSrc(OTHER);
127 }
128 return *this;
129 }
134 MD5Joint(MD5Joint&& src) noexcept {
135 _moveFromSrc(src);
136 }
142 MD5Joint& operator=(MD5Joint&& src) noexcept {
143 if (this != &src) {
144 _moveFromSrc(src);
145 }
146 return *this;
147 }
148 private:
153 void _copyFromSrc(const MD5Joint& src) {
154 strncpy(this->name, src.name, MAX_NAME_LENGTH);
155 this->parent = src.parent;
156 this->position = src.position;
157 this->orientation = src.orientation;
158 }
164 void _moveFromSrc(MD5Joint& src) {
165 // copy values from source
166 _copyFromSrc(src);
167 // reset source
168 strncpy(src.name, "", MAX_NAME_LENGTH);
169 src.parent = NULL_JOINT;
170 src.position = glm::vec3(0.0f, 0.0f, 0.0f);
171 src.orientation = glm::quat(0.0f, 0.0f, 0.0f, 0.0f);
172 }
173 };
174
178 struct MD5Vertex {
182 glm::vec2 texCoord = {0.0f, 0.0f};
186 GLint start = 0;
190 GLint count = 0;
191
195 MD5Vertex() = default;
200 MD5Vertex(const MD5Vertex &OTHER) {
201 _copyFromSrc(OTHER);
202 }
209 if (this != &OTHER) {
210 _copyFromSrc(OTHER);
211 }
212 return *this;
213 }
218 MD5Vertex(MD5Vertex&& src) noexcept {
219 _moveFromSrc(src);
220 }
226 MD5Vertex& operator=(MD5Vertex&& src) noexcept {
227 if (this != &src) {
228 _moveFromSrc(src);
229 }
230 return *this;
231 }
232 private:
237 void _copyFromSrc(const MD5Vertex &src) {
238 this->texCoord = src.texCoord;
239 this->start = src.start;
240 this->count = src.count;
241 }
246 void _moveFromSrc(MD5Vertex& src) {
247 _copyFromSrc(src);
248
249 src.texCoord = glm::vec2(0.0f, 0.0f);
250 src.start = 0;
251 src.count = 0;
252 }
253 };
254
258 struct MD5Triangle {
262 static constexpr GLshort NUM_VERTICES = 3;
266 GLint index[NUM_VERTICES] = {0};
267
271 MD5Triangle() = default;
276 MD5Triangle(const MD5Triangle &OTHER) {
277 _copyFromSrc(OTHER);
278 }
285 if (this != &OTHER) {
286 _copyFromSrc(OTHER);
287 }
288 return *this;
289 }
294 MD5Triangle(MD5Triangle&& src) noexcept {
295 _moveFromSrc(src);
296 }
303 if (this != &src) {
304 _moveFromSrc(src);
305 }
306 return *this;
307 }
308 private:
313 void _copyFromSrc(const MD5Triangle &src) {
314 for (GLshort i = 0; i < NUM_VERTICES; i++) {
315 this->index[i] = src.index[i];
316 }
317 }
322 void _moveFromSrc(MD5Triangle &src) {
323 _copyFromSrc(src);
324
325 for (GLint & i : src.index) {
326 i = 0;
327 }
328 }
329 };
330
334 struct MD5Weight {
338 GLint joint = MD5Joint::NULL_JOINT;
342 GLfloat bias = 0.f;
346 glm::vec3 position = {0.0f, 0.0f, 0.0f};
347
351 MD5Weight() = default;
356 MD5Weight(const MD5Weight &OTHER) {
357 _copyFromSrc(OTHER);
358 }
365 if (this != &OTHER) {
366 _copyFromSrc(OTHER);
367 }
368 return *this;
369 }
374 MD5Weight(MD5Weight&& src) noexcept {
375 _moveFromSrc(src);
376 }
382 MD5Weight& operator=(MD5Weight&& src) noexcept {
383 if (this != &src) {
384 _moveFromSrc(src);
385 }
386 return *this;
387 }
388 private:
393 void _copyFromSrc(const MD5Weight &src) {
394 this->joint = src.joint;
395 this->bias = src.bias;
396 this->position = src.position;
397 }
402 void _moveFromSrc(MD5Weight &src) {
403 _copyFromSrc(src);
404
405 src.joint = MD5Joint::NULL_JOINT;
406 src.bias = 0.0f;
407 src.position = glm::vec3(0.0f, 0.0f, 0.0f);
408 }
409 };
410
414 struct MD5Texture {
418 static constexpr GLshort MAX_NAME_LENGTH = 512;
422 GLuint texHandle = 0;
426 char filename[MAX_NAME_LENGTH] = "";
427
431 MD5Texture() = default;
436 MD5Texture(const MD5Texture &OTHER) {
437 _copyFromSrc(OTHER);
438 }
445 if (this != &OTHER) {
446 _copyFromSrc(OTHER);
447 }
448 return *this;
449 }
454 MD5Texture(MD5Texture&& src) noexcept {
455 _moveFromSrc(src);
456 }
462 MD5Texture& operator=(MD5Texture&& src) noexcept {
463 if (this != &src) {
464 _moveFromSrc(src);
465 }
466 return *this;
467 }
468 private:
473 void _copyFromSrc(const MD5Texture &src) {
474 this->texHandle = src.texHandle;
475 strncpy(this->filename, src.filename, MAX_NAME_LENGTH);
476 }
481 void _moveFromSrc(MD5Texture& src) {
482 _copyFromSrc(src);
483
484 src.texHandle = 0;
485 strncpy(src.filename, "", MAX_NAME_LENGTH);
486 }
487 };
488
492 struct MD5Mesh {
496 static constexpr GLshort MAX_NAME_LENGTH = 512;
500 static constexpr GLshort NUM_TEXTURES = 4;
504 MD5Vertex* vertices = nullptr;
508 MD5Triangle* triangles = nullptr;
512 MD5Weight* weights = nullptr;
516 MD5Texture textures[NUM_TEXTURES];
536 HEIGHT
537 };
541 GLint numVertices = 0;
545 GLint numTriangles = 0;
549 GLint numWeights = 0;
550
554 char shader[MAX_NAME_LENGTH] = "";
555
559 MD5Mesh() = default;
564 delete[] vertices;
565 vertices = nullptr;
566
567 delete[] triangles;
568 triangles = nullptr;
569
570 delete[] weights;
571 weights = nullptr;
572 }
573
578 MD5Mesh(const MD5Mesh &OTHER) = delete;
583 MD5Mesh& operator=(const MD5Mesh &OTHER) = delete;
584
589 MD5Mesh(MD5Mesh&& src) noexcept {
590 _moveFromSrc(src);
591 }
597 MD5Mesh& operator=(MD5Mesh&& src) noexcept {
598 if (this != &src) {
599 _moveFromSrc(src);
600 }
601 return *this;
602 }
603 private:
608 void _moveFromSrc(MD5Mesh& src) {
609 this->vertices = src.vertices;
610 src.vertices = nullptr;
611
612 this->triangles = src.triangles;
613 src.triangles = nullptr;
614
615 this->weights = src.weights;
616 src.weights = nullptr;
617
618 for (GLshort i = 0; i < NUM_TEXTURES; i++) {
619 this->textures[i] = std::move( src.textures[i] );
620 }
621
622 this->numVertices = src.numVertices;
623 src.numVertices = 0;
624
625 this->numTriangles = src.numTriangles;
626 src.numTriangles = 0;
627
628 this->numWeights = src.numWeights;
629 src.numWeights = 0;
630
631 strncpy(this->shader, src.shader, MAX_NAME_LENGTH);
632 strncpy(src.shader, "", MAX_NAME_LENGTH);
633 }
634 };
635
636 // md5anim types
644 static constexpr GLshort MAX_NAME_LENGTH = 256;
648 char name[MAX_NAME_LENGTH] = "";
652 GLint parent = MD5Joint::NULL_JOINT;
656 GLuint flags = 0;
660 GLint startIndex = 0;
661
665 MD5JointInfo() = default;
671 _copyFromSrc(OTHER);
672 }
679 if (this != &OTHER) {
680 _copyFromSrc(OTHER);
681 }
682 return *this;
683 }
688 MD5JointInfo(MD5JointInfo&& src) noexcept {
689 _moveFromSrc(src);
690 }
697 if (this != &src) {
698 _moveFromSrc(src);
699 }
700 return *this;
701 }
702 private:
707 void _copyFromSrc(const MD5JointInfo &src) {
708 strncpy(this->name, src.name, MAX_NAME_LENGTH);
709 this->parent = src.parent;
710 this->flags = src.flags;
711 this->startIndex = src.startIndex;
712 }
717 void _moveFromSrc(MD5JointInfo &src) {
718 _copyFromSrc(src);
719
720 strncpy(src.name, "", MAX_NAME_LENGTH);
721 src.parent = MD5Joint::NULL_JOINT;
722 src.flags = 0;
723 src.startIndex = 0;
724 }
725 };
726
734 glm::vec3 position = {0.0f, 0.0f, 0.0f};
738 glm::quat orientation = {0.0f, 0.0f, 0.0f, 0.0f};
739
743 MD5BaseFrameJoint() = default;
749 _copyFromSrc(OTHER);
750 }
757 if (this != &OTHER) {
758 _copyFromSrc(OTHER);
759 }
760 return *this;
761 }
767 _moveFromSrc(src);
768 }
775 if (this != &src) {
776 _moveFromSrc(src);
777 }
778 return *this;
779 }
780 private:
785 void _copyFromSrc(const MD5BaseFrameJoint &src) {
786 this->position = src.position;
787 this->orientation = src.orientation;
788 }
793 void _moveFromSrc(MD5BaseFrameJoint &src) {
794 _copyFromSrc(src);
795
796 src.position = glm::vec3(0.0f, 0.0f, 0.0f);
797 src.orientation = glm::quat(0.0f, 0.0f, 0.0f, 0.0f);
798 }
799 };
800
809 glm::vec3 min = {0.0f, 0.0f, 0.0f};
813 glm::vec3 max = {0.0f, 0.0f, 0.0f};
814
818 MD5BoundingBox() = default;
824 _copyFromSrc(OTHER);
825 }
832 if (this != &OTHER) {
833 _copyFromSrc(OTHER);
834 }
835 return *this;
836 }
842 _moveFromSrc(src);
843 }
850 if (this != &src) {
851 _moveFromSrc(src);
852 }
853 return *this;
854 }
855 private:
860 void _copyFromSrc(const MD5BoundingBox& src) {
861 this->min = src.min;
862 this->max = src.max;
863 }
868 void _moveFromSrc(MD5BoundingBox& src) {
869 _copyFromSrc(src);
870
871 src.min = glm::vec3(0.0f, 0.0f, 0.0f);
872 src.max = glm::vec3(0.0f, 0.0f, 0.0f);
873 }
874 };
875
880 public:
885 GLint frameRate = 0;
886
891 [[nodiscard]] GLint getNumberOfFrames() const { return _numFrames; }
898 void setNumberOfFrames(const GLint numFrames) {
899 // if previously set, delete prior allocation to avoid memory leak
900 if(_skeletonFrames != nullptr) {
901 for (GLint i = 0; i < _numFrames; ++i) {
902 delete _skeletonFrames[i];
903 }
904 }
905 delete[] _skeletonFrames;
906 _skeletonFrames = nullptr; // no longer exists, in event new set equals zero
907 delete[] _boundingBoxes;
908 _boundingBoxes = nullptr; // no longer exists, in event new set equals zero
909
910 _numFrames = numFrames;
911 if ( _numFrames > 0 ) {
912 _skeletonFrames = new MD5Joint*[_numFrames];
913 for (GLint i = 0; i < _numFrames; ++i) {
914 _skeletonFrames[i] = nullptr;
915 }
916 _boundingBoxes = new MD5BoundingBox[_numFrames];
917 }
918 }
919
924 [[nodiscard]] GLint getNumberOfJoints() const { return _numJoints; }
932 void setNumberOfJoints(const GLint numJoints) {
933 // if previously set, delete prior allocation to avoid memory leak
934 if(_skeletonFrames != nullptr) {
935 for (GLint i = 0; i < _numFrames; ++i) {
936 delete _skeletonFrames[i];
937 _skeletonFrames[i] = nullptr; // no longer exists, in event new set equals zero
938 }
939 }
940
941 _numJoints = numJoints;
942 if (_numJoints > 0) {
943 if(_skeletonFrames != nullptr) {
944 for(GLint i = 0; i < _numFrames; ++i) {
945 // Allocate memory for joints of each frame
946 _skeletonFrames[i] = new MD5Joint[_numJoints];
947 }
948 }
949 }
950 }
951
959 [[nodiscard]] const MD5Joint* getSkeletonFrame(const GLint frameIndex) const {
960 if ( frameIndex < 0 || frameIndex >= _numFrames ) {
961 throw std::out_of_range("frameIndex out of range");
962 }
963 if (_skeletonFrames == nullptr) {
964 throw std::out_of_range("skeleton frames are null, setNumberOfFrames() may not have been called");
965 }
966 return _skeletonFrames[frameIndex];
967 }
977 [[nodiscard]] MD5Joint& getSkeletonFrameJoint(const GLint frameIndex, const GLint jointIndex) const {
978 if ( frameIndex < 0 || frameIndex >= _numFrames ) {
979 throw std::out_of_range("frameIndex out of range");
980 }
981 if ( jointIndex < 0 || jointIndex >= _numJoints ) {
982 throw std::out_of_range("jointIndex out of range");
983 }
984 if (_skeletonFrames == nullptr) {
985 throw std::out_of_range("skeleton frames are null, setNumberOfFrames() may not have been called");
986 }
987 if (_skeletonFrames[frameIndex] == nullptr) {
988 throw std::out_of_range("skeleton joints are null, setNumberOfJoints() may not have been called");
989 }
990 return _skeletonFrames[frameIndex][jointIndex];
991 }
992
1000 [[nodiscard]] MD5BoundingBox& getBoundingBox(const GLint frameIndex) const {
1001 if(frameIndex < 0 || frameIndex >= _numFrames) {
1002 throw std::out_of_range("frameIndex out of range");
1003 }
1004 if (_boundingBoxes == nullptr) {
1005 throw std::out_of_range("bounding boxes are null, setNumberOfFrames() may not have been called");
1006 }
1007 return _boundingBoxes[frameIndex];
1008 }
1009
1013 MD5Animation() = default;
1018 if(_skeletonFrames != nullptr) {
1019 for (GLint i = 0; i < _numFrames; i++) {
1020 delete[] _skeletonFrames[i];
1021 _skeletonFrames[i] = nullptr;
1022 }
1023 }
1024 delete[] _skeletonFrames;
1025 _skeletonFrames = nullptr;
1026
1027 delete[] _boundingBoxes;
1028 _boundingBoxes = nullptr;
1029 }
1030
1035 MD5Animation(const MD5Animation& OTHER) = delete;
1040 MD5Animation& operator=(const MD5Animation& OTHER) = delete;
1041
1046 MD5Animation(MD5Animation&& src) noexcept {
1047 _moveFromSrc(src);
1048 }
1055 if (this != &src) {
1056 _moveFromSrc(src);
1057 }
1058 return *this;
1059 }
1060 private:
1065 GLint _numFrames = 0;
1070 GLint _numJoints = 0;
1075 MD5Joint** _skeletonFrames = nullptr;
1080 MD5BoundingBox* _boundingBoxes = nullptr;
1081
1086 void _moveFromSrc(MD5Animation &src) {
1087 this->_numFrames = src._numFrames;
1088 src._numFrames = 0;
1089
1090 this->_numJoints = src._numJoints;
1091 src._numJoints = 0;
1092
1093 this->frameRate = src.frameRate;
1094 src.frameRate = 0;
1095
1096 this->_skeletonFrames = src._skeletonFrames;
1097 src._skeletonFrames = nullptr;
1098
1099 this->_boundingBoxes = src._boundingBoxes;
1100 src._boundingBoxes = nullptr;
1101 }
1102 };
1103
1111 GLint currFrame = 0;
1115 GLint nextFrame = 0;
1119 GLfloat lastTime = 0.0f;
1124 GLfloat maxTime = 0.0f;
1125
1135 _copyFromSrc(OTHER);
1136 }
1143 if (this != &OTHER) {
1144 _copyFromSrc(OTHER);
1145 }
1146 return *this;
1147 }
1153 _moveFromSrc(src);
1154 }
1161 if (this != &src) {
1162 _moveFromSrc(src);
1163 }
1164 return *this;
1165 }
1166 private:
1171 void _copyFromSrc(const MD5AnimationState &src) {
1172 this->currFrame = src.currFrame;
1173 this->nextFrame = src.nextFrame;
1174 this->lastTime = src.lastTime;
1175 this->maxTime = src.maxTime;
1176 }
1181 void _moveFromSrc(MD5AnimationState &src) {
1182 _copyFromSrc(src);
1183
1184 src.currFrame = 0;
1185 src.nextFrame = 0;
1186 src.lastTime = 0.0f;
1187 src.maxTime = 0.0f;
1188 }
1189 };
1190
1191 public:
1196 MD5Model() : _animation( new MD5Animation() ) {}
1200 ~MD5Model();
1201
1205 MD5Model(const MD5Model&) = delete;
1209 MD5Model& operator=(const MD5Model&) = delete;
1210
1214 MD5Model(MD5Model&& src) noexcept {
1215 _moveFromSrc(src);
1216 }
1220 MD5Model& operator=(MD5Model&& src) noexcept {
1221 if (this != &src) {
1222 _moveFromSrc(src);
1223 }
1224 return *this;
1225 }
1226
1233 [[maybe_unused]] bool loadMD5Model(const char* MD5_MESH_FILE, const char* MD5_ANIM_FILE = "");
1234
1239 [[nodiscard]] bool isAnimated() const { return _isAnimated; }
1240
1241 // md5mesh prototypes
1247 [[nodiscard]] bool readMD5Model(const char* FILENAME);
1256 [[maybe_unused]] void allocVertexArrays(GLuint vPosAttribLoc, GLuint vColorAttribLoc, GLuint vTexCoordAttribLoc);
1257
1265 [[maybe_unused]] void setActiveTextures(GLint diffuseMapActiveTexture, GLint specularMapActiveTexture, GLint normalMapActiveTexture, GLint heightMapActiveTexture);
1266
1270 [[maybe_unused]] void draw() const;
1274 [[maybe_unused]] void drawSkeleton() const;
1275
1276 // md5anim prototypes
1282 [[nodiscard]] bool readMD5Anim(const char* filename);
1287 void animate(GLfloat dt);
1288
1289 private:
1294 MD5Joint* _baseSkeleton = nullptr;
1299 MD5Mesh* _meshes = nullptr;
1304 GLint _numJoints = 0;
1309 GLint _numMeshes = 0;
1310
1311 // vertex array related stuff
1316 GLint _maxVertices = 0;
1321 GLint _maxTriangles = 0;
1326 glm::vec3* _vertexArray = nullptr;
1331 glm::vec2* _texelArray = nullptr;
1336 GLuint* _vertexIndicesArray = nullptr;
1340 GLuint _vao = 0;
1346 GLuint _vbo[2] = {0, 0};
1347
1348 // skeleton related stuff
1352 GLuint _skeletonVAO = 0;
1356 GLuint _skeletonVBO = 0;
1360 MD5Joint* _skeleton = nullptr;
1361
1362 // animation related stuff
1366 MD5Animation* _animation = nullptr;
1370 bool _isAnimated = false;
1374 MD5AnimationState _animationInfo;
1375
1379 GLint _diffuseActiveTexture = GL_TEXTURE0;
1383 GLint _specularActiveTexture = GL_TEXTURE1;
1387 GLint _normalActiveTexture = GL_TEXTURE2;
1391 GLint _heightActiveTexture = GL_TEXTURE3;
1392
1393 // helper functions
1399 void _prepareMesh(const MD5Mesh* pMESH) const;
1404 void _drawMesh(const MD5Mesh* pMESH) const;
1410 [[nodiscard]] bool _checkAnimValidity() const;
1419 static void _buildFrameSkeleton(const MD5JointInfo* pJOINT_INFOS,
1420 const MD5BaseFrameJoint* pBASE_FRAME,
1421 const GLfloat* pANIM_FRAME_DATA,
1422 const MD5Joint* pSkeletonFrame,
1423 GLint NUM_JOINTS);
1428 void _interpolateSkeletons(GLfloat interp);
1432 void _freeModel();
1436 void _freeVertexArrays();
1440 void _freeAnim();
1441
1446 void _moveFromSrc(MD5Model &src);
1447 };
1448}
1449
1450//----------------------------------------------------------------------------------------------------
1451
1452inline
1454{
1455 _freeModel();
1456 _freeVertexArrays();
1457 _freeAnim();
1458}
1459
1460// load our MD5 model
1461[[maybe_unused]]
1462inline bool
1464 const char* MD5_MESH_FILE,
1465 const char* MD5_ANIM_FILE
1466) {
1467 // Load MD5 _model file
1468 if( readMD5Model(MD5_MESH_FILE) ) {
1469 // if MD5 animation file name provided
1470 if(strcmp(MD5_ANIM_FILE, "") != 0 ) {
1471 // Load MD5 animation file
1472 if( !readMD5Anim(MD5_ANIM_FILE) ) {
1473 return false;
1474 }
1475 }
1476 if( !isAnimated() ) {
1477 printf ("[.MD5_ANIM_FILE]: no animation loaded.\n");
1478 }
1479 } else {
1480 return false;
1481 }
1482 return true;
1483}
1484
1485// Load an MD5 model from file.
1486inline bool
1488 const char* FILENAME
1489) {
1490 char buff[512];
1491 GLint version = 0;
1492 GLint currentMesh = 0;
1493
1494 GLint totalVertices = 0;
1495 GLint totalWeights = 0;
1496 GLint totalTriangles = 0;
1497
1498 GLfloat minX = 999999, minY = 999999, minZ = 999999;
1499 GLfloat maxX = -999999, maxY = -999999, maxZ = -999999;
1500
1501 printf("[.md5mesh]: about to read %s\n", FILENAME );
1502
1503 FILE *fp = fopen(FILENAME, "rb" );
1504 if( !fp ) {
1505 fprintf (stderr, "[.md5mesh]: Error: couldn't open \"%s\"!\n", FILENAME);
1506 return false;
1507 }
1508
1509 while( !feof(fp) ) {
1510 // Read whole line
1511 fgets( buff, sizeof(buff), fp );
1512
1513 if( sscanf(buff, " MD5Version %d", &version) == 1 ) {
1514 if( version != 10 ) {
1515 // Bad version
1516 fprintf (stderr, "[.md5mesh]: Error: bad model version\n");
1517 fclose (fp);
1518 return false;
1519 }
1520 } else if( sscanf(buff, " numJoints %d", &_numJoints) == 1 ) {
1521 if( _numJoints > 0 ) {
1522 // Allocate memory for base skeleton joints
1523 _baseSkeleton = new MD5Joint[_numJoints];
1524 }
1525 } else if( sscanf(buff, " numMeshes %d", &_numMeshes) == 1 ) {
1526 if( _numMeshes > 0 ) {
1527 // Allocate memory for meshes
1528 _meshes = new MD5Mesh[_numMeshes];
1529 }
1530 } else if( strncmp(buff, "joints {", 8) == 0 ) {
1531 // Read each joint
1532 for(GLint i = 0; i < _numJoints; ++i) {
1533 MD5Joint *joint = &_baseSkeleton[i];
1534
1535 // Read whole line
1536 fgets( buff, sizeof(buff), fp );
1537
1538 if( sscanf(buff, "%s %d ( %f %f %f ) ( %f %f %f )",
1539 joint->name, &joint->parent,
1540 &joint->position[0], &joint->position[1], &joint->position[2],
1541 &joint->orientation[0],&joint->orientation[1], &joint->orientation[2]) == 8
1542 ) {
1543 // Compute the w component
1544 joint->orientation.w = glm::extractRealComponent(joint->orientation);
1545 }
1546 }
1547 } else if( strncmp(buff, "mesh {", 6) == 0 ) {
1548 MD5Mesh *mesh = &_meshes[currentMesh];
1549 GLint vert_index = 0;
1550 GLint tri_index = 0;
1551 GLint weight_index = 0;
1552 GLfloat floatData[4];
1553 GLint intData[3];
1554
1555 while( buff[0] != '}' && !feof(fp) ) {
1556 // Read whole line
1557 fgets( buff, sizeof(buff), fp );
1558
1559 if( strstr( buff, "shader ") ) {
1560 GLint quote = 0, j = 0;
1561
1562 // Copy the shader name without the quote marks
1563 for(unsigned long uli = 0; uli < sizeof(buff) && (quote < 2); ++uli) {
1564 if( buff[uli] == '\"' )
1565 quote++;
1566
1567 if( (quote == 1) && (buff[uli] != '\"') ) {
1568 mesh->shader[j] = buff[uli];
1569 j++;
1570 }
1571 }
1572 // there was a shader name
1573 if( j > 0 ) {
1574 // diffuse map
1575 strcpy(mesh->textures[MD5Mesh::TextureMap::DIFFUSE].filename, mesh->shader);
1576 strcat(mesh->textures[MD5Mesh::TextureMap::DIFFUSE].filename, ".tga");
1577 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);
1578 if( mesh->textures[MD5Mesh::TextureMap::DIFFUSE].texHandle == 0 ) {
1579 strcpy(mesh->textures[MD5Mesh::TextureMap::DIFFUSE].filename, mesh->shader);
1580 strcat(mesh->textures[MD5Mesh::TextureMap::DIFFUSE].filename, "_d.tga");
1581 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 );
1582 if( mesh->textures[MD5Mesh::TextureMap::DIFFUSE].texHandle == 0 ) {
1583 strcpy(mesh->textures[MD5Mesh::TextureMap::DIFFUSE].filename, mesh->shader);
1584 strcat(mesh->textures[MD5Mesh::TextureMap::DIFFUSE].filename, ".png");
1585 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 );
1586 if( mesh->textures[MD5Mesh::TextureMap::DIFFUSE].texHandle == 0 ) {
1587 fprintf(stderr, "[.md5mesh | ERROR]: Could not load diffuse map for shader %s\n", mesh->shader);
1588 }
1589 }
1590 }
1591
1592 // specular map
1593 strcpy(mesh->textures[MD5Mesh::TextureMap::SPECULAR].filename, mesh->shader);
1594 strcat(mesh->textures[MD5Mesh::TextureMap::SPECULAR].filename, "_s.tga");
1595 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 );
1596 if( mesh->textures[MD5Mesh::TextureMap::SPECULAR].texHandle == 0 ) {
1597 strcpy(mesh->textures[MD5Mesh::TextureMap::SPECULAR].filename, mesh->shader);
1598 strcat(mesh->textures[MD5Mesh::TextureMap::SPECULAR].filename, "_s.png");
1599 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 );
1600 if( mesh->textures[MD5Mesh::TextureMap::SPECULAR].texHandle == 0 ) {
1601 fprintf(stderr, "[.md5mesh | ERROR]: Could not load specular map for shader %s\n", mesh->shader);
1602 }
1603 }
1604
1605 // normal map
1606 strcpy(mesh->textures[MD5Mesh::TextureMap::NORMAL].filename, mesh->shader);
1607 strcat(mesh->textures[MD5Mesh::TextureMap::NORMAL].filename, "_local.tga");
1608 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 );
1609 if( mesh->textures[MD5Mesh::TextureMap::NORMAL].texHandle == 0 ) {
1610 strcpy(mesh->textures[MD5Mesh::TextureMap::NORMAL].filename, mesh->shader);
1611 strcat(mesh->textures[MD5Mesh::TextureMap::NORMAL].filename, "_local.png");
1612 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 );
1613 if( mesh->textures[MD5Mesh::TextureMap::NORMAL].texHandle == 0 ) {
1614 fprintf(stderr, "[.md5mesh | ERROR]: Could not load normal map for shader %s\n", mesh->shader);
1615 }
1616 }
1617
1618 // height map
1619 strcpy(mesh->textures[MD5Mesh::TextureMap::HEIGHT].filename, mesh->shader);
1620 strcat(mesh->textures[MD5Mesh::TextureMap::HEIGHT].filename, "_h.tga");
1621 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 );
1622 if( mesh->textures[MD5Mesh::TextureMap::HEIGHT].texHandle == 0 ) {
1623 strcpy(mesh->textures[MD5Mesh::TextureMap::HEIGHT].filename, mesh->shader);
1624 strcat(mesh->textures[MD5Mesh::TextureMap::HEIGHT].filename, "_h.png");
1625 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 );
1626 if( mesh->textures[MD5Mesh::TextureMap::HEIGHT].texHandle == 0 ) {
1627 fprintf(stderr, "[.md5mesh | ERROR]: Could not load height map for shader %s\n", mesh->shader);
1628 }
1629 }
1630 }
1631 } else if( sscanf(buff, " numverts %d", &mesh->numVertices) == 1 ) {
1632 if( mesh->numVertices > 0 ) {
1633 // Allocate memory for vertices
1634 mesh->vertices = new MD5Vertex[mesh->numVertices];
1635 }
1636
1637 if( mesh->numVertices > _maxVertices )
1638 _maxVertices = mesh->numVertices;
1639
1640 totalVertices += mesh->numVertices;
1641 } else if( sscanf(buff, " numtris %d", &mesh->numTriangles) == 1 ) {
1642 if( mesh->numTriangles > 0 ) {
1643 // Allocate memory for triangles
1644 mesh->triangles = new MD5Triangle[mesh->numTriangles];
1645 }
1646
1647 if( mesh->numTriangles > _maxTriangles )
1648 _maxTriangles = mesh->numTriangles;
1649
1650 totalTriangles += mesh->numTriangles;
1651 } else if( sscanf(buff, " numweights %d", &mesh->numWeights) == 1 ) {
1652 if( mesh->numWeights > 0 ) {
1653 // Allocate memory for vertex weights
1654 mesh->weights = new MD5Weight[mesh->numWeights];
1655 }
1656
1657 totalWeights += mesh->numWeights;
1658 } else if( sscanf(buff, " vert %d ( %f %f ) %d %d",
1659 &vert_index,
1660 &floatData[0], &floatData[1],
1661 &intData[0], &intData[1]) == 5
1662 ) {
1663 // Copy vertex data
1664 mesh->vertices[vert_index].texCoord.s = floatData[0];
1665 mesh->vertices[vert_index].texCoord.t = floatData[1];
1666 mesh->vertices[vert_index].start = intData[0];
1667 mesh->vertices[vert_index].count = intData[1];
1668 } else if( sscanf(buff, " tri %d %d %d %d",
1669 &tri_index,
1670 &intData[0], &intData[1], &intData[2]) == 4
1671 ) {
1672 // Copy triangle data
1673 mesh->triangles[tri_index ].index[0] = intData[0];
1674 mesh->triangles[tri_index ].index[1] = intData[1];
1675 mesh->triangles[tri_index ].index[2] = intData[2];
1676 } else if( sscanf(buff, " weight %d %d %f ( %f %f %f )",
1677 &weight_index, &intData[0], &floatData[3],
1678 &floatData[0], &floatData[1], &floatData[2]) == 6
1679 ) {
1680 // Copy vertex data
1681 mesh->weights[weight_index].joint = intData[0];
1682 mesh->weights[weight_index].bias = floatData[3];
1683 mesh->weights[weight_index].position[0] = floatData[0];
1684 mesh->weights[weight_index].position[1] = floatData[1];
1685 mesh->weights[weight_index].position[2] = floatData[2];
1686
1687 if( floatData[0] < minX ) { minX = floatData[0]; }
1688 if( floatData[0] > maxX ) { maxX = floatData[0]; }
1689 if( floatData[1] < minY ) { minY = floatData[1]; }
1690 if( floatData[1] > maxY ) { maxY = floatData[1]; }
1691 if( floatData[2] < minZ ) { minZ = floatData[2]; }
1692 if( floatData[2] > maxZ ) { maxZ = floatData[2]; }
1693 }
1694 }
1695
1696 currentMesh++;
1697 }
1698 }
1699
1700 fclose(fp);
1701
1702 _skeleton = _baseSkeleton;
1703
1704 printf("[.md5mesh]: finished reading %s\n", FILENAME );
1705 printf("[.md5mesh]: read in %d meshes, %d joints, %d vertices, %d weights, and %d triangles\n", _numMeshes, _numJoints, totalVertices, totalWeights, totalTriangles );
1706 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) );
1707 printf( "\n" );
1708
1709 return true;
1710}
1711
1712inline void
1713CSCI441::MD5Model::_freeModel()
1714{
1715 delete[] _baseSkeleton;
1716 if (_baseSkeleton == _skeleton) _skeleton = nullptr; // if there is no animation, prevent double delete
1717 _baseSkeleton = nullptr;
1718
1719 delete[] _meshes;
1720 _meshes = nullptr;
1721}
1722
1723[[maybe_unused]]
1724inline void
1726{
1727 // Draw each mesh of the model
1728 for(GLint i = 0; i < _numMeshes; ++i) {
1729 MD5Mesh& mesh = _meshes[i]; // get the mesh
1730 _prepareMesh(&mesh); // do some preprocessing on it
1731 _drawMesh(&mesh);
1732 }
1733}
1734
1735inline void
1736CSCI441::MD5Model::_prepareMesh(
1737 const MD5Mesh *pMESH
1738) const {
1739 GLint i, j, k;
1740
1741 // Setup vertex indices
1742 for(k = 0, i = 0; i < pMESH->numTriangles; ++i) {
1743 for(j = 0; j < 3; ++j, ++k)
1744 _vertexIndicesArray[k] = pMESH->triangles[i].index[j];
1745 }
1746
1747 // Setup vertices
1748 for(i = 0; i < pMESH->numVertices; ++i) {
1749 glm::vec3 finalVertex = {0.0f, 0.0f, 0.0f };
1750
1751 // Calculate final vertex to draw with weights
1752 for(j = 0; j < pMESH->vertices[i].count; ++j) {
1753 const MD5Weight *weight = &pMESH->weights[pMESH->vertices[i].start + j];
1754 const MD5Joint *joint = &_skeleton[weight->joint];
1755
1756 // Calculate transformed vertex for this weight
1757 const glm::vec3 weightedVertex = glm::rotate(joint->orientation, glm::vec4(weight->position, 0.0f));
1758
1759 // The sum of all weight->bias should be 1.0
1760 finalVertex.x += (joint->position.x + weightedVertex.x) * weight->bias;
1761 finalVertex.y += (joint->position.y + weightedVertex.y) * weight->bias;
1762 finalVertex.z += (joint->position.z + weightedVertex.z) * weight->bias;
1763 }
1764
1765 _vertexArray[i].x = finalVertex.x;
1766 _vertexArray[i].y = finalVertex.y;
1767 _vertexArray[i].z = finalVertex.z;
1768
1769 _texelArray[i].s = pMESH->vertices[i].texCoord.s;
1770 _texelArray[i].t = pMESH->vertices[i].texCoord.t;
1771 }
1772
1773 glBindVertexArray(_vao );
1774
1775 glBindBuffer(GL_ARRAY_BUFFER, _vbo[0] );
1776 glBufferSubData(GL_ARRAY_BUFFER, 0, static_cast<GLsizeiptr>(sizeof(glm::vec3)) * pMESH->numVertices, &_vertexArray[0] );
1777 glBufferSubData(GL_ARRAY_BUFFER, static_cast<GLsizeiptr>(sizeof(glm::vec3)) * _maxVertices, static_cast<GLsizeiptr>(sizeof(glm::vec2)) * pMESH->numVertices, &_texelArray[0] );
1778
1779 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vbo[1] );
1780 glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, static_cast<GLsizeiptr>(sizeof(GLuint)) * pMESH->numTriangles * 3, _vertexIndicesArray );
1781}
1782
1783inline void
1784CSCI441::MD5Model::_drawMesh(
1785 const MD5Mesh *pMESH
1786) const {
1787 if (pMESH->textures[MD5Mesh::TextureMap::SPECULAR].texHandle != 0) {
1788 // Bind Specular Map if exists
1789 glActiveTexture(_specularActiveTexture);
1790 glBindTexture(GL_TEXTURE_2D, pMESH->textures[MD5Mesh::TextureMap::SPECULAR].texHandle );
1791 }
1792 if (pMESH->textures[MD5Mesh::TextureMap::NORMAL].texHandle != 0) {
1793 // Bind Normal Map if exists
1794 glActiveTexture(_normalActiveTexture);
1795 glBindTexture(GL_TEXTURE_2D, pMESH->textures[MD5Mesh::TextureMap::NORMAL].texHandle );
1796 }
1797 if (pMESH->textures[MD5Mesh::TextureMap::HEIGHT].texHandle != 0) {
1798 // Bind Height Map if exists
1799 glActiveTexture(_heightActiveTexture);
1800 glBindTexture(GL_TEXTURE_2D, pMESH->textures[MD5Mesh::TextureMap::HEIGHT].texHandle );
1801 }
1802 // bind diffuse last because ideally it is texture 0 and will remain for future renderings
1803 if (pMESH->textures[MD5Mesh::TextureMap::DIFFUSE].texHandle != 0) {
1804 // Bind Diffuse Map if exists
1805 glActiveTexture(_diffuseActiveTexture);
1806 glBindTexture(GL_TEXTURE_2D, pMESH->textures[MD5Mesh::TextureMap::DIFFUSE].texHandle );
1807 }
1808 if (_diffuseActiveTexture != GL_TEXTURE0) {
1809 // reset back to active texture being zero
1810 glActiveTexture(GL_TEXTURE0);
1811 }
1812
1813 glBindVertexArray(_vao );
1814 glDrawElements(GL_TRIANGLES, pMESH->numTriangles * 3, GL_UNSIGNED_INT, (void*)nullptr );
1815}
1816
1817[[maybe_unused]]
1818inline void
1820 const GLuint vPosAttribLoc,
1821 const GLuint vColorAttribLoc,
1822 const GLuint vTexCoordAttribLoc
1823) {
1824 _vertexArray = new glm::vec3[_maxVertices];
1825 _texelArray = new glm::vec2[_maxVertices];
1826 _vertexIndicesArray = new GLuint[_maxTriangles * 3];
1827
1828 glGenVertexArrays( 1, &_vao );
1829 glBindVertexArray(_vao );
1830
1831 glGenBuffers(2, _vbo );
1832 glBindBuffer(GL_ARRAY_BUFFER, _vbo[0] );
1833 glBufferData(GL_ARRAY_BUFFER, static_cast<GLsizeiptr>(sizeof(glm::vec3)) * _maxVertices + static_cast<GLsizeiptr>(sizeof(glm::vec2)) * _maxVertices, nullptr, GL_DYNAMIC_DRAW );
1834
1835 glEnableVertexAttribArray( vPosAttribLoc );
1836 glVertexAttribPointer( vPosAttribLoc, 3, GL_FLOAT, GL_FALSE, 0, static_cast<void *>(nullptr) );
1837
1838 glEnableVertexAttribArray( vTexCoordAttribLoc );
1839 glVertexAttribPointer( vTexCoordAttribLoc, 2, GL_FLOAT, GL_FALSE, 0, reinterpret_cast<void *>(sizeof(glm::vec3) * _maxVertices) );
1840
1841 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vbo[1] );
1842 glBufferData(GL_ELEMENT_ARRAY_BUFFER, static_cast<GLsizeiptr>(sizeof(GLuint)) * _maxTriangles * 3, nullptr, GL_DYNAMIC_DRAW );
1843
1844 printf("[.md5mesh]: Model VAO/VBO/IBO registered at %u/%u/%u\n", _vao, _vbo[0], _vbo[1] );
1845
1846 glGenVertexArrays( 1, &_skeletonVAO );
1847 glBindVertexArray(_skeletonVAO );
1848
1849 glGenBuffers( 1, &_skeletonVBO );
1850 glBindBuffer(GL_ARRAY_BUFFER, _skeletonVBO );
1851 glBufferData(GL_ARRAY_BUFFER, static_cast<GLsizeiptr>(sizeof(glm::vec3)) * _numJoints * 3 * 2, nullptr, GL_DYNAMIC_DRAW );
1852
1853 glEnableVertexAttribArray( vPosAttribLoc ); // vPos
1854 glVertexAttribPointer( vPosAttribLoc, 3, GL_FLOAT, GL_FALSE, 0, static_cast<void *>(nullptr) );
1855
1856 glEnableVertexAttribArray( vColorAttribLoc ); // vColor
1857 glVertexAttribPointer( vColorAttribLoc, 3, GL_FLOAT, GL_FALSE, 0, reinterpret_cast<void *>(sizeof(glm::vec3) * _numJoints * 3) );
1858
1859 printf("[.md5mesh]: Skeleton VAO/VBO registered at %u/%u\n", _skeletonVAO, _skeletonVBO );
1860}
1861
1862inline void
1864 const GLint diffuseMapActiveTexture = GL_TEXTURE0,
1865 const GLint specularMapActiveTexture = GL_TEXTURE1,
1866 const GLint normalMapActiveTexture = GL_TEXTURE2,
1867 const GLint heightMapActiveTexture = GL_TEXTURE3
1868) {
1869 _diffuseActiveTexture = diffuseMapActiveTexture;
1870 _specularActiveTexture = specularMapActiveTexture;
1871 _normalActiveTexture = normalMapActiveTexture;
1872 _heightActiveTexture = heightMapActiveTexture;
1873}
1874
1875inline void
1876CSCI441::MD5Model::_freeVertexArrays()
1877{
1878 delete[] _vertexArray;
1879 _vertexArray = nullptr;
1880
1881 delete[] _vertexIndicesArray;
1882 _vertexIndicesArray = nullptr;
1883
1884 delete[] _texelArray;
1885 _texelArray = nullptr;
1886
1887 glDeleteVertexArrays( 1, &_vao );
1888 _vao = 0;
1889
1890 glDeleteBuffers(2, _vbo );
1891 _vbo[0] = 0;
1892 _vbo[1] = 0;
1893
1894 glDeleteVertexArrays( 1, &_skeletonVAO );
1895 _skeletonVAO = 0;
1896
1897 glDeleteBuffers( 1, &_skeletonVBO );
1898 _skeletonVBO = 0;
1899}
1900
1901[[maybe_unused]]
1902inline void
1904{
1905 glBindVertexArray(_skeletonVAO );
1906 glBindBuffer(GL_ARRAY_BUFFER, _skeletonVBO );
1907
1908 constexpr glm::vec3 jointColor = {1.0f, 1.0f, 0.0f };
1909 constexpr glm::vec3 boneColor = {1.0f, 0.0f, 1.0f };
1910
1911 // put in points for joints
1912 for(GLint i = 0; i < _numJoints; ++i ) {
1913 glBufferSubData(GL_ARRAY_BUFFER, i * static_cast<GLsizeiptr>(sizeof(glm::vec3)), sizeof(glm::vec3), &(_skeleton[i].position) );
1914 glBufferSubData(GL_ARRAY_BUFFER, i * static_cast<GLsizeiptr>(sizeof(glm::vec3)) + static_cast<GLsizeiptr>(sizeof(glm::vec3)) * _numJoints * 3, sizeof(glm::vec3), &jointColor[0]);
1915 }
1916
1917 // put in lines for bones
1918 GLint numBones = 0;
1919 for(GLint i = 0; i < _numJoints; ++i ) {
1920 if( _skeleton[i].parent != MD5Joint::NULL_JOINT ) {
1921 glBufferSubData(
1922 GL_ARRAY_BUFFER,
1923 static_cast<GLsizeiptr>(sizeof(glm::vec3)) * _numJoints + (i * 2) * static_cast<GLsizeiptr>(sizeof(glm::vec3)),
1924 static_cast<GLsizeiptr>(sizeof(glm::vec3)),
1925 &(_skeleton[_skeleton[i].parent].position)
1926 );
1927 glBufferSubData(
1928 GL_ARRAY_BUFFER,
1929 static_cast<GLsizeiptr>(sizeof(glm::vec3)) * _numJoints + (i * 2) * static_cast<GLsizeiptr>(sizeof(glm::vec3)) + static_cast<GLsizeiptr>(sizeof(glm::vec3)) * _numJoints * 3,
1930 sizeof(glm::vec3),
1931 &boneColor[0]
1932 );
1933
1934 glBufferSubData(
1935 GL_ARRAY_BUFFER,
1936 static_cast<GLsizeiptr>(sizeof(glm::vec3)) * _numJoints + (i * 2) * static_cast<GLsizeiptr>(sizeof(glm::vec3)) + static_cast<GLsizeiptr>(sizeof(glm::vec3)),
1937 static_cast<GLsizeiptr>(sizeof(glm::vec3)),
1938 &(_skeleton[i].position)
1939 );
1940 glBufferSubData(
1941 GL_ARRAY_BUFFER,
1942 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,
1943 sizeof(glm::vec3),
1944 &boneColor[0]
1945 );
1946 numBones++;
1947 }
1948 }
1949
1950 glPointSize(5.0f);
1951 glDrawArrays(GL_POINTS, 0, _numJoints );
1952 glPointSize(1.0f);
1953
1954 glLineWidth( 3.0f );
1955 glDrawArrays(GL_LINES, _numJoints, numBones * 2 );
1956 glLineWidth(1.0f);
1957}
1958
1959inline bool
1960CSCI441::MD5Model::_checkAnimValidity() const
1961{
1962 // md5mesh and md5anim must have the same number of joints
1963 if( _numJoints != _animation->getNumberOfJoints() ) {
1964 fprintf(stdout, "\n[.md5anim]: skeleton and animation do not have same number of joints. cannot apply animation to skeleton\n\n");
1965 return false;
1966 }
1967 if (_animation->getNumberOfJoints() == 0 ) {
1968 fprintf(stdout, "\n[.md5anim]: animation has zero joints. cannot apply animation to skeleton\n\n");
1969 return false;
1970 }
1971
1972 // We just check with frame[0]
1973 for(GLint i = 0; i < _numJoints; ++i) {
1974 // Joints must have the same parent index
1975 if (_baseSkeleton[i].parent != _animation->getSkeletonFrameJoint(0, i).parent) {
1976 fprintf(stdout, "\n[.md5anim]: skeleton and animation joints do not have same parent index. cannot apply animation to skeleton\n\n");
1977 return false;
1978 }
1979
1980 // Joints must have the same name
1981 if (strcmp (_baseSkeleton[i].name, _animation->getSkeletonFrameJoint(0, i).name) != 0) {
1982 fprintf(stdout, "\n[.md5anim]: skeleton and animation joints do not have same name. cannot apply animation to skeleton\n\n");
1983 return false;
1984 }
1985 }
1986
1987 fprintf(stdout, "\n[.md5anim]: skeleton and animation match. animation can be applied to skeleton\n\n");
1988 return true;
1989}
1990
1991inline void
1992CSCI441::MD5Model::_buildFrameSkeleton(
1993 const MD5JointInfo* pJOINT_INFOS,
1994 const MD5BaseFrameJoint* pBASE_FRAME,
1995 const GLfloat* pANIM_FRAME_DATA,
1996 const MD5Joint* pSkeletonFrame,
1997 const GLint NUM_JOINTS
1998) {
1999 if(pJOINT_INFOS == nullptr
2000 || pBASE_FRAME == nullptr
2001 || pANIM_FRAME_DATA == nullptr
2002 || pSkeletonFrame == nullptr) return;
2003
2004 for(GLint i = 0; i < NUM_JOINTS; ++i) {
2005 const MD5BaseFrameJoint *baseJoint = &pBASE_FRAME[i];
2006 glm::vec3 animatedPosition = baseJoint->position;
2007 glm::quat animatedOrientation = baseJoint->orientation;
2008 GLint j = 0;
2009
2010 // Tx
2011 if(pJOINT_INFOS[i].flags & 1 ) {
2012 animatedPosition.x = pANIM_FRAME_DATA[pJOINT_INFOS[i].startIndex + j];
2013 ++j;
2014 }
2015
2016 // Ty
2017 if(pJOINT_INFOS[i].flags & 2 ) {
2018 animatedPosition.y = pANIM_FRAME_DATA[pJOINT_INFOS[i].startIndex + j];
2019 ++j;
2020 }
2021
2022 // Tz
2023 if(pJOINT_INFOS[i].flags & 4 ) {
2024 animatedPosition.z = pANIM_FRAME_DATA[pJOINT_INFOS[i].startIndex + j];
2025 ++j;
2026 }
2027
2028 // Qx
2029 if(pJOINT_INFOS[i].flags & 8 ) {
2030 animatedOrientation.x = pANIM_FRAME_DATA[pJOINT_INFOS[i].startIndex + j];
2031 ++j;
2032 }
2033
2034 // Qy
2035 if(pJOINT_INFOS[i].flags & 16 ) {
2036 animatedOrientation.y = pANIM_FRAME_DATA[pJOINT_INFOS[i].startIndex + j];
2037 ++j;
2038 }
2039
2040 // Qz
2041 if(pJOINT_INFOS[i].flags & 32 ) {
2042 animatedOrientation.z = pANIM_FRAME_DATA[pJOINT_INFOS[i].startIndex + j];
2043 }
2044
2045 // Compute orientation quaternion's w value
2046 animatedOrientation.w = glm::extractRealComponent(animatedOrientation);
2047
2048 // NOTE: we assume that this joint's parent has
2049 // already been calculated, i.e. joint's ID should
2050 // never be smaller than its parent ID.
2051 const auto thisJoint = const_cast<MD5Joint *>(&pSkeletonFrame[i]);
2052
2053 const GLint parent = pJOINT_INFOS[i].parent;
2054 thisJoint->parent = parent;
2055 strcpy (thisJoint->name, pJOINT_INFOS[i].name);
2056
2057 // Has parent?
2058 if( thisJoint->parent == MD5Joint::NULL_JOINT ) {
2059 thisJoint->position = animatedPosition;
2060 thisJoint->orientation = animatedOrientation;
2061 } else {
2062 const MD5Joint *parentJoint = &pSkeletonFrame[parent];
2063 glm::vec3 rotatedPosition = glm::rotate(parentJoint->orientation, glm::vec4(animatedPosition, 0.0f));
2064
2065 // Add positions
2066 thisJoint->position = parentJoint->position + rotatedPosition;
2067
2068 // Concatenate rotations
2069 thisJoint->orientation = glm::normalize( glm::cross(parentJoint->orientation, animatedOrientation) );
2070 }
2071 }
2072}
2073
2074inline bool
2076 const char *filename
2077) {
2078 char buff[512];
2079 MD5JointInfo *jointInfos = nullptr;
2080 MD5BaseFrameJoint *baseFrame = nullptr;
2081 GLfloat *animFrameData = nullptr;
2082 GLint version;
2083 GLint numAnimatedComponents;
2084 GLint frameIndex, numFrames, numJoints;
2085 GLint i;
2086
2087 printf( "[.md5anim]: about to read %s\n", filename );
2088
2089 FILE *fp = fopen( filename, "rb" );
2090 if( !fp ) {
2091 fprintf (stderr, "[.md5anim]: Error: couldn't open \"%s\"!\n", filename);
2092 return false;
2093 }
2094
2095 while( !feof(fp) ) {
2096 // Read whole line
2097 fgets( buff, sizeof(buff), fp );
2098
2099 if( sscanf(buff, " MD5Version %d", &version) == 1 ) {
2100 if( version != 10 ) {
2101 // Bad version
2102 fprintf (stderr, "[.md5anim]: Error: bad animation version\n");
2103 fclose (fp);
2104 return false;
2105 }
2106 } else if( sscanf(buff, " numFrames %d", &numFrames) == 1 ) {
2107 // Allocate memory for skeleton frames and bounding boxes
2108 _animation->setNumberOfFrames(numFrames);
2109 } else if( sscanf(buff, " numJoints %d", &numJoints) == 1 ) {
2110 if (jointInfos != nullptr) {
2111 fprintf( stderr, "[.md5anim]: Error: md5anim file malformed. numJoints already specified\n" );
2112 }
2113 if( numJoints > 0 ) {
2114 _animation->setNumberOfJoints(numJoints);
2115
2116 // Allocate temporary memory for building skeleton frames
2117 jointInfos = new MD5JointInfo[numJoints];
2118 baseFrame = new MD5BaseFrameJoint[numJoints];
2119 }
2120 } else if( sscanf(buff, " frameRate %d", &_animation->frameRate) == 1 ) {
2121
2122 } else if( sscanf(buff, " numAnimatedComponents %d", &numAnimatedComponents) == 1 ) {
2123 if (animFrameData != nullptr) {
2124 fprintf( stderr, "[.md5anim]: Error: md5anim file malformed. numAnimatedComponents already specified\n" );
2125 }
2126 if( numAnimatedComponents > 0 ) {
2127 // Allocate memory for animation frame data
2128 animFrameData = new GLfloat[numAnimatedComponents];
2129 }
2130 } else if( strncmp(buff, "hierarchy {", 11) == 0 ) {
2131 if (jointInfos == nullptr) {
2132 fprintf( stderr, "[.md5anim]: Error: md5anim file malformed. numJoints not specified prior to hierarchy\n" );
2133 } else {
2134 for(i = 0; i < numJoints; ++i) {
2135 // Read whole line
2136 fgets( buff, sizeof(buff), fp );
2137
2138 // Read joint info
2139 sscanf(buff, " %s %d %d %d",
2140 jointInfos[i].name, &jointInfos[i].parent,
2141 &jointInfos[i].flags, &jointInfos[i].startIndex);
2142 }
2143 }
2144 } else if( strncmp(buff, "bounds {", 8) == 0 ) {
2145 if (_animation->getNumberOfFrames() == 0) {
2146 fprintf( stderr, "[.md5anim]: Error: md5anim file malformed. numFrames not specified prior to bounds\n" );
2147 } else {
2148 for(i = 0; i < _animation->getNumberOfFrames(); ++i) {
2149 // Read whole line
2150 fgets( buff, sizeof(buff), fp );
2151
2152 // Read bounding box
2153 sscanf(buff, " ( %f %f %f ) ( %f %f %f )",
2154 &_animation->getBoundingBox(i).min[0], &_animation->getBoundingBox(i).min[1], &_animation->getBoundingBox(i).min[2],
2155 &_animation->getBoundingBox(i).max[0], &_animation->getBoundingBox(i).max[1], &_animation->getBoundingBox(i).max[2]);
2156 }
2157 }
2158 } else if( strncmp(buff, "baseframe {", 10) == 0 ) {
2159 if (baseFrame == nullptr) {
2160 fprintf( stderr, "[.md5anim]: Error: md5anim file malformed. numJoints not specified prior to baseframe\n" );
2161 } else {
2162 for(i = 0; i < numJoints; ++i) {
2163 // Read whole line
2164 fgets( buff, sizeof(buff), fp );
2165
2166 // Read base frame joint
2167 if( sscanf(buff, " ( %f %f %f ) ( %f %f %f )",
2168 &baseFrame[i].position[0], &baseFrame[i].position[1], &baseFrame[i].position[2],
2169 &baseFrame[i].orientation[0], &baseFrame[i].orientation[1], &baseFrame[i].orientation[2]) == 6 ) {
2170 // Compute the w component
2171 baseFrame[i].orientation.w = glm::extractRealComponent(baseFrame[i].orientation);
2172 }
2173 }
2174 }
2175 } else if(sscanf(buff, " frame %d", &frameIndex) == 1 ) {
2176 if (animFrameData == nullptr) {
2177 fprintf( stderr, "[.md5anim]: Error: md5anim file malformed. numAnimatedComponents not specified prior to frame\n" );
2178 } else if (_animation->getNumberOfFrames() == 0) {
2179 fprintf( stderr, "[.md5anim]: Error: md5anim file malformed. numFrames not specified prior to frame\n" );
2180 } else if (baseFrame == nullptr) {
2181 fprintf( stderr, "[.md5anim]: Error: md5anim file malformed. baseframe not specified prior to frame\n" );
2182 } else if (jointInfos == nullptr) {
2183 fprintf( stderr, "[.md5anim]: Error: md5anim file malformed. numJoints not specified prior to frame\n" );
2184 } else {
2185 // Read frame data
2186 for(i = 0; i < numAnimatedComponents; ++i)
2187 fscanf( fp, "%f", &animFrameData[i] );
2188
2189 // Build frame _skeleton from the collected data
2190 _buildFrameSkeleton(jointInfos, baseFrame, animFrameData,
2191 _animation->getSkeletonFrame(frameIndex),
2192 numJoints);
2193 }
2194 }
2195 }
2196
2197 fclose( fp );
2198
2199 printf( "[.md5anim]: finished reading %s\n", filename );
2200 printf( "[.md5anim]: read in %d frames of %d joints with %d animated components\n", _animation->getNumberOfFrames(), _animation->getNumberOfJoints(), numAnimatedComponents );
2201 printf( "[.md5anim]: animation's frame rate is %d\n", _animation->frameRate );
2202
2203 // Free temporary data allocated
2204 delete[] animFrameData;
2205 delete[] baseFrame;
2206 delete[] jointInfos;
2207
2208 // successful loading...set up animation parameters
2209 _animationInfo.currFrame = 0;
2210 _animationInfo.nextFrame = 1;
2211
2212 _animationInfo.lastTime = 0.0f;
2213 _animationInfo.maxTime = 1.0f / static_cast<GLfloat>(_animation->frameRate);
2214
2215 // Allocate memory for animated _skeleton
2216 if (_animation->getNumberOfJoints() == 0) {
2217 fprintf( stderr, "[.md5anim]: Error: md5anim file malformed. numJoints never specified\n" );
2218 } else {
2219 _skeleton = new MD5Joint[_animation->getNumberOfJoints()];
2220 }
2221
2222 if( _checkAnimValidity() ) {
2223 _isAnimated = true;
2224 // compute initial pose
2225 animate(0.0);
2226 }
2227
2228 return true;
2229}
2230
2231inline void
2232CSCI441::MD5Model::_freeAnim()
2233{
2234 delete[] _skeleton;
2235 _skeleton = nullptr;
2236
2237 delete _animation;
2238 _animation = nullptr;
2239}
2240
2241inline void CSCI441::MD5Model::_moveFromSrc(MD5Model &src) {
2242 this->_baseSkeleton = src._baseSkeleton;
2243 src._baseSkeleton = nullptr;
2244
2245 this->_meshes = src._meshes;
2246 src._meshes = nullptr;
2247
2248 this->_maxVertices = src._maxVertices;
2249 src._maxVertices = 0;
2250
2251 this->_maxTriangles = src._maxTriangles;
2252 src._maxTriangles = 0;
2253
2254 this->_vertexArray = src._vertexArray;
2255 src._vertexArray = nullptr;
2256
2257 this->_texelArray = src._texelArray;
2258 src._texelArray = nullptr;
2259
2260 this->_vertexIndicesArray = src._vertexIndicesArray;
2261 src._vertexIndicesArray = nullptr;
2262
2263 this->_vao = src._vao;
2264 src._vao = 0;
2265
2266 this->_vbo[0] = src._vbo[0];
2267 this->_vbo[1] = src._vbo[1];
2268 src._vbo[0] = 0;
2269 src._vbo[1] = 0;
2270
2271 this->_skeletonVAO = src._skeletonVAO;
2272 src._skeletonVAO = 0;
2273
2274 this->_skeletonVBO = src._skeletonVBO;
2275 src._skeletonVBO = 0;
2276
2277 this->_skeleton = src._skeleton;
2278 src._skeleton = nullptr;
2279
2280 this->_animation = src._animation;
2281 src._animation = nullptr;
2282
2283 this->_isAnimated = src._isAnimated;
2284 src._isAnimated = false;
2285
2286 this->_animationInfo = std::move( src._animationInfo );
2287}
2288
2289inline void
2290CSCI441::MD5Model::_interpolateSkeletons(const GLfloat interp)
2291{
2292 const MD5Joint *skeletonA = _animation->getSkeletonFrame(_animationInfo.currFrame);
2293 const MD5Joint *skeletonB = _animation->getSkeletonFrame(_animationInfo.nextFrame);
2294
2295 for(GLint i = 0; i < _animation->getNumberOfJoints(); ++i) {
2296 // Copy parent index
2297 _skeleton[i].parent = skeletonA[i].parent;
2298
2299 // Linear interpolation for position
2300 _skeleton[i].position[0] = skeletonA[i].position[0] + interp * (skeletonB[i].position[0] - skeletonA[i].position[0]);
2301 _skeleton[i].position[1] = skeletonA[i].position[1] + interp * (skeletonB[i].position[1] - skeletonA[i].position[1]);
2302 _skeleton[i].position[2] = skeletonA[i].position[2] + interp * (skeletonB[i].position[2] - skeletonA[i].position[2]);
2303
2304 // Spherical linear interpolation for orientation
2305 _skeleton[i].orientation = glm::slerp(skeletonA[i].orientation, skeletonB[i].orientation, interp);
2306 }
2307}
2308
2309// Perform animation related computations. Calculate the current and
2310// next frames, given a delta time.
2311inline void
2313{
2314 const GLint maxFrames = _animation->getNumberOfFrames() - 1;
2315 if (maxFrames <= 0) return;
2316
2317 _animationInfo.lastTime += dt;
2318
2319 // move to next frame
2320 if( _animationInfo.lastTime >= _animationInfo.maxTime ) {
2321 _animationInfo.currFrame++;
2322 _animationInfo.nextFrame++;
2323 _animationInfo.lastTime = 0.0;
2324
2325 if( _animationInfo.currFrame > maxFrames )
2326 _animationInfo.currFrame = 0;
2327
2328 if( _animationInfo.nextFrame > maxFrames )
2329 _animationInfo.nextFrame = 0;
2330 }
2331
2332 // Interpolate skeletons between two frames
2333 _interpolateSkeletons( _animationInfo.lastTime * static_cast<GLfloat>(_animation->frameRate) );
2334}
2335
2336#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:879
MD5Animation & operator=(MD5Animation &&src) noexcept
reassign object by moving an existing object
Definition: MD5Model.hpp:1054
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:1046
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:891
void setNumberOfFrames(const GLint numFrames)
sets the number of frames and allocates skeletonFrames and boundingBoxes to the associated size
Definition: MD5Model.hpp:898
const MD5Joint * getSkeletonFrame(const GLint frameIndex) const
get the skeleton for a specific animation frame
Definition: MD5Model.hpp:959
GLint frameRate
number of frames per second to draw for the animation
Definition: MD5Model.hpp:885
~MD5Animation()
deallocate animation arrays
Definition: MD5Model.hpp:1017
MD5Joint & getSkeletonFrameJoint(const GLint frameIndex, const GLint jointIndex) const
get the specific joint from a skeleton for a specific animation frame
Definition: MD5Model.hpp:977
MD5BoundingBox & getBoundingBox(const GLint frameIndex) const
get the specific bounding box for a target frame
Definition: MD5Model.hpp:1000
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:932
GLint getNumberOfJoints() const
get the number of joints in each skeleton frame
Definition: MD5Model.hpp:924
stores a Doom3 MD5 Mesh + Animation
Definition: MD5Model.hpp:76
void draw() const
draws all the meshes that make up the model
Definition: MD5Model.hpp:1725
bool readMD5Model(const char *FILENAME)
parses md5mesh file and allocates corresponding mesh data
Definition: MD5Model.hpp:1487
bool isAnimated() const
returns if the MD5 Model has an accompanying animation
Definition: MD5Model.hpp:1239
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:1463
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:1863
MD5Model()
initializes an empty MD5 Model
Definition: MD5Model.hpp:1196
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:2075
void allocVertexArrays(GLuint vPosAttribLoc, GLuint vColorAttribLoc, GLuint vTexCoordAttribLoc)
binds model VBOs to attribute pointer locations
Definition: MD5Model.hpp:1819
void animate(GLfloat dt)
advances the model forward in its animation sequence the corresponding amount of time based on frame ...
Definition: MD5Model.hpp:2312
MD5Model & operator=(MD5Model &&src) noexcept
do not allow MD5 models to be moved
Definition: MD5Model.hpp:1220
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:1214
~MD5Model()
deallocates any used memory on the CPU and GPU
Definition: MD5Model.hpp:1453
void drawSkeleton() const
draws the skeleton joints (as points) and bones (as lines)
Definition: MD5Model.hpp:1903
CSCI441 Helper Functions for OpenGL.
Definition: ArcballCam.hpp:17
stores state of current animation frame
Definition: MD5Model.hpp:1107
GLint currFrame
index of current frame model is in
Definition: MD5Model.hpp:1111
MD5AnimationState(MD5AnimationState &&src) noexcept
construct an object by moving an existing object
Definition: MD5Model.hpp:1152
MD5AnimationState(const MD5AnimationState &OTHER)
construct an object by copying an existing object
Definition: MD5Model.hpp:1134
MD5AnimationState()=default
construct a default object
GLint nextFrame
index of next frame model will move to
Definition: MD5Model.hpp:1115
MD5AnimationState & operator=(MD5AnimationState &&src) noexcept
reassign object by moving an existing object
Definition: MD5Model.hpp:1160
MD5AnimationState & operator=(const MD5AnimationState &OTHER)
reassign object by copying an existing object
Definition: MD5Model.hpp:1142
GLfloat lastTime
time of last frame interpolation
Definition: MD5Model.hpp:1119
GLfloat maxTime
duration of a single frame
Definition: MD5Model.hpp:1124
base frame joint
Definition: MD5Model.hpp:730
glm::quat orientation
joint orientation expressed as a quaternion in object space
Definition: MD5Model.hpp:738
MD5BaseFrameJoint & operator=(const MD5BaseFrameJoint &OTHER)
reassign object by copying an existing object
Definition: MD5Model.hpp:756
MD5BaseFrameJoint()=default
construct a default object
MD5BaseFrameJoint(MD5BaseFrameJoint &&src) noexcept
construct object by moving an existing object
Definition: MD5Model.hpp:766
glm::vec3 position
position of the joint in object space
Definition: MD5Model.hpp:734
MD5BaseFrameJoint(const MD5BaseFrameJoint &OTHER)
construct an object by copying an existing object
Definition: MD5Model.hpp:748
MD5BaseFrameJoint & operator=(MD5BaseFrameJoint &&src) noexcept
reassign object by moving an existing object
Definition: MD5Model.hpp:774
bounding box containing the model during animation
Definition: MD5Model.hpp:805
MD5BoundingBox(const MD5BoundingBox &OTHER)
construct an object by copying an existing object
Definition: MD5Model.hpp:823
MD5BoundingBox & operator=(MD5BoundingBox &&src) noexcept
reassign object by moving an existing object
Definition: MD5Model.hpp:849
glm::vec3 min
minimum dimension bound
Definition: MD5Model.hpp:809
MD5BoundingBox(MD5BoundingBox &&src) noexcept
construct an object by moving an existing object
Definition: MD5Model.hpp:841
glm::vec3 max
maximum dimension bound
Definition: MD5Model.hpp:813
MD5BoundingBox & operator=(const MD5BoundingBox &OTHER)
reassign object by copying an existing object
Definition: MD5Model.hpp:831
MD5BoundingBox()=default
construct a default object
a joint of the MD5 Skeleton
Definition: MD5Model.hpp:82
MD5Joint(const MD5Joint &OTHER)
construct a joint object by copying an existing joint
Definition: MD5Model.hpp:116
char name[MAX_NAME_LENGTH]
joint identifier
Definition: MD5Model.hpp:94
MD5Joint(MD5Joint &&src) noexcept
construct a joint object by moving an existing object
Definition: MD5Model.hpp:134
glm::vec3 position
position of the joint in object space
Definition: MD5Model.hpp:102
MD5Joint & operator=(const MD5Joint &OTHER)
reassign an existing joint object by copying another joint object
Definition: MD5Model.hpp:124
glm::quat orientation
joint orientation expressed as a quaternion in object space
Definition: MD5Model.hpp:106
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:142
GLint parent
index of the parent joint on skeletal tree
Definition: MD5Model.hpp:98
information pertaining to each animation joint
Definition: MD5Model.hpp:640
MD5JointInfo & operator=(MD5JointInfo &&src) noexcept
reassign object by moving an existing object
Definition: MD5Model.hpp:696
GLint startIndex
index of starting parameter
Definition: MD5Model.hpp:660
MD5JointInfo(const MD5JointInfo &OTHER)
construct a joint info object by copying an existing object
Definition: MD5Model.hpp:670
MD5JointInfo(MD5JointInfo &&src) noexcept
construct joint info object by moving an existing object
Definition: MD5Model.hpp:688
GLint parent
index of parent joint on skeletal tree
Definition: MD5Model.hpp:652
char name[MAX_NAME_LENGTH]
joint identifier
Definition: MD5Model.hpp:648
MD5JointInfo & operator=(const MD5JointInfo &OTHER)
reassign object by copying an existing object
Definition: MD5Model.hpp:678
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:656
mesh that comprises the model's skin
Definition: MD5Model.hpp:492
GLint numTriangles
number of triangles in the mesh triangle array
Definition: MD5Model.hpp:545
MD5Texture textures[NUM_TEXTURES]
texture map array
Definition: MD5Model.hpp:516
TextureMap
named entities for different texture maps applied to the model
Definition: MD5Model.hpp:520
@ DIFFUSE
diffuse map
Definition: MD5Model.hpp:524
@ SPECULAR
specular map
Definition: MD5Model.hpp:528
@ NORMAL
normal map
Definition: MD5Model.hpp:532
MD5Mesh(MD5Mesh &&src) noexcept
construct a new mesh by moving an existing object
Definition: MD5Model.hpp:589
MD5Mesh()=default
construct a default mesh object
GLint numVertices
number of vertices in the mesh vertex array
Definition: MD5Model.hpp:541
MD5Mesh & operator=(MD5Mesh &&src) noexcept
reassign mesh object by moving an existing object
Definition: MD5Model.hpp:597
MD5Triangle * triangles
array triangles comprising the mesh
Definition: MD5Model.hpp:508
char shader[MAX_NAME_LENGTH]
base filename for all textures applied to mesh
Definition: MD5Model.hpp:554
MD5Vertex * vertices
array of vertices comprising the mesh
Definition: MD5Model.hpp:504
GLint numWeights
number of weights in the mesh weight array
Definition: MD5Model.hpp:549
~MD5Mesh()
deallocate member arrays
Definition: MD5Model.hpp:563
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:512
MD5Mesh(const MD5Mesh &OTHER)=delete
do not allow meshes to be copied
texture handle for the model
Definition: MD5Model.hpp:414
char filename[MAX_NAME_LENGTH]
filename texture was loaded from
Definition: MD5Model.hpp:426
MD5Texture & operator=(const MD5Texture &OTHER)
reassign texture object by copying an existing object
Definition: MD5Model.hpp:444
MD5Texture & operator=(MD5Texture &&src) noexcept
reassign texture object by moving an existing object
Definition: MD5Model.hpp:462
MD5Texture(const MD5Texture &OTHER)
construct a texture object by copying an existing object
Definition: MD5Model.hpp:436
GLuint texHandle
handle of texture stored on the GPU
Definition: MD5Model.hpp:422
MD5Texture()=default
constructs a default texture object
MD5Texture(MD5Texture &&src) noexcept
construct a texture object by moving an existing object
Definition: MD5Model.hpp:454
a triangle on the mesh
Definition: MD5Model.hpp:258
MD5Triangle & operator=(const MD5Triangle &OTHER)
reassign triangle object by copying an existing triangle
Definition: MD5Model.hpp:284
MD5Triangle(MD5Triangle &&src) noexcept
construct a triangle object by moving an existing triangle
Definition: MD5Model.hpp:294
MD5Triangle & operator=(MD5Triangle &&src) noexcept
reassign triangle object by moving an existing triangle
Definition: MD5Model.hpp:302
MD5Triangle(const MD5Triangle &OTHER)
construct a triangle by copying an existing triangle
Definition: MD5Model.hpp:276
MD5Triangle()=default
construct a default triangle object
GLint index[NUM_VERTICES]
vertex indices that make up triangle
Definition: MD5Model.hpp:266
a vertex on the mesh
Definition: MD5Model.hpp:178
MD5Vertex & operator=(const MD5Vertex &OTHER)
reassign existing vertex object by copying an existing vertex object
Definition: MD5Model.hpp:208
MD5Vertex & operator=(MD5Vertex &&src) noexcept
reassign existing vertex object by moving an existing vertex object
Definition: MD5Model.hpp:226
MD5Vertex(MD5Vertex &&src) noexcept
construct a vertex object by moving an existing vertex
Definition: MD5Model.hpp:218
glm::vec2 texCoord
texture coordinate for vertex
Definition: MD5Model.hpp:182
MD5Vertex(const MD5Vertex &OTHER)
construct a vertex object by copying an existing vertex
Definition: MD5Model.hpp:200
GLint start
index of starting weight
Definition: MD5Model.hpp:186
GLint count
number of weights that determine vertex's position
Definition: MD5Model.hpp:190
MD5Vertex()=default
construct a default vertex object
the weight for a mesh vertex
Definition: MD5Model.hpp:334
MD5Weight(MD5Weight &&src) noexcept
construct a weight object by moving an existing object
Definition: MD5Model.hpp:374
MD5Weight & operator=(MD5Weight &&src) noexcept
reassign an existing weight object by moving another
Definition: MD5Model.hpp:382
MD5Weight & operator=(const MD5Weight &OTHER)
reassign an existing weight object by copying another
Definition: MD5Model.hpp:364
MD5Weight(const MD5Weight &OTHER)
construct a weight object by copying an existing weight
Definition: MD5Model.hpp:356
GLfloat bias
contribution of the weight
Definition: MD5Model.hpp:342
GLint joint
index of joint the weight depends on
Definition: MD5Model.hpp:338
MD5Weight()=default
construct a default weight object
glm::vec3 position
weight's position in object space
Definition: MD5Model.hpp:346