- 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
|
@ -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 );
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue