mirror of
https://github.com/ioquake/ioq3.git
synced 2024-11-10 07:11:46 +00:00
- 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)
This commit is contained in:
parent
d34c6b7e0b
commit
c5e2654b54
8 changed files with 1299 additions and 140 deletions
1
Makefile
1
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 \
|
||||
|
|
107
code/renderer/iqm.h
Normal file
107
code/renderer/iqm.h
Normal file
|
@ -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
|
||||
|
|
@ -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 );
|
||||
|
|
|
@ -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 );
|
||||
|
||||
/*
|
||||
=============================================================
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
|
|
839
code/renderer/tr_model_iqm.c
Normal file
839
code/renderer/tr_model_iqm.c
Normal file
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue