From c5e2654b54d37569b03b617e10db9889fcfbf302 Mon Sep 17 00:00:00 2001 From: Thilo Schulz Date: Mon, 2 May 2011 15:50:19 +0000 Subject: [PATCH] - 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) --- Makefile | 1 + code/renderer/iqm.h | 107 +++++ code/renderer/tr_animation.c | 2 +- code/renderer/tr_local.h | 52 ++- code/renderer/tr_main.c | 3 + code/renderer/tr_model.c | 434 ++++++++++++------ code/renderer/tr_model_iqm.c | 839 +++++++++++++++++++++++++++++++++++ code/renderer/tr_surface.c | 1 + 8 files changed, 1299 insertions(+), 140 deletions(-) create mode 100644 code/renderer/iqm.h create mode 100644 code/renderer/tr_model_iqm.c diff --git a/Makefile b/Makefile index c63f894f..5cbcab99 100644 --- a/Makefile +++ b/Makefile @@ -1423,6 +1423,7 @@ Q3OBJ = \ $(B)/client/tr_image_tga.o \ $(B)/client/tr_image_pcx.o \ $(B)/client/tr_init.o \ + $(B)/client/tr_model_iqm.o \ $(B)/client/tr_light.o \ $(B)/client/tr_main.o \ $(B)/client/tr_marks.o \ diff --git a/code/renderer/iqm.h b/code/renderer/iqm.h new file mode 100644 index 00000000..bbfc053f --- /dev/null +++ b/code/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/code/renderer/tr_animation.c b/code/renderer/tr_animation.c index 8f424569..93b9fd31 100644 --- a/code/renderer/tr_animation.c +++ b/code/renderer/tr_animation.c @@ -45,7 +45,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/code/renderer/tr_local.h b/code/renderer/tr_local.h index 8fcd2dae..fe97ea8d 100644 --- a/code/renderer/tr_local.h +++ b/code/renderer/tr_local.h @@ -29,6 +29,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; @@ -533,6 +534,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, @@ -639,6 +641,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 *); @@ -747,19 +782,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; @@ -1491,6 +1527,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/code/renderer/tr_main.c b/code/renderer/tr_main.c index 2f8fd2ad..064f7522 100644 --- a/code/renderer/tr_main.c +++ b/code/renderer/tr_main.c @@ -1259,6 +1259,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/code/renderer/tr_model.c b/code/renderer/tr_model.c index 8356ca24..e4953864 100644 --- a/code/renderer/tr_model.c +++ b/code/renderer/tr_model.c @@ -25,13 +25,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 @@ -83,16 +264,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" ); @@ -131,125 +309,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; - } - - 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_RegisterModel: couldn't load mdr file %s\n", name); - mod->type = MOD_BAD; - return 0; - } - - 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 ) { - 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); - - if ( !loaded ) { - if ( lod == 0 ) { - goto fail; - } else { + if( !Q_stricmp( ext, modelLoaders[ i ].ext ) ) + { + // Load + hModel = modelLoaders[ i ].ModelLoader( localName, mod ); break; } - } 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; -// } + } + + // A loader was found + if( i < numModelLoaders ) + { + 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; + } } } - 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]; + // Try and find a suitable match using all + // the model formats supported + for( i = 0; i < numModelLoaders; i++ ) + { + if (i == orgLoader) + continue; + + altName = va( "%s.%s", localName, modelLoaders[ i ].ext ); + + // 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 ); + } + + 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 @@ -779,7 +907,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); @@ -1070,16 +1198,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 ); @@ -1121,28 +1253,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); + + 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 = (mdrHeader_t *)model->modelData; + frame = (mdrFrame_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; + + if(iqmData->bounds) + { + VectorCopy(iqmData->bounds, mins); + VectorCopy(iqmData->bounds + 3, maxs); + return; + } } - if ( !model->md3[0] ) { - VectorClear( mins ); - VectorClear( maxs ); - return; - } - - header = model->md3[0]; - - frame = (md3Frame_t *)( (byte *)header + header->ofsFrames ); - - VectorCopy( frame->bounds[0], mins ); - VectorCopy( frame->bounds[1], maxs ); + VectorClear( mins ); + VectorClear( maxs ); } - diff --git a/code/renderer/tr_model_iqm.c b/code/renderer/tr_model_iqm.c new file mode 100644 index 00000000..2a0b1aad --- /dev/null +++ b/code/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/code/renderer/tr_surface.c b/code/renderer/tr_surface.c index 964e0d9d..645b0771 100644 --- a/code/renderer/tr_surface.c +++ b/code/renderer/tr_surface.c @@ -1242,6 +1242,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