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