// Filename:- R_Model.cpp // // interface to all block-paste other-format code that doesn't know it's inside ModView // #include "stdafx.h" #include "includes.h" #include "R_Common.h" #include "textures.h" // #include "R_Model.h" trGlobals_t tr; refimport_t ri; void Com_Printf( const char *format, ... ) { va_list argptr; static char string[16][1024]; static index = 0; index = (++index)&15; va_start (argptr, format); vsprintf (string[index], format,argptr); va_end (argptr); OutputDebugString(string[index]); // assert(0); ErrorBox(string[index]); } void Q_strncpyz( char *dest, LPCSTR src, int destlen) { strncpy(dest,src,destlen); dest[destlen-1] = '\0'; } float Com_Clamp( float min, float max, float value ) { if ( value < min ) { return min; } if ( value > max ) { return max; } return value; } /* ============ COM_SkipPath ============ */ char *COM_SkipPath (char *pathname) { char *last; last = pathname; while (*pathname) { if (*pathname=='/') last = pathname+1; pathname++; } return last; } /* ============ COM_StripExtension ============ */ void COM_StripExtension( const char *in, char *out ) { while ( *in && *in != '.' ) { *out++ = *in++; } *out = 0; } void QDECL Com_sprintf( char *dest, int size, const char *fmt, ...) { int len; va_list argptr; char bigbuffer[32000]; // big, but small enough to fit in PPC stack va_start (argptr,fmt); len = vsprintf (bigbuffer,fmt,argptr); va_end (argptr); if ( len >= sizeof( bigbuffer ) ) { Com_Error( ERR_FATAL, "Com_sprintf: overflowed bigbuffer" ); } if (len >= size) { Com_Printf ("Com_sprintf: overflow of %i in %i ( string: \"%s\" )\n", len, size, bigbuffer); } Q_strncpyz (dest, bigbuffer, size ); } int LongSwap (int l) { byte b1,b2,b3,b4; b1 = l&255; b2 = (l>>8)&255; b3 = (l>>16)&255; b4 = (l>>24)&255; return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; } /* ================== COM_DefaultExtension ================== */ void COM_DefaultExtension (char *path, int maxSize, const char *extension ) { char oldPath[MAX_QPATH]; char *src; // // if path doesn't have a .EXT, append extension // (extension should include the .) // src = path + strlen(path) - 1; while (*src != '/' && src != path) { if ( *src == '.' ) { return; // it has an extension } src--; } Q_strncpyz( oldPath, path, sizeof( oldPath ) ); Com_sprintf( path, maxSize, "%s%s", oldPath, extension ); } void Crap_Printf( int printLevel, const char *format, ...) { va_list argptr; static char string[16][16384]; static index = 0; index = (++index)&15; va_start (argptr, format); // vsprintf (string[index], format,argptr); _vsnprintf(string[index], sizeof(string[0]), format, argptr); va_end (argptr); string[index][sizeof(string[0])-1] = '\0'; char *psString = string[index]; switch (printLevel) { case PRINT_ALL: InfoBox(psString); break; case PRINT_DEVELOPER: OutputDebugString( psString); break; case PRINT_WARNING: WarningBox(psString); break; default: assert(0); case PRINT_ERROR: ErrorBox(psString); break; } } void Crap_Error( int errorLevel, const char *format, ...) { va_list argptr; static char string[1024]; va_start (argptr, format); vsprintf (string, format,argptr); va_end (argptr); // should maybe switch-case off these, but for now... // /* typedef enum { ERR_FATAL, // exit the entire game with a popup window ERR_DROP, // print to console and disconnect from game ERR_DISCONNECT, // don't kill server ERR_NEED_CD // pop up the need-cd dialog } errorParm_t; */ throw (string ); } void *Crap_Malloc( int bytes ) { void *pvBlah = malloc(bytes); if (pvBlah) { memset(pvBlah,0,bytes); } else { ErrorBox(va("Dammit: Failed to allocate %d bytes for some reason\n\nGonna have to exit....",bytes)); exit(1); } return pvBlah; } void Crap_Free( void *buf ) { free(buf); } // change this? Nah.... void *Crap_HunkAlloc(int size) { return Crap_Malloc(size); } // ============ // FS_ReadFile // // Filename are relative to the quake search path // a null buffer will just return the file length without loading // ============ bool bHackToAllowFullPathDuringTestFunc = false; // leave as false!!!!!!!! int Crap_FS_ReadFile( const char *qpath, void **buffer ) { byte* buf = NULL; if ( !gamedir[0]) { Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); } if ( !qpath || !qpath[0] ) { Com_Error( ERR_FATAL, "FS_ReadFile with empty name\n" ); } char sTemp[1024]; sprintf(sTemp,"%s%s",bHackToAllowFullPathDuringTestFunc?"":gamedir,qpath); if (!FileExists(sTemp)) { if ( buffer ) { *buffer = NULL; } return -1; } if ( !buffer ) { return FileLen( sTemp ); } return LoadFile (sTemp, buffer); } // ============ // FS_ReadFile // // Filename are relative to the quake search path // a null buffer will just return the file length without loading // ============ // -1 return = fail, else len written int Crap_FS_WriteFile( const char *qpath, const void *pBuffer, int iSize ) { if ( !gamedir[0]) { Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); } if ( !qpath || !qpath[0] ) { Com_Error( ERR_FATAL, "FS_WriteFile with empty name\n" ); } char sTemp[1024]; sprintf(sTemp,"%s%s",gamedir,qpath); return SaveFile (sTemp, pBuffer, iSize); } void Crap_FS_FreeFile( void *buffer ) { if ( !buffer ) { Com_Error( ERR_FATAL, "FS_FreeFile( NULL )" ); } free(buffer); } int R_ComputeLOD( trRefEntity_t *ent ) { return AppVars.iLOD; // MODVIEW change: this is under manual/user control in the viewer) } void AxisClear( vec3_t axis[3] ) { axis[0][0] = 1; axis[0][1] = 0; axis[0][2] = 0; axis[1][0] = 0; axis[1][1] = 1; axis[1][2] = 0; axis[2][0] = 0; axis[2][1] = 0; axis[2][2] = 1; } /* the drawsurf sort data is packed into a single 32 bit value so it can be compared quickly during the qsorting process the bits are allocated as follows: 22 - 31 : sorted shader index 12 - 21 : entity index 3 - 7 : fog index 2 : used to be clipped flag 0 - 1 : dlightmap index */ #define QSORT_SHADERNUM_SHIFT 22 #define QSORT_ENTITYNUM_SHIFT 12 #define QSORT_FOGNUM_SHIFT 3 // void R_AddDrawSurf( surfaceType_t *surface, GLuint gluiTextureBind) { // instead of checking for overflow, we just mask the index // so it wraps around int index = tr.refdef.numDrawSurfs & DRAWSURF_MASK; tr.refdef.drawSurfs[index].sort = (gluiTextureBind/*shader->sortedIndex*/ << QSORT_SHADERNUM_SHIFT) | (tr.currentEntityNum << QSORT_ENTITYNUM_SHIFT) | (0/* fogIndex*/ << QSORT_FOGNUM_SHIFT) | 0/*(int)dlightMap*/; tr.refdef.drawSurfs[index].surface = surface; tr.refdef.numDrawSurfs++; } void R_DecomposeSort( unsigned sort, int *entityNum, GLuint* gluiTextureBind // MODVIEWREM //,shader_t **shader, int *fogNum, int *dlightMap ) { // *fogNum = ( sort >> QSORT_FOGNUM_SHIFT ) & 31; // *shader = tr.sortedShaders[ ( sort >> QSORT_SHADERNUM_SHIFT ) & (MAX_SHADERS-1) ]; *gluiTextureBind = ( sort >> QSORT_SHADERNUM_SHIFT ); *entityNum = ( sort >> QSORT_ENTITYNUM_SHIFT ) & 1023; // *dlightMap = sort & 3; } //model_t *loadmodel; static model_t *R_AllocModel( void ) { model_t *mod; if ( tr.numModels == MAX_MOD_KNOWN ) { return NULL; } mod = (model_s *)ri.Hunk_Alloc( sizeof( *tr.models[tr.numModels] ) ); mod->index = tr.numModels; tr.models[tr.numModels] = mod; tr.numModels++; return mod; } static void RE_EnsureOneBadModel(void) { if (tr.numModels == 0) { model_t *mod = R_AllocModel(); mod->type = MOD_BAD; } } // note, I only offer an API call to delete all models at once, since model handles are just indexes into // an array, and any stored pointers would invalidate if I deleted any in the middle // void RE_DeleteModels( void ) { for (int i=0; iname,"*default.gla")) // can't free global fake-GLA struct { SAFEFREE(tr.models[i]->pvData); } SAFEFREE(tr.models[i]); } tr.numModels = 0; } static void R_ModelInitOnceOnly( void ) { RE_DeleteModels(); RE_EnsureOneBadModel(); } model_t *R_GetModelByHandle( ModelHandle_t index ) { RE_EnsureOneBadModel(); // out of range gets the default model if ( index < 1 || index >= tr.numModels ) { return tr.models[0]; } model_t *mod = tr.models[index]; return mod; } //=============================================================================== // used by higher-level app to gain access to actual loaded data... // // returns NULL if error... // void *RE_GetModelData( ModelHandle_t hModel ) { RE_EnsureOneBadModel(); model_t *pModel = R_GetModelByHandle( hModel ); if (pModel->type != MOD_BAD) return pModel->pvData; assert(0); return NULL; } modtype_t RE_GetModelType( ModelHandle_t hModel ) { RE_EnsureOneBadModel(); model_t *pModel = R_GetModelByHandle( hModel ); return pModel->type; } // called only from media_delete... // typedef struct { byte *pData; int iSize; } CachedBin_t; typedef map CachedModelBins_t; CachedModelBins_t CachedModelBins; void RE_ModelBinCache_DeleteAll() { for (CachedModelBins_t::iterator it = CachedModelBins.begin(); it != CachedModelBins.end(); ++it) { CachedBin_t &Bin = (*it).second; free(Bin.pData); } CachedModelBins.clear(); } static byte *RE_ModelBinCache_Find( const char *psName, int &iSize ) { CachedModelBins_t::iterator it = CachedModelBins.find(psName); if (it != CachedModelBins.end()) { CachedBin_t &Bin = (*it).second; iSize = Bin.iSize; return Bin.pData; } return NULL; } static void RE_ModelBinCache_Insert( const char *psName, int iBytesRead, const byte *pBuf ) { // don't exceed (say) 300MB?... // int iTotalBytes = 0; for (CachedModelBins_t::iterator it = CachedModelBins.begin(); it != CachedModelBins.end(); ++it) { CachedBin_t &Bin = (*it).second; iTotalBytes += Bin.iSize; } if (iTotalBytes < 300 * 1024 * 1024) { CachedBin_t Cache; Cache.pData = (byte *) malloc(iBytesRead); Cache.iSize = iBytesRead; memcpy(Cache.pData,pBuf,iBytesRead); CachedModelBins[ psName ] = Cache; } } /* ==================== 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. ==================== */ ModelHandle_t RE_RegisterModel( const char *name ) { model_t *mod; unsigned *buf; int lod; int ident; qboolean loaded; int numLoaded; RE_EnsureOneBadModel(); if ( !name || !name[0] ) { ri.Printf( PRINT_ALL, "RE_RegisterModel: NULL name\n" ); return 0; } if ( strlen( name ) >= MAX_QPATH ) { Com_Printf( "Model name %s exceeds MAX_QPATH\n",name ); return 0; } // MODVIEW: Note, because of the way I pass model handles to functions as unique handles that then have their // owning-containers derived from that handle then they must be unique, so ignore cached models. Yes I know // it's lame, but it was easier thatn rewriting the whole thing once I'd realised what direction this app was // going etc. Anyway, since the model cache is already deleted when the primary is loaded then the only time // this becomes inefficient is when you have (say) a guy with the same weapon bolted into both his left and right // hands, then I end up with 2 copies of the loaded models, not one. Whoopee-do. This is a quick-an-dirty app, // and I've got other things to get on with so it's a lot quicker to do something like this that makes no hands-on // difference to the user, no matter how evil it is inside... :-) /* // // search the currently loaded models // for (qhandle_t 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; } 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; int LODStart=MD3_MAX_LODS-1; //this loads the md3s in reverse so they can be biased if (strstr (name, ".mdr") || strstr (name, ".gla") || strstr (name, ".glm")) { LODStart = 0; } for ( lod = LODStart ; lod >= 0 ; lod-- ) { char filename[1024]; strcpy( filename, name ); strlwr( filename ); // for bin map<> cacheing to work if ( lod != 0 ) { char namebuf[80]; if ( strrchr( filename, '.' ) ) { *strrchr( filename, '.' ) = 0; } sprintf( namebuf, "_%d.md3", lod ); strcat( filename, namebuf ); } bool bIsFakeGLA = false; if (!strcmp(filename,"*default.gla")) { bIsFakeGLA = true; } if (!bIsFakeGLA) { int iBytesRead; const byte *pCachedVersion = RE_ModelBinCache_Find( filename, iBytesRead ); if (pCachedVersion) { buf = (unsigned *) malloc(iBytesRead); memcpy(buf,pCachedVersion, iBytesRead); } else { iBytesRead = ri.FS_ReadFile( filename, (void **)&buf ); if (buf) { RE_ModelBinCache_Insert( filename, iBytesRead, (byte *) buf ); } } } else { buf = (unsigned*) GLMModel_GetDefaultGLA(); } if ( !buf ) { continue; } // loadmodel = mod; ident = LL(*(unsigned *)buf); // shouldn't really LL this if a fake GLA, but ModView is Intel-endian, so NP. if ( ident == MD4_IDENT ) { loaded = R_LoadMD4( mod, buf, name ); lod = MD3_MAX_LODS; } else if ( ident == MDXA_IDENT ) { if (!bIsFakeGLA) { loaded = R_LoadMDXA( mod, buf, name ); } else { mod->type = MOD_MDXA; mod->dataSize += ((mdxaHeader_t *)buf)->ofsEnd; mod->mdxa = (mdxaHeader_t *) buf; loaded = qtrue; } } else if ( ident == MDXM_IDENT ) { loaded = R_LoadMDXM( mod, buf, name ); // else try load the file as an md3 } 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, name ); } if (!bIsFakeGLA) { ri.FS_FreeFile (buf); } if ( !loaded ) { if ( lod == 0 ) { goto fail; } else { 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; // } } } 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; } 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; } /* ================= R_LoadMD3 ================= */ static qboolean R_LoadMD3 (model_t *mod, int lod, void *buffer, const char *mod_name ) { int i, j; md3Header_t *pinmodel; md3Frame_t *frame; md3Surface_t *surf; md3Shader_t *shader; md3Triangle_t *tri; md3St_t *st; md3XyzNormal_t *xyz; md3Tag_t *tag; int version; int size; pinmodel = (md3Header_t *)buffer; version = LL (pinmodel->version); if (version != MD3_VERSION) { ri.Printf( PRINT_WARNING, "R_LoadMD3: %s has wrong version (%i should be %i)\n", mod_name, version, MD3_VERSION); return qfalse; } mod->type = MOD_MESH; size = LL(pinmodel->ofsEnd); mod->dataSize += size; mod->md3[lod] = (md3Header_t *)ri.Hunk_Alloc( size ); memcpy (mod->md3[lod], buffer, LL(pinmodel->ofsEnd) ); LL(mod->md3[lod]->ident); LL(mod->md3[lod]->version); LL(mod->md3[lod]->numFrames); LL(mod->md3[lod]->numTags); LL(mod->md3[lod]->numSurfaces); LL(mod->md3[lod]->ofsFrames); LL(mod->md3[lod]->ofsTags); LL(mod->md3[lod]->ofsSurfaces); LL(mod->md3[lod]->ofsEnd); if ( mod->md3[lod]->numFrames < 1 ) { ri.Printf( PRINT_WARNING, "R_LoadMD3: %s has no frames\n", mod_name ); return qfalse; } // swap all the frames frame = (md3Frame_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsFrames ); for ( i = 0 ; i < mod->md3[lod]->numFrames ; i++, frame++) { frame->radius = LF( frame->radius ); for ( j = 0 ; j < 3 ; j++ ) { frame->bounds[0][j] = LF( frame->bounds[0][j] ); frame->bounds[1][j] = LF( frame->bounds[1][j] ); frame->localOrigin[j] = LF( frame->localOrigin[j] ); } } // swap all the tags tag = (md3Tag_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsTags ); for ( i = 0 ; i < mod->md3[lod]->numTags * mod->md3[lod]->numFrames ; i++, tag++) { for ( j = 0 ; j < 3 ; j++ ) { tag->origin[j] = LF( tag->origin[j] ); tag->axis[0][j] = LF( tag->axis[0][j] ); tag->axis[1][j] = LF( tag->axis[1][j] ); tag->axis[2][j] = LF( tag->axis[2][j] ); } } // swap all the surfaces surf = (md3Surface_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsSurfaces ); for ( i = 0 ; i < mod->md3[lod]->numSurfaces ; i++) { LL(surf->ident); LL(surf->flags); LL(surf->numFrames); LL(surf->numShaders); LL(surf->numTriangles); LL(surf->ofsTriangles); LL(surf->numVerts); LL(surf->ofsShaders); LL(surf->ofsSt); LL(surf->ofsXyzNormals); LL(surf->ofsEnd); if ( surf->numVerts > (bQ3RulesApply?SHADER_MAX_VERTEXES:ACTUAL_SHADER_MAX_VERTEXES) ) { ri.Error (ERR_DROP, "R_LoadMD3: %s has more than %i verts on a surface (%i)", mod_name, (bQ3RulesApply?SHADER_MAX_VERTEXES:ACTUAL_SHADER_MAX_VERTEXES), surf->numVerts ); } if ( surf->numTriangles*3 > (bQ3RulesApply?SHADER_MAX_INDEXES:ACTUAL_SHADER_MAX_INDEXES) ) { ri.Error (ERR_DROP, "R_LoadMD3: %s has more than %i triangles on a surface (%i)", mod_name, (bQ3RulesApply?SHADER_MAX_INDEXES:ACTUAL_SHADER_MAX_INDEXES) / 3, surf->numTriangles ); } // change to surface identifier surf->ident = SF_MD3; // lowercase the surface name so skin compares are faster Q_strlwr( surf->name ); // strip off a trailing _1 or _2 // this is a crutch for q3data being a mess j = strlen( surf->name ); if ( j > 2 && surf->name[j-2] == '_' ) { surf->name[j-2] = 0; } // register the shaders shader = (md3Shader_t *) ( (byte *)surf + surf->ofsShaders ); for ( j = 0 ; j < surf->numShaders ; j++, shader++ ) { // shader_t *sh; shader->shaderIndex = Texture_Load(shader->name); } // swap all the triangles tri = (md3Triangle_t *) ( (byte *)surf + surf->ofsTriangles ); for ( j = 0 ; j < surf->numTriangles ; j++, tri++ ) { LL(tri->indexes[0]); LL(tri->indexes[1]); LL(tri->indexes[2]); } // swap all the ST st = (md3St_t *) ( (byte *)surf + surf->ofsSt ); for ( j = 0 ; j < surf->numVerts ; j++, st++ ) { st->st[0] = LF( st->st[0] ); st->st[1] = LF( st->st[1] ); } // swap all the XyzNormals xyz = (md3XyzNormal_t *) ( (byte *)surf + surf->ofsXyzNormals ); for ( j = 0 ; j < surf->numVerts * surf->numFrames ; j++, xyz++ ) { xyz->xyz[0] = LS( xyz->xyz[0] ); xyz->xyz[1] = LS( xyz->xyz[1] ); xyz->xyz[2] = LS( xyz->xyz[2] ); xyz->normal = LS( xyz->normal ); } // find the next surface surf = (md3Surface_t *)( (byte *)surf + surf->ofsEnd ); } return qtrue; } /* ================= R_LoadMD4 ================= */ static qboolean R_LoadMD4( model_t *mod, void *buffer, const char *mod_name ) { int i, j, k; md4Header_t *pinmodel, *md4; md4Frame_t *frame; md4LOD_t *lod; md4Surface_t *surf; md4Triangle_t *tri; md4Vertex_t *v; int version; int size; // shader_t *sh; int frameSize; pinmodel = (md4Header_t *)buffer; version = LL (pinmodel->version); if (version != MD4_VERSION) { ri.Printf( PRINT_WARNING, "R_LoadMD4: %s has wrong version (%i should be %i)\n", mod_name, version, MD4_VERSION); return qfalse; } mod->type = MOD_MD4; size = LL(pinmodel->ofsEnd); mod->dataSize += size; md4 = mod->md4 = (md4Header_t *)ri.Hunk_Alloc( size ); memcpy( md4, buffer, LL(pinmodel->ofsEnd) ); LL(md4->ident); LL(md4->version); LL(md4->numFrames); LL(md4->numBones); LL(md4->numLODs); LL(md4->ofsFrames); LL(md4->ofsLODs); LL(md4->ofsEnd); if ( md4->numFrames < 1 ) { ri.Printf( PRINT_WARNING, "R_LoadMD4: %s has no frames\n", mod_name ); return qfalse; } // we don't need to swap tags in the renderer, they aren't used // swap all the frames frameSize = (int)( &((md4Frame_t *)0)->bones[ md4->numBones ] ); for ( i = 0 ; i < md4->numFrames ; i++, frame++) { frame = (md4Frame_t *) ( (byte *)md4 + md4->ofsFrames + i * frameSize ); frame->radius = LF( frame->radius ); for ( j = 0 ; j < 3 ; j++ ) { frame->bounds[0][j] = LF( frame->bounds[0][j] ); frame->bounds[1][j] = LF( frame->bounds[1][j] ); frame->localOrigin[j] = LF( frame->localOrigin[j] ); } for ( j = 0 ; j < md4->numBones * sizeof( md4Bone_t ) / 4 ; j++ ) { ((float *)frame->bones)[j] = LF( ((float *)frame->bones)[j] ); } } // swap all the LOD's lod = (md4LOD_t *) ( (byte *)md4 + md4->ofsLODs ); for ( i = 0 ; i < md4->numLODs ; i++) { // swap all the surfaces surf = (md4Surface_t *) ( (byte *)lod + lod->ofsSurfaces ); for ( i = 0 ; i < lod->numSurfaces ; i++) { LL(surf->ident); LL(surf->numTriangles); LL(surf->ofsTriangles); LL(surf->numVerts); LL(surf->ofsVerts); LL(surf->ofsEnd); if ( surf->numVerts > (bQ3RulesApply?SHADER_MAX_VERTEXES:ACTUAL_SHADER_MAX_VERTEXES) ) { ri.Error (ERR_DROP, "R_LoadMD3: %s has more than %i verts on a surface (%i)", mod_name, (bQ3RulesApply?SHADER_MAX_VERTEXES:ACTUAL_SHADER_MAX_VERTEXES), surf->numVerts ); } if ( surf->numTriangles*3 > (bQ3RulesApply?SHADER_MAX_INDEXES:ACTUAL_SHADER_MAX_INDEXES) ) { ri.Error (ERR_DROP, "R_LoadMD3: %s has more than %i triangles on a surface (%i)", mod_name, (bQ3RulesApply?SHADER_MAX_INDEXES:ACTUAL_SHADER_MAX_INDEXES) / 3, surf->numTriangles ); } // register the shaders surf->shaderIndex = Texture_Load(surf->shader); // swap all the triangles tri = (md4Triangle_t *) ( (byte *)surf + surf->ofsTriangles ); for ( j = 0 ; j < surf->numTriangles ; j++, tri++ ) { LL(tri->indexes[0]); LL(tri->indexes[1]); LL(tri->indexes[2]); } // swap all the vertexes v = (md4Vertex_t *) ( (byte *)surf + surf->ofsVerts ); for ( j = 0 ; j < surf->numVerts ; j++ ) { v->normal[0] = LF( v->normal[0] ); v->normal[1] = LF( v->normal[1] ); v->normal[2] = LF( v->normal[2] ); v->texCoords[0] = LF( v->texCoords[0] ); v->texCoords[1] = LF( v->texCoords[1] ); v->numWeights = LL( v->numWeights ); for ( k = 0 ; k < v->numWeights ; k++ ) { v->weights[k].boneIndex = LL( v->weights[k].boneIndex ); v->weights[k].boneWeight = LF( v->weights[k].boneWeight ); v->weights[k].offset[0] = LF( v->weights[k].offset[0] ); v->weights[k].offset[1] = LF( v->weights[k].offset[1] ); v->weights[k].offset[2] = LF( v->weights[k].offset[2] ); } v = (md4Vertex_t *)&v->weights[v->numWeights]; } // find the next surface surf = (md4Surface_t *)( (byte *)surf + surf->ofsEnd ); } // find the next LOD lod = (md4LOD_t *)( (byte *)lod + lod->ofsEnd ); } return qtrue; } //============================================================================= /* void RE_BeginRegistration( glconfig_t *glconfigOut ) { ri.Hunk_Clear(); R_Init(); *glconfigOut = glConfig; R_SyncRenderThread(); tr.viewCluster = -1; // force markleafs to regenerate R_ClearFlares(); RE_ClearScene(); tr.registered = qtrue; } */ //============================================================================= /* void R_Modellist_f( void ) { int i, j; model_t *mod; int total; int lods; total = 0; for ( i = 1 ; i < tr.numModels; i++ ) { mod = tr.models[i]; if (mod->mdxm || mod->mdxa) { ri.Printf( PRINT_ALL, "%8i : (%i) %s\n",mod->dataSize, mod->numLods, mod->name ); } else { lods = 1; for ( j = 1 ; j < MD3_MAX_LODS ; j++ ) { if ( mod->md3[j] && mod->md3[j] != mod->md3[j-1] ) { lods++; } } ri.Printf( PRINT_ALL, "%8i : (%i) %s\n",mod->dataSize, lods, mod->name ); } total += mod->dataSize; } ri.Printf( PRINT_ALL, "%8i : Total models\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 } //============================================================================= static md3Tag_t *R_GetTag( md3Header_t *mod, int frame, const char *tagName ) { md3Tag_t *tag; int i; if ( frame >= mod->numFrames ) { // it is possible to have a bad frame while changing models, so don't error frame = mod->numFrames - 1; } tag = (md3Tag_t *)((byte *)mod + mod->ofsTags) + frame * mod->numTags; for ( i = 0 ; i < mod->numTags ; i++, tag++ ) { if ( !strcmp( tag->name, tagName ) ) { return tag; // found it } } return NULL; } void R_LerpTag( orientation_t *tag, qhandle_t handle, int startFrame, int endFrame, float frac, const char *tagName ) { md3Tag_t *start, *end; int i; float frontLerp, backLerp; model_t *model; model = R_GetModelByHandle( handle ); if ( !model->md3[0] ) { AxisClear( tag->axis ); VectorClear( tag->origin ); return; } start = R_GetTag( model->md3[0], startFrame, tagName ); end = R_GetTag( model->md3[0], endFrame, tagName ); if ( !start || !end ) { AxisClear( tag->axis ); VectorClear( tag->origin ); return; } frontLerp = frac; backLerp = 1.0 - 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] ); } 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 ) { VectorCopy( model->bmodel->bounds[0], mins ); VectorCopy( model->bmodel->bounds[1], 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 ); } */ void OnceOnlyCrap(void) { ri.Hunk_Alloc = Crap_HunkAlloc; ri.Hunk_AllocateTempMemory = Crap_HunkAlloc; ri.Hunk_FreeTempMemory = Crap_Free; ri.Malloc = Crap_Malloc; ri.Free = Crap_Free; ri.Printf = Crap_Printf; ri.Error = Crap_Error; ri.FS_ReadFile = Crap_FS_ReadFile; ri.FS_WriteFile = Crap_FS_WriteFile; ri.FS_FreeFile = Crap_FS_FreeFile; ZEROMEM(tr); ZEROMEM(tess); R_ModelInitOnceOnly(); } void trap_G2_SurfaceOffList( int a, void *b) { G2_GetSurfaceList( (qhandle_t) a, (surfaceInfo_t *) b); } qboolean trap_G2_SetSurfaceOnOff (qhandle_t model, surfaceInfo_t *slist, const char *surfaceName, const SurfaceOnOff_t offFlags, const int surface) { return G2_SetSurfaceOnOff(model, slist, surfaceName, offFlags, surface); } SurfaceOnOff_t trap_G2_IsSurfaceOff (qhandle_t model, surfaceInfo_t *slist, const char *surfaceName) { return G2_IsSurfaceOff (model, slist, surfaceName); } void trap_G2_Init_Bone_List(void *a) { G2_Init_Bone_List((boneInfo_t *)a); } qboolean trap_G2_Set_Bone_Anim(int a, void *b, void *c, int d, int e, int f, float g) { return G2_Set_Bone_Anim((qhandle_t) a, (boneInfo_t *) b, (char *) c, d, e, f, g); } //////////////// eof ///////////////