summaryrefslogtreecommitdiff
path: root/src/renderer
diff options
context:
space:
mode:
authorThilo Schulz <arny@ats.s.bawue.de>2011-05-02 15:50:19 +0000
committerTim Angus <tim@ngus.net>2013-01-09 20:50:35 +0000
commit5d081049e208bfb983a850b8e0b83711c74d75ca (patch)
tree571888d08344a57a435c7541fd98fcc6e5fb89c5 /src/renderer
parent8a58b6a7deb03a07e8169790bd4b9a8f38ecc718 (diff)
- Add Inter-Quake Model (IQM) Format support, patch by gimhael with minor modifications by myself (#4965) - Changed model_t::md4 to model_t::modelData - Fix R_ModelBounds for MD4, MDR and IQM models (#4966) - Support Model format fallback similar to image formats in tr_image.c, patch by Zack Middleton (#4967)
Diffstat (limited to 'src/renderer')
-rw-r--r--src/renderer/iqm.h107
-rw-r--r--src/renderer/tr_animation.c2
-rw-r--r--src/renderer/tr_local.h52
-rw-r--r--src/renderer/tr_main.c3
-rw-r--r--src/renderer/tr_model.c416
-rw-r--r--src/renderer/tr_model_iqm.c839
-rw-r--r--src/renderer/tr_surface.c1
7 files changed, 1289 insertions, 131 deletions
diff --git a/src/renderer/iqm.h b/src/renderer/iqm.h
new file mode 100644
index 00000000..bbfc053f
--- /dev/null
+++ b/src/renderer/iqm.h
@@ -0,0 +1,107 @@
+#ifndef __IQM_H__
+#define __IQM_H__
+
+#define IQM_MAGIC "INTERQUAKEMODEL"
+#define IQM_VERSION 1
+
+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_t;
+
+typedef struct iqmmesh
+{
+ unsigned int name;
+ unsigned int material;
+ unsigned int first_vertex, num_vertexes;
+ unsigned int first_triangle, num_triangles;
+} iqmMesh_t;
+
+enum
+{
+ IQM_POSITION = 0,
+ IQM_TEXCOORD = 1,
+ IQM_NORMAL = 2,
+ IQM_TANGENT = 3,
+ IQM_BLENDINDEXES = 4,
+ IQM_BLENDWEIGHTS = 5,
+ IQM_COLOR = 6,
+ IQM_CUSTOM = 0x10
+};
+
+enum
+{
+ IQM_BYTE = 0,
+ IQM_UBYTE = 1,
+ IQM_SHORT = 2,
+ IQM_USHORT = 3,
+ IQM_INT = 4,
+ IQM_UINT = 5,
+ IQM_HALF = 6,
+ IQM_FLOAT = 7,
+ IQM_DOUBLE = 8,
+};
+
+typedef struct iqmtriangle
+{
+ unsigned int vertex[3];
+} iqmTriangle_t;
+
+typedef struct iqmjoint
+{
+ unsigned int name;
+ int parent;
+ float translate[3], rotate[3], scale[3];
+} iqmJoint_t;
+
+typedef struct iqmpose
+{
+ int parent;
+ unsigned int mask;
+ float channeloffset[9];
+ float channelscale[9];
+} iqmPose_t;
+
+typedef struct iqmanim
+{
+ unsigned int name;
+ unsigned int first_frame, num_frames;
+ float framerate;
+ unsigned int flags;
+} iqmAnim_t;
+
+enum
+{
+ IQM_LOOP = 1<<0
+};
+
+typedef struct iqmvertexarray
+{
+ unsigned int type;
+ unsigned int flags;
+ unsigned int format;
+ unsigned int size;
+ unsigned int offset;
+} iqmVertexArray_t;
+
+typedef struct iqmbounds
+{
+ float bbmin[3], bbmax[3];
+ float xyradius, radius;
+} iqmBounds_t;
+
+#endif
+
diff --git a/src/renderer/tr_animation.c b/src/renderer/tr_animation.c
index 0aa628b5..513fc082 100644
--- a/src/renderer/tr_animation.c
+++ b/src/renderer/tr_animation.c
@@ -46,7 +46,7 @@ void R_AddAnimSurfaces( trRefEntity_t *ent ) {
shader_t *shader;
int i;
- header = (md4Header_t *) tr.currentModel->md4;
+ header = (md4Header_t *) tr.currentModel->modelData;
lod = (md4LOD_t *)( (byte *)header + header->ofsLODs );
surface = (md4Surface_t *)( (byte *)lod + lod->ofsSurfaces );
diff --git a/src/renderer/tr_local.h b/src/renderer/tr_local.h
index a1978c3a..922dca21 100644
--- a/src/renderer/tr_local.h
+++ b/src/renderer/tr_local.h
@@ -30,6 +30,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "../qcommon/qcommon.h"
#include "tr_public.h"
#include "qgl.h"
+#include "iqm.h"
#define GL_INDEX_TYPE GL_UNSIGNED_INT
typedef unsigned int glIndex_t;
@@ -534,6 +535,7 @@ typedef enum {
#ifdef RAVENMD4
SF_MDR,
#endif
+ SF_IQM,
SF_FLARE,
SF_ENTITY, // beams, rails, lightning, etc that can be determined by entity
SF_DISPLAY_LIST,
@@ -640,6 +642,39 @@ typedef struct {
drawVert_t *verts;
} srfTriangles_t;
+// inter-quake-model
+typedef struct {
+ int num_vertexes;
+ int num_triangles;
+ int num_frames;
+ int num_surfaces;
+ int num_joints;
+ struct srfIQModel_s *surfaces;
+
+ float *positions;
+ float *texcoords;
+ float *normals;
+ float *tangents;
+ byte *blendIndexes;
+ byte *blendWeights;
+ byte *colors;
+ int *triangles;
+
+ int *jointParents;
+ float *poseMats;
+ float *bounds;
+ char *names;
+} iqmData_t;
+
+// inter-quake-model surface
+typedef struct srfIQModel_s {
+ surfaceType_t surfaceType;
+ shader_t *shader;
+ iqmData_t *data;
+ int first_vertex, num_vertexes;
+ int first_triangle, num_triangles;
+} srfIQModel_t;
+
extern void (*rb_surfaceTable[SF_NUM_SURFACE_TYPES])(void *);
@@ -748,19 +783,20 @@ typedef enum {
MOD_MESH,
MOD_MD4,
#ifdef RAVENMD4
- MOD_MDR
+ MOD_MDR,
#endif
+ MOD_IQM
} modtype_t;
typedef struct model_s {
char name[MAX_QPATH];
modtype_t type;
- int index; // model = tr.models[model->index]
+ int index; // model = tr.models[model->index]
- int dataSize; // just for listing purposes
- bmodel_t *bmodel; // only if type == MOD_BRUSH
+ int dataSize; // just for listing purposes
+ bmodel_t *bmodel; // only if type == MOD_BRUSH
md3Header_t *md3[MD3_MAX_LODS]; // only if type == MOD_MESH
- void *md4; // only if type == (MOD_MD4 | MOD_MDR)
+ void *modelData; // only if type == (MOD_MD4 | MOD_MDR | MOD_IQM)
int numLods;
} model_t;
@@ -1490,6 +1526,12 @@ void RB_SurfaceAnim( md4Surface_t *surfType );
void R_MDRAddAnimSurfaces( trRefEntity_t *ent );
void RB_MDRSurfaceAnim( md4Surface_t *surface );
#endif
+qboolean R_LoadIQM (model_t *mod, void *buffer, int filesize, const char *name );
+void R_AddIQMSurfaces( trRefEntity_t *ent );
+void RB_IQMSurfaceAnim( surfaceType_t *surface );
+int R_IQMLerpTag( orientation_t *tag, iqmData_t *data,
+ int startFrame, int endFrame,
+ float frac, const char *tagName );
/*
=============================================================
diff --git a/src/renderer/tr_main.c b/src/renderer/tr_main.c
index 23ef8241..ef096061 100644
--- a/src/renderer/tr_main.c
+++ b/src/renderer/tr_main.c
@@ -1260,6 +1260,9 @@ void R_AddEntitySurfaces (void) {
R_MDRAddAnimSurfaces( ent );
break;
#endif
+ case MOD_IQM:
+ R_AddIQMSurfaces( ent );
+ break;
case MOD_BRUSH:
R_AddBrushModelSurfaces( ent );
break;
diff --git a/src/renderer/tr_model.c b/src/renderer/tr_model.c
index b02d52a4..2933e7e0 100644
--- a/src/renderer/tr_model.c
+++ b/src/renderer/tr_model.c
@@ -26,13 +26,194 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define LL(x) x=LittleLong(x)
-static qboolean R_LoadMD3 (model_t *mod, int lod, void *buffer, const char *name );
-static qboolean R_LoadMD4 (model_t *mod, void *buffer, const char *name );
+static qboolean R_LoadMD3(model_t *mod, int lod, void *buffer, const char *name );
+static qboolean R_LoadMD4(model_t *mod, void *buffer, const char *name );
#ifdef RAVENMD4
-static qboolean R_LoadMDR (model_t *mod, void *buffer, int filesize, const char *name );
+static qboolean R_LoadMDR(model_t *mod, void *buffer, int filesize, const char *name );
#endif
-model_t *loadmodel;
+/*
+====================
+R_RegisterMD3
+====================
+*/
+qhandle_t R_RegisterMD3(const char *name, model_t *mod)
+{
+ union {
+ unsigned *u;
+ void *v;
+ } buf;
+ int lod;
+ int ident;
+ qboolean loaded = qfalse;
+ int numLoaded;
+ char filename[MAX_QPATH], namebuf[MAX_QPATH+20];
+ char *fext, defex[] = "md3";
+
+ numLoaded = 0;
+
+ strcpy(filename, name);
+
+ fext = strchr(filename, '.');
+ if(!fext)
+ fext = defex;
+ else
+ {
+ *fext = '\0';
+ fext++;
+ }
+
+ for (lod = MD3_MAX_LODS - 1 ; lod >= 0 ; lod--)
+ {
+ if(lod)
+ Com_sprintf(namebuf, sizeof(namebuf), "%s_%d.%s", filename, lod, fext);
+ else
+ Com_sprintf(namebuf, sizeof(namebuf), "%s.%s", filename, fext);
+
+ ri.FS_ReadFile( namebuf, &buf.v );
+ if(!buf.u)
+ continue;
+
+ ident = LittleLong(* (unsigned *) buf.u);
+ if (ident == MD4_IDENT)
+ loaded = R_LoadMD4(mod, buf.u, name);
+ else
+ {
+ if (ident == MD3_IDENT)
+ loaded = R_LoadMD3(mod, lod, buf.u, name);
+ else
+ ri.Printf(PRINT_WARNING,"RE_RegisterMD3: unknown fileid for %s\n", name);
+ }
+
+ ri.FS_FreeFile(buf.v);
+
+ if(loaded)
+ {
+ mod->numLods++;
+ numLoaded++;
+ }
+ else
+ break;
+ }
+
+ if(numLoaded)
+ {
+ // duplicate into higher lod spots that weren't
+ // loaded, in case the user changes r_lodbias on the fly
+ for(lod--; lod >= 0; lod--)
+ {
+ mod->numLods++;
+ mod->md3[lod] = mod->md3[lod + 1];
+ }
+
+ return mod->index;
+ }
+
+#ifdef _DEBUG
+ ri.Printf(PRINT_WARNING,"RE_RegisterMD3: couldn't load %s\n", name);
+#endif
+
+ mod->type = MOD_BAD;
+ return 0;
+}
+
+#ifdef RAVENMD4
+/*
+====================
+R_RegisterMDR
+====================
+*/
+qhandle_t R_RegisterMDR(const char *name, model_t *mod)
+{
+ union {
+ unsigned *u;
+ void *v;
+ } buf;
+ int ident;
+ qboolean loaded = qfalse;
+ int filesize;
+
+ filesize = ri.FS_ReadFile(name, (void **) &buf.v);
+ if(!buf.u)
+ {
+ mod->type = MOD_BAD;
+ return 0;
+ }
+
+ ident = LittleLong(*(unsigned *)buf.u);
+ if(ident == MDR_IDENT)
+ loaded = R_LoadMDR(mod, buf.u, filesize, name);
+
+ ri.FS_FreeFile (buf.v);
+
+ if(!loaded)
+ {
+ ri.Printf(PRINT_WARNING,"RE_RegisterMDR: couldn't load mdr file %s\n", name);
+ mod->type = MOD_BAD;
+ return 0;
+ }
+
+ return mod->index;
+}
+#endif
+
+/*
+====================
+R_RegisterIQM
+====================
+*/
+qhandle_t R_RegisterIQM(const char *name, model_t *mod)
+{
+ union {
+ unsigned *u;
+ void *v;
+ } buf;
+ qboolean loaded = qfalse;
+ int filesize;
+
+ filesize = ri.FS_ReadFile(name, (void **) &buf.v);
+ if(!buf.u)
+ {
+ mod->type = MOD_BAD;
+ return 0;
+ }
+
+ loaded = R_LoadIQM(mod, buf.u, filesize, name);
+
+ ri.FS_FreeFile (buf.v);
+
+ if(!loaded)
+ {
+ ri.Printf(PRINT_WARNING,"RE_RegisterIQM: couldn't load mdr file %s\n", name);
+ mod->type = MOD_BAD;
+ return 0;
+ }
+
+ return mod->index;
+}
+
+
+typedef struct
+{
+ char *ext;
+ qhandle_t (*ModelLoader)( const char *, model_t * );
+} modelExtToLoaderMap_t;
+
+// Note that the ordering indicates the order of preference used
+// when there are multiple models of different formats available
+static modelExtToLoaderMap_t modelLoaders[ ] =
+{
+#ifdef RAVENMD4
+ { "mdr", R_RegisterMDR },
+#endif
+ { "md4", R_RegisterMD3 },
+ { "md3", R_RegisterMD3 },
+ { "iqm", R_RegisterIQM }
+};
+
+static int numModelLoaders = ARRAY_LEN(modelLoaders);
+
+//===============================================================================
/*
** R_GetModelByHandle
@@ -84,16 +265,13 @@ asked for again.
*/
qhandle_t RE_RegisterModel( const char *name ) {
model_t *mod;
- union {
- unsigned *u;
- void *v;
- } buf;
- int lod;
- int ident;
- qboolean loaded = qfalse;
qhandle_t hModel;
- int numLoaded;
- char *fext, defex[] = "md3", filename[MAX_QPATH], namebuf[MAX_QPATH+20];
+ qboolean orgNameFailed = qfalse;
+ int orgLoader = -1;
+ int i;
+ char localName[ MAX_QPATH ];
+ const char *ext;
+ char *altName;
if ( !name || !name[0] ) {
ri.Printf( PRINT_ALL, "RE_RegisterModel: NULL name\n" );
@@ -132,125 +310,75 @@ qhandle_t RE_RegisterModel( const char *name ) {
// make sure the render thread is stopped
R_SyncRenderThread();
+ mod->type = MOD_BAD;
mod->numLods = 0;
//
// load the files
//
- numLoaded = 0;
+ Q_strncpyz( localName, name, MAX_QPATH );
- strcpy(filename, name);
+ ext = COM_GetExtension( localName );
- fext = strchr(filename, '.');
- if(!fext)
- fext = defex;
- else
+ if( *ext )
{
- *fext = '\0';
- fext++;
- }
-
-#ifdef RAVENMD4
- if(!Q_stricmp(fext, "mdr"))
- {
- int filesize;
-
- filesize = ri.FS_ReadFile(name, (void **) &buf.v);
- if(!buf.u)
+ // Look for the correct loader and use it
+ for( i = 0; i < numModelLoaders; i++ )
{
- ri.Printf (PRINT_WARNING,"RE_RegisterModel: couldn't load %s\n", name);
- mod->type = MOD_BAD;
- return 0;
+ if( !Q_stricmp( ext, modelLoaders[ i ].ext ) )
+ {
+ // Load
+ hModel = modelLoaders[ i ].ModelLoader( localName, mod );
+ break;
+ }
}
-
- ident = LittleLong(*(unsigned *)buf.u);
- if(ident == MDR_IDENT)
- loaded = R_LoadMDR(mod, buf.u, filesize, name);
- ri.FS_FreeFile (buf.v);
-
- if(!loaded)
+ // A loader was found
+ if( i < numModelLoaders )
{
- ri.Printf(PRINT_WARNING,"RE_RegisterModel: couldn't load mdr file %s\n", name);
- mod->type = MOD_BAD;
- return 0;
+ if( !hModel )
+ {
+ // Loader failed, most likely because the file isn't there;
+ // try again without the extension
+ orgNameFailed = qtrue;
+ orgLoader = i;
+ COM_StripExtension( name, localName, MAX_QPATH );
+ }
+ else
+ {
+ // Something loaded
+ return mod->index;
+ }
}
-
- return mod->index;
}
-#endif
-
- fext = defex;
-
- for ( lod = MD3_MAX_LODS - 1 ; lod >= 0 ; lod-- ) {
- if ( lod )
- Com_sprintf(namebuf, sizeof(namebuf), "%s_%d.%s", filename, lod, fext);
- else
- Com_sprintf(namebuf, sizeof(namebuf), "%s.%s", filename, fext);
- ri.FS_ReadFile( namebuf, &buf.v );
- if ( !buf.u ) {
+ // Try and find a suitable match using all
+ // the model formats supported
+ for( i = 0; i < numModelLoaders; i++ )
+ {
+ if (i == orgLoader)
continue;
- }
-
- loadmodel = mod;
-
- ident = LittleLong(*(unsigned *)buf.u);
- if ( ident == MD4_IDENT ) {
- loaded = R_LoadMD4( mod, buf.u, name );
- } else {
- if ( ident != MD3_IDENT ) {
- ri.Printf (PRINT_WARNING,"RE_RegisterModel: unknown fileid for %s\n", name);
- goto fail;
- }
- loaded = R_LoadMD3( mod, lod, buf.u, name );
- }
-
- ri.FS_FreeFile (buf.v);
+ altName = va( "%s.%s", localName, modelLoaders[ i ].ext );
- if ( !loaded ) {
- if ( lod == 0 ) {
- goto fail;
- } else {
- break;
+ // Load
+ hModel = modelLoaders[ i ].ModelLoader( altName, mod );
+
+ if( hModel )
+ {
+ if( orgNameFailed )
+ {
+ ri.Printf( PRINT_DEVELOPER, "WARNING: %s not present, using %s instead\n",
+ name, altName );
}
- } else {
- mod->numLods++;
- numLoaded++;
- // if we have a valid model and are biased
- // so that we won't see any higher detail ones,
- // stop loading them
-// if ( lod <= r_lodbias->integer ) {
-// break;
-// }
- }
- }
- if ( numLoaded ) {
- // duplicate into higher lod spots that weren't
- // loaded, in case the user changes r_lodbias on the fly
- for ( lod-- ; lod >= 0 ; lod-- ) {
- mod->numLods++;
- mod->md3[lod] = mod->md3[lod+1];
+ break;
}
-
- return mod->index;
- }
-#ifdef _DEBUG
- else {
- ri.Printf (PRINT_WARNING,"RE_RegisterModel: couldn't load %s\n", name);
}
-#endif
-fail:
- // we still keep the model_t around, so if the model name is asked for
- // again, we won't bother scanning the filesystem
- mod->type = MOD_BAD;
- return 0;
+ return hModel;
}
-
/*
=================
R_LoadMD3
@@ -780,7 +908,7 @@ static qboolean R_LoadMD4( model_t *mod, void *buffer, const char *mod_name ) {
mod->type = MOD_MD4;
size = LittleLong(pinmodel->ofsEnd);
mod->dataSize += size;
- md4 = mod->md4 = ri.Hunk_Alloc( size, h_low );
+ mod->modelData = md4 = ri.Hunk_Alloc( size, h_low );
Com_Memcpy(md4, buffer, size);
@@ -1071,16 +1199,20 @@ int R_LerpTag( orientation_t *tag, qhandle_t handle, int startFrame, int endFram
if ( !model->md3[0] )
{
#ifdef RAVENMD4
- if(model->md4)
+ if(model->type == MOD_MDR)
{
start = &start_space;
end = &end_space;
- R_GetAnimTag((mdrHeader_t *) model->md4, startFrame, tagName, start);
- R_GetAnimTag((mdrHeader_t *) model->md4, endFrame, tagName, end);
+ R_GetAnimTag((mdrHeader_t *) model->modelData, startFrame, tagName, start);
+ R_GetAnimTag((mdrHeader_t *) model->modelData, endFrame, tagName, end);
}
else
#endif
- {
+ if( model->type == MOD_IQM ) {
+ return R_IQMLerpTag( tag, model->modelData,
+ startFrame, endFrame,
+ frac, tagName );
+ } else {
AxisClear( tag->axis );
VectorClear( tag->origin );
@@ -1122,28 +1254,62 @@ R_ModelBounds
*/
void R_ModelBounds( qhandle_t handle, vec3_t mins, vec3_t maxs ) {
model_t *model;
- md3Header_t *header;
- md3Frame_t *frame;
model = R_GetModelByHandle( handle );
- if ( model->bmodel ) {
+ if(model->type == MOD_BRUSH) {
VectorCopy( model->bmodel->bounds[0], mins );
VectorCopy( model->bmodel->bounds[1], maxs );
+
return;
- }
+ } else if (model->type == MOD_MESH) {
+ md3Header_t *header;
+ md3Frame_t *frame;
+
+ header = model->md3[0];
+ frame = (md3Frame_t *) ((byte *)header + header->ofsFrames);
- if ( !model->md3[0] ) {
- VectorClear( mins );
- VectorClear( maxs );
+ VectorCopy( frame->bounds[0], mins );
+ VectorCopy( frame->bounds[1], maxs );
+
return;
- }
+ } else if (model->type == MOD_MD4) {
+ md4Header_t *header;
+ md4Frame_t *frame;
+
+ header = (md4Header_t *)model->modelData;
+ frame = (md4Frame_t *) ((byte *)header + header->ofsFrames);
+
+ VectorCopy( frame->bounds[0], mins );
+ VectorCopy( frame->bounds[1], maxs );
+
+ return;
+#ifdef RAVENMD4
+ } else if (model->type == MOD_MDR) {
+ mdrHeader_t *header;
+ mdrFrame_t *frame;
- header = model->md3[0];
+ header = (mdrHeader_t *)model->modelData;
+ frame = (mdrFrame_t *) ((byte *)header + header->ofsFrames);
- frame = (md3Frame_t *)( (byte *)header + header->ofsFrames );
+ VectorCopy( frame->bounds[0], mins );
+ VectorCopy( frame->bounds[1], maxs );
+
+ return;
+#endif
+ } else if(model->type == MOD_IQM) {
+ iqmData_t *iqmData;
+
+ iqmData = model->modelData;
- VectorCopy( frame->bounds[0], mins );
- VectorCopy( frame->bounds[1], maxs );
-}
+ if(iqmData->bounds)
+ {
+ VectorCopy(iqmData->bounds, mins);
+ VectorCopy(iqmData->bounds + 3, maxs);
+ return;
+ }
+ }
+ VectorClear( mins );
+ VectorClear( maxs );
+}
diff --git a/src/renderer/tr_model_iqm.c b/src/renderer/tr_model_iqm.c
new file mode 100644
index 00000000..2a0b1aad
--- /dev/null
+++ b/src/renderer/tr_model_iqm.c
@@ -0,0 +1,839 @@
+/* copyright */
+
+#include "tr_local.h"
+
+#define LL(x) x=LittleLong(x)
+
+static qboolean IQM_CheckRange( iqmHeader_t *header, int offset,
+ int count,int size ) {
+ // return true if the range specified by offset, count and size
+ // doesn't fit into the file
+ return ( count <= 0 ||
+ offset < 0 ||
+ offset > header->filesize ||
+ offset + count * size < 0 ||
+ offset + count * size > header->filesize );
+}
+// "multiply" 3x4 matrices, these are assumed to be the top 3 rows
+// of a 4x4 matrix with the last row = (0 0 0 1)
+static void Matrix34Multiply( float *a, float *b, float *out ) {
+ out[ 0] = a[0] * b[0] + a[1] * b[4] + a[ 2] * b[ 8];
+ out[ 1] = a[0] * b[1] + a[1] * b[5] + a[ 2] * b[ 9];
+ out[ 2] = a[0] * b[2] + a[1] * b[6] + a[ 2] * b[10];
+ out[ 3] = a[0] * b[3] + a[1] * b[7] + a[ 2] * b[11] + a[ 3];
+ out[ 4] = a[4] * b[0] + a[5] * b[4] + a[ 6] * b[ 8];
+ out[ 5] = a[4] * b[1] + a[5] * b[5] + a[ 6] * b[ 9];
+ out[ 6] = a[4] * b[2] + a[5] * b[6] + a[ 6] * b[10];
+ out[ 7] = a[4] * b[3] + a[5] * b[7] + a[ 6] * b[11] + a[ 7];
+ out[ 8] = a[8] * b[0] + a[9] * b[4] + a[10] * b[ 8];
+ out[ 9] = a[8] * b[1] + a[9] * b[5] + a[10] * b[ 9];
+ out[10] = a[8] * b[2] + a[9] * b[6] + a[10] * b[10];
+ out[11] = a[8] * b[3] + a[9] * b[7] + a[10] * b[11] + a[11];
+}
+static void InterpolateMatrix( float *a, float *b, float lerp, float *mat ) {
+ float unLerp = 1.0f - lerp;
+
+ mat[ 0] = a[ 0] * unLerp + b[ 0] * lerp;
+ mat[ 1] = a[ 1] * unLerp + b[ 1] * lerp;
+ mat[ 2] = a[ 2] * unLerp + b[ 2] * lerp;
+ mat[ 3] = a[ 3] * unLerp + b[ 3] * lerp;
+ mat[ 4] = a[ 4] * unLerp + b[ 4] * lerp;
+ mat[ 5] = a[ 5] * unLerp + b[ 5] * lerp;
+ mat[ 6] = a[ 6] * unLerp + b[ 6] * lerp;
+ mat[ 7] = a[ 7] * unLerp + b[ 7] * lerp;
+ mat[ 8] = a[ 8] * unLerp + b[ 8] * lerp;
+ mat[ 9] = a[ 9] * unLerp + b[ 9] * lerp;
+ mat[10] = a[10] * unLerp + b[10] * lerp;
+ mat[11] = a[11] * unLerp + b[11] * lerp;
+}
+
+/*
+=================
+R_LoadIQM
+=================
+Load an IQM model and compute the joint matrices for every frame.
+*/
+qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_name ) {
+ iqmHeader_t *header;
+ iqmVertexArray_t *vertexarray;
+ iqmTriangle_t *triangle;
+ iqmMesh_t *mesh;
+ iqmJoint_t *joint;
+ iqmPose_t *pose;
+ iqmBounds_t *bounds;
+ unsigned short *framedata;
+ char *str;
+ int i, j;
+ float *jointMats, *mat;
+ size_t size, joint_names;
+ iqmData_t *iqmData;
+ srfIQModel_t *surface;
+
+ if( filesize < sizeof(iqmHeader_t) ) {
+ return qfalse;
+ }
+
+ header = (iqmHeader_t *)buffer;
+ if( Q_strncmp( header->magic, IQM_MAGIC, sizeof(header->magic) ) ) {
+ return qfalse;
+ }
+
+ LL( header->version );
+ if( header->version != IQM_VERSION ) {
+ return qfalse;
+ }
+
+ LL( header->filesize );
+ if( header->filesize > filesize || header->filesize > 16<<20 ) {
+ return qfalse;
+ }
+
+ LL( header->flags );
+ LL( header->num_text );
+ LL( header->ofs_text );
+ LL( header->num_meshes );
+ LL( header->ofs_meshes );
+ LL( header->num_vertexarrays );
+ LL( header->num_vertexes );
+ LL( header->ofs_vertexarrays );
+ LL( header->num_triangles );
+ LL( header->ofs_triangles );
+ LL( header->ofs_adjacency );
+ LL( header->num_joints );
+ LL( header->ofs_joints );
+ LL( header->num_poses );
+ LL( header->ofs_poses );
+ LL( header->num_anims );
+ LL( header->ofs_anims );
+ LL( header->num_frames );
+ LL( header->num_framechannels );
+ LL( header->ofs_frames );
+ LL( header->ofs_bounds );
+ LL( header->num_comment );
+ LL( header->ofs_comment );
+ LL( header->num_extensions );
+ LL( header->ofs_extensions );
+
+ // check and swap vertex arrays
+ if( IQM_CheckRange( header, header->ofs_vertexarrays,
+ header->num_vertexarrays,
+ sizeof(iqmVertexArray_t) ) ) {
+ return qfalse;
+ }
+ vertexarray = (iqmVertexArray_t *)((byte *)header + header->ofs_vertexarrays);
+ for( i = 0; i < header->num_vertexarrays; i++, vertexarray++ ) {
+ int j, n, *intPtr;
+
+ if( vertexarray->size <= 0 || vertexarray->size > 4 ) {
+ return qfalse;
+ }
+
+ // total number of values
+ n = header->num_vertexes * vertexarray->size;
+
+ switch( vertexarray->format ) {
+ case IQM_BYTE:
+ case IQM_UBYTE:
+ // 1 byte, no swapping necessary
+ if( IQM_CheckRange( header, vertexarray->offset,
+ n, sizeof(byte) ) ) {
+ return qfalse;
+ }
+ break;
+ case IQM_INT:
+ case IQM_UINT:
+ case IQM_FLOAT:
+ // 4-byte swap
+ if( IQM_CheckRange( header, vertexarray->offset,
+ n, sizeof(float) ) ) {
+ return qfalse;
+ }
+ intPtr = (int *)((byte *)header + vertexarray->offset);
+ for( j = 0; j < n; j++, intPtr++ ) {
+ LL( *intPtr );
+ }
+ break;
+ default:
+ // not supported
+ return qfalse;
+ break;
+ }
+
+ switch( vertexarray->type ) {
+ case IQM_POSITION:
+ case IQM_NORMAL:
+ if( vertexarray->format != IQM_FLOAT ||
+ vertexarray->size != 3 ) {
+ return qfalse;
+ }
+ break;
+ case IQM_TANGENT:
+ if( vertexarray->format != IQM_FLOAT ||
+ vertexarray->size != 4 ) {
+ return qfalse;
+ }
+ break;
+ case IQM_TEXCOORD:
+ if( vertexarray->format != IQM_FLOAT ||
+ vertexarray->size != 2 ) {
+ return qfalse;
+ }
+ break;
+ case IQM_BLENDINDEXES:
+ case IQM_BLENDWEIGHTS:
+ if( vertexarray->format != IQM_UBYTE ||
+ vertexarray->size != 4 ) {
+ return qfalse;
+ }
+ break;
+ case IQM_COLOR:
+ if( vertexarray->format != IQM_UBYTE ||
+ vertexarray->size != 4 ) {
+ return qfalse;
+ }
+ break;
+ }
+ }
+
+ // check and swap triangles
+ if( IQM_CheckRange( header, header->ofs_triangles,
+ header->num_triangles, sizeof(iqmTriangle_t) ) ) {
+ return qfalse;
+ }
+ triangle = (iqmTriangle_t *)((byte *)header + header->ofs_triangles);
+ for( i = 0; i < header->num_triangles; i++, triangle++ ) {
+ LL( triangle->vertex[0] );
+ LL( triangle->vertex[1] );
+ LL( triangle->vertex[2] );
+
+ if( triangle->vertex[0] < 0 || triangle->vertex[0] > header->num_vertexes ||
+ triangle->vertex[1] < 0 || triangle->vertex[1] > header->num_vertexes ||
+ triangle->vertex[2] < 0 || triangle->vertex[2] > header->num_vertexes ) {
+ return qfalse;
+ }
+ }
+
+ // check and swap meshes
+ if( IQM_CheckRange( header, header->ofs_meshes,
+ header->num_meshes, sizeof(iqmMesh_t) ) ) {
+ return qfalse;
+ }
+ mesh = (iqmMesh_t *)((byte *)header + header->ofs_meshes);
+ for( i = 0; i < header->num_meshes; i++, mesh++) {
+ LL( mesh->name );
+ LL( mesh->material );
+ LL( mesh->first_vertex );
+ LL( mesh->num_vertexes );
+ LL( mesh->first_triangle );
+ LL( mesh->num_triangles );
+
+ if( mesh->first_vertex >= header->num_vertexes ||
+ mesh->first_vertex + mesh->num_vertexes > header->num_vertexes ||
+ mesh->first_triangle >= header->num_triangles ||
+ mesh->first_triangle + mesh->num_triangles > header->num_triangles ||
+ mesh->name < 0 ||
+ mesh->name >= header->num_text ||
+ mesh->material < 0 ||
+ mesh->material >= header->num_text ) {
+ return qfalse;
+ }
+ }
+
+ // check and swap joints
+ if( IQM_CheckRange( header, header->ofs_joints,
+ header->num_joints, sizeof(iqmJoint_t) ) ) {
+ return qfalse;
+ }
+ joint = (iqmJoint_t *)((byte *)header + header->ofs_joints);
+ joint_names = 0;
+ for( i = 0; i < header->num_joints; i++, joint++ ) {
+ LL( joint->name );
+ LL( joint->parent );
+ LL( joint->translate[0] );
+ LL( joint->translate[1] );
+ LL( joint->translate[2] );
+ LL( joint->rotate[0] );
+ LL( joint->rotate[1] );
+ LL( joint->rotate[2] );
+ LL( joint->scale[0] );
+ LL( joint->scale[1] );
+ LL( joint->scale[2] );
+
+ if( joint->parent < -1 ||
+ joint->parent >= (int)header->num_joints ||
+ joint->name < 0 ||
+ joint->name >= (int)header->num_text ) {
+ return qfalse;
+ }
+ joint_names += strlen( (char *)header + header->ofs_text +
+ joint->name ) + 1;
+ }
+
+ // check and swap poses
+ if( header->num_poses != header->num_joints ) {
+ return qfalse;
+ }
+ if( IQM_CheckRange( header, header->ofs_poses,
+ header->num_poses, sizeof(iqmPose_t) ) ) {
+ return qfalse;
+ }
+ pose = (iqmPose_t *)((byte *)header + header->ofs_poses);
+ for( i = 0; i < header->num_poses; i++, pose++ ) {
+ LL( pose->parent );
+ LL( pose->mask );
+ LL( pose->channeloffset[0] );
+ LL( pose->channeloffset[1] );
+ LL( pose->channeloffset[2] );
+ LL( pose->channeloffset[3] );
+ LL( pose->channeloffset[4] );
+ LL( pose->channeloffset[5] );
+ LL( pose->channeloffset[6] );
+ LL( pose->channeloffset[7] );
+ LL( pose->channeloffset[8] );
+ LL( pose->channelscale[0] );
+ LL( pose->channelscale[1] );
+ LL( pose->channelscale[2] );
+ LL( pose->channelscale[3] );
+ LL( pose->channelscale[4] );
+ LL( pose->channelscale[5] );
+ LL( pose->channelscale[6] );
+ LL( pose->channelscale[7] );
+ LL( pose->channelscale[8] );
+ }
+
+ // check and swap model bounds
+ if(IQM_CheckRange(header, header->ofs_bounds,
+ header->num_frames, sizeof(*bounds)))
+ {
+ return qfalse;
+ }
+ bounds = (iqmBounds_t *) ((byte *) header + header->ofs_bounds);
+ for(i = 0; i < header->num_poses; i++)
+ {
+ LL(bounds->bbmin[0]);
+ LL(bounds->bbmin[1]);
+ LL(bounds->bbmin[2]);
+ LL(bounds->bbmax[0]);
+ LL(bounds->bbmax[1]);
+ LL(bounds->bbmax[2]);
+
+ bounds++;
+ }
+
+ // allocate the model and copy the data
+ size = sizeof(iqmData_t);
+ size += header->num_meshes * sizeof( srfIQModel_t );
+ size += header->num_joints * header->num_frames * 12 * sizeof( float );
+ if(header->ofs_bounds)
+ size += header->num_frames * 6 * sizeof(float); // model bounds
+ size += header->num_vertexes * 3 * sizeof(float); // positions
+ size += header->num_vertexes * 2 * sizeof(float); // texcoords
+ size += header->num_vertexes * 3 * sizeof(float); // normals
+ size += header->num_vertexes * 4 * sizeof(float); // tangents
+ size += header->num_vertexes * 4 * sizeof(byte); // blendIndexes
+ size += header->num_vertexes * 4 * sizeof(byte); // blendWeights
+ size += header->num_vertexes * 4 * sizeof(byte); // colors
+ size += header->num_joints * sizeof(int); // parents
+ size += header->num_triangles * 3 * sizeof(int); // triangles
+ size += joint_names; // joint names
+
+ mod->type = MOD_IQM;
+ iqmData = (iqmData_t *)ri.Hunk_Alloc( size, h_low );
+ mod->modelData = iqmData;
+
+ // fill header
+ iqmData->num_vertexes = header->num_vertexes;
+ iqmData->num_triangles = header->num_triangles;
+ iqmData->num_frames = header->num_frames;
+ iqmData->num_surfaces = header->num_meshes;
+ iqmData->num_joints = header->num_joints;
+ iqmData->surfaces = (srfIQModel_t *)(iqmData + 1);
+ iqmData->poseMats = (float *) (iqmData->surfaces + iqmData->num_surfaces);
+ if(header->ofs_bounds)
+ {
+ iqmData->bounds = iqmData->poseMats + 12 * header->num_joints * header->num_frames;
+ iqmData->positions = iqmData->bounds + 6 * header->num_frames;
+ }
+ else
+ iqmData->positions = iqmData->poseMats + 12 * header->num_joints * header->num_frames;
+ iqmData->texcoords = iqmData->positions + 3 * header->num_vertexes;
+ iqmData->normals = iqmData->texcoords + 2 * header->num_vertexes;
+ iqmData->tangents = iqmData->normals + 3 * header->num_vertexes;
+ iqmData->blendIndexes = (byte *)(iqmData->tangents + 4 * header->num_vertexes);
+ iqmData->blendWeights = iqmData->blendIndexes + 4 * header->num_vertexes;
+ iqmData->colors = iqmData->blendWeights + 4 * header->num_vertexes;
+ iqmData->jointParents = (int *)(iqmData->colors + 4 * header->num_vertexes);
+ iqmData->triangles = iqmData->jointParents + header->num_joints;
+ iqmData->names = (char *)(iqmData->triangles + 3 * header->num_triangles);
+
+ // calculate joint matrices and their inverses
+ // they are needed only until the pose matrices are calculated
+ jointMats = (float *)ri.Hunk_AllocateTempMemory( header->num_joints * 2 * 3 * 4 * sizeof(float) );
+ mat = jointMats;
+ joint = (iqmJoint_t *)((byte *)header + header->ofs_joints);
+ for( i = 0; i < header->num_joints; i++, joint++ ) {
+ float tmpMat[12];
+
+ float rotW = DotProduct(joint->rotate, joint->rotate);
+ rotW = -SQRTFAST(1.0f - rotW);
+
+ float xx = 2.0f * joint->rotate[0] * joint->rotate[0];
+ float yy = 2.0f * joint->rotate[1] * joint->rotate[1];
+ float zz = 2.0f * joint->rotate[2] * joint->rotate[2];
+ float xy = 2.0f * joint->rotate[0] * joint->rotate[1];
+ float xz = 2.0f * joint->rotate[0] * joint->rotate[2];
+ float yz = 2.0f * joint->rotate[1] * joint->rotate[2];
+ float wx = 2.0f * rotW * joint->rotate[0];
+ float wy = 2.0f * rotW * joint->rotate[1];
+ float wz = 2.0f * rotW * joint->rotate[2];
+
+ tmpMat[ 0] = joint->scale[0] * (1.0f - (yy + zz));
+ tmpMat[ 1] = joint->scale[0] * (xy - wz);
+ tmpMat[ 2] = joint->scale[0] * (xz + wy);
+ tmpMat[ 3] = joint->translate[0];
+ tmpMat[ 4] = joint->scale[1] * (xy + wz);
+ tmpMat[ 5] = joint->scale[1] * (1.0f - (xx + zz));
+ tmpMat[ 6] = joint->scale[1] * (yz - wx);
+ tmpMat[ 7] = joint->translate[1];
+ tmpMat[ 8] = joint->scale[2] * (xz - wy);
+ tmpMat[ 9] = joint->scale[2] * (yz + wx);
+ tmpMat[10] = joint->scale[2] * (1.0f - (xx + yy));
+ tmpMat[11] = joint->translate[2];
+
+ if( joint->parent >= 0 ) {
+ // premultiply with parent-matrix
+ Matrix34Multiply( jointMats + 2 * 12 * joint->parent,
+ tmpMat, mat);
+ } else {
+ Com_Memcpy( mat, tmpMat, sizeof(tmpMat) );
+ }
+
+ mat += 12;
+
+ // compute the inverse matrix by combining the
+ // inverse scale, rotation and translation
+ tmpMat[ 0] = joint->scale[0] * (1.0f - (yy + zz));
+ tmpMat[ 1] = joint->scale[1] * (xy + wz);
+ tmpMat[ 2] = joint->scale[2] * (xz - wy);
+ tmpMat[ 3] = -DotProduct((tmpMat + 0), joint->translate);
+ tmpMat[ 4] = joint->scale[0] * (xy - wz);
+ tmpMat[ 5] = joint->scale[1] * (1.0f - (xx + zz));
+ tmpMat[ 6] = joint->scale[2] * (yz + wx);
+ tmpMat[ 7] = -DotProduct((tmpMat + 4), joint->translate);
+ tmpMat[ 8] = joint->scale[0] * (xz + wy);
+ tmpMat[ 9] = joint->scale[1] * (yz - wx);
+ tmpMat[10] = joint->scale[2] * (1.0f - (xx + yy));
+ tmpMat[11] = -DotProduct((tmpMat + 8), joint->translate);
+
+ if( joint->parent >= 0 ) {
+ // premultiply with inverse parent-matrix
+ Matrix34Multiply( tmpMat,
+ jointMats + 2 * 12 * joint->parent + 12,
+ mat);
+ } else {
+ Com_Memcpy( mat, tmpMat, sizeof(tmpMat) );
+ }
+
+ mat += 12;
+ }
+
+ // calculate pose matrices
+ framedata = (unsigned short *)((byte *)header + header->ofs_frames);
+ mat = iqmData->poseMats;
+ for( i = 0; i < header->num_frames; i++ ) {
+ pose = (iqmPose_t *)((byte *)header + header->ofs_poses);
+ for( j = 0; j < header->num_poses; j++, pose++ ) {
+ vec3_t translate, rotate, scale;
+ float mat1[12], mat2[12];
+
+ translate[0] = pose->channeloffset[0];
+ if( pose->mask & 0x001)
+ translate[0] += *framedata++ * pose->channelscale[0];
+ translate[1] = pose->channeloffset[1];
+ if( pose->mask & 0x002)
+ translate[1] += *framedata++ * pose->channelscale[1];
+ translate[2] = pose->channeloffset[2];
+ if( pose->mask & 0x004)
+ translate[2] += *framedata++ * pose->channelscale[2];
+ rotate[0] = pose->channeloffset[3];
+ if( pose->mask & 0x008)
+ rotate[0] += *framedata++ * pose->channelscale[3];
+ rotate[1] = pose->channeloffset[4];
+ if( pose->mask & 0x010)
+ rotate[1] += *framedata++ * pose->channelscale[4];
+ rotate[2] = pose->channeloffset[5];
+ if( pose->mask & 0x020)
+ rotate[2] += *framedata++ * pose->channelscale[5];
+ scale[0] = pose->channeloffset[6];
+ if( pose->mask & 0x040)
+ scale[0] += *framedata++ * pose->channelscale[6];
+ scale[1] = pose->channeloffset[7];
+ if( pose->mask & 0x080)
+ scale[1] += *framedata++ * pose->channelscale[7];
+ scale[2] = pose->channeloffset[8];
+ if( pose->mask & 0x100)
+ scale[2] += *framedata++ * pose->channelscale[8];
+
+ // construct transformation matrix
+ float rotW = DotProduct(rotate, rotate);
+ rotW = -SQRTFAST(1.0f - rotW);
+
+ float xx = 2.0f * rotate[0] * rotate[0];
+ float yy = 2.0f * rotate[1] * rotate[1];
+ float zz = 2.0f * rotate[2] * rotate[2];
+ float xy = 2.0f * rotate[0] * rotate[1];
+ float xz = 2.0f * rotate[0] * rotate[2];
+ float yz = 2.0f * rotate[1] * rotate[2];
+ float wx = 2.0f * rotW * rotate[0];
+ float wy = 2.0f * rotW * rotate[1];
+ float wz = 2.0f * rotW * rotate[2];
+
+ mat1[ 0] = scale[0] * (1.0f - (yy + zz));
+ mat1[ 1] = scale[0] * (xy - wz);
+ mat1[ 2] = scale[0] * (xz + wy);
+ mat1[ 3] = translate[0];
+ mat1[ 4] = scale[1] * (xy + wz);
+ mat1[ 5] = scale[1] * (1.0f - (xx + zz));
+ mat1[ 6] = scale[1] * (yz - wx);
+ mat1[ 7] = translate[1];
+ mat1[ 8] = scale[2] * (xz - wy);
+ mat1[ 9] = scale[2] * (yz + wx);
+ mat1[10] = scale[2] * (1.0f - (xx + yy));
+ mat1[11] = translate[2];
+
+ if( pose->parent >= 0 ) {
+ Matrix34Multiply( jointMats + 12 * 2 * pose->parent,
+ mat1, mat2 );
+ } else {
+ Com_Memcpy( mat2, mat1, sizeof(mat1) );
+ }
+
+ Matrix34Multiply( mat2, jointMats + 12 * (2 * j + 1), mat );
+ mat += 12;
+ }
+ }
+ ri.Hunk_FreeTempMemory( jointMats );
+
+ // register shaders
+ // overwrite the material offset with the shader index
+ mesh = (iqmMesh_t *)((byte *)header + header->ofs_meshes);
+ surface = iqmData->surfaces;
+ str = (char *)header + header->ofs_text;
+ for( i = 0; i < header->num_meshes; i++, mesh++, surface++ ) {
+ surface->surfaceType = SF_IQM;
+ surface->shader = R_FindShader( str + mesh->material, LIGHTMAP_NONE, qtrue );
+ if( surface->shader->defaultShader )
+ surface->shader = tr.defaultShader;
+ surface->data = iqmData;
+ surface->first_vertex = mesh->first_vertex;
+ surface->num_vertexes = mesh->num_vertexes;
+ surface->first_triangle = mesh->first_triangle;
+ surface->num_triangles = mesh->num_triangles;
+ }
+
+ // copy vertexarrays and indexes
+ vertexarray = (iqmVertexArray_t *)((byte *)header + header->ofs_vertexarrays);
+ for( i = 0; i < header->num_vertexarrays; i++, vertexarray++ ) {
+ int n;
+
+ // total number of values
+ n = header->num_vertexes * vertexarray->size;
+
+ switch( vertexarray->type ) {
+ case IQM_POSITION:
+ Com_Memcpy( iqmData->positions,
+ (byte *)header + vertexarray->offset,
+ n * sizeof(float) );
+ break;
+ case IQM_NORMAL:
+ Com_Memcpy( iqmData->normals,
+ (byte *)header + vertexarray->offset,
+ n * sizeof(float) );
+ break;
+ case IQM_TANGENT:
+ Com_Memcpy( iqmData->tangents,
+ (byte *)header + vertexarray->offset,
+ n * sizeof(float) );
+ break;
+ case IQM_TEXCOORD:
+ Com_Memcpy( iqmData->texcoords,
+ (byte *)header + vertexarray->offset,
+ n * sizeof(float) );
+ break;
+ case IQM_BLENDINDEXES:
+ Com_Memcpy( iqmData->blendIndexes,
+ (byte *)header + vertexarray->offset,
+ n * sizeof(byte) );
+ break;
+ case IQM_BLENDWEIGHTS:
+ Com_Memcpy( iqmData->blendWeights,
+ (byte *)header + vertexarray->offset,
+ n * sizeof(byte) );
+ break;
+ case IQM_COLOR:
+ Com_Memcpy( iqmData->colors,
+ (byte *)header + vertexarray->offset,
+ n * sizeof(byte) );
+ break;
+ }
+ }
+
+ // copy joint parents
+ joint = (iqmJoint_t *)((byte *)header + header->ofs_joints);
+ for( i = 0; i < header->num_joints; i++, joint++ ) {
+ iqmData->jointParents[i] = joint->parent;
+ }
+
+ // copy triangles
+ triangle = (iqmTriangle_t *)((byte *)header + header->ofs_triangles);
+ for( i = 0; i < header->num_triangles; i++, triangle++ ) {
+ iqmData->triangles[3*i+0] = triangle->vertex[0];
+ iqmData->triangles[3*i+1] = triangle->vertex[1];
+ iqmData->triangles[3*i+2] = triangle->vertex[2];
+ }
+
+ // copy joint names
+ str = iqmData->names;
+ joint = (iqmJoint_t *)((byte *)header + header->ofs_joints);
+ for( i = 0; i < header->num_joints; i++, joint++ ) {
+ char *name = (char *)header + header->ofs_text +
+ joint->name;
+ int len = strlen( name ) + 1;
+ Com_Memcpy( str, name, len );
+ str += len;
+ }
+
+ // copy model bounds
+ if(header->ofs_bounds)
+ {
+ mat = iqmData->bounds;
+ bounds = (iqmBounds_t *) ((byte *) header + header->ofs_bounds);
+ for(i = 0; i < header->num_frames; i++)
+ {
+ mat[0] = bounds->bbmin[0];
+ mat[1] = bounds->bbmin[1];
+ mat[2] = bounds->bbmin[2];
+ mat[3] = bounds->bbmax[0];
+ mat[4] = bounds->bbmax[1];
+ mat[5] = bounds->bbmax[2];
+
+ mat += 6;
+ bounds++;
+ }
+ }
+
+ return qtrue;
+}
+
+/*
+=================
+R_AddIQMSurfaces
+=================
+Add all surfaces of this model
+*/
+void R_AddIQMSurfaces( trRefEntity_t *ent ) {
+ iqmData_t *data;
+ srfIQModel_t *surface;
+ int i;
+
+ data = tr.currentModel->modelData;
+ surface = data->surfaces;
+
+ R_SetupEntityLighting( &tr.refdef, ent );
+
+ for ( i = 0 ; i < data->num_surfaces ; i++ ) {
+ R_AddDrawSurf( &surface->surfaceType,
+ surface->shader, 0 /*fogNum*/, 0 );
+ surface++;
+ }
+}
+
+
+static void ComputeJointMats( iqmData_t *data, int frame, int oldframe,
+ float backlerp, float *mat ) {
+ float *mat1, *mat2;
+ int *joint = data->jointParents;
+ int i;
+
+ if ( oldframe == frame ) {
+ mat1 = mat2 = data->poseMats + 12 * data->num_joints * frame;
+ for( i = 0; i < data->num_joints; i++, joint++ ) {
+ if( *joint >= 0 ) {
+ Matrix34Multiply( mat + 12 * *joint,
+ mat1 + 12*i, mat + 12*i );
+ } else {
+ Com_Memcpy( mat + 12*i, mat1 + 12*i, 12 * sizeof(float) );
+ }
+ }
+ } else {
+ mat1 = data->poseMats + 12 * data->num_joints * frame;
+ mat2 = data->poseMats + 12 * data->num_joints * oldframe;
+
+ for( i = 0; i < 12 * data->num_joints; i++, joint++ ) {
+ if( *joint >= 0 ) {
+ float tmpMat[12];
+ InterpolateMatrix( mat1 + 12*i, mat2 + 12*i,
+ backlerp, tmpMat );
+ Matrix34Multiply( mat + 12 * *joint,
+ tmpMat, mat + 12*i );
+
+ } else {
+ InterpolateMatrix( mat1 + 12*i, mat2 + 12*i,
+ backlerp, mat );
+ }
+ }
+ }
+}
+
+
+/*
+=================
+RB_AddIQMSurfaces
+=================
+Compute vertices for this model surface
+*/
+void RB_IQMSurfaceAnim( surfaceType_t *surface ) {
+ srfIQModel_t *surf = (srfIQModel_t *)surface;
+ iqmData_t *data = surf->data;
+ int i;
+
+ vec4_t *outXYZ = &tess.xyz[tess.numVertexes];
+ vec4_t *outNormal = &tess.normal[tess.numVertexes];
+ vec2_t (*outTexCoord)[2] = &tess.texCoords[tess.numVertexes];
+ color4ub_t *outColor = &tess.vertexColors[tess.numVertexes];
+
+ float mat[data->num_joints * 12];
+ int frame = backEnd.currentEntity->e.frame % data->num_frames;
+ int oldframe = backEnd.currentEntity->e.oldframe % data->num_frames;
+ float backlerp = backEnd.currentEntity->e.backlerp;
+
+ RB_CHECKOVERFLOW( surf->num_vertexes, surf->num_triangles * 3 );
+
+ // compute interpolated joint matrices
+ ComputeJointMats( data, frame, oldframe, backlerp, mat );
+
+ // transform vertexes and fill other data
+ for( i = 0; i < surf->num_vertexes;
+ i++, outXYZ++, outNormal++, outTexCoord++, outColor++ ) {
+ int j, k;
+ float vtxMat[12];
+ float nrmMat[9];
+ int vtx = i + surf->first_vertex;
+
+ // compute the vertex matrix by blending the up to
+ // four blend weights
+ for( k = 0; k < 12; k++ )
+ vtxMat[k] = data->blendWeights[4*vtx]
+ * mat[12*data->blendIndexes[4*vtx] + k];
+ for( j = 1; j < 4; j++ ) {
+ if( data->blendWeights[4*vtx + j] <= 0 )
+ break;
+ for( k = 0; k < 12; k++ )
+ vtxMat[k] += data->blendWeights[4*vtx + j]
+ * mat[12*data->blendIndexes[4*vtx + j] + k];
+ }
+ for( k = 0; k < 12; k++ )
+ vtxMat[k] *= 1.0f / 255.0f;
+
+ // compute the normal matrix as transpose of the adjoint
+ // of the vertex matrix
+ nrmMat[ 0] = vtxMat[ 5]*vtxMat[10] - vtxMat[ 6]*vtxMat[ 9];
+ nrmMat[ 1] = vtxMat[ 6]*vtxMat[ 8] - vtxMat[ 4]*vtxMat[10];
+ nrmMat[ 2] = vtxMat[ 4]*vtxMat[ 9] - vtxMat[ 5]*vtxMat[ 8];
+ nrmMat[ 3] = vtxMat[ 2]*vtxMat[ 9] - vtxMat[ 1]*vtxMat[10];
+ nrmMat[ 4] = vtxMat[ 0]*vtxMat[10] - vtxMat[ 2]*vtxMat[ 8];
+ nrmMat[ 5] = vtxMat[ 1]*vtxMat[ 8] - vtxMat[ 0]*vtxMat[ 9];
+ nrmMat[ 6] = vtxMat[ 1]*vtxMat[ 6] - vtxMat[ 2]*vtxMat[ 5];
+ nrmMat[ 7] = vtxMat[ 2]*vtxMat[ 4] - vtxMat[ 0]*vtxMat[ 6];
+ nrmMat[ 8] = vtxMat[ 0]*vtxMat[ 5] - vtxMat[ 1]*vtxMat[ 4];
+
+ (*outTexCoord)[0][0] = data->texcoords[2*vtx + 0];
+ (*outTexCoord)[0][1] = data->texcoords[2*vtx + 1];
+ (*outTexCoord)[1][0] = (*outTexCoord)[0][0];
+ (*outTexCoord)[1][1] = (*outTexCoord)[0][1];
+
+ (*outXYZ)[0] =
+ vtxMat[ 0] * data->positions[3*vtx+0] +
+ vtxMat[ 1] * data->positions[3*vtx+1] +
+ vtxMat[ 2] * data->positions[3*vtx+2] +
+ vtxMat[ 3];
+ (*outXYZ)[1] =
+ vtxMat[ 4] * data->positions[3*vtx+0] +
+ vtxMat[ 5] * data->positions[3*vtx+1] +
+ vtxMat[ 6] * data->positions[3*vtx+2] +
+ vtxMat[ 7];
+ (*outXYZ)[2] =
+ vtxMat[ 8] * data->positions[3*vtx+0] +
+ vtxMat[ 9] * data->positions[3*vtx+1] +
+ vtxMat[10] * data->positions[3*vtx+2] +
+ vtxMat[11];
+ (*outXYZ)[3] = 1.0f;
+
+ (*outNormal)[0] =
+ nrmMat[ 0] * data->normals[3*vtx+0] +
+ nrmMat[ 1] * data->normals[3*vtx+1] +
+ nrmMat[ 2] * data->normals[3*vtx+2];
+ (*outNormal)[1] =
+ nrmMat[ 3] * data->normals[3*vtx+0] +
+ nrmMat[ 4] * data->normals[3*vtx+1] +
+ nrmMat[ 5] * data->normals[3*vtx+2];
+ (*outNormal)[2] =
+ nrmMat[ 6] * data->normals[3*vtx+0] +
+ nrmMat[ 7] * data->normals[3*vtx+1] +
+ nrmMat[ 8] * data->normals[3*vtx+2];
+ (*outNormal)[3] = 0.0f;
+
+ (*outColor)[0] = data->colors[4*vtx+0];
+ (*outColor)[1] = data->colors[4*vtx+1];
+ (*outColor)[2] = data->colors[4*vtx+2];
+ (*outColor)[3] = data->colors[4*vtx+3];
+ }
+
+ int *tri = data->triangles;
+ tri += 3 * surf->first_triangle;
+
+ glIndex_t *ptr = &tess.indexes[tess.numIndexes];
+ glIndex_t base = tess.numVertexes;
+
+ for( i = 0; i < surf->num_triangles; i++ ) {
+ *ptr++ = base + (*tri++ - surf->first_vertex);
+ *ptr++ = base + (*tri++ - surf->first_vertex);
+ *ptr++ = base + (*tri++ - surf->first_vertex);
+ }
+
+ tess.numIndexes += 3 * surf->num_triangles;
+ tess.numVertexes += surf->num_vertexes;
+}
+
+int R_IQMLerpTag( orientation_t *tag, iqmData_t *data,
+ int startFrame, int endFrame,
+ float frac, const char *tagName ) {
+ int joint;
+ char *names = data->names;
+ float mat[data->num_joints * 12];
+
+ // get joint number by reading the joint names
+ for( joint = 0; joint < data->num_joints; joint++ ) {
+ if( !strcmp( tagName, names ) )
+ break;
+ names += strlen( names ) + 1;
+ }
+ if( joint >= data->num_joints )
+ return qfalse;
+
+ ComputeJointMats( data, startFrame, endFrame, frac, mat );
+ tag->axis[0][0] = mat[12 * joint + 0];
+ tag->axis[1][0] = mat[12 * joint + 1];
+ tag->axis[2][0] = mat[12 * joint + 2];
+ tag->origin[0] = mat[12 * joint + 3];
+ tag->axis[0][1] = mat[12 * joint + 4];
+ tag->axis[1][1] = mat[12 * joint + 5];
+ tag->axis[2][1] = mat[12 * joint + 6];
+ tag->origin[1] = mat[12 * joint + 7];
+ tag->axis[0][2] = mat[12 * joint + 8];
+ tag->axis[1][2] = mat[12 * joint + 9];
+ tag->axis[2][2] = mat[12 * joint + 10];
+ tag->origin[0] = mat[12 * joint + 11];
+
+ return qfalse;
+}
diff --git a/src/renderer/tr_surface.c b/src/renderer/tr_surface.c
index f14501f7..afb4121e 100644
--- a/src/renderer/tr_surface.c
+++ b/src/renderer/tr_surface.c
@@ -1243,6 +1243,7 @@ void (*rb_surfaceTable[SF_NUM_SURFACE_TYPES])( void *) = {
#ifdef RAVENMD4
(void(*)(void*))RB_MDRSurfaceAnim, // SF_MDR,
#endif
+ (void(*)(void*))RB_IQMSurfaceAnim, // SF_IQM,
(void(*)(void*))RB_SurfaceFlare, // SF_FLARE,
(void(*)(void*))RB_SurfaceEntity, // SF_ENTITY
(void(*)(void*))RB_SurfaceDisplayList // SF_DISPLAY_LIST