[quake3-commits] r1953 - in trunk: . code/renderer

DONOTREPLY at icculus.org DONOTREPLY at icculus.org
Mon May 2 11:50:19 EDT 2011


Author: thilo
Date: 2011-05-02 11:50:19 -0400 (Mon, 02 May 2011)
New Revision: 1953

Added:
   trunk/code/renderer/iqm.h
   trunk/code/renderer/tr_model_iqm.c
Modified:
   trunk/Makefile
   trunk/code/renderer/tr_animation.c
   trunk/code/renderer/tr_local.h
   trunk/code/renderer/tr_main.c
   trunk/code/renderer/tr_model.c
   trunk/code/renderer/tr_surface.c
Log:
- 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)


Modified: trunk/Makefile
===================================================================
--- trunk/Makefile	2011-04-27 20:17:45 UTC (rev 1952)
+++ trunk/Makefile	2011-05-02 15:50:19 UTC (rev 1953)
@@ -1423,6 +1423,7 @@
   $(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 \

Added: trunk/code/renderer/iqm.h
===================================================================
--- trunk/code/renderer/iqm.h	                        (rev 0)
+++ trunk/code/renderer/iqm.h	2011-05-02 15:50:19 UTC (rev 1953)
@@ -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
+

Modified: trunk/code/renderer/tr_animation.c
===================================================================
--- trunk/code/renderer/tr_animation.c	2011-04-27 20:17:45 UTC (rev 1952)
+++ trunk/code/renderer/tr_animation.c	2011-05-02 15:50:19 UTC (rev 1953)
@@ -45,7 +45,7 @@
 	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 );

Modified: trunk/code/renderer/tr_local.h
===================================================================
--- trunk/code/renderer/tr_local.h	2011-04-27 20:17:45 UTC (rev 1952)
+++ trunk/code/renderer/tr_local.h	2011-05-02 15:50:19 UTC (rev 1953)
@@ -29,6 +29,7 @@
 #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 @@
 #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,7 +641,40 @@
 	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 @@
 	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 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 );
 
 /*
 =============================================================

Modified: trunk/code/renderer/tr_main.c
===================================================================
--- trunk/code/renderer/tr_main.c	2011-04-27 20:17:45 UTC (rev 1952)
+++ trunk/code/renderer/tr_main.c	2011-05-02 15:50:19 UTC (rev 1953)
@@ -1259,6 +1259,9 @@
 					R_MDRAddAnimSurfaces( ent );
 					break;
 #endif
+				case MOD_IQM:
+                                        R_AddIQMSurfaces( ent );
+                                        break;
 				case MOD_BRUSH:
 					R_AddBrushModelSurfaces( ent );
 					break;

Modified: trunk/code/renderer/tr_model.c
===================================================================
--- trunk/code/renderer/tr_model.c	2011-04-27 20:17:45 UTC (rev 1952)
+++ trunk/code/renderer/tr_model.c	2011-05-02 15:50:19 UTC (rev 1953)
@@ -25,15 +25,196 @@
 
 #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
 */
 model_t	*R_GetModelByHandle( qhandle_t index ) {
@@ -83,16 +264,13 @@
 */
 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 @@
 	// 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;
+	// Try and find a suitable match using all
+	// the model formats supported
+	for( i = 0; i < numModelLoaders; i++ )
+	{
+		if (i == orgLoader)
+			continue;
 
-	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);
+		altName = va( "%s.%s", localName, modelLoaders[ i ].ext );
 
-		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;
-			}
+		// Load
+		hModel = modelLoaders[ i ].ModelLoader( altName, mod );
 
-			loaded = R_LoadMD3( mod, lod, buf.u, name );
-		}
-		
-		ri.FS_FreeFile (buf.v);
-
-		if ( !loaded ) {
-			if ( lod == 0 ) {
-				goto fail;
-			} else {
-				break;
+		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
@@ -779,7 +907,7 @@
 	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 @@
 	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 @@
 */
 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;
 
-	if ( !model->md3[0] ) {
-		VectorClear( mins );
-		VectorClear( maxs );
+		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 = model->md3[0];
+		header = (md4Header_t *)model->modelData;
+		frame = (md4Frame_t *) ((byte *)header + header->ofsFrames);
 
-	frame = (md3Frame_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;
 
-	VectorCopy( frame->bounds[0], mins );
-	VectorCopy( frame->bounds[1], maxs );
+		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;
+		}
+	}
+
+	VectorClear( mins );
+	VectorClear( maxs );
 }
-

Added: trunk/code/renderer/tr_model_iqm.c
===================================================================
--- trunk/code/renderer/tr_model_iqm.c	                        (rev 0)
+++ trunk/code/renderer/tr_model_iqm.c	2011-05-02 15:50:19 UTC (rev 1953)
@@ -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;
+}

Modified: trunk/code/renderer/tr_surface.c
===================================================================
--- trunk/code/renderer/tr_surface.c	2011-04-27 20:17:45 UTC (rev 1952)
+++ trunk/code/renderer/tr_surface.c	2011-05-02 15:50:19 UTC (rev 1953)
@@ -1242,6 +1242,7 @@
 #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



More information about the quake3-commits mailing list