CSCI441 OpenGL Library 5.13.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 "TextureUtils.hpp"
49
50#ifdef CSCI441_USE_GLEW
51 #include <GL/glew.h>
52#else
53 #include <glad/gl.h>
54#endif
55
56#include <glm/exponential.hpp>
57#include <glm/ext/quaternion_common.hpp>
58#include <glm/ext/quaternion_float.hpp>
59
60#define GLM_ENABLE_EXPERIMENTAL
61#include <glm/gtx/quaternion.hpp>
62
63#include <cassert>
64#include <cmath>
65#include <cstdio>
66#include <cstdlib>
67#include <cstring>
68
69namespace CSCI441 {
70
75 class [[maybe_unused]] MD5Model {
76 protected:
77 // md5mesh types
81 struct MD5Joint {
85 static constexpr GLint NULL_JOINT = -1;
89 char name[256] = "";
93 GLint parent = NULL_JOINT;
97 glm::vec3 position = {0.0f, 0.0f, 0.0f};
101 glm::quat orientation = {0.0f, 0.0f, 0.0f, 0.0f};
102 };
103
107 struct MD5Vertex {
111 glm::vec2 texCoord = {0.0f, 0.0f};
115 GLint start = 0;
119 GLint count = 0;
120 };
121
125 struct MD5Triangle {
129 GLint index[3] = {0};
130 };
131
135 struct MD5Weight {
139 GLint joint = MD5Joint::NULL_JOINT;
143 GLfloat bias = 0.f;
147 glm::vec3 position = {0.0f, 0.0f, 0.0f};
148 };
149
153 struct MD5Texture {
157 GLuint texHandle = 0;
161 char filename[512] = "";
162 };
163
167 struct MD5Mesh {
171 MD5Vertex *vertices = nullptr;
175 MD5Triangle *triangles = nullptr;
179 MD5Weight *weights = nullptr;
183 MD5Texture textures[4];
203 HEIGHT
204 };
205
209 GLint numVertices = 0;
213 GLint numTriangles = 0;
217 GLint numWeights = 0;
218
222 char shader[512] = "";
223 };
224
225 // md5anim types
233 char name[256] = "";
237 GLint parent = MD5Joint::NULL_JOINT;
241 GLuint flags = 0;
245 GLint startIndex = 0;
246 };
247
255 glm::vec3 position = {0.0f, 0.0f, 0.0f};
259 glm::quat orientation = {0.0f, 0.0f, 0.0f, 0.0f};
260 };
261
270 glm::vec3 min = {0.0f, 0.0f, 0.0f};
274 glm::vec3 max = {0.0f, 0.0f, 0.0f};
275 };
276
284 GLint numFrames = 0;
289 GLint numJoints = 0;
294 GLint frameRate = 0;
295
299 MD5Joint **skeletonFrames = nullptr;
303 MD5BoundingBox *boundingBoxes = nullptr;
304 };
305
313 GLint currFrame = 0;
317 GLint nextFrame = 0;
318
322 GLfloat lastTime = 0.0f;
327 GLfloat maxTime = 0.0f;
328 };
329
330 public:
335 MD5Model();
339 ~MD5Model();
340
344 MD5Model(const MD5Model&) = delete;
348 MD5Model& operator=(const MD5Model&) = delete;
349
353 MD5Model(MD5Model&&) = delete;
358
365 [[maybe_unused]] bool loadMD5Model(const char* MD5_MESH_FILE, const char* MD5_ANIM_FILE = "");
366
371 [[nodiscard]] bool isAnimated() const { return _isAnimated; }
372
373 // md5mesh prototypes
379 [[nodiscard]] bool readMD5Model(const char* FILENAME);
388 [[maybe_unused]]void allocVertexArrays(GLuint vPosAttribLoc, GLuint vColorAttribLoc, GLuint vTexCoordAttribLoc);
392 [[maybe_unused]] void draw() const;
396 [[maybe_unused]] void drawSkeleton() const;
397
398 // md5anim prototypes
404 [[nodiscard]] bool readMD5Anim(const char* filename);
409 void animate(GLfloat dt);
410
411 private:
412 MD5Joint* _baseSkeleton;
413 MD5Mesh* _meshes;
414
415 GLint _numJoints;
416 GLint _numMeshes;
417
418 // vertex array related stuff
419 GLint _maxVertices;
420 GLint _maxTriangles;
421
422 glm::vec3* _vertexArray;
423 glm::vec2* _texelArray;
424 GLuint* _vertexIndicesArray;
425
426 GLuint _vao;
427 GLuint _vbo[2];
428
429 GLuint _skeletonVAO;
430 GLuint _skeletonVBO;
431
435 MD5Joint* _skeleton;
439 MD5Animation _animation;
443 bool _isAnimated;
447 MD5AnimationState _animationInfo;
448
449 void _prepareMesh(const MD5Mesh* pMESH) const;
450 void _drawMesh(const MD5Mesh* pMESH) const;
451 [[nodiscard]] bool _checkAnimValidity() const;
452 static void _buildFrameSkeleton(const MD5JointInfo* pJOINT_INFOS,
453 const MD5BaseFrameJoint* pBASE_FRAME,
454 const GLfloat* pANIM_FRAME_DATA,
455 MD5Joint* pSkeletonFrame,
456 GLint NUM_JOINTS);
457 void _interpolateSkeletons(GLfloat interp);
458 void _freeModel();
459 void _freeVertexArrays();
460 void _freeAnim();
461 };
462}
463
464//----------------------------------------------------------------------------------------------------
465
467{
468 _baseSkeleton = nullptr;
469 _meshes = nullptr;
470 _numJoints = 0;
471 _numMeshes = 0;
472 _maxVertices = 0;
473 _maxTriangles = 0;
474 _vertexArray = nullptr;
475 _texelArray = nullptr;
476 _vertexIndicesArray = nullptr;
477 _vao = 0;
478 _vbo[0] = 0;
479 _vbo[1] = 0;
480 _skeletonVAO = 0;
481 _skeletonVBO = 0;
482 _animation = MD5Animation();
483 _skeleton = nullptr;
484 _animationInfo = MD5AnimationState();
485 _isAnimated = false;
486}
487
489{
490 _freeVertexArrays();
491 _freeAnim();
492 _freeModel();
493}
494
495// load our MD5 model
496[[maybe_unused]]
497inline bool
499 const char* MD5_MESH_FILE,
500 const char* MD5_ANIM_FILE
501) {
502 // Load MD5 _model file
503 if( readMD5Model(MD5_MESH_FILE) ) {
504 // if MD5 animation file name provided
505 if(strcmp(MD5_ANIM_FILE, "") != 0 ) {
506 // Load MD5 animation file
507 if( !readMD5Anim(MD5_ANIM_FILE) ) {
508 return false;
509 }
510 }
511 if( !isAnimated() ) {
512 printf ("[.MD5_ANIM_FILE]: no animation loaded.\n");
513 }
514 } else {
515 return false;
516 }
517 return true;
518}
519
520// Load an MD5 model from file.
521inline bool
523 const char* FILENAME
524) {
525 FILE *fp;
526 char buff[512];
527 GLint version;
528 GLint currentMesh = 0;
529 GLint i;
530 unsigned long uli;
531
532 GLint totalVertices = 0;
533 GLint totalWeights = 0;
534 GLint totalTriangles = 0;
535
536 GLfloat minX = 999999, minY = 999999, minZ = 999999;
537 GLfloat maxX = -999999, maxY = -999999, maxZ = -999999;
538
539 printf("[.md5mesh]: about to read %s\n", FILENAME );
540
541 fp = fopen(FILENAME, "rb" );
542 if( !fp ) {
543 fprintf (stderr, "[.md5mesh]: Error: couldn't open \"%s\"!\n", FILENAME);
544 return false;
545 }
546
547 while( !feof(fp) ) {
548 // Read whole line
549 fgets( buff, sizeof(buff), fp );
550
551 if( sscanf(buff, " MD5Version %d", &version) == 1 ) {
552 if( version != 10 ) {
553 // Bad version
554 fprintf (stderr, "[.md5mesh]: Error: bad model version\n");
555 fclose (fp);
556 return false;
557 }
558 } else if( sscanf(buff, " numJoints %d", &_numJoints) == 1 ) {
559 if( _numJoints > 0 ) {
560 // Allocate memory for base skeleton joints
561 _baseSkeleton = new MD5Joint[_numJoints];
562 }
563 } else if( sscanf(buff, " numMeshes %d", &_numMeshes) == 1 ) {
564 if( _numMeshes > 0 ) {
565 // Allocate memory for meshes
566 _meshes = new MD5Mesh[_numMeshes];
567 }
568 } else if( strncmp(buff, "joints {", 8) == 0 ) {
569 // Read each joint
570 for(i = 0; i < _numJoints; ++i) {
571 MD5Joint *joint = &_baseSkeleton[i];
572
573 // Read whole line
574 fgets( buff, sizeof(buff), fp );
575
576 if( sscanf(buff, "%s %d ( %f %f %f ) ( %f %f %f )",
577 joint->name, &joint->parent,
578 &joint->position[0], &joint->position[1], &joint->position[2],
579 &joint->orientation[0],&joint->orientation[1], &joint->orientation[2]) == 8
580 ) {
581 // Compute the w component
582 joint->orientation.w = glm::extractRealComponent(joint->orientation);
583 }
584 }
585 } else if( strncmp(buff, "mesh {", 6) == 0 ) {
586 MD5Mesh *mesh = &_meshes[currentMesh];
587 GLint vert_index = 0;
588 GLint tri_index = 0;
589 GLint weight_index = 0;
590 GLfloat fdata[4];
591 GLint idata[3];
592
593 while( buff[0] != '}' && !feof(fp) ) {
594 // Read whole line
595 fgets( buff, sizeof(buff), fp );
596
597 if( strstr( buff, "shader ") ) {
598 GLint quote = 0, j = 0;
599
600 // Copy the shader name without the quote marks
601 for(uli = 0; uli < sizeof(buff) && (quote < 2); ++uli) {
602 if( buff[uli] == '\"' )
603 quote++;
604
605 if( (quote == 1) && (buff[uli] != '\"') ) {
606 mesh->shader[j] = buff[uli];
607 j++;
608 }
609 }
610 // there was a shader name
611 if( j > 0 ) {
612 // diffuse map
613 strcpy(mesh->textures[MD5Mesh::TextureMap::DIFFUSE].filename, mesh->shader);
614 strcat(mesh->textures[MD5Mesh::TextureMap::DIFFUSE].filename, ".tga");
615 mesh->textures[MD5Mesh::TextureMap::DIFFUSE].texHandle = CSCI441::TextureUtils::loadAndRegisterTexture( mesh->textures[MD5Mesh::TextureMap::DIFFUSE].filename, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, GL_REPEAT, GL_REPEAT, GL_FALSE, GL_FALSE);
616 if( mesh->textures[MD5Mesh::TextureMap::DIFFUSE].texHandle == 0 ) {
617 strcpy(mesh->textures[MD5Mesh::TextureMap::DIFFUSE].filename, mesh->shader);
618 strcat(mesh->textures[MD5Mesh::TextureMap::DIFFUSE].filename, "_d.tga");
619 mesh->textures[MD5Mesh::TextureMap::DIFFUSE].texHandle = CSCI441::TextureUtils::loadAndRegisterTexture( mesh->textures[MD5Mesh::TextureMap::DIFFUSE].filename, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, GL_REPEAT, GL_REPEAT, GL_FALSE, GL_FALSE );
620 if( mesh->textures[MD5Mesh::TextureMap::DIFFUSE].texHandle == 0 ) {
621 strcpy(mesh->textures[MD5Mesh::TextureMap::DIFFUSE].filename, mesh->shader);
622 strcat(mesh->textures[MD5Mesh::TextureMap::DIFFUSE].filename, ".png");
623 mesh->textures[MD5Mesh::TextureMap::DIFFUSE].texHandle = CSCI441::TextureUtils::loadAndRegisterTexture(mesh->textures[MD5Mesh::TextureMap::DIFFUSE].filename, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, GL_REPEAT, GL_REPEAT, GL_FALSE, GL_FALSE );
624 if( mesh->textures[MD5Mesh::TextureMap::DIFFUSE].texHandle == 0 ) {
625 printf("[.md5mesh | ERROR]: Could not load diffuse map %s\n", mesh->shader);
626 }
627 }
628 }
629
630 // specular map
631 strcpy(mesh->textures[MD5Mesh::TextureMap::SPECULAR].filename, mesh->shader);
632 strcat(mesh->textures[MD5Mesh::TextureMap::SPECULAR].filename, "_s.tga");
633 mesh->textures[MD5Mesh::TextureMap::SPECULAR].texHandle = CSCI441::TextureUtils::loadAndRegisterTexture( mesh->textures[MD5Mesh::TextureMap::SPECULAR].filename, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, GL_REPEAT, GL_REPEAT, GL_FALSE, GL_FALSE );
634 if( mesh->textures[MD5Mesh::TextureMap::SPECULAR].texHandle == 0 ) {
635 strcpy(mesh->textures[MD5Mesh::TextureMap::SPECULAR].filename, mesh->shader);
636 strcat(mesh->textures[MD5Mesh::TextureMap::SPECULAR].filename, "_s.png");
637 mesh->textures[MD5Mesh::TextureMap::SPECULAR].texHandle = CSCI441::TextureUtils::loadAndRegisterTexture( mesh->textures[MD5Mesh::TextureMap::SPECULAR].filename, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, GL_REPEAT, GL_REPEAT, GL_FALSE, GL_FALSE );
638 if( mesh->textures[MD5Mesh::TextureMap::DIFFUSE].texHandle == 0 ) {
639 printf("[.md5mesh | ERROR]: Could not load specular map %s\n", mesh->shader);
640 }
641 }
642
643 // normal map
644 strcpy(mesh->textures[MD5Mesh::TextureMap::NORMAL].filename, mesh->shader);
645 strcat(mesh->textures[MD5Mesh::TextureMap::NORMAL].filename, "_local.tga");
646 mesh->textures[MD5Mesh::TextureMap::NORMAL].texHandle = CSCI441::TextureUtils::loadAndRegisterTexture( mesh->textures[MD5Mesh::TextureMap::NORMAL].filename, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, GL_REPEAT, GL_REPEAT, GL_FALSE, GL_FALSE );
647 if( mesh->textures[MD5Mesh::TextureMap::NORMAL].texHandle == 0 ) {
648 strcpy(mesh->textures[MD5Mesh::TextureMap::NORMAL].filename, mesh->shader);
649 strcat(mesh->textures[MD5Mesh::TextureMap::NORMAL].filename, "_local.png");
650 mesh->textures[MD5Mesh::TextureMap::NORMAL].texHandle = CSCI441::TextureUtils::loadAndRegisterTexture( mesh->textures[MD5Mesh::TextureMap::NORMAL].filename, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, GL_REPEAT, GL_REPEAT, GL_FALSE, GL_FALSE );
651 if( mesh->textures[MD5Mesh::TextureMap::DIFFUSE].texHandle == 0 ) {
652 printf("[.md5mesh | ERROR]: Could not load normal map %s\n", mesh->shader);
653 }
654 }
655
656 // height map
657 strcpy(mesh->textures[MD5Mesh::TextureMap::HEIGHT].filename, mesh->shader);
658 strcat(mesh->textures[MD5Mesh::TextureMap::HEIGHT].filename, "_h.tga");
659 mesh->textures[MD5Mesh::TextureMap::HEIGHT].texHandle = CSCI441::TextureUtils::loadAndRegisterTexture( mesh->textures[MD5Mesh::TextureMap::HEIGHT].filename, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, GL_REPEAT, GL_REPEAT, GL_FALSE, GL_FALSE );
660 if( mesh->textures[MD5Mesh::TextureMap::HEIGHT].texHandle == 0 ) {
661 strcpy(mesh->textures[MD5Mesh::TextureMap::HEIGHT].filename, mesh->shader);
662 strcat(mesh->textures[MD5Mesh::TextureMap::HEIGHT].filename, "_h.png");
663 mesh->textures[MD5Mesh::TextureMap::HEIGHT].texHandle = CSCI441::TextureUtils::loadAndRegisterTexture( mesh->textures[MD5Mesh::TextureMap::HEIGHT].filename, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, GL_REPEAT, GL_REPEAT, GL_FALSE, GL_FALSE );
664 if( mesh->textures[MD5Mesh::TextureMap::DIFFUSE].texHandle == 0 ) {
665 printf("[.md5mesh | ERROR]: Could not load height map %s\n", mesh->shader);
666 }
667 }
668 }
669 } else if( sscanf(buff, " numverts %d", &mesh->numVertices) == 1 ) {
670 if( mesh->numVertices > 0 ) {
671 // Allocate memory for vertices
672 mesh->vertices = new MD5Vertex[mesh->numVertices];
673 }
674
675 if( mesh->numVertices > _maxVertices )
676 _maxVertices = mesh->numVertices;
677
678 totalVertices += mesh->numVertices;
679 } else if( sscanf(buff, " numtris %d", &mesh->numTriangles) == 1 ) {
680 if( mesh->numTriangles > 0 ) {
681 // Allocate memory for triangles
682 mesh->triangles = new MD5Triangle[mesh->numTriangles];
683 }
684
685 if( mesh->numTriangles > _maxTriangles )
686 _maxTriangles = mesh->numTriangles;
687
688 totalTriangles += mesh->numTriangles;
689 } else if( sscanf(buff, " numweights %d", &mesh->numWeights) == 1 ) {
690 if( mesh->numWeights > 0 ) {
691 // Allocate memory for vertex weights
692 mesh->weights = new MD5Weight[mesh->numWeights];
693 }
694
695 totalWeights += mesh->numWeights;
696 } else if( sscanf(buff, " vert %d ( %f %f ) %d %d",
697 &vert_index,
698 &fdata[0], &fdata[1],
699 &idata[0], &idata[1]) == 5
700 ) {
701 // Copy vertex data
702 mesh->vertices[vert_index].texCoord.s = fdata[0];
703 mesh->vertices[vert_index].texCoord.t = fdata[1];
704 mesh->vertices[vert_index].start = idata[0];
705 mesh->vertices[vert_index].count = idata[1];
706 } else if( sscanf(buff, " tri %d %d %d %d",
707 &tri_index,
708 &idata[0], &idata[1], &idata[2]) == 4
709 ) {
710 // Copy triangle data
711 mesh->triangles[tri_index ].index[0] = idata[0];
712 mesh->triangles[tri_index ].index[1] = idata[1];
713 mesh->triangles[tri_index ].index[2] = idata[2];
714 } else if( sscanf(buff, " weight %d %d %f ( %f %f %f )",
715 &weight_index, &idata[0], &fdata[3],
716 &fdata[0], &fdata[1], &fdata[2]) == 6
717 ) {
718 // Copy vertex data
719 mesh->weights[weight_index].joint = idata[0];
720 mesh->weights[weight_index].bias = fdata[3];
721 mesh->weights[weight_index].position[0] = fdata[0];
722 mesh->weights[weight_index].position[1] = fdata[1];
723 mesh->weights[weight_index].position[2] = fdata[2];
724
725 if( fdata[0] < minX ) { minX = fdata[0]; }
726 if( fdata[0] > maxX ) { maxX = fdata[0]; }
727 if( fdata[1] < minY ) { minY = fdata[1]; }
728 if( fdata[1] > maxY ) { maxY = fdata[1]; }
729 if( fdata[2] < minZ ) { minZ = fdata[2]; }
730 if( fdata[2] > maxZ ) { maxZ = fdata[2]; }
731 }
732 }
733
734 currentMesh++;
735 }
736 }
737
738 fclose(fp);
739
740 _skeleton = _baseSkeleton;
741
742 printf("[.md5mesh]: finished reading %s\n", FILENAME );
743 printf("[.md5mesh]: read in %d meshes, %d joints, %d vertices, %d weights, and %d triangles\n", _numMeshes, _numJoints, totalVertices, totalWeights, totalTriangles );
744 printf( "[.md5mesh]: base pose %f units across in X, %f units across in Y, %f units across in Z\n", (maxX - minX), (maxY-minY), (maxZ - minZ) );
745 printf( "\n" );
746
747 return true;
748}
749
750//Free resources allocated for the model.
751inline void
752CSCI441::MD5Model::_freeModel()
753{
754 delete _baseSkeleton;
755 _baseSkeleton = nullptr;
756
757 // Free mesh data
758 for(GLint i = 0; i < _numMeshes; ++i) {
759 delete _meshes[i].vertices;
760 _meshes[i].vertices = nullptr;
761
762 delete _meshes[i].triangles;
763 _meshes[i].triangles = nullptr;
764
765 delete _meshes[i].weights;
766 _meshes[i].weights = nullptr;
767 }
768
769 delete _meshes;
770 _meshes = nullptr;
771}
772
773[[maybe_unused]]
774inline void
776{
777 // Draw each mesh of the model
778 for(GLint i = 0; i < _numMeshes; ++i) {
779 MD5Mesh mesh = _meshes[i]; // get the mesh
780 _prepareMesh(&mesh); // do some preprocessing on it
781 _drawMesh(&mesh);
782 }
783}
784
785// Prepare a mesh for drawing. Compute mesh's final vertex positions
786// given a skeleton. Put the vertices in vertex arrays.
787inline void
788CSCI441::MD5Model::_prepareMesh(
789 const MD5Mesh *pMESH
790) const {
791 GLint i, j, k;
792
793 // Setup vertex indices
794 for(k = 0, i = 0; i < pMESH->numTriangles; ++i) {
795 for(j = 0; j < 3; ++j, ++k)
796 _vertexIndicesArray[k] = pMESH->triangles[i].index[j];
797 }
798
799 // Setup vertices
800 for(i = 0; i < pMESH->numVertices; ++i) {
801 glm::vec3 finalVertex = {0.0f, 0.0f, 0.0f };
802
803 // Calculate final vertex to draw with weights
804 for(j = 0; j < pMESH->vertices[i].count; ++j) {
805 const MD5Weight *weight = &pMESH->weights[pMESH->vertices[i].start + j];
806 const MD5Joint *joint = &_skeleton[weight->joint];
807
808 // Calculate transformed vertex for this weight
809 glm::vec3 weightedVertex;
810 weightedVertex = glm::rotate(joint->orientation, glm::vec4(weight->position, 0.0f));
811
812 // The sum of all weight->bias should be 1.0
813 finalVertex.x += (joint->position.x + weightedVertex.x) * weight->bias;
814 finalVertex.y += (joint->position.y + weightedVertex.y) * weight->bias;
815 finalVertex.z += (joint->position.z + weightedVertex.z) * weight->bias;
816 }
817
818 _vertexArray[i].x = finalVertex.x;
819 _vertexArray[i].y = finalVertex.y;
820 _vertexArray[i].z = finalVertex.z;
821
822 _texelArray[i].s = pMESH->vertices[i].texCoord.s;
823 _texelArray[i].t = pMESH->vertices[i].texCoord.t;
824 }
825
826 glBindVertexArray(_vao );
827 glBindBuffer(GL_ARRAY_BUFFER, _vbo[0] );
828 glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(glm::vec3) * pMESH->numVertices, &_vertexArray[0] );
829 glBufferSubData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * _maxVertices, sizeof(glm::vec2) * pMESH->numVertices, &_texelArray[0] );
830 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vbo[1] );
831 glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(GLuint) * pMESH->numTriangles * 3, _vertexIndicesArray );
832}
833
834inline void
835CSCI441::MD5Model::_drawMesh(
836 const MD5Mesh *pMESH
837) const {
838 // Bind Diffuse Map
839 glBindTexture(GL_TEXTURE_2D, pMESH->textures[MD5Mesh::TextureMap::DIFFUSE].texHandle );
840
841 glBindVertexArray(_vao );
842 glDrawElements(GL_TRIANGLES, pMESH->numTriangles * 3, GL_UNSIGNED_INT, (void*)nullptr );
843}
844
845[[maybe_unused]]
846inline void
848 GLuint vPosAttribLoc,
849 GLuint vColorAttribLoc,
850 GLuint vTexCoordAttribLoc
851) {
852 _vertexArray = new glm::vec3[_maxVertices];
853 _texelArray = new glm::vec2[_maxVertices];
854 _vertexIndicesArray = new GLuint[_maxTriangles * 3];
855
856 glGenVertexArrays( 1, &_vao );
857 glBindVertexArray(_vao );
858
859 glGenBuffers(2, _vbo );
860 glBindBuffer(GL_ARRAY_BUFFER, _vbo[0] );
861 glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * _maxVertices + sizeof(glm::vec2) * _maxVertices, nullptr, GL_DYNAMIC_DRAW );
862
863 glEnableVertexAttribArray( vPosAttribLoc );
864 glVertexAttribPointer( vPosAttribLoc, 3, GL_FLOAT, GL_FALSE, 0, (void*)nullptr );
865
866 glEnableVertexAttribArray( vTexCoordAttribLoc );
867 glVertexAttribPointer( vTexCoordAttribLoc, 2, GL_FLOAT, GL_FALSE, 0, (void*)(sizeof(glm::vec3) * _maxVertices) );
868
869 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vbo[1] );
870 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * _maxTriangles * 3, nullptr, GL_DYNAMIC_DRAW );
871
872 printf("[.md5mesh]: Model VAO/VBO/IBO registered at %u/%u/%u\n", _vao, _vbo[0], _vbo[1] );
873
874 glGenVertexArrays( 1, &_skeletonVAO );
875 glBindVertexArray(_skeletonVAO );
876
877 glGenBuffers( 1, &_skeletonVBO );
878 glBindBuffer(GL_ARRAY_BUFFER, _skeletonVBO );
879 glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * _numJoints * 3 * 2, nullptr, GL_DYNAMIC_DRAW );
880
881 glEnableVertexAttribArray( vPosAttribLoc ); // vPos
882 glVertexAttribPointer( vPosAttribLoc, 3, GL_FLOAT, GL_FALSE, 0, (void*)nullptr );
883
884 glEnableVertexAttribArray( vColorAttribLoc ); // vColor
885 glVertexAttribPointer( vColorAttribLoc, 3, GL_FLOAT, GL_FALSE, 0, (void*)(sizeof(glm::vec3) * _numJoints * 3) );
886
887 printf("[.md5mesh]: Skeleton VAO/VBO registered at %u/%u\n", _skeletonVAO, _skeletonVBO );
888}
889
890inline void
891CSCI441::MD5Model::_freeVertexArrays()
892{
893 delete[] _vertexArray;
894 _vertexArray = nullptr;
895
896 delete[] _vertexIndicesArray;
897 _vertexIndicesArray = nullptr;
898
899 delete[] _texelArray;
900 _texelArray = nullptr;
901
902 glDeleteVertexArrays( 1, &_vao );
903 glDeleteBuffers(2, _vbo );
904 glDeleteVertexArrays( 1, &_skeletonVAO );
905 glDeleteBuffers( 1, &_skeletonVBO );
906}
907
908// Draw the skeleton as lines and points (for joints).
909[[maybe_unused]]
910inline void
912{
913 glBindVertexArray(_skeletonVAO );
914 glBindBuffer(GL_ARRAY_BUFFER, _skeletonVBO );
915
916 glm::vec3 jointColor = {1.0f, 1.0f, 0.0f };
917 glm::vec3 boneColor = {1.0f, 0.0f, 1.0f };
918
919 // put in points for joints
920 for(GLint i = 0; i < _numJoints; ++i ) {
921 glBufferSubData(GL_ARRAY_BUFFER, i * sizeof(glm::vec3), sizeof(glm::vec3), &(_skeleton[i].position) );
922 glBufferSubData(GL_ARRAY_BUFFER, i * sizeof(glm::vec3) + sizeof(glm::vec3) * _numJoints * 3, sizeof(glm::vec3), &jointColor[0]);
923 }
924
925 // put in lines for bones
926 GLint numBones = 0;
927 for(GLint i = 0; i < _numJoints; ++i ) {
928 if( _skeleton[i].parent != MD5Joint::NULL_JOINT ) {
929 glBufferSubData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * _numJoints + (i * 2) * sizeof(glm::vec3), sizeof(glm::vec3), &(_skeleton[_skeleton[i].parent].position) );
930 glBufferSubData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * _numJoints + (i * 2) * sizeof(glm::vec3) + sizeof(glm::vec3) * _numJoints * 3, sizeof(glm::vec3), &boneColor[0]);
931
932 glBufferSubData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * _numJoints + (i * 2) * sizeof(glm::vec3) + sizeof(glm::vec3), sizeof(glm::vec3), &(_skeleton[i].position) );
933 glBufferSubData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * _numJoints + (i * 2) * sizeof(glm::vec3) + sizeof(glm::vec3) + sizeof(glm::vec3) * _numJoints * 3, sizeof(glm::vec3), &boneColor[0]);
934 numBones++;
935 }
936 }
937
938 glPointSize (5.0f);
939 glDrawArrays(GL_POINTS, 0, _numJoints );
940 glPointSize(1.0f);
941
942 glLineWidth( 3.0f );
943 glDrawArrays(GL_LINES, _numJoints, numBones * 2 );
944 glLineWidth(1.0f);
945}
946
947// Check if an animation can be used for a given model. Model's
948// skeleton and animation's skeleton must match.
949inline bool
950CSCI441::MD5Model::_checkAnimValidity() const
951{
952 // md5mesh and md5anim must have the same number of joints
953 if( _numJoints != _animation.numJoints ) {
954 printf("\n[.md5anim]: skeleton and animation do not have same number of joints. cannot apply animation to skeleton\n\n");
955 return false;
956 }
957
958 // We just check with frame[0]
959 for(GLint i = 0; i < _numJoints; ++i) {
960 // Joints must have the same parent index
961 if (_baseSkeleton[i].parent != _animation.skeletonFrames[0][i].parent) {
962 printf("\n[.md5anim]: skeleton and animation joints do not have same parent index. cannot apply animation to skeleton\n\n");
963 return false;
964 }
965
966 // Joints must have the same name
967 if (strcmp (_baseSkeleton[i].name, _animation.skeletonFrames[0][i].name) != 0) {
968 printf("\n[.md5anim]: skeleton and animation joints do not have same name. cannot apply animation to skeleton\n\n");
969 return false;
970 }
971 }
972
973 printf("\n[.md5anim]: skeleton and animation match. animation can be applied to skeleton\n\n");
974 return true;
975}
976
977// Build _skeleton for a given frame data.
978inline void
979CSCI441::MD5Model::_buildFrameSkeleton(
980 const MD5JointInfo* pJOINT_INFOS,
981 const MD5BaseFrameJoint* pBASE_FRAME,
982 const GLfloat* pANIM_FRAME_DATA,
983 MD5Joint* pSkeletonFrame,
984 const GLint NUM_JOINTS
985) {
986 if(pJOINT_INFOS == nullptr
987 || pBASE_FRAME == nullptr
988 || pANIM_FRAME_DATA == nullptr
989 || pSkeletonFrame == nullptr) return;
990
991 GLint i;
992
993 for(i = 0; i < NUM_JOINTS; ++i) {
994 const MD5BaseFrameJoint *baseJoint = &pBASE_FRAME[i];
995 glm::vec3 animatedPosition = baseJoint->position;
996 glm::quat animatedOrientation = baseJoint->orientation;
997 GLint j = 0;
998
999 // Tx
1000 if(pJOINT_INFOS[i].flags & 1 ) {
1001 animatedPosition.x = pANIM_FRAME_DATA[pJOINT_INFOS[i].startIndex + j];
1002 ++j;
1003 }
1004
1005 // Ty
1006 if(pJOINT_INFOS[i].flags & 2 ) {
1007 animatedPosition.y = pANIM_FRAME_DATA[pJOINT_INFOS[i].startIndex + j];
1008 ++j;
1009 }
1010
1011 // Tz
1012 if(pJOINT_INFOS[i].flags & 4 ) {
1013 animatedPosition.z = pANIM_FRAME_DATA[pJOINT_INFOS[i].startIndex + j];
1014 ++j;
1015 }
1016
1017 // Qx
1018 if(pJOINT_INFOS[i].flags & 8 ) {
1019 animatedOrientation.x = pANIM_FRAME_DATA[pJOINT_INFOS[i].startIndex + j];
1020 ++j;
1021 }
1022
1023 // Qy
1024 if(pJOINT_INFOS[i].flags & 16 ) {
1025 animatedOrientation.y = pANIM_FRAME_DATA[pJOINT_INFOS[i].startIndex + j];
1026 ++j;
1027 }
1028
1029 // Qz
1030 if(pJOINT_INFOS[i].flags & 32 ) {
1031 animatedOrientation.z = pANIM_FRAME_DATA[pJOINT_INFOS[i].startIndex + j];
1032 }
1033
1034 // Compute orientation quaternion's w value
1035 animatedOrientation.w = glm::extractRealComponent(animatedOrientation);
1036
1037 // NOTE: we assume that this joint's parent has
1038 // already been calculated, i.e. joint's ID should
1039 // never be smaller than its parent ID.
1040 MD5Joint *thisJoint = &pSkeletonFrame[i];
1041
1042 GLint parent = pJOINT_INFOS[i].parent;
1043 thisJoint->parent = parent;
1044 strcpy (thisJoint->name, pJOINT_INFOS[i].name);
1045
1046 // Has parent?
1047 if( thisJoint->parent == MD5Joint::NULL_JOINT ) {
1048 thisJoint->position = animatedPosition;
1049 thisJoint->orientation = animatedOrientation;
1050 } else {
1051 MD5Joint *parentJoint = &pSkeletonFrame[parent];
1052 glm::vec3 rotatedPosition = glm::rotate(parentJoint->orientation, glm::vec4(animatedPosition, 0.0f));
1053
1054 // Add positions
1055 thisJoint->position = parentJoint->position + rotatedPosition;
1056
1057 // Concatenate rotations
1058 thisJoint->orientation = glm::normalize( glm::cross(parentJoint->orientation, animatedOrientation) );
1059 }
1060 }
1061}
1062
1063// Load an MD5 animation from file.
1064inline bool
1066 const char *filename
1067) {
1068 char buff[512];
1069 MD5JointInfo *jointInfos = nullptr;
1070 MD5BaseFrameJoint *baseFrame = nullptr;
1071 GLfloat *animFrameData = nullptr;
1072 GLint version;
1073 GLint numAnimatedComponents;
1074 GLint frameIndex;
1075 GLint i;
1076
1077 printf( "[.md5anim]: about to read %s\n", filename );
1078
1079 FILE *fp = fopen( filename, "rb" );
1080 if( !fp ) {
1081 fprintf (stderr, "[.md5anim]: Error: couldn't open \"%s\"!\n", filename);
1082 return false;
1083 }
1084
1085 while( !feof(fp) ) {
1086 // Read whole line
1087 fgets( buff, sizeof(buff), fp );
1088
1089 if( sscanf(buff, " MD5Version %d", &version) == 1 ) {
1090 if( version != 10 ) {
1091 // Bad version
1092 fprintf (stderr, "[.md5anim]: Error: bad animation version\n");
1093 fclose (fp);
1094 return false;
1095 }
1096 } else if( sscanf(buff, " numFrames %d", &_animation.numFrames) == 1 ) {
1097 // Allocate memory for skeleton frames and bounding boxes
1098 if( _animation.numFrames > 0 ) {
1099 _animation.skeletonFrames = new MD5Joint*[_animation.numFrames];
1100 _animation.boundingBoxes = new MD5BoundingBox[_animation.numFrames];
1101 }
1102 } else if( sscanf(buff, " numJoints %d", &_animation.numJoints) == 1 ) {
1103 if( _animation.numJoints > 0 ) {
1104 for(i = 0; i < _animation.numFrames; ++i) {
1105 // Allocate memory for joints of each frame
1106 _animation.skeletonFrames[i] = new MD5Joint[_animation.numJoints];
1107 }
1108
1109 // Allocate temporary memory for building skeleton frames
1110 jointInfos = new MD5JointInfo[_animation.numJoints];
1111
1112 baseFrame = new MD5BaseFrameJoint[_animation.numJoints];
1113 }
1114 } else if( sscanf(buff, " frameRate %d", &_animation.frameRate) == 1 ) {
1115
1116 } else if( sscanf(buff, " numAnimatedComponents %d", &numAnimatedComponents) == 1 ) {
1117 if( numAnimatedComponents > 0 ) {
1118 // Allocate memory for animation frame data
1119 animFrameData = new GLfloat[numAnimatedComponents];
1120 }
1121 } else if( strncmp(buff, "hierarchy {", 11) == 0 ) {
1122 for(i = 0; i < _animation.numJoints; ++i) {
1123 // Read whole line
1124 fgets( buff, sizeof(buff), fp );
1125
1126 // Read joint info
1127 sscanf(buff, " %s %d %d %d",
1128 jointInfos[i].name, &jointInfos[i].parent,
1129 &jointInfos[i].flags, &jointInfos[i].startIndex);
1130 }
1131 } else if( strncmp(buff, "bounds {", 8) == 0 ) {
1132 for(i = 0; i < _animation.numFrames; ++i) {
1133 // Read whole line
1134 fgets( buff, sizeof(buff), fp );
1135
1136 // Read bounding box
1137 sscanf(buff, " ( %f %f %f ) ( %f %f %f )",
1138 &_animation.boundingBoxes[i].min[0], &_animation.boundingBoxes[i].min[1], &_animation.boundingBoxes[i].min[2],
1139 &_animation.boundingBoxes[i].max[0], &_animation.boundingBoxes[i].max[1], &_animation.boundingBoxes[i].max[2]);
1140 }
1141 } else if( strncmp(buff, "baseframe {", 10) == 0 ) {
1142 for(i = 0; i < _animation.numJoints; ++i) {
1143 // Read whole line
1144 fgets( buff, sizeof(buff), fp );
1145
1146 // Read base frame joint
1147 if( sscanf(buff, " ( %f %f %f ) ( %f %f %f )",
1148 &baseFrame[i].position[0], &baseFrame[i].position[1], &baseFrame[i].position[2],
1149 &baseFrame[i].orientation[0], &baseFrame[i].orientation[1], &baseFrame[i].orientation[2]) == 6 ) {
1150 // Compute the w component
1151 baseFrame[i].orientation.w = glm::extractRealComponent(baseFrame[i].orientation);
1152 }
1153 }
1154 } else if(sscanf(buff, " frame %d", &frameIndex) == 1 ) {
1155 // Read frame data
1156 for(i = 0; i < numAnimatedComponents; ++i)
1157 fscanf( fp, "%f", &animFrameData[i] );
1158
1159 // Build frame _skeleton from the collected data
1160 _buildFrameSkeleton(jointInfos, baseFrame, animFrameData,
1161 _animation.skeletonFrames[frameIndex],
1162 _animation.numJoints);
1163 }
1164 }
1165
1166 fclose( fp );
1167
1168 printf( "[.md5anim]: finished reading %s\n", filename );
1169 printf( "[.md5anim]: read in %d frames of %d joints with %d animated components\n", _animation.numFrames, _animation.numJoints, numAnimatedComponents );
1170 printf( "[.md5anim]: animation's frame rate is %d\n", _animation.frameRate );
1171
1172 // Free temporary data allocated
1173 if( animFrameData )
1174 free( animFrameData );
1175
1176 if( baseFrame )
1177 free( baseFrame );
1178
1179 if( jointInfos )
1180 free( jointInfos );
1181
1182 // successful loading...set up animation parameters
1183 _animationInfo.currFrame = 0;
1184 _animationInfo.nextFrame = 1;
1185
1186 _animationInfo.lastTime = 0.0f;
1187 _animationInfo.maxTime = 1.0f / (GLfloat)_animation.frameRate;
1188
1189 // Allocate memory for animated _skeleton
1190 _skeleton = new MD5Joint[_animation.numJoints];
1191
1192 if( _checkAnimValidity() ) {
1193 _isAnimated = true;
1194 // compute initial pose
1195 animate(0.0);
1196 }
1197
1198 return true;
1199}
1200
1201// Free resources allocated for the animation.
1202inline void
1203CSCI441::MD5Model::_freeAnim()
1204{
1205 GLint i;
1206
1207 for(i = 0; i < _animation.numFrames; ++i) {
1208 delete _animation.skeletonFrames[i];
1209 _animation.skeletonFrames[i] = nullptr;
1210 }
1211
1212 delete[] _animation.skeletonFrames;
1213 _animation.skeletonFrames = nullptr;
1214
1215 delete _animation.boundingBoxes;
1216 _animation.boundingBoxes = nullptr;
1217
1218 delete _skeleton;
1219 _skeleton = nullptr;
1220}
1221
1222// Smoothly interpolate two skeletons
1223inline void
1224CSCI441::MD5Model::_interpolateSkeletons(GLfloat interp)
1225{
1226 const MD5Joint *skeletonA = _animation.skeletonFrames[_animationInfo.currFrame];
1227 const MD5Joint *skeletonB = _animation.skeletonFrames[_animationInfo.nextFrame];
1228
1229 GLint i;
1230
1231 for(i = 0; i < _animation.numJoints; ++i) {
1232 // Copy parent index
1233 _skeleton[i].parent = skeletonA[i].parent;
1234
1235 // Linear interpolation for position
1236 _skeleton[i].position[0] = skeletonA[i].position[0] + interp * (skeletonB[i].position[0] - skeletonA[i].position[0]);
1237 _skeleton[i].position[1] = skeletonA[i].position[1] + interp * (skeletonB[i].position[1] - skeletonA[i].position[1]);
1238 _skeleton[i].position[2] = skeletonA[i].position[2] + interp * (skeletonB[i].position[2] - skeletonA[i].position[2]);
1239
1240 // Spherical linear interpolation for orientation
1241 _skeleton[i].orientation = glm::slerp(skeletonA[i].orientation, skeletonB[i].orientation, interp);
1242 }
1243}
1244
1245// Perform animation related computations. Calculate the current and
1246// next frames, given a delta time.
1247inline void
1249{
1250 GLint maxFrames = _animation.numFrames - 1;
1251
1252 _animationInfo.lastTime += dt;
1253
1254 // move to next frame
1255 if( _animationInfo.lastTime >= _animationInfo.maxTime ) {
1256 _animationInfo.currFrame++;
1257 _animationInfo.nextFrame++;
1258 _animationInfo.lastTime = 0.0;
1259
1260 if( _animationInfo.currFrame > maxFrames )
1261 _animationInfo.currFrame = 0;
1262
1263 if( _animationInfo.nextFrame > maxFrames )
1264 _animationInfo.nextFrame = 0;
1265 }
1266
1267 // Interpolate skeletons between two frames
1268 _interpolateSkeletons( _animationInfo.lastTime * _animation.frameRate );
1269}
1270
1271#endif//CSCI441_MD5_MODEL_HPP
Helper functions to work with OpenGL Textures.
stores a Doom3 MD5 Mesh + Animation
Definition: MD5Model.hpp:75
MD5Model(MD5Model &&)=delete
do not allow MD5 models to be moved
void draw() const
draws all the meshes that make up the model
Definition: MD5Model.hpp:775
MD5Model & operator=(MD5Model &&)=delete
do not allow MD5 models to be moved
bool readMD5Model(const char *FILENAME)
parses md5mesh file and allocates corresponding mesh data
Definition: MD5Model.hpp:522
bool isAnimated() const
returns if the MD5 Model has an accompanying animation
Definition: MD5Model.hpp:371
bool loadMD5Model(const char *MD5_MESH_FILE, const char *MD5_ANIM_FILE="")
loads a corresponding md5mesh and md5anim file to the object
Definition: MD5Model.hpp:498
MD5Model()
initializes an empty MD5 Model
Definition: MD5Model.hpp:466
MD5Model & operator=(const MD5Model &)=delete
do not allow MD5 models to be copied
bool readMD5Anim(const char *filename)
reads in an animation sequence from an external file
Definition: MD5Model.hpp:1065
void allocVertexArrays(GLuint vPosAttribLoc, GLuint vColorAttribLoc, GLuint vTexCoordAttribLoc)
binds model VBOs to attribute pointer locations
Definition: MD5Model.hpp:847
void animate(GLfloat dt)
advances the model forward in its animation sequence the corresponding amount of time based on frame ...
Definition: MD5Model.hpp:1248
MD5Model(const MD5Model &)=delete
do not allow MD5 models to be copied
~MD5Model()
deallocates any used memory on the CPU and GPU
Definition: MD5Model.hpp:488
void drawSkeleton() const
draws the skeleton joints (as points) and bones (as lines)
Definition: MD5Model.hpp:911
CSCI441 Helper Functions for OpenGL.
Definition: ArcballCam.hpp:17
stores an entire animation sequence for a given MD5 Model
Definition: MD5Model.hpp:280
stores state of current animation frame
Definition: MD5Model.hpp:309
base frame joint
Definition: MD5Model.hpp:251
glm::quat orientation
joint orientation expressed as a quaternion in object space
Definition: MD5Model.hpp:259
bounding box containing the model during animation
Definition: MD5Model.hpp:266
a joint of the MD5 Skeleton
Definition: MD5Model.hpp:81
glm::vec3 position
position of the joint in object space
Definition: MD5Model.hpp:97
glm::quat orientation
joint orientation expressed as a quaternion in object space
Definition: MD5Model.hpp:101
char name[256]
joint identifier
Definition: MD5Model.hpp:89
GLint parent
index of the parent joint on skeletal tree
Definition: MD5Model.hpp:93
information pertaining to each animation joint
Definition: MD5Model.hpp:229
mesh that comprises the model's skin
Definition: MD5Model.hpp:167
GLint numTriangles
number of triangles in the mesh triangle array
Definition: MD5Model.hpp:213
TextureMap
named entities for different texture maps applied to the model
Definition: MD5Model.hpp:187
@ DIFFUSE
diffuse map
Definition: MD5Model.hpp:191
@ SPECULAR
specular map
Definition: MD5Model.hpp:195
@ NORMAL
normal map
Definition: MD5Model.hpp:199
MD5Texture textures[4]
texture map array
Definition: MD5Model.hpp:183
GLint numVertices
number of vertices in the mesh vertex array
Definition: MD5Model.hpp:209
MD5Triangle * triangles
array triangles comprising the mesh
Definition: MD5Model.hpp:175
MD5Vertex * vertices
array of vertices comprising the mesh
Definition: MD5Model.hpp:171
GLint numWeights
number of weights in the mesh weight array
Definition: MD5Model.hpp:217
MD5Weight * weights
array of weights to determine vertex position based on joint positions
Definition: MD5Model.hpp:179
char shader[512]
base filename for all textures applied to mesh
Definition: MD5Model.hpp:222
texture handle for the model
Definition: MD5Model.hpp:153
GLuint texHandle
handle of texture stored on the GPU
Definition: MD5Model.hpp:157
char filename[512]
filename texture was loaded from
Definition: MD5Model.hpp:161
a triangle on the mesh
Definition: MD5Model.hpp:125
GLint index[3]
vertex indices that make up triangle
Definition: MD5Model.hpp:129
a vertex on the mesh
Definition: MD5Model.hpp:107
glm::vec2 texCoord
texture coordinate for vertex
Definition: MD5Model.hpp:111
GLint start
index of starting weight
Definition: MD5Model.hpp:115
GLint count
number of weights that determine vertex's position
Definition: MD5Model.hpp:119
the weight for a mesh vertex
Definition: MD5Model.hpp:135
GLfloat bias
contribution of the weight
Definition: MD5Model.hpp:143
GLint joint
index of joint the weight depends on
Definition: MD5Model.hpp:139
glm::vec3 position
weight's position in object space
Definition: MD5Model.hpp:147