mirror of
https://github.com/openmoh/openmohaa.git
synced 2025-05-06 19:01:04 +03:00
937 lines
20 KiB
C
937 lines
20 KiB
C
![]() |
/*
|
||
|
===========================================================================
|
||
|
Copyright (C) 1999-2005 Id Software, Inc.
|
||
|
Copyright (C) 2006-2011 Robert Beckebans <trebor_7@users.sourceforge.net>
|
||
|
|
||
|
This file is part of XreaL source code.
|
||
|
|
||
|
XreaL source code is free software; you can redistribute it
|
||
|
and/or modify it under the terms of the GNU General Public License as
|
||
|
published by the Free Software Foundation; either version 2 of the License,
|
||
|
or (at your option) any later version.
|
||
|
|
||
|
XreaL source code is distributed in the hope that it will be
|
||
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with XreaL source code; if not, write to the Free Software
|
||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||
|
===========================================================================
|
||
|
*/
|
||
|
// tr_models.c -- model loading and caching
|
||
|
#include "tr_local.h"
|
||
|
|
||
|
#define LL(x) x=LittleLong(x)
|
||
|
#define LF(x) x=LittleFloat(x)
|
||
|
|
||
|
qboolean R_LoadMD3(model_t * mod, int lod, void *buffer, int bufferSize, const char *name);
|
||
|
|
||
|
#if defined(COMPAT_ET)
|
||
|
qboolean R_LoadMDC(model_t * mod, int lod, void *buffer, int bufferSize, const char *name);
|
||
|
qboolean R_LoadMDM(model_t * mod, void *buffer, const char *name);
|
||
|
static qboolean R_LoadMDX(model_t * mod, void *buffer, const char *name);
|
||
|
#endif
|
||
|
|
||
|
qboolean R_LoadMD5(model_t * mod, void *buffer, int bufferSize, const char *name);
|
||
|
qboolean R_LoadPSK(model_t * mod, void *buffer, int bufferSize, const char *name);
|
||
|
|
||
|
model_t *loadmodel;
|
||
|
|
||
|
/*
|
||
|
** R_GetModelByHandle
|
||
|
*/
|
||
|
model_t *R_GetModelByHandle(qhandle_t index)
|
||
|
{
|
||
|
model_t *mod;
|
||
|
|
||
|
// out of range gets the defualt model
|
||
|
if(index < 1 || index >= tr.numModels)
|
||
|
{
|
||
|
return tr.models[0];
|
||
|
}
|
||
|
|
||
|
mod = tr.models[index];
|
||
|
|
||
|
return mod;
|
||
|
}
|
||
|
|
||
|
//===============================================================================
|
||
|
|
||
|
/*
|
||
|
** R_AllocModel
|
||
|
*/
|
||
|
model_t *R_AllocModel(void)
|
||
|
{
|
||
|
model_t *mod;
|
||
|
|
||
|
if(tr.numModels == MAX_MOD_KNOWN)
|
||
|
{
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
mod = ri.Hunk_Alloc(sizeof(*tr.models[tr.numModels]), h_low);
|
||
|
mod->index = tr.numModels;
|
||
|
tr.models[tr.numModels] = mod;
|
||
|
tr.numModels++;
|
||
|
|
||
|
return mod;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
====================
|
||
|
RE_RegisterModel
|
||
|
|
||
|
Loads in a model for the given name
|
||
|
|
||
|
Zero will be returned if the model fails to load.
|
||
|
An entry will be retained for failed models as an
|
||
|
optimization to prevent disk rescanning if they are
|
||
|
asked for again.
|
||
|
====================
|
||
|
*/
|
||
|
qhandle_t RE_RegisterModel(const char *name)
|
||
|
{
|
||
|
model_t *mod;
|
||
|
unsigned *buffer;
|
||
|
int bufferLen = 0;
|
||
|
int lod;
|
||
|
int ident;
|
||
|
qboolean loaded;
|
||
|
qhandle_t hModel;
|
||
|
int numLoaded;
|
||
|
|
||
|
if(!name || !name[0])
|
||
|
{
|
||
|
ri.Printf(PRINT_ALL, "RE_RegisterModel: NULL name\n");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if(strlen(name) >= MAX_QPATH)
|
||
|
{
|
||
|
Com_Printf("Model name exceeds MAX_QPATH\n");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// search the currently loaded models
|
||
|
for(hModel = 1; hModel < tr.numModels; hModel++)
|
||
|
{
|
||
|
mod = tr.models[hModel];
|
||
|
if(!strcmp(mod->name, name))
|
||
|
{
|
||
|
if(mod->type == MOD_BAD)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
return hModel;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// allocate a new model_t
|
||
|
if((mod = R_AllocModel()) == NULL)
|
||
|
{
|
||
|
ri.Printf(PRINT_WARNING, "RE_RegisterModel: R_AllocModel() failed for '%s'\n", name);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// only set the name after the model has been successfully loaded
|
||
|
Q_strncpyz(mod->name, name, sizeof(mod->name));
|
||
|
|
||
|
// make sure the render thread is stopped
|
||
|
R_SyncRenderThread();
|
||
|
|
||
|
mod->numLods = 0;
|
||
|
|
||
|
// load the files
|
||
|
numLoaded = 0;
|
||
|
|
||
|
#if defined(COMPAT_ET)
|
||
|
if(strstr(name, ".mds") || strstr(name, ".mdm") || strstr(name, ".mdx") || strstr(name, ".md5mesh") || strstr(name, ".psk"))
|
||
|
#else
|
||
|
if(strstr(name, ".md5mesh") || strstr(name, ".psk"))
|
||
|
#endif
|
||
|
{
|
||
|
// try loading skeletal file
|
||
|
|
||
|
loaded = qfalse;
|
||
|
bufferLen = ri.FS_ReadFile(name, (void **)&buffer);
|
||
|
if(buffer)
|
||
|
{
|
||
|
loadmodel = mod;
|
||
|
|
||
|
ident = LittleLong(*(unsigned *)buffer);
|
||
|
#if defined(COMPAT_ET)
|
||
|
#if 0
|
||
|
if(ident == MDS_IDENT)
|
||
|
{
|
||
|
loaded = R_LoadMDS(mod, buffer, name);
|
||
|
} else
|
||
|
#endif
|
||
|
if(ident == MDM_IDENT)
|
||
|
{
|
||
|
loaded = R_LoadMDM(mod, buffer, name);
|
||
|
}
|
||
|
else if(ident == MDX_IDENT)
|
||
|
{
|
||
|
loaded = R_LoadMDX(mod, buffer, name);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#if defined(USE_REFENTITY_ANIMATIONSYSTEM)
|
||
|
if(!Q_stricmpn((const char *)buffer, "MD5Version", 10))
|
||
|
{
|
||
|
loaded = R_LoadMD5(mod, buffer, bufferLen, name);
|
||
|
}
|
||
|
else if(!Q_stricmpn((const char *)buffer, PSK_IDENTSTRING, PSK_IDENTLEN))
|
||
|
{
|
||
|
loaded = R_LoadPSK(mod, buffer, bufferLen, name);
|
||
|
}
|
||
|
#endif
|
||
|
ri.FS_FreeFile(buffer);
|
||
|
}
|
||
|
|
||
|
if(loaded)
|
||
|
{
|
||
|
// make sure the VBO glState entries are save
|
||
|
R_BindNullVBO();
|
||
|
R_BindNullIBO();
|
||
|
|
||
|
return mod->index;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for(lod = MD3_MAX_LODS - 1; lod >= 0; lod--)
|
||
|
{
|
||
|
char filename[1024];
|
||
|
|
||
|
strcpy(filename, name);
|
||
|
|
||
|
if(lod != 0)
|
||
|
{
|
||
|
char namebuf[80];
|
||
|
|
||
|
if(strrchr(filename, '.'))
|
||
|
{
|
||
|
*strrchr(filename, '.') = 0;
|
||
|
}
|
||
|
sprintf(namebuf, "_%d.md3", lod);
|
||
|
strcat(filename, namebuf);
|
||
|
}
|
||
|
|
||
|
filename[strlen(filename) - 1] = 'c'; // try MDC first
|
||
|
ri.FS_ReadFile(filename, (void **)&buffer);
|
||
|
|
||
|
if(!buffer)
|
||
|
{
|
||
|
filename[strlen(filename) - 1] = '3'; // try MD3 second
|
||
|
ri.FS_ReadFile(filename, (void **)&buffer);
|
||
|
if(!buffer)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
loadmodel = mod;
|
||
|
|
||
|
ident = LittleLong(*(unsigned *)buffer);
|
||
|
|
||
|
|
||
|
if(ident == MD3_IDENT)
|
||
|
{
|
||
|
loaded = R_LoadMD3(mod, lod, buffer, bufferLen, name);
|
||
|
ri.FS_FreeFile(buffer);
|
||
|
}
|
||
|
#if defined(COMPAT_ET)
|
||
|
else if(ident == MDC_IDENT)
|
||
|
{
|
||
|
loaded = R_LoadMDC(mod, lod, buffer, bufferLen, name);
|
||
|
ri.FS_FreeFile(buffer);
|
||
|
}
|
||
|
#endif
|
||
|
else
|
||
|
{
|
||
|
ri.FS_FreeFile(buffer);
|
||
|
|
||
|
ri.Printf(PRINT_WARNING, "RE_RegisterModel: unknown fileid for %s\n", name);
|
||
|
goto fail;
|
||
|
}
|
||
|
|
||
|
if(!loaded)
|
||
|
{
|
||
|
if(lod == 0)
|
||
|
{
|
||
|
goto fail;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// make sure the VBO glState entries are save
|
||
|
R_BindNullVBO();
|
||
|
R_BindNullIBO();
|
||
|
|
||
|
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;
|
||
|
// }
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// make sure the VBO glState entries are save
|
||
|
R_BindNullVBO();
|
||
|
R_BindNullIBO();
|
||
|
|
||
|
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->mdv[lod] = mod->mdv[lod + 1];
|
||
|
}
|
||
|
|
||
|
return mod->index;
|
||
|
}
|
||
|
#ifdef _DEBUG
|
||
|
else
|
||
|
{
|
||
|
ri.Printf(PRINT_WARNING, "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;
|
||
|
|
||
|
// make sure the VBO glState entries are save
|
||
|
R_BindNullVBO();
|
||
|
R_BindNullIBO();
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
=================
|
||
|
R_LoadMDX
|
||
|
=================
|
||
|
*/
|
||
|
#if defined(COMPAT_ET)
|
||
|
static qboolean R_LoadMDX(model_t * mod, void *buffer, const char *mod_name)
|
||
|
{
|
||
|
int i, j;
|
||
|
mdxHeader_t *pinmodel, *mdx;
|
||
|
mdxFrame_t *frame;
|
||
|
short *bframe;
|
||
|
mdxBoneInfo_t *bi;
|
||
|
int version;
|
||
|
int size;
|
||
|
int frameSize;
|
||
|
|
||
|
pinmodel = (mdxHeader_t *) buffer;
|
||
|
|
||
|
version = LittleLong(pinmodel->version);
|
||
|
if(version != MDX_VERSION)
|
||
|
{
|
||
|
ri.Printf(PRINT_WARNING, "R_LoadMDX: %s has wrong version (%i should be %i)\n", mod_name, version, MDX_VERSION);
|
||
|
return qfalse;
|
||
|
}
|
||
|
|
||
|
mod->type = MOD_MDX;
|
||
|
size = LittleLong(pinmodel->ofsEnd);
|
||
|
mod->dataSize += size;
|
||
|
mdx = mod->mdx = ri.Hunk_Alloc(size, h_low);
|
||
|
|
||
|
memcpy(mdx, buffer, LittleLong(pinmodel->ofsEnd));
|
||
|
|
||
|
LL(mdx->ident);
|
||
|
LL(mdx->version);
|
||
|
LL(mdx->numFrames);
|
||
|
LL(mdx->numBones);
|
||
|
LL(mdx->ofsFrames);
|
||
|
LL(mdx->ofsBones);
|
||
|
LL(mdx->ofsEnd);
|
||
|
LL(mdx->torsoParent);
|
||
|
|
||
|
if(LittleLong(1) != 1)
|
||
|
{
|
||
|
// swap all the frames
|
||
|
frameSize = (int)(sizeof(mdxBoneFrameCompressed_t)) * mdx->numBones;
|
||
|
for(i = 0; i < mdx->numFrames; i++)
|
||
|
{
|
||
|
frame = (mdxFrame_t *) ((byte *) mdx + mdx->ofsFrames + i * frameSize + i * sizeof(mdxFrame_t));
|
||
|
frame->radius = LittleFloat(frame->radius);
|
||
|
for(j = 0; j < 3; j++)
|
||
|
{
|
||
|
frame->bounds[0][j] = LittleFloat(frame->bounds[0][j]);
|
||
|
frame->bounds[1][j] = LittleFloat(frame->bounds[1][j]);
|
||
|
frame->localOrigin[j] = LittleFloat(frame->localOrigin[j]);
|
||
|
frame->parentOffset[j] = LittleFloat(frame->parentOffset[j]);
|
||
|
}
|
||
|
|
||
|
bframe = (short *)((byte *) mdx + mdx->ofsFrames + i * frameSize + ((i + 1) * sizeof(mdxFrame_t)));
|
||
|
for(j = 0; j < mdx->numBones * sizeof(mdxBoneFrameCompressed_t) / sizeof(short); j++)
|
||
|
{
|
||
|
((short *)bframe)[j] = LittleShort(((short *)bframe)[j]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// swap all the bones
|
||
|
for(i = 0; i < mdx->numBones; i++)
|
||
|
{
|
||
|
bi = (mdxBoneInfo_t *) ((byte *) mdx + mdx->ofsBones + i * sizeof(mdxBoneInfo_t));
|
||
|
LL(bi->parent);
|
||
|
bi->torsoWeight = LittleFloat(bi->torsoWeight);
|
||
|
bi->parentDist = LittleFloat(bi->parentDist);
|
||
|
LL(bi->flags);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return qtrue;
|
||
|
}
|
||
|
#endif // #if defined(COMPAT_ET)
|
||
|
|
||
|
|
||
|
|
||
|
//=============================================================================
|
||
|
|
||
|
/*
|
||
|
=================
|
||
|
R_XMLError
|
||
|
=================
|
||
|
*/
|
||
|
void R_XMLError(void *ctx, const char *fmt, ...)
|
||
|
{
|
||
|
va_list argptr;
|
||
|
static char msg[4096];
|
||
|
|
||
|
va_start(argptr, fmt);
|
||
|
Q_vsnprintf(msg, sizeof(msg), fmt, argptr);
|
||
|
va_end(argptr);
|
||
|
|
||
|
ri.Printf(PRINT_WARNING, "%s", msg);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=================
|
||
|
R_LoadDAE
|
||
|
=================
|
||
|
*/
|
||
|
/*
|
||
|
static qboolean R_LoadDAE(model_t * mod, void *buffer, int bufferLen, const char *modName)
|
||
|
{
|
||
|
xmlDocPtr doc;
|
||
|
xmlNodePtr node;
|
||
|
|
||
|
// setup error function handler
|
||
|
xmlInitParser();
|
||
|
xmlSetGenericErrorFunc(NULL, R_XMLError);
|
||
|
|
||
|
ri.Printf(PRINT_ALL, "...loading DAE '%s'\n", modName);
|
||
|
|
||
|
doc = xmlParseMemory(buffer, bufferLen);
|
||
|
if(doc == NULL)
|
||
|
{
|
||
|
ri.Printf(PRINT_WARNING, "R_LoadDAE: '%s' xmlParseMemory returned NULL\n", modName);
|
||
|
return qfalse;
|
||
|
}
|
||
|
node = xmlDocGetRootElement(doc);
|
||
|
|
||
|
if(node == NULL)
|
||
|
{
|
||
|
ri.Printf(PRINT_WARNING, "R_LoadDAE: '%s' empty document\n", modName);
|
||
|
xmlFreeDoc(doc);
|
||
|
return qfalse;
|
||
|
}
|
||
|
|
||
|
if(xmlStrcmp(node->name, (const xmlChar *) "COLLADA"))
|
||
|
{
|
||
|
ri.Printf(PRINT_WARNING, "R_LoadDAE: '%s' document of the wrong type, root node != COLLADA", modName);
|
||
|
xmlFreeDoc(doc);
|
||
|
return qfalse;
|
||
|
}
|
||
|
|
||
|
//TODO
|
||
|
|
||
|
xmlFreeDoc(doc);
|
||
|
|
||
|
ri.Printf(PRINT_ALL, "...finished DAE '%s'\n", modName);
|
||
|
|
||
|
return qfalse;
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
//=============================================================================
|
||
|
|
||
|
/*
|
||
|
** RE_BeginRegistration
|
||
|
*/
|
||
|
void RE_BeginRegistration(glconfig_t * glconfigOut/*, glconfig2_t * glconfigOut2*/)
|
||
|
{
|
||
|
R_Init();
|
||
|
|
||
|
*glconfigOut = glConfig;
|
||
|
//*glconfigOut2 = glConfig2;
|
||
|
|
||
|
R_SyncRenderThread();
|
||
|
|
||
|
tr.visIndex = 0;
|
||
|
memset(tr.visClusters, -2, sizeof(tr.visClusters)); // force markleafs to regenerate
|
||
|
|
||
|
#if defined(USE_D3D10)
|
||
|
// TODO
|
||
|
#else
|
||
|
R_ClearFlares();
|
||
|
#endif
|
||
|
|
||
|
RE_ClearScene();
|
||
|
|
||
|
// HACK: give world entity white color for "colored" shader keyword
|
||
|
tr.worldEntity.e.shaderRGBA[0] = 255;
|
||
|
tr.worldEntity.e.shaderRGBA[1] = 255;
|
||
|
tr.worldEntity.e.shaderRGBA[2] = 255;
|
||
|
tr.worldEntity.e.shaderRGBA[3] = 255;
|
||
|
|
||
|
tr.worldEntity.e.nonNormalizedAxes = qfalse;
|
||
|
|
||
|
// RB: world will be never ignored by occusion query test
|
||
|
tr.worldEntity.occlusionQuerySamples = 1;
|
||
|
|
||
|
tr.registered = qtrue;
|
||
|
|
||
|
// NOTE: this sucks, for some reason the first stretch pic is never drawn
|
||
|
// without this we'd see a white flash on a level load because the very
|
||
|
// first time the level shot would not be drawn
|
||
|
RE_StretchPic(0, 0, 0, 0, 0, 0, 1, 1, 0);
|
||
|
}
|
||
|
|
||
|
//=============================================================================
|
||
|
|
||
|
/*
|
||
|
===============
|
||
|
R_ModelInit
|
||
|
===============
|
||
|
*/
|
||
|
void R_ModelInit(void)
|
||
|
{
|
||
|
model_t *mod;
|
||
|
|
||
|
// leave a space for NULL model
|
||
|
tr.numModels = 0;
|
||
|
|
||
|
mod = R_AllocModel();
|
||
|
mod->type = MOD_BAD;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
R_Modellist_f
|
||
|
================
|
||
|
*/
|
||
|
void R_Modellist_f(void)
|
||
|
{
|
||
|
int i, j, k;
|
||
|
model_t *mod;
|
||
|
int total;
|
||
|
int totalDataSize;
|
||
|
qboolean showFrames;
|
||
|
|
||
|
if(!strcmp(ri.Cmd_Argv(1), "frames"))
|
||
|
{
|
||
|
showFrames = qtrue;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
showFrames = qfalse;
|
||
|
}
|
||
|
|
||
|
total = 0;
|
||
|
totalDataSize = 0;
|
||
|
for(i = 1; i < tr.numModels; i++)
|
||
|
{
|
||
|
mod = tr.models[i];
|
||
|
|
||
|
|
||
|
if(mod->type == MOD_MESH)
|
||
|
{
|
||
|
for(j = 0; j < MD3_MAX_LODS; j++)
|
||
|
{
|
||
|
if(mod->mdv[j] && mod->mdv[j] != mod->mdv[j - 1])
|
||
|
{
|
||
|
mdvModel_t *mdvModel;
|
||
|
mdvSurface_t *mdvSurface;
|
||
|
mdvTagName_t *mdvTagName;
|
||
|
|
||
|
mdvModel = mod->mdv[j];
|
||
|
|
||
|
total++;
|
||
|
ri.Printf(PRINT_ALL, "%d.%02d MB '%s' LOD = %i\n", mod->dataSize / (1024 * 1024),
|
||
|
(mod->dataSize % (1024 * 1024)) * 100 / (1024 * 1024),
|
||
|
mod->name, j);
|
||
|
|
||
|
if(showFrames && mdvModel->numFrames > 1)
|
||
|
{
|
||
|
ri.Printf(PRINT_ALL, "\tnumSurfaces = %i\n", mdvModel->numSurfaces);
|
||
|
ri.Printf(PRINT_ALL, "\tnumFrames = %i\n", mdvModel->numFrames);
|
||
|
|
||
|
for(k = 0, mdvSurface = mdvModel->surfaces; k < mdvModel->numSurfaces; k++, mdvSurface++)
|
||
|
{
|
||
|
ri.Printf(PRINT_ALL, "\t\tmesh = '%s'\n", mdvSurface->name);
|
||
|
ri.Printf(PRINT_ALL, "\t\t\tnumVertexes = %i\n", mdvSurface->numVerts);
|
||
|
ri.Printf(PRINT_ALL, "\t\t\tnumTriangles = %i\n", mdvSurface->numTriangles);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ri.Printf(PRINT_ALL, "\t\tnumTags = %i\n", mdvModel->numTags);
|
||
|
for(k = 0, mdvTagName = mdvModel->tagNames; k < mdvModel->numTags; k++, mdvTagName++)
|
||
|
{
|
||
|
ri.Printf(PRINT_ALL, "\t\t\ttagName = '%s'\n", mdvTagName->name);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ri.Printf(PRINT_ALL, "%d.%02d MB '%s'\n", mod->dataSize / (1024 * 1024),
|
||
|
(mod->dataSize % (1024 * 1024)) * 100 / (1024 * 1024),
|
||
|
mod->name);
|
||
|
|
||
|
total++;
|
||
|
}
|
||
|
|
||
|
totalDataSize += mod->dataSize;
|
||
|
}
|
||
|
|
||
|
ri.Printf(PRINT_ALL, " %d.%02d MB total model memory\n", totalDataSize / (1024 * 1024),
|
||
|
(totalDataSize % (1024 * 1024)) * 100 / (1024 * 1024));
|
||
|
ri.Printf(PRINT_ALL, " %i total models\n\n", total);
|
||
|
|
||
|
#if 0 // not working right with new hunk
|
||
|
if(tr.world)
|
||
|
{
|
||
|
ri.Printf(PRINT_ALL, "\n%8i : %s\n", tr.world->dataSize, tr.world->name);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
//=============================================================================
|
||
|
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
R_GetTag
|
||
|
================
|
||
|
*/
|
||
|
static int R_GetTag(mdvModel_t * model, int frame, const char *_tagName, int startTagIndex, mdvTag_t ** outTag)
|
||
|
{
|
||
|
int i;
|
||
|
mdvTag_t *tag;
|
||
|
mdvTagName_t *tagName;
|
||
|
|
||
|
// it is possible to have a bad frame while changing models, so don't error
|
||
|
frame = Q_bound(0, frame, model->numFrames - 1);
|
||
|
|
||
|
if(startTagIndex > model->numTags)
|
||
|
{
|
||
|
*outTag = NULL;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
#if 1
|
||
|
tag = model->tags + frame * model->numTags;
|
||
|
tagName = model->tagNames;
|
||
|
for(i = 0; i < model->numTags; i++, tag++, tagName++)
|
||
|
{
|
||
|
if((i >= startTagIndex) && !strcmp(tagName->name, _tagName))
|
||
|
{
|
||
|
*outTag = tag;
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
*outTag = NULL;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
RE_LerpTagQ3A
|
||
|
================
|
||
|
*/
|
||
|
int RE_LerpTagQ3A(orientation_t * tag, qhandle_t handle, int startFrame, int endFrame, float frac, const char *tagNameIn)
|
||
|
{
|
||
|
mdvTag_t *start, *end;
|
||
|
int i;
|
||
|
float frontLerp, backLerp;
|
||
|
model_t *model;
|
||
|
char tagName[MAX_QPATH];
|
||
|
int retval;
|
||
|
|
||
|
Q_strncpyz(tagName, tagNameIn, MAX_QPATH);
|
||
|
|
||
|
model = R_GetModelByHandle(handle);
|
||
|
if(!model->mdv[0])
|
||
|
{
|
||
|
AxisClear(tag->axis);
|
||
|
VectorClear(tag->origin);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
start = end = NULL;
|
||
|
|
||
|
retval = R_GetTag(model->mdv[0], startFrame, tagName, 0, &start);
|
||
|
retval = R_GetTag(model->mdv[0], endFrame, tagName, 0, &end);
|
||
|
if(!start || !end)
|
||
|
{
|
||
|
AxisClear(tag->axis);
|
||
|
VectorClear(tag->origin);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
frontLerp = frac;
|
||
|
backLerp = 1.0f - frac;
|
||
|
|
||
|
for(i = 0; i < 3; i++)
|
||
|
{
|
||
|
tag->origin[i] = start->origin[i] * backLerp + end->origin[i] * frontLerp;
|
||
|
tag->axis[0][i] = start->axis[0][i] * backLerp + end->axis[0][i] * frontLerp;
|
||
|
tag->axis[1][i] = start->axis[1][i] * backLerp + end->axis[1][i] * frontLerp;
|
||
|
tag->axis[2][i] = start->axis[2][i] * backLerp + end->axis[2][i] * frontLerp;
|
||
|
}
|
||
|
VectorNormalize(tag->axis[0]);
|
||
|
VectorNormalize(tag->axis[1]);
|
||
|
VectorNormalize(tag->axis[2]);
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
RE_LerpTag
|
||
|
================
|
||
|
*/
|
||
|
#if defined(COMPAT_ET)
|
||
|
int RE_LerpTagET(orientation_t * tag, const refEntity_t * refent, const char *tagNameIn, int startIndex)
|
||
|
{
|
||
|
mdvTag_t *start, *end;
|
||
|
int i;
|
||
|
float frontLerp, backLerp;
|
||
|
model_t *model;
|
||
|
char tagName[MAX_QPATH]; //, *ch;
|
||
|
int retval;
|
||
|
qhandle_t handle;
|
||
|
int startFrame, endFrame;
|
||
|
float frac;
|
||
|
|
||
|
handle = refent->hModel;
|
||
|
startFrame = refent->oldframe;
|
||
|
endFrame = refent->frame;
|
||
|
frac = 1.0 - refent->backlerp;
|
||
|
|
||
|
Q_strncpyz(tagName, tagNameIn, MAX_QPATH);
|
||
|
/*
|
||
|
// if the tagName has a space in it, then it is passing through the starting tag number
|
||
|
if (ch = strrchr(tagName, ' ')) {
|
||
|
*ch = 0;
|
||
|
ch++;
|
||
|
startIndex = atoi(ch);
|
||
|
}
|
||
|
*/
|
||
|
model = R_GetModelByHandle(handle);
|
||
|
/*
|
||
|
if(!model->mdv[0]) //if(!model->model.md3[0] && !model->model.mdc[0] && !model->model.mds)
|
||
|
{
|
||
|
AxisClear(tag->axis);
|
||
|
VectorClear(tag->origin);
|
||
|
return -1;
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
frontLerp = frac;
|
||
|
backLerp = 1.0 - frac;
|
||
|
|
||
|
start = end = NULL;
|
||
|
|
||
|
if(model->type == MOD_MESH)
|
||
|
{
|
||
|
// old MD3 style
|
||
|
retval = R_GetTag(model->mdv[0], startFrame, tagName, startIndex, &start);
|
||
|
retval = R_GetTag(model->mdv[0], endFrame, tagName, startIndex, &end);
|
||
|
|
||
|
}
|
||
|
/*
|
||
|
else if(model->type == MOD_MDS)
|
||
|
{
|
||
|
// use bone lerping
|
||
|
retval = R_GetBoneTag(tag, model->model.mds, startIndex, refent, tagNameIn);
|
||
|
|
||
|
if(retval >= 0)
|
||
|
{
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
// failed
|
||
|
return -1;
|
||
|
|
||
|
}
|
||
|
*/
|
||
|
else if(model->type == MOD_MDM)
|
||
|
{
|
||
|
// use bone lerping
|
||
|
retval = R_MDM_GetBoneTag(tag, model->mdm, startIndex, refent, tagNameIn);
|
||
|
|
||
|
if(retval >= 0)
|
||
|
{
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
// failed
|
||
|
return -1;
|
||
|
|
||
|
}
|
||
|
/*
|
||
|
else
|
||
|
{
|
||
|
// psuedo-compressed MDC tags
|
||
|
mdcTag_t *cstart, *cend;
|
||
|
|
||
|
retval = R_GetMDCTag((byte *) model->model.mdc[0], startFrame, tagName, startIndex, &cstart);
|
||
|
retval = R_GetMDCTag((byte *) model->model.mdc[0], endFrame, tagName, startIndex, &cend);
|
||
|
|
||
|
// uncompress the MDC tags into MD3 style tags
|
||
|
if(cstart && cend)
|
||
|
{
|
||
|
for(i = 0; i < 3; i++)
|
||
|
{
|
||
|
ustart.origin[i] = (float)cstart->xyz[i] * MD3_XYZ_SCALE;
|
||
|
uend.origin[i] = (float)cend->xyz[i] * MD3_XYZ_SCALE;
|
||
|
sangles[i] = (float)cstart->angles[i] * MDC_TAG_ANGLE_SCALE;
|
||
|
eangles[i] = (float)cend->angles[i] * MDC_TAG_ANGLE_SCALE;
|
||
|
}
|
||
|
|
||
|
AnglesToAxis(sangles, ustart.axis);
|
||
|
AnglesToAxis(eangles, uend.axis);
|
||
|
|
||
|
start = &ustart;
|
||
|
end = &uend;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
start = NULL;
|
||
|
end = NULL;
|
||
|
}
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
if(!start || !end)
|
||
|
{
|
||
|
AxisClear(tag->axis);
|
||
|
VectorClear(tag->origin);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
for(i = 0; i < 3; i++)
|
||
|
{
|
||
|
tag->origin[i] = start->origin[i] * backLerp + end->origin[i] * frontLerp;
|
||
|
tag->axis[0][i] = start->axis[0][i] * backLerp + end->axis[0][i] * frontLerp;
|
||
|
tag->axis[1][i] = start->axis[1][i] * backLerp + end->axis[1][i] * frontLerp;
|
||
|
tag->axis[2][i] = start->axis[2][i] * backLerp + end->axis[2][i] * frontLerp;
|
||
|
}
|
||
|
|
||
|
VectorNormalize(tag->axis[0]);
|
||
|
VectorNormalize(tag->axis[1]);
|
||
|
VectorNormalize(tag->axis[2]);
|
||
|
|
||
|
return retval;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
RE_BoneIndex
|
||
|
================
|
||
|
*/
|
||
|
int RE_BoneIndex(qhandle_t hModel, const char *boneName)
|
||
|
{
|
||
|
int i;
|
||
|
md5Bone_t *bone;
|
||
|
md5Model_t *md5;
|
||
|
model_t *model;
|
||
|
|
||
|
model = R_GetModelByHandle(hModel);
|
||
|
if(!model->md5)
|
||
|
{
|
||
|
return -1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
md5 = model->md5;
|
||
|
}
|
||
|
|
||
|
for(i = 0, bone = md5->bones; i < md5->numBones; i++, bone++)
|
||
|
{
|
||
|
if(!Q_stricmp(bone->name, boneName))
|
||
|
{
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
====================
|
||
|
R_ModelBounds
|
||
|
====================
|
||
|
*/
|
||
|
void R_ModelBounds(qhandle_t handle, vec3_t mins, vec3_t maxs)
|
||
|
{
|
||
|
model_t *model;
|
||
|
mdvModel_t *header;
|
||
|
mdvFrame_t *frame;
|
||
|
|
||
|
model = R_GetModelByHandle(handle);
|
||
|
|
||
|
if(model->bsp)
|
||
|
{
|
||
|
VectorCopy(model->bsp->bounds[0], mins);
|
||
|
VectorCopy(model->bsp->bounds[1], maxs);
|
||
|
}
|
||
|
else if(model->mdv[0])
|
||
|
{
|
||
|
header = model->mdv[0];
|
||
|
|
||
|
frame = header->frames;
|
||
|
|
||
|
VectorCopy(frame->bounds[0], mins);
|
||
|
VectorCopy(frame->bounds[1], maxs);
|
||
|
}
|
||
|
else if(model->md5)
|
||
|
{
|
||
|
VectorCopy(model->md5->bounds[0], mins);
|
||
|
VectorCopy(model->md5->bounds[1], maxs);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
VectorClear(mins);
|
||
|
VectorClear(maxs);
|
||
|
}
|
||
|
}
|