summaryrefslogtreecommitdiffhomepage
path: root/examples/others/iqm_loader/riqm.h
diff options
context:
space:
mode:
Diffstat (limited to 'examples/others/iqm_loader/riqm.h')
-rw-r--r--examples/others/iqm_loader/riqm.h1259
1 files changed, 1259 insertions, 0 deletions
diff --git a/examples/others/iqm_loader/riqm.h b/examples/others/iqm_loader/riqm.h
new file mode 100644
index 00000000..307eaac8
--- /dev/null
+++ b/examples/others/iqm_loader/riqm.h
@@ -0,0 +1,1259 @@
+/**********************************************************************************************
+*
+* riqm - InterQuake Model format (IQM) loader for animated meshes
+*
+* CONFIGURATION:
+*
+* #define RIQM_IMPLEMENTATION
+* Generates the implementation of the library into the included file.
+* If not defined, the library is in header only mode and can be included in other headers
+* or source files without problems. But only ONE file should hold the implementation.
+*
+*
+* LICENSE: zlib/libpng
+*
+* Copyright (c) 2018 Jonas Daeyaert (@culacant) and Ramon Santamaria (@raysan5)
+*
+* This software is provided "as-is", without any express or implied warranty. In no event
+* will the authors be held liable for any damages arising from the use of this software.
+*
+* Permission is granted to anyone to use this software for any purpose, including commercial
+* applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+* 1. The origin of this software must not be misrepresented; you must not claim that you
+* wrote the original software. If you use this software in a product, an acknowledgment
+* in the product documentation would be appreciated but is not required.
+*
+* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
+* as being the original software.
+*
+* 3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+#ifndef RIQM_H
+#define RIQM_H
+
+// TODO dont break everything
+static bool vaoSupported = false;
+static Matrix modelview;
+static Matrix projection;
+
+#include <stdio.h> // Required for: FILE
+
+//#define RIQM_STATIC
+#ifdef RIQM_STATIC
+ #define RIQMDEF static // Functions just visible to module including this file
+#else
+ #ifdef __cplusplus
+ #define RIQMDEF extern "C" // Functions visible from other files (no name mangling of functions in C++)
+ #else
+ #define RIQMDEF extern // Functions visible from other files
+ #endif
+#endif
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+
+#define JOINT_NAME_LENGTH 32 // Joint name string length
+#define MESH_NAME_LENGTH 32 // Mesh name string length
+
+typedef struct Joint {
+ char name[JOINT_NAME_LENGTH];
+ int parent;
+} Joint;
+
+typedef struct Pose {
+ Vector3 translation;
+ Quaternion rotation;
+ Vector3 scale;
+} Pose;
+
+typedef struct Animation {
+ int jointCount;
+ Joint *joints; // NOTE: Joints in anims do not have names
+
+ int frameCount;
+ float framerate;
+
+ Pose **framepose;
+} Animation;
+
+typedef struct AnimatedMesh {
+ char name[MESH_NAME_LENGTH];
+
+ int vertexCount;
+ int triangleCount;
+
+ float *vertices;
+ float *normals;
+ float *texcoords;
+ float *animVertices;
+ float *animNormals;
+ unsigned short *triangles;
+
+ int *weightId;
+ float *weightBias;
+
+ unsigned int vaoId;
+ unsigned int vboId[7];
+} AnimatedMesh;
+
+typedef struct AnimatedModel {
+ int meshCount;
+ AnimatedMesh *mesh;
+
+ int materialCount;
+ int *meshMaterialId;
+ Material *materials;
+
+ int jointCount;
+ Joint *joints;
+
+ Pose *basepose;
+ Matrix transform;
+} AnimatedModel;
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+
+// Loading/Unloading functions
+RIQMDEF AnimatedModel LoadAnimatedModel(const char *filename);
+RIQMDEF void UnloadAnimatedModel(AnimatedModel model);
+RIQMDEF Animation LoadAnimation(const char *filename);
+RIQMDEF void UnloadAnimation(Animation anim);
+
+RIQMDEF AnimatedModel AnimatedModelAddTexture(AnimatedModel model,const char *filename); // GENERIC!
+RIQMDEF AnimatedModel SetMeshMaterial(AnimatedModel model,int meshid, int textureid); // GENERIC!
+
+
+// Usage functionality
+RIQMDEF bool CheckSkeletonsMatch(AnimatedModel model, Animation anim);
+RIQMDEF void AnimateModel(AnimatedModel model, Animation anim, int frame);
+RIQMDEF void DrawAnimatedModel(AnimatedModel model,Vector3 position,float scale,Color tint);
+RIQMDEF void DrawAnimatedModelEx(AnimatedModel model,Vector3 position,Vector3 rotationAxis,float rotationAngle, Vector3 scale,Color tint);
+
+#endif // RIQM_H
+
+
+/***********************************************************************************
+*
+* RIQM IMPLEMENTATION
+*
+************************************************************************************/
+
+#if defined(RIQM_IMPLEMENTATION)
+
+//#include "utils.h" // Required for: fopen() Android mapping
+
+#include <stdio.h> // Required for: FILE, fopen(), fclose(), feof(), fseek(), fread()
+#include <stdlib.h> // Required for: malloc(), free()
+#include <string.h> // Required for: strncmp(),strcpy()
+
+#include "raymath.h" // Required for: Vector3, Quaternion functions
+#include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2
+
+#include "glad.h"
+
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+#define IQM_MAGIC "INTERQUAKEMODEL" // IQM file magic number
+#define IQM_VERSION 2 // only IQM version 2 supported
+#define ANIMJOINTNAME "ANIMJOINT" // default joint name (used in Animation)
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+// iqm file structs
+typedef struct IQMHeader {
+ char magic[16];
+ unsigned int version;
+ unsigned int filesize;
+ unsigned int flags;
+ unsigned int num_text, ofs_text;
+ unsigned int num_meshes, ofs_meshes;
+ unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays;
+ unsigned int num_triangles, ofs_triangles, ofs_adjacency;
+ unsigned int num_joints, ofs_joints;
+ unsigned int num_poses, ofs_poses;
+ unsigned int num_anims, ofs_anims;
+ unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds;
+ unsigned int num_comment, ofs_comment;
+ unsigned int num_extensions, ofs_extensions;
+} IQMHeader;
+
+typedef struct IQMMesh {
+ unsigned int name;
+ unsigned int material;
+ unsigned int first_vertex, num_vertexes;
+ unsigned int first_triangle, num_triangles;
+} IQMMesh;
+
+typedef enum {
+ IQM_POSITION = 0,
+ IQM_TEXCOORD = 1,
+ IQM_NORMAL = 2,
+ IQM_TANGENT = 3, // tangents unused by default
+ IQM_BLENDINDEXES = 4,
+ IQM_BLENDWEIGHTS = 5,
+ IQM_COLOR = 6, // vertex colors unused by default
+ IQM_CUSTOM = 0x10 // custom vertex values unused by default
+} IQMVertexType;
+
+typedef struct IQMTriangle {
+ unsigned int vertex[3];
+} IQMTriangle;
+
+typedef struct IQMAdjacency { // adjacency unused by default
+ unsigned int triangle[3];
+} IQMAdjacency;
+
+typedef struct IQMJoint {
+ unsigned int name;
+ int parent;
+ float translate[3], rotate[4], scale[3];
+} IQMJoint;
+
+typedef struct IQMPose {
+ int parent;
+ unsigned int mask;
+ float channeloffset[10];
+ float channelscale[10];
+} IQMPose;
+
+typedef struct IQMAnim {
+ unsigned int name;
+ unsigned int first_frame, num_frames;
+ float framerate;
+ unsigned int flags;
+} IQMAnim;
+
+typedef struct IQMVertexArray {
+ unsigned int type;
+ unsigned int flags;
+ unsigned int format;
+ unsigned int size;
+ unsigned int offset;
+} IQMVertexArray;
+
+typedef struct IQMBounds { // bounds unused by default
+ float bbmin[3], bbmax[3];
+ float xyradius, radius;
+} IQMBounds;
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+
+//----------------------------------------------------------------------------------
+// Module specific Functions Declaration
+//----------------------------------------------------------------------------------
+void rlLoadAnimatedMesh(AnimatedMesh *amesh, bool dynamic);
+void rlUnloadAnimatedMesh(AnimatedMesh *amesh);
+void rlUpdateAnimatedMesh(AnimatedMesh *amesh);
+void rlDrawAnimatedMesh(AnimatedMesh amesh, Material material, Matrix transform);
+
+static AnimatedModel LoadIQM(const char *filename);
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+void rlLoadAnimatedMesh(AnimatedMesh *amesh, bool dynamic)
+{
+ amesh->vaoId = 0; // Vertex Array Object
+ amesh->vboId[0] = 0; // Vertex positions VBO << these are the animated vertices in animVertices
+ amesh->vboId[1] = 0; // Vertex texcoords VBO
+ amesh->vboId[2] = 0; // Vertex normals VBO << these are the animated normals in animNormals
+ amesh->vboId[3] = 0; // Vertex colors VBO
+ amesh->vboId[4] = 0; // Vertex tangents VBO UNUSED
+ amesh->vboId[5] = 0; // Vertex texcoords2 VBO UNUSED
+ amesh->vboId[6] = 0; // Vertex indices VBO
+
+#if defined(GRAPHICS_API_OPENGL_11)
+TraceLog(LOG_WARNING, "OGL 11");
+#endif
+#if defined(GRAPHICS_API_OPENGL_21)
+TraceLog(LOG_WARNING, "OGL 21");
+#endif
+#if defined(GRAPHICS_API_OPENGL_33)
+TraceLog(LOG_WARNING, "OGL 33");
+#endif
+#if defined(GRAPHICS_API_OPENGL_ES2)
+TraceLog(LOG_WARNING, "OGL ES2");
+#endif
+#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
+ int drawHint = GL_STATIC_DRAW;
+ if (dynamic) drawHint = GL_DYNAMIC_DRAW;
+
+ if (vaoSupported)
+ {
+ // Initialize Quads VAO (Buffer A)
+ glGenVertexArrays(1, &amesh->vaoId);
+ glBindVertexArray(amesh->vaoId);
+ }
+
+ // NOTE: Attributes must be uploaded considering default locations points
+
+ // Enable vertex attributes: position (shader-location = 0)
+ glGenBuffers(1, &amesh->vboId[0]);
+ glBindBuffer(GL_ARRAY_BUFFER, amesh->vboId[0]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*amesh->vertexCount, amesh->animVertices, drawHint);
+ glVertexAttribPointer(0, 3, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(0);
+
+ // Enable vertex attributes: texcoords (shader-location = 1)
+ glGenBuffers(1, &amesh->vboId[1]);
+ glBindBuffer(GL_ARRAY_BUFFER, amesh->vboId[1]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*amesh->vertexCount, amesh->texcoords, drawHint);
+ glVertexAttribPointer(1, 2, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(1);
+
+ // Enable vertex attributes: normals (shader-location = 2)
+ if (amesh->animNormals != NULL)
+ {
+ glGenBuffers(1, &amesh->vboId[2]);
+ glBindBuffer(GL_ARRAY_BUFFER, amesh->vboId[2]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*amesh->vertexCount, amesh->animNormals, drawHint);
+ glVertexAttribPointer(2, 3, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(2);
+ }
+ else
+ {
+ // Default color vertex attribute set to WHITE
+ glVertexAttrib3f(2, 1.0f, 1.0f, 1.0f);
+ glDisableVertexAttribArray(2);
+ }
+// colors UNUSED
+/*
+ // Default color vertex attribute (shader-location = 3)
+ if (mesh->colors != NULL)
+ {
+ glGenBuffers(1, &amesh->vboId[3]);
+ glBindBuffer(GL_ARRAY_BUFFER, amesh->vboId[3]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*mesh->vertexCount, mesh->colors, drawHint);
+ glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
+ glEnableVertexAttribArray(3);
+ }
+ else
+ {
+ // Default color vertex attribute set to WHITE
+ glVertexAttrib4f(3, 1.0f, 1.0f, 1.0f, 1.0f);
+ glDisableVertexAttribArray(3);
+ }
+*/
+// colors to default
+ glVertexAttrib4f(3, 1.0f, 1.0f, 1.0f, 1.0f);
+ glDisableVertexAttribArray(3);
+
+// tangents UNUSED
+/*
+ // Default tangent vertex attribute (shader-location = 4)
+ if (mesh->tangents != NULL)
+ {
+ glGenBuffers(1, &mesh->vboId[4]);
+ glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId[4]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*mesh->vertexCount, mesh->tangents, drawHint);
+ glVertexAttribPointer(4, 4, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(4);
+ }
+ else
+ {
+ // Default tangents vertex attribute
+ glVertexAttrib4f(4, 0.0f, 0.0f, 0.0f, 0.0f);
+ glDisableVertexAttribArray(4);
+ }
+*/
+// tangents to default
+ glVertexAttrib4f(4, 0.0f, 0.0f, 0.0f, 0.0f);
+ glDisableVertexAttribArray(4);
+
+// texcoords2 UNUSED
+/*
+ // Default texcoord2 vertex attribute (shader-location = 5)
+ if (mesh->texcoords2 != NULL)
+ {
+ glGenBuffers(1, &mesh->vboId[5]);
+ glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId[5]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*mesh->vertexCount, mesh->texcoords2, drawHint);
+ glVertexAttribPointer(5, 2, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(5);
+ }
+ else
+ {
+ // Default texcoord2 vertex attribute
+ glVertexAttrib2f(5, 0.0f, 0.0f);
+ glDisableVertexAttribArray(5);
+ }
+*/
+// texcoords2 to default
+ glVertexAttrib2f(5, 0.0f, 0.0f);
+ glDisableVertexAttribArray(5);
+
+ if (amesh->triangles != NULL)
+ {
+ glGenBuffers(1, &amesh->vboId[6]);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, amesh->vboId[6]);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned short)*amesh->triangleCount*3, amesh->triangles, GL_STATIC_DRAW);
+ }
+
+ if (vaoSupported)
+ {
+ if (amesh->vaoId > 0) TraceLog(LOG_INFO, "[VAO ID %i] Mesh uploaded successfully to VRAM (GPU)", amesh->vaoId);
+ else TraceLog(LOG_WARNING, "Mesh could not be uploaded to VRAM (GPU)");
+ }
+ else
+ {
+ TraceLog(LOG_INFO, "[VBOs] Mesh uploaded successfully to VRAM (GPU)");
+ }
+#endif
+}
+
+// Unload mesh data from CPU and GPU
+void rlUnloadAnimatedMesh(AnimatedMesh *amesh)
+{
+ if (amesh->vertices != NULL) free(amesh->vertices);
+ if (amesh->animVertices != NULL) free(amesh->animVertices);
+ if (amesh->texcoords != NULL) free(amesh->texcoords);
+ if (amesh->normals != NULL) free(amesh->normals);
+ if (amesh->animNormals != NULL) free(amesh->animNormals);
+// if (mesh->colors != NULL) free(mesh->colors);
+// if (mesh->tangents != NULL) free(mesh->tangents);
+// if (mesh->texcoords2 != NULL) free(mesh->texcoords2);
+ if (amesh->triangles != NULL) free(amesh->triangles);
+ if (amesh->weightId != NULL) free(amesh->weightId);
+ if (amesh->weightBias != NULL) free(amesh->weightBias);
+
+ rlDeleteBuffers(amesh->vboId[0]); // vertex
+ rlDeleteBuffers(amesh->vboId[1]); // texcoords
+ rlDeleteBuffers(amesh->vboId[2]); // normals
+ rlDeleteBuffers(amesh->vboId[3]); // colors
+ rlDeleteBuffers(amesh->vboId[4]); // tangents
+ rlDeleteBuffers(amesh->vboId[5]); // texcoords2
+ rlDeleteBuffers(amesh->vboId[6]); // indices
+
+ rlDeleteVertexArrays(amesh->vaoId);
+}
+
+// Update vertex and normal data into GPU
+void rlUpdateAnimatedMesh(AnimatedMesh *amesh)
+{
+#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
+ // Activate mesh VAO
+ if (vaoSupported) glBindVertexArray(amesh->vaoId);
+
+ // Update positions data
+ glBindBuffer(GL_ARRAY_BUFFER, amesh->vboId[0]);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*amesh->vertexCount, amesh->animVertices);
+
+ // Update normals data
+ glBindBuffer(GL_ARRAY_BUFFER, amesh->vboId[2]);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*amesh->vertexCount, amesh->animNormals);
+
+ // Unbind the current VAO
+ if (vaoSupported) glBindVertexArray(0);
+
+ //mesh.vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE);
+ // Now we can modify vertices
+ //glUnmapBuffer(GL_ARRAY_BUFFER);
+#endif
+}
+
+// Draw a 3d mesh with material and transform
+void rlDrawAnimatedMesh(AnimatedMesh amesh, Material material, Matrix transform)
+{
+#if defined(GRAPHICS_API_OPENGL_11)
+/*
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, material.maps[MAP_DIFFUSE].texture.id);
+
+ // NOTE: On OpenGL 1.1 we use Vertex Arrays to draw model
+ glEnableClientState(GL_VERTEX_ARRAY); // Enable vertex array
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY); // Enable texture coords array
+
+ //if (amesh.normals != NULL) glEnableClientState(GL_NORMAL_ARRAY); // Enable normals array
+ //if (amesh.colors != NULL) glEnableClientState(GL_COLOR_ARRAY); // Enable colors array
+
+ glVertexPointer(3, GL_FLOAT, 0, amesh.animVertices); // Pointer to vertex coords array
+ glTexCoordPointer(2, GL_FLOAT, 0, amesh.texcoords); // Pointer to texture coords array
+ if (amesh.animNormals != NULL) glNormalPointer(GL_FLOAT, 0, amesh.animNormals); // Pointer to normals array
+ //if (mesh.colors != NULL) glColorPointer(4, GL_UNSIGNED_BYTE, 0, mesh.colors); // Pointer to colors array
+
+ rlPushMatrix();
+ rlMultMatrixf(MatrixToFloat(transform));
+ rlColor4ub(material.maps[MAP_DIFFUSE].color.r, material.maps[MAP_DIFFUSE].color.g, material.maps[MAP_DIFFUSE].color.b, material.maps[MAP_DIFFUSE].color.a);
+
+ if (amesh.triangles != NULL) glDrawElements(GL_TRIANGLES, amesh.triangleCount*3, GL_UNSIGNED_SHORT, amesh.triangles);
+ else glDrawArrays(GL_TRIANGLES, 0, amesh.vertexCount);
+ rlPopMatrix();
+
+ glDisableClientState(GL_VERTEX_ARRAY); // Disable vertex array
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY); // Disable texture coords array
+ if (amesh.animNormals != NULL) glDisableClientState(GL_NORMAL_ARRAY); // Disable normals array
+ //if (mesh.colors != NULL) glDisableClientState(GL_NORMAL_ARRAY); // Disable colors array
+
+ glDisable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, 0);
+*/
+#endif
+
+#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
+ // Bind shader program
+ glUseProgram(material.shader.id);
+
+ // Matrices and other values required by shader
+ //-----------------------------------------------------
+ // Calculate and send to shader model matrix (used by PBR shader)
+ if (material.shader.locs[LOC_MATRIX_MODEL] != -1) SetShaderValueMatrix(material.shader, material.shader.locs[LOC_MATRIX_MODEL], transform);
+
+ // Upload to shader material.colDiffuse
+ if (material.shader.locs[LOC_COLOR_DIFFUSE] != -1)
+ glUniform4f(material.shader.locs[LOC_COLOR_DIFFUSE], (float)material.maps[MAP_DIFFUSE].color.r/255.0f,
+ (float)material.maps[MAP_DIFFUSE].color.g/255.0f,
+ (float)material.maps[MAP_DIFFUSE].color.b/255.0f,
+ (float)material.maps[MAP_DIFFUSE].color.a/255.0f);
+
+ // Upload to shader material.colSpecular (if available)
+ if (material.shader.locs[LOC_COLOR_SPECULAR] != -1)
+ glUniform4f(material.shader.locs[LOC_COLOR_SPECULAR], (float)material.maps[MAP_SPECULAR].color.r/255.0f,
+ (float)material.maps[MAP_SPECULAR].color.g/255.0f,
+ (float)material.maps[MAP_SPECULAR].color.b/255.0f,
+ (float)material.maps[MAP_SPECULAR].color.a/255.0f);
+
+ if (material.shader.locs[LOC_MATRIX_VIEW] != -1) SetShaderValueMatrix(material.shader, material.shader.locs[LOC_MATRIX_VIEW], modelview);
+ if (material.shader.locs[LOC_MATRIX_PROJECTION] != -1) SetShaderValueMatrix(material.shader, material.shader.locs[LOC_MATRIX_PROJECTION], projection);
+
+ // At this point the modelview matrix just contains the view matrix (camera)
+ // That's because BeginMode3D() sets it an no model-drawing function modifies it, all use rlPushMatrix() and rlPopMatrix()
+ Matrix matView = modelview; // View matrix (camera)
+ Matrix matProjection = projection; // Projection matrix (perspective)
+
+ // Calculate model-view matrix combining matModel and matView
+ Matrix matModelView = MatrixMultiply(transform, matView); // Transform to camera-space coordinates
+ //-----------------------------------------------------
+
+ // Bind active texture maps (if available)
+ for (int i = 0; i < MAX_MATERIAL_MAPS; i++)
+ {
+ if (material.maps[i].texture.id > 0)
+ {
+ glActiveTexture(GL_TEXTURE0 + i);
+ if ((i == MAP_IRRADIANCE) || (i == MAP_PREFILTER) || (i == MAP_CUBEMAP)) glBindTexture(GL_TEXTURE_CUBE_MAP, material.maps[i].texture.id);
+ else glBindTexture(GL_TEXTURE_2D, material.maps[i].texture.id);
+
+ glUniform1i(material.shader.locs[LOC_MAP_DIFFUSE + i], i);
+ }
+ }
+
+ // Bind vertex array objects (or VBOs)
+ if (vaoSupported) glBindVertexArray(amesh.vaoId);
+ else
+ {
+ // TODO: Simplify VBO binding into a for loop
+
+ // Bind mesh VBO data: vertex position (shader-location = 0)
+ glBindBuffer(GL_ARRAY_BUFFER, amesh.vboId[0]);
+ glVertexAttribPointer(material.shader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(material.shader.locs[LOC_VERTEX_POSITION]);
+
+ // Bind mesh VBO data: vertex texcoords (shader-location = 1)
+ glBindBuffer(GL_ARRAY_BUFFER, amesh.vboId[1]);
+ glVertexAttribPointer(material.shader.locs[LOC_VERTEX_TEXCOORD01], 2, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(material.shader.locs[LOC_VERTEX_TEXCOORD01]);
+
+ // Bind mesh VBO data: vertex normals (shader-location = 2, if available)
+ if (material.shader.locs[LOC_VERTEX_NORMAL] != -1)
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, amesh.vboId[2]);
+ glVertexAttribPointer(material.shader.locs[LOC_VERTEX_NORMAL], 3, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(material.shader.locs[LOC_VERTEX_NORMAL]);
+ }
+
+ // Bind mesh VBO data: vertex colors (shader-location = 3, if available)
+ if (material.shader.locs[LOC_VERTEX_COLOR] != -1)
+ {
+ if (amesh.vboId[3] != 0)
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, amesh.vboId[3]);
+ glVertexAttribPointer(material.shader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
+ glEnableVertexAttribArray(material.shader.locs[LOC_VERTEX_COLOR]);
+ }
+ else
+ {
+ // Set default value for unused attribute
+ // NOTE: Required when using default shader and no VAO support
+ glVertexAttrib4f(material.shader.locs[LOC_VERTEX_COLOR], 1.0f, 1.0f, 1.0f, 1.0f);
+ glDisableVertexAttribArray(material.shader.locs[LOC_VERTEX_COLOR]);
+ }
+ }
+
+ // Bind mesh VBO data: vertex tangents (shader-location = 4, if available)
+ if (material.shader.locs[LOC_VERTEX_TANGENT] != -1)
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, amesh.vboId[4]);
+ glVertexAttribPointer(material.shader.locs[LOC_VERTEX_TANGENT], 4, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(material.shader.locs[LOC_VERTEX_TANGENT]);
+ }
+
+ // Bind mesh VBO data: vertex texcoords2 (shader-location = 5, if available)
+ if (material.shader.locs[LOC_VERTEX_TEXCOORD02] != -1)
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, amesh.vboId[5]);
+ glVertexAttribPointer(material.shader.locs[LOC_VERTEX_TEXCOORD02], 2, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(material.shader.locs[LOC_VERTEX_TEXCOORD02]);
+ }
+
+ if (amesh.triangles != NULL) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, amesh.vboId[6]);
+ }
+
+ int eyesCount = 1;
+#if defined(SUPPORT_VR_SIMULATOR)
+ if (vrStereoRender) eyesCount = 2;
+#endif
+
+ for (int eye = 0; eye < eyesCount; eye++)
+ {
+ if (eyesCount == 1) modelview = matModelView;
+ #if defined(SUPPORT_VR_SIMULATOR)
+ else SetStereoView(eye, matProjection, matModelView);
+ #endif
+
+ // Calculate model-view-projection matrix (MVP)
+ Matrix matMVP = MatrixMultiply(modelview, projection); // Transform to screen-space coordinates
+
+ // Send combined model-view-projection matrix to shader
+ glUniformMatrix4fv(material.shader.locs[LOC_MATRIX_MVP], 1, false, MatrixToFloat(matMVP));
+
+ // Draw call!
+ if (amesh.triangles != NULL) glDrawElements(GL_TRIANGLES, amesh.triangleCount*3, GL_UNSIGNED_SHORT, 0); // Indexed vertices draw
+ else glDrawArrays(GL_TRIANGLES, 0, amesh.vertexCount);
+ }
+
+ // Unbind all binded texture maps
+ for (int i = 0; i < MAX_MATERIAL_MAPS; i++)
+ {
+ glActiveTexture(GL_TEXTURE0 + i); // Set shader active texture
+ if ((i == MAP_IRRADIANCE) || (i == MAP_PREFILTER) || (i == MAP_CUBEMAP)) glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
+ else glBindTexture(GL_TEXTURE_2D, 0); // Unbind current active texture
+ }
+
+ // Unind vertex array objects (or VBOs)
+ if (vaoSupported) glBindVertexArray(0);
+ else
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ if (amesh.triangles != NULL) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ }
+
+ // Unbind shader program
+ glUseProgram(0);
+
+ // Restore projection/modelview matrices
+ // NOTE: In stereo rendering matrices are being modified to fit every eye
+ projection = matProjection;
+ modelview = matView;
+#endif
+}
+
+#ifdef __cplusplus
+extern "C" { // Prevents name mangling of functions
+#endif
+
+// Load .iqm file and initialize animated model
+AnimatedModel LoadAnimatedModel(const char *filename)
+{
+ AnimatedModel out = LoadIQM(filename);
+
+ for (int i = 0; i < out.meshCount; i++) rlLoadAnimatedMesh(&out.mesh[i], false);
+
+ out.transform = MatrixIdentity();
+ out.meshMaterialId = malloc(sizeof(int)*out.meshCount);
+ out.materials = NULL;
+ out.materialCount = 0;
+
+ for (int i = 0; i < out.meshCount; i++) out.meshMaterialId[i] = -1;
+
+ return out;
+}
+
+// Add a texture to an animated model
+AnimatedModel AnimatedModelAddTexture(AnimatedModel model, const char *filename)
+{
+ Texture2D texture = LoadTexture(filename);
+
+ model.materials = realloc(model.materials, sizeof(Material)*(model.materialCount + 1));
+ model.materials[model.materialCount] = LoadMaterialDefault();
+ model.materials[model.materialCount].maps[MAP_DIFFUSE].texture = texture;
+ model.materialCount++;
+
+ return model;
+}
+
+// Set the material for a mesh
+AnimatedModel SetMeshMaterial(AnimatedModel model, int meshid, int textureid)
+{
+ if (meshid > model.meshCount)
+ {
+ TraceLog(LOG_WARNING, "MeshId greater than meshCount\n");
+ return model;
+ }
+
+ if (textureid > model.materialCount)
+ {
+ TraceLog(LOG_WARNING,"textureid greater than materialCount\n");
+ return model;
+ }
+
+ model.meshMaterialId[meshid] = textureid;
+
+ return model;
+}
+
+// Load animations from a .iqm file
+Animation LoadAnimationFromIQM(const char *filename)
+{
+ Animation animation = { 0 };
+
+ FILE *iqmFile;
+ IQMHeader iqm;
+
+ iqmFile = fopen(filename,"rb");
+
+ if (!iqmFile)
+ {
+ TraceLog(LOG_ERROR, "[%s] Unable to open file", filename);
+ return animation;
+ }
+
+ // header
+ fread(&iqm, sizeof(IQMHeader), 1, iqmFile);
+
+ if (strncmp(iqm.magic, IQM_MAGIC, sizeof(IQM_MAGIC)))
+ {
+ TraceLog(LOG_ERROR, "Magic Number \"%s\"does not match.", iqm.magic);
+ fclose(iqmFile);
+ return animation;
+ }
+
+ if (iqm.version != IQM_VERSION)
+ {
+ TraceLog(LOG_ERROR, "IQM version %i is incorrect.", iqm.version);
+ fclose(iqmFile);
+ return animation;
+ }
+
+ // header
+ if (iqm.num_anims > 1) TraceLog(LOG_WARNING, "More than 1 animation in file, only the first one will get loaded");
+
+ // joints
+ IQMPose *poses;
+ poses = malloc(sizeof(IQMPose)*iqm.num_poses);
+ fseek(iqmFile, iqm.ofs_poses, SEEK_SET);
+ fread(poses, sizeof(IQMPose)*iqm.num_poses, 1, iqmFile);
+
+ animation.jointCount = iqm.num_poses;
+ animation.joints = malloc(sizeof(Joint)*iqm.num_poses);
+
+ for (int j = 0; j < iqm.num_poses; j++)
+ {
+ strcpy(animation.joints[j].name, ANIMJOINTNAME);
+ animation.joints[j].parent = poses[j].parent;
+ }
+
+ // animations
+ IQMAnim anim = {0};
+ fseek(iqmFile, iqm.ofs_anims, SEEK_SET);
+ fread(&anim, sizeof(IQMAnim), 1, iqmFile);
+
+ animation.frameCount = anim.num_frames;
+ animation.framerate = anim.framerate;
+
+ // frameposes
+ unsigned short *framedata = malloc(sizeof(unsigned short)*iqm.num_frames*iqm.num_framechannels);
+ fseek(iqmFile, iqm.ofs_frames, SEEK_SET);
+ fread(framedata, sizeof(unsigned short)*iqm.num_frames*iqm.num_framechannels, 1, iqmFile);
+
+ animation.framepose = malloc(sizeof(Pose*)*anim.num_frames);
+ for (int j = 0; j < anim.num_frames; j++) animation.framepose[j] = malloc(sizeof(Pose)*iqm.num_poses);
+
+ int dcounter = anim.first_frame*iqm.num_framechannels;
+
+ for (int frame = 0; frame < anim.num_frames; frame++)
+ {
+ for (int i = 0; i < iqm.num_poses; i++)
+ {
+ animation.framepose[frame][i].translation.x = poses[i].channeloffset[0];
+
+ if (poses[i].mask & 0x01)
+ {
+ animation.framepose[frame][i].translation.x += framedata[dcounter]*poses[i].channelscale[0];
+ dcounter++;
+ }
+
+ animation.framepose[frame][i].translation.y = poses[i].channeloffset[1];
+
+ if (poses[i].mask & 0x02)
+ {
+ animation.framepose[frame][i].translation.y += framedata[dcounter]*poses[i].channelscale[1];
+ dcounter++;
+ }
+
+ animation.framepose[frame][i].translation.z = poses[i].channeloffset[2];
+
+ if (poses[i].mask & 0x04)
+ {
+ animation.framepose[frame][i].translation.z += framedata[dcounter]*poses[i].channelscale[2];
+ dcounter++;
+ }
+
+ animation.framepose[frame][i].rotation.x = poses[i].channeloffset[3];
+
+ if (poses[i].mask & 0x08)
+ {
+ animation.framepose[frame][i].rotation.x += framedata[dcounter]*poses[i].channelscale[3];
+ dcounter++;
+ }
+
+ animation.framepose[frame][i].rotation.y = poses[i].channeloffset[4];
+
+ if (poses[i].mask & 0x10)
+ {
+ animation.framepose[frame][i].rotation.y += framedata[dcounter]*poses[i].channelscale[4];
+ dcounter++;
+ }
+
+ animation.framepose[frame][i].rotation.z = poses[i].channeloffset[5];
+
+ if (poses[i].mask & 0x20)
+ {
+ animation.framepose[frame][i].rotation.z += framedata[dcounter]*poses[i].channelscale[5];
+ dcounter++;
+ }
+
+ animation.framepose[frame][i].rotation.w = poses[i].channeloffset[6];
+
+ if (poses[i].mask & 0x40)
+ {
+ animation.framepose[frame][i].rotation.w += framedata[dcounter]*poses[i].channelscale[6];
+ dcounter++;
+ }
+
+ animation.framepose[frame][i].scale.x = poses[i].channeloffset[7];
+
+ if (poses[i].mask & 0x80)
+ {
+ animation.framepose[frame][i].scale.x += framedata[dcounter]*poses[i].channelscale[7];
+ dcounter++;
+ }
+
+ animation.framepose[frame][i].scale.y = poses[i].channeloffset[8];
+
+ if (poses[i].mask & 0x100)
+ {
+ animation.framepose[frame][i].scale.y += framedata[dcounter]*poses[i].channelscale[8];
+ dcounter++;
+ }
+
+ animation.framepose[frame][i].scale.z = poses[i].channeloffset[9];
+
+ if (poses[i].mask & 0x200)
+ {
+ animation.framepose[frame][i].scale.z += framedata[dcounter]*poses[i].channelscale[9];
+ dcounter++;
+ }
+
+ animation.framepose[frame][i].rotation = QuaternionNormalize(animation.framepose[frame][i].rotation);
+ }
+ }
+
+ // Build frameposes
+ for (int frame = 0; frame < anim.num_frames; frame++)
+ {
+ for (int i = 0; i < animation.jointCount; i++)
+ {
+ if (animation.joints[i].parent >= 0)
+ {
+ animation.framepose[frame][i].rotation = QuaternionMultiply(animation.framepose[frame][animation.joints[i].parent].rotation, animation.framepose[frame][i].rotation);
+ animation.framepose[frame][i].translation = Vector3RotateByQuaternion(animation.framepose[frame][i].translation, animation.framepose[frame][animation.joints[i].parent].rotation);
+ animation.framepose[frame][i].translation = Vector3Add(animation.framepose[frame][i].translation, animation.framepose[frame][animation.joints[i].parent].translation);
+ animation.framepose[frame][i].scale = Vector3MultiplyV(animation.framepose[frame][i].scale, animation.framepose[frame][animation.joints[i].parent].scale);
+ }
+ }
+ }
+
+ free(framedata);
+ free(poses);
+
+ fclose(iqmFile);
+
+ return animation;
+}
+
+// Unload animated model
+void UnloadAnimatedModel(AnimatedModel model)
+{
+ free(model.materials);
+ free(model.meshMaterialId);
+ free(model.joints);
+ free(model.basepose);
+
+ for (int i = 0; i < model.meshCount; i++) rlUnloadAnimatedMesh(&model.mesh[i]);
+
+ free(model.mesh);
+}
+
+// Unload animation
+void UnloadAnimation(Animation anim)
+{
+ free(anim.joints);
+ free(anim.framepose);
+
+ for (int i = 0; i < anim.frameCount; i++) free(anim.framepose[i]);
+}
+
+// Check if skeletons match, only parents and jointCount are checked
+bool CheckSkeletonsMatch(AnimatedModel model, Animation anim)
+{
+ if (model.jointCount != anim.jointCount) return 0;
+
+ for (int i = 0; i < model.jointCount; i++)
+ {
+ if (model.joints[i].parent != anim.joints[i].parent) return 0;
+ }
+
+ return 1;
+}
+
+// Calculate the animated vertex positions and normals based on an animation at a given frame
+void AnimateModel(AnimatedModel model, Animation anim, int frame)
+{
+ if (frame >= anim.frameCount) frame = frame%anim.frameCount;
+
+ for (int m = 0; m < model.meshCount; m++)
+ {
+ Vector3 outv = {0};
+ Vector3 outn = {0};
+
+ Vector3 baset = {0};
+ Quaternion baser = {0};
+ Vector3 bases = {0};
+
+ Vector3 outt = {0};
+ Quaternion outr = {0};
+ Vector3 outs = {0};
+
+ int vcounter = 0;
+ int wcounter = 0;
+ int weightId = 0;
+
+ for (int i = 0; i < model.mesh[m].vertexCount; i++)
+ {
+ weightId = model.mesh[m].weightId[wcounter];
+ baset = model.basepose[weightId].translation;
+ baser = model.basepose[weightId].rotation;
+ bases = model.basepose[weightId].scale;
+ outt = anim.framepose[frame][weightId].translation;
+ outr = anim.framepose[frame][weightId].rotation;
+ outs = anim.framepose[frame][weightId].scale;
+
+ // vertices
+ outv = (Vector3){model.mesh[m].vertices[vcounter],model.mesh[m].vertices[vcounter + 1],model.mesh[m].vertices[vcounter + 2]};
+ outv = Vector3MultiplyV(outv,outs);
+ outv = Vector3Subtract(outv,baset);
+ outv = Vector3RotateByQuaternion(outv,QuaternionMultiply(outr,QuaternionInvert(baser)));
+ outv = Vector3Add(outv,outt);
+ model.mesh[m].animVertices[vcounter] = outv.x;
+ model.mesh[m].animVertices[vcounter + 1] = outv.y;
+ model.mesh[m].animVertices[vcounter + 2] = outv.z;
+
+ // normals
+ outn = (Vector3){model.mesh[m].normals[vcounter],model.mesh[m].normals[vcounter + 1],model.mesh[m].normals[vcounter + 2]};
+ outn = Vector3RotateByQuaternion(outn,QuaternionMultiply(outr,QuaternionInvert(baser)));
+ model.mesh[m].animNormals[vcounter] = outn.x;
+ model.mesh[m].animNormals[vcounter + 1] = outn.y;
+ model.mesh[m].animNormals[vcounter + 2] = outn.z;
+ vcounter += 3;
+ wcounter += 4;
+ }
+ }
+}
+
+// Draw an animated model
+void DrawAnimatedModel(AnimatedModel model,Vector3 position,float scale,Color tint)
+{
+ Vector3 vScale = { scale, scale, scale };
+ Vector3 rotationAxis = { 0.0f,0.0f,0.0f };
+
+ DrawAnimatedModelEx(model, position, rotationAxis, 0.0f, vScale, tint);
+}
+
+// Draw an animated model with extended parameters
+void DrawAnimatedModelEx(AnimatedModel model,Vector3 position,Vector3 rotationAxis,float rotationAngle, Vector3 scale,Color tint)
+{
+ if (model.materialCount == 0)
+ {
+ TraceLog(LOG_WARNING,"No materials set, can't draw animated mesh\n");
+ return;
+ }
+
+ Matrix matScale = MatrixScale(scale.x,scale.y,scale.z);
+ Matrix matRotation = MatrixRotate(rotationAxis,rotationAngle*DEG2RAD);
+ Matrix matTranslation = MatrixTranslate(position.x,position.y,position.z);
+
+ Matrix matTransform = MatrixMultiply(MatrixMultiply(matScale,matRotation),matTranslation);
+ model.transform = MatrixMultiply(model.transform,matTransform);
+
+ for (int i = 0; i < model.meshCount; i++)
+ {
+ rlUpdateAnimatedMesh(&model.mesh[i]);
+ rlDrawAnimatedMesh(model.mesh[i],model.materials[model.meshMaterialId[i]],MatrixIdentity());
+ }
+}
+
+
+
+
+
+
+// Load animated model meshes from IQM file
+static AnimatedModel LoadIQM(const char *filename)
+{
+ AnimatedModel model = { 0 };
+
+ FILE *iqmFile;
+ IQMHeader iqm;
+
+ IQMMesh *imesh;
+ IQMTriangle *tri;
+ IQMVertexArray *va;
+ IQMJoint *ijoint;
+
+ float *vertex;
+ float *normal;
+ float *text;
+ char *blendi;
+ unsigned char *blendw;
+
+ iqmFile = fopen(filename, "rb");
+
+ if (!iqmFile)
+ {
+ TraceLog(LOG_ERROR, "[%s] Unable to open file", filename);
+ return model;
+ }
+
+ // header
+ fread(&iqm,sizeof(IQMHeader), 1, iqmFile);
+
+ if (strncmp(iqm.magic, IQM_MAGIC, sizeof(IQM_MAGIC)))
+ {
+ TraceLog(LOG_ERROR, "Magic Number \"%s\"does not match.", iqm.magic);
+ fclose(iqmFile);
+ return model;
+ }
+
+ if(iqm.version != IQM_VERSION)
+ {
+ TraceLog(LOG_ERROR, "IQM version %i is incorrect.", iqm.version);
+ fclose(iqmFile);
+ return model;
+ }
+
+ // meshes
+ imesh = malloc(sizeof(IQMMesh)*iqm.num_meshes);
+ fseek(iqmFile, iqm.ofs_meshes, SEEK_SET);
+ fread(imesh, sizeof(IQMMesh)*iqm.num_meshes, 1, iqmFile);
+
+ model.meshCount = iqm.num_meshes;
+ model.mesh = malloc(sizeof(AnimatedMesh)*iqm.num_meshes);
+
+ for (int i = 0; i < iqm.num_meshes; i++)
+ {
+ fseek(iqmFile,iqm.ofs_text+imesh[i].name,SEEK_SET);
+ fread(model.mesh[i].name, sizeof(char)*MESH_NAME_LENGTH, 1, iqmFile);
+ model.mesh[i].vertexCount = imesh[i].num_vertexes;
+ model.mesh[i].vertices = malloc(sizeof(float)*imesh[i].num_vertexes*3);
+ model.mesh[i].normals = malloc(sizeof(float)*imesh[i].num_vertexes*3);
+ model.mesh[i].texcoords = malloc(sizeof(float)*imesh[i].num_vertexes*2);
+ model.mesh[i].weightId = malloc(sizeof(int)*imesh[i].num_vertexes*4);
+ model.mesh[i].weightBias = malloc(sizeof(float)*imesh[i].num_vertexes*4);
+ model.mesh[i].triangleCount = imesh[i].num_triangles;
+ model.mesh[i].triangles = malloc(sizeof(unsigned short)*imesh[i].num_triangles*3);
+ model.mesh[i].animVertices = malloc(sizeof(float)*imesh[i].num_vertexes*3);
+ model.mesh[i].animNormals = malloc(sizeof(float)*imesh[i].num_vertexes*3);
+ }
+
+ // tris
+ tri = malloc(sizeof(IQMTriangle)*iqm.num_triangles);
+ fseek(iqmFile, iqm.ofs_triangles, SEEK_SET);
+ fread(tri, sizeof(IQMTriangle)*iqm.num_triangles, 1, iqmFile);
+
+ for (int m = 0; m < iqm.num_meshes; m++)
+ {
+ int tcounter = 0;
+
+ for (int i=imesh[m].first_triangle; i < imesh[m].first_triangle+imesh[m].num_triangles; i++)
+ {
+ // IQM triangles are stored counter clockwise, but raylib sets opengl to clockwise drawing, so we swap them around
+ model.mesh[m].triangles[tcounter+2] = tri[i].vertex[0] - imesh[m].first_vertex;
+ model.mesh[m].triangles[tcounter+1] = tri[i].vertex[1] - imesh[m].first_vertex;
+ model.mesh[m].triangles[tcounter] = tri[i].vertex[2] - imesh[m].first_vertex;
+ tcounter += 3;
+ }
+ }
+
+ // vertarrays
+ va = malloc(sizeof(IQMVertexArray)*iqm.num_vertexarrays);
+ fseek(iqmFile, iqm.ofs_vertexarrays, SEEK_SET);
+ fread(va, sizeof(IQMVertexArray)*iqm.num_vertexarrays, 1, iqmFile);
+
+ for (int i = 0; i < iqm.num_vertexarrays; i++)
+ {
+ switch (va[i].type)
+ {
+ case IQM_POSITION:
+ {
+ vertex = malloc(sizeof(float)*iqm.num_vertexes*3);
+ fseek(iqmFile, va[i].offset, SEEK_SET);
+ fread(vertex, sizeof(float)*iqm.num_vertexes*3, 1, iqmFile);
+
+ for (int m = 0; m < iqm.num_meshes; m++)
+ {
+ int vcounter = 0;
+ for (int i = imesh[m].first_vertex*3; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*3; i++)
+ {
+ model.mesh[m].vertices[vcounter] = vertex[i];
+ model.mesh[m].animVertices[vcounter] = vertex[i];
+ vcounter++;
+ }
+ }
+ } break;
+ case IQM_NORMAL:
+ {
+ normal = malloc(sizeof(float)*iqm.num_vertexes*3);
+ fseek(iqmFile, va[i].offset, SEEK_SET);
+ fread(normal, sizeof(float)*iqm.num_vertexes*3, 1, iqmFile);
+
+ for (int m = 0; m < iqm.num_meshes; m++)
+ {
+ int vcounter = 0;
+ for (int i = imesh[m].first_vertex*3; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*3; i++)
+ {
+ model.mesh[m].normals[vcounter] = normal[i];
+ model.mesh[m].animNormals[vcounter] = normal[i];
+ vcounter++;
+ }
+ }
+ } break;
+ case IQM_TEXCOORD:
+ {
+ text = malloc(sizeof(float)*iqm.num_vertexes*2);
+ fseek(iqmFile, va[i].offset, SEEK_SET);
+ fread(text, sizeof(float)*iqm.num_vertexes*2, 1, iqmFile);
+
+ for (int m = 0; m < iqm.num_meshes; m++)
+ {
+ int vcounter = 0;
+ for (int i = imesh[m].first_vertex*2; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*2; i++)
+ {
+ model.mesh[m].texcoords[vcounter] = text[i];
+ vcounter++;
+ }
+ }
+ } break;
+ case IQM_BLENDINDEXES:
+ {
+ blendi = malloc(sizeof(char)*iqm.num_vertexes*4);
+ fseek(iqmFile, va[i].offset, SEEK_SET);
+ fread(blendi, sizeof(char)*iqm.num_vertexes*4, 1, iqmFile);
+
+ for (int m = 0; m < iqm.num_meshes; m++)
+ {
+ int vcounter = 0;
+ for (int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++)
+ {
+ model.mesh[m].weightId[vcounter] = blendi[i];
+ vcounter++;
+ }
+ }
+ } break;
+ case IQM_BLENDWEIGHTS:
+ {
+ blendw = malloc(sizeof(unsigned char)*iqm.num_vertexes*4);
+ fseek(iqmFile,va[i].offset,SEEK_SET);
+ fread(blendw,sizeof(unsigned char)*iqm.num_vertexes*4,1,iqmFile);
+
+ for (int m = 0; m < iqm.num_meshes; m++)
+ {
+ int vcounter = 0;
+ for (int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++)
+ {
+ model.mesh[m].weightBias[vcounter] = blendw[i]/255.0f;
+ vcounter++;
+ }
+ }
+ } break;
+ }
+ }
+
+ // joints, include base poses
+ ijoint = malloc(sizeof(IQMJoint)*iqm.num_joints);
+ fseek(iqmFile, iqm.ofs_joints, SEEK_SET);
+ fread(ijoint, sizeof(IQMJoint)*iqm.num_joints, 1, iqmFile);
+
+ model.jointCount = iqm.num_joints;
+ model.joints = malloc(sizeof(Joint)*iqm.num_joints);
+ model.basepose = malloc(sizeof(Pose)*iqm.num_joints);
+
+ for (int i = 0; i < iqm.num_joints; i++)
+ {
+ // joints
+ model.joints[i].parent = ijoint[i].parent;
+ fseek(iqmFile, iqm.ofs_text + ijoint[i].name, SEEK_SET);
+ fread(model.joints[i].name,sizeof(char)*JOINT_NAME_LENGTH, 1, iqmFile);
+
+ // basepose
+ model.basepose[i].translation.x = ijoint[i].translate[0];
+ model.basepose[i].translation.y = ijoint[i].translate[1];
+ model.basepose[i].translation.z = ijoint[i].translate[2];
+
+ model.basepose[i].rotation.x = ijoint[i].rotate[0];
+ model.basepose[i].rotation.y = ijoint[i].rotate[1];
+ model.basepose[i].rotation.z = ijoint[i].rotate[2];
+ model.basepose[i].rotation.w = ijoint[i].rotate[3];
+
+ model.basepose[i].scale.x = ijoint[i].scale[0];
+ model.basepose[i].scale.y = ijoint[i].scale[1];
+ model.basepose[i].scale.z = ijoint[i].scale[2];
+ }
+
+ // build base pose
+ for (int i = 0; i < model.jointCount; i++)
+ {
+ if (model.joints[i].parent >= 0)
+ {
+ model.basepose[i].rotation = QuaternionMultiply(model.basepose[model.joints[i].parent].rotation, model.basepose[i].rotation);
+ model.basepose[i].translation = Vector3RotateByQuaternion(model.basepose[i].translation, model.basepose[model.joints[i].parent].rotation);
+ model.basepose[i].translation = Vector3Add(model.basepose[i].translation, model.basepose[model.joints[i].parent].translation);
+ model.basepose[i].scale = Vector3MultiplyV(model.basepose[i].scale, model.basepose[model.joints[i].parent].scale);
+ }
+ }
+
+ fclose(iqmFile);
+ free(imesh);
+ free(tri);
+ free(va);
+ free(vertex);
+ free(normal);
+ free(text);
+ free(blendi);
+ free(blendw);
+ free(ijoint);
+
+ return model;
+}
+
+#endif