DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

ModelManager.cpp (20466B)


      1 /*
      2 ===========================================================================
      3 
      4 Doom 3 BFG Edition GPL Source Code
      5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 
      6 
      7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").  
      8 
      9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
     10 it under the terms of the GNU General Public License as published by
     11 the Free Software Foundation, either version 3 of the License, or
     12 (at your option) any later version.
     13 
     14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
     15 but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 GNU General Public License for more details.
     18 
     19 You should have received a copy of the GNU General Public License
     20 along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.
     23 
     24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
     25 
     26 ===========================================================================
     27 */
     28 
     29 #pragma hdrstop
     30 #include "../idlib/precompiled.h"
     31 
     32 #include "Model_local.h"
     33 #include "tr_local.h"	// just for R_FreeWorldInteractions and R_CreateWorldInteractions
     34 
     35 idCVar r_binaryLoadRenderModels( "r_binaryLoadRenderModels", "1", 0, "enable binary load/write of render models" );
     36 idCVar preload_MapModels( "preload_MapModels", "1", CVAR_SYSTEM | CVAR_BOOL, "preload models during begin or end levelload" );
     37 
     38 class idRenderModelManagerLocal : public idRenderModelManager {
     39 public:
     40 							idRenderModelManagerLocal();
     41 	virtual					~idRenderModelManagerLocal() {}
     42 
     43 	virtual void			Init();
     44 	virtual void			Shutdown();
     45 	virtual idRenderModel *	AllocModel();
     46 	virtual void			FreeModel( idRenderModel *model );
     47 	virtual idRenderModel *	FindModel( const char *modelName );
     48 	virtual idRenderModel *	CheckModel( const char *modelName );
     49 	virtual idRenderModel *	DefaultModel();
     50 	virtual void			AddModel( idRenderModel *model );
     51 	virtual void			RemoveModel( idRenderModel *model );
     52 	virtual void			ReloadModels( bool forceAll = false );
     53 	virtual void			FreeModelVertexCaches();
     54 	virtual void			WritePrecacheCommands( idFile *file );
     55 	virtual void			BeginLevelLoad();
     56 	virtual void			EndLevelLoad();
     57 	virtual void			Preload( const idPreloadManifest &manifest );
     58 
     59 	virtual	void			PrintMemInfo( MemInfo_t *mi );
     60 
     61 private:
     62 	idList<idRenderModel*, TAG_MODEL>	models;
     63 	idHashIndex				hash;
     64 	idRenderModel *			defaultModel;
     65 	idRenderModel *			beamModel;
     66 	idRenderModel *			spriteModel;
     67 	bool					insideLevelLoad;		// don't actually load now
     68 
     69 	idRenderModel *			GetModel( const char *modelName, bool createIfNotFound );
     70 
     71 	static void				PrintModel_f( const idCmdArgs &args );
     72 	static void				ListModels_f( const idCmdArgs &args );
     73 	static void				ReloadModels_f( const idCmdArgs &args );
     74 	static void				TouchModel_f( const idCmdArgs &args );
     75 };
     76 
     77 
     78 idRenderModelManagerLocal	localModelManager;
     79 idRenderModelManager *		renderModelManager = &localModelManager;
     80 
     81 /*
     82 ==============
     83 idRenderModelManagerLocal::idRenderModelManagerLocal
     84 ==============
     85 */
     86 idRenderModelManagerLocal::idRenderModelManagerLocal() {
     87 	defaultModel = NULL;
     88 	beamModel = NULL;
     89 	spriteModel = NULL;
     90 	insideLevelLoad = false;
     91 }
     92 
     93 /*
     94 ==============
     95 idRenderModelManagerLocal::PrintModel_f
     96 ==============
     97 */
     98 void idRenderModelManagerLocal::PrintModel_f( const idCmdArgs &args ) {
     99 	idRenderModel	*model;
    100 
    101 	if ( args.Argc() != 2 ) {
    102 		common->Printf( "usage: printModel <modelName>\n" );
    103 		return;
    104 	}
    105 
    106 	model = renderModelManager->CheckModel( args.Argv( 1 ) );
    107 	if ( !model ) {
    108 		common->Printf( "model \"%s\" not found\n", args.Argv( 1 ) );
    109 		return;
    110 	}
    111 
    112 	model->Print();
    113 }
    114 
    115 /*
    116 ==============
    117 idRenderModelManagerLocal::ListModels_f
    118 ==============
    119 */
    120 void idRenderModelManagerLocal::ListModels_f( const idCmdArgs &args ) {
    121 	int		totalMem = 0;
    122 	int		inUse = 0;
    123 
    124 	common->Printf( " mem   srf verts tris\n" );
    125 	common->Printf( " ---   --- ----- ----\n" );
    126 
    127 	for ( int i = 0; i < localModelManager.models.Num(); i++ ) {
    128 		idRenderModel	*model = localModelManager.models[i];
    129 
    130 		if ( !model->IsLoaded() ) {
    131 			continue;
    132 		}
    133 		model->List();
    134 		totalMem += model->Memory();
    135 		inUse++;
    136 	}
    137 
    138 	common->Printf( " ---   --- ----- ----\n" );
    139 	common->Printf( " mem   srf verts tris\n" );
    140 
    141 	common->Printf( "%i loaded models\n", inUse );
    142 	common->Printf( "total memory: %4.1fM\n", (float)totalMem / (1024*1024) );
    143 }
    144 
    145 /*
    146 ==============
    147 idRenderModelManagerLocal::ReloadModels_f
    148 ==============
    149 */
    150 void idRenderModelManagerLocal::ReloadModels_f( const idCmdArgs &args ) {
    151 	if ( idStr::Icmp( args.Argv(1), "all" ) == 0 ) {
    152 		localModelManager.ReloadModels( true );
    153 	} else {
    154 		localModelManager.ReloadModels( false );
    155 	}
    156 }
    157 
    158 /*
    159 ==============
    160 idRenderModelManagerLocal::TouchModel_f
    161 
    162 Precache a specific model
    163 ==============
    164 */
    165 void idRenderModelManagerLocal::TouchModel_f( const idCmdArgs &args ) {
    166 	const char	*model = args.Argv( 1 );
    167 
    168 	if ( !model[0] ) {
    169 		common->Printf( "usage: touchModel <modelName>\n" );
    170 		return;
    171 	}
    172 
    173 	common->Printf( "touchModel %s\n", model );
    174 	const bool captureToImage = false;
    175 	common->UpdateScreen( captureToImage );
    176 	idRenderModel *m = renderModelManager->CheckModel( model );
    177 	if ( !m ) {
    178 		common->Printf( "...not found\n" );
    179 	}
    180 }
    181 
    182 /*
    183 =================
    184 idRenderModelManagerLocal::WritePrecacheCommands
    185 =================
    186 */
    187 void idRenderModelManagerLocal::WritePrecacheCommands( idFile *f ) {
    188 	for ( int i = 0; i < models.Num(); i++ ) {
    189 		idRenderModel	*model = models[i];
    190 
    191 		if ( !model ) {
    192 			continue;
    193 		}
    194 		if ( !model->IsReloadable() ) {
    195 			continue;
    196 		}
    197 
    198 		char	str[1024];
    199 		sprintf( str, "touchModel %s\n", model->Name() );
    200 		common->Printf( "%s", str );
    201 		f->Printf( "%s", str );
    202 	}
    203 }
    204 
    205 /*
    206 =================
    207 idRenderModelManagerLocal::Init
    208 =================
    209 */
    210 void idRenderModelManagerLocal::Init() {
    211 	cmdSystem->AddCommand( "listModels", ListModels_f, CMD_FL_RENDERER, "lists all models" );
    212 	cmdSystem->AddCommand( "printModel", PrintModel_f, CMD_FL_RENDERER, "prints model info", idCmdSystem::ArgCompletion_ModelName );
    213 	cmdSystem->AddCommand( "reloadModels", ReloadModels_f, CMD_FL_RENDERER|CMD_FL_CHEAT, "reloads models" );
    214 	cmdSystem->AddCommand( "touchModel", TouchModel_f, CMD_FL_RENDERER, "touches a model", idCmdSystem::ArgCompletion_ModelName );
    215 
    216 	insideLevelLoad = false;
    217 
    218 	// create a default model
    219 	idRenderModelStatic *model = new (TAG_MODEL) idRenderModelStatic;
    220 	model->InitEmpty( "_DEFAULT" );
    221 	model->MakeDefaultModel();
    222 	model->SetLevelLoadReferenced( true );
    223 	defaultModel = model;
    224 	AddModel( model );
    225 
    226 	// create the beam model
    227 	idRenderModelStatic *beam = new (TAG_MODEL) idRenderModelBeam;
    228 	beam->InitEmpty( "_BEAM" );
    229 	beam->SetLevelLoadReferenced( true );
    230 	beamModel = beam;
    231 	AddModel( beam );
    232 
    233 	idRenderModelStatic *sprite = new (TAG_MODEL) idRenderModelSprite;
    234 	sprite->InitEmpty( "_SPRITE" );
    235 	sprite->SetLevelLoadReferenced( true );
    236 	spriteModel = sprite;
    237 	AddModel( sprite );
    238 }
    239 
    240 /*
    241 =================
    242 idRenderModelManagerLocal::Shutdown
    243 =================
    244 */
    245 void idRenderModelManagerLocal::Shutdown() {
    246 	models.DeleteContents( true );
    247 	hash.Free();
    248 }
    249 
    250 /*
    251 =================
    252 idRenderModelManagerLocal::GetModel
    253 =================
    254 */
    255 idRenderModel *idRenderModelManagerLocal::GetModel( const char *_modelName, bool createIfNotFound ) {
    256 
    257 	if ( !_modelName || !_modelName[0] ) {
    258 		return NULL;
    259 	}
    260 
    261 	idStrStatic< MAX_OSPATH > canonical = _modelName;
    262 	canonical.ToLower();
    263 
    264 	idStrStatic< MAX_OSPATH > extension;
    265 	canonical.ExtractFileExtension( extension );
    266 
    267 	// see if it is already present
    268 	int key = hash.GenerateKey( canonical, false );
    269 	for ( int i = hash.First( key ); i != -1; i = hash.Next( i ) ) {
    270 		idRenderModel *model = models[i];
    271 
    272 		if ( canonical.Icmp( model->Name() ) == 0 ) {
    273 			if ( !model->IsLoaded() ) {
    274 				// reload it if it was purged
    275 				idStr generatedFileName = "generated/rendermodels/";
    276 				generatedFileName.AppendPath( canonical );
    277 				generatedFileName.SetFileExtension( va( "b%s", extension.c_str() ) );
    278 				if ( model->SupportsBinaryModel() && r_binaryLoadRenderModels.GetBool() ) {
    279 					idFileLocal file( fileSystem->OpenFileReadMemory( generatedFileName ) );
    280 					model->PurgeModel();
    281 					if ( !model->LoadBinaryModel( file, 0 ) ) {
    282 						model->LoadModel();
    283 					}
    284 				} else {
    285 					model->LoadModel();
    286 				}
    287 			} else if ( insideLevelLoad && !model->IsLevelLoadReferenced() ) {
    288 				// we are reusing a model already in memory, but
    289 				// touch all the materials to make sure they stay
    290 				// in memory as well
    291 				model->TouchData();
    292 			}
    293 			model->SetLevelLoadReferenced( true );
    294 			return model;
    295 		}
    296 	}
    297 
    298 	// see if we can load it
    299 
    300 	// determine which subclass of idRenderModel to initialize
    301 
    302 	idRenderModel * model = NULL;
    303 
    304 	if ( ( extension.Icmp( "ase" ) == 0 ) || ( extension.Icmp( "lwo" ) == 0 ) || ( extension.Icmp( "flt" ) == 0 ) || ( extension.Icmp( "ma" ) == 0 ) ) {
    305 		model = new (TAG_MODEL) idRenderModelStatic;
    306 	} else if ( extension.Icmp( MD5_MESH_EXT ) == 0 ) {
    307 		model = new (TAG_MODEL) idRenderModelMD5;
    308 	} else if ( extension.Icmp( "md3" ) == 0 ) {
    309 		model = new (TAG_MODEL) idRenderModelMD3;
    310 	} else if ( extension.Icmp( "prt" ) == 0  ) {
    311 		model = new (TAG_MODEL) idRenderModelPrt;
    312 	} else if ( extension.Icmp( "liquid" ) == 0  ) {
    313 		model = new (TAG_MODEL) idRenderModelLiquid;
    314 	}
    315 
    316 	idStrStatic< MAX_OSPATH > generatedFileName;
    317 
    318 	if ( model != NULL ) {
    319 
    320 		generatedFileName = "generated/rendermodels/";
    321 		generatedFileName.AppendPath( canonical );
    322 		generatedFileName.SetFileExtension( va( "b%s", extension.c_str() ) );
    323 
    324 		// Get the timestamp on the original file, if it's newer than what is stored in binary model, regenerate it
    325 		ID_TIME_T sourceTimeStamp = fileSystem->GetTimestamp( canonical );
    326 
    327 		idFileLocal file( fileSystem->OpenFileReadMemory( generatedFileName ) );
    328 
    329 		if ( !model->SupportsBinaryModel() || !r_binaryLoadRenderModels.GetBool() ) {
    330 			model->InitFromFile( canonical );
    331 		} else {
    332 			if ( !model->LoadBinaryModel( file, sourceTimeStamp ) ) {
    333 				model->InitFromFile( canonical );
    334 
    335 				idFileLocal outputFile( fileSystem->OpenFileWrite( generatedFileName, "fs_basepath" ) );
    336 				idLib::Printf( "Writing %s\n", generatedFileName.c_str() );
    337 				model->WriteBinaryModel( outputFile );
    338 			} /* else {
    339 				idLib::Printf( "loaded binary model %s from file %s\n", model->Name(), generatedFileName.c_str() );
    340 			} */
    341 		} 
    342 	}
    343 
    344 	// Not one of the known formats
    345 	if ( model == NULL ) {
    346 
    347 		if ( extension.Length() ) {
    348 			common->Warning( "unknown model type '%s'", canonical.c_str() );
    349 		}
    350 
    351 		if ( !createIfNotFound ) {
    352 			return NULL;
    353 		}
    354 
    355 		idRenderModelStatic	*smodel = new (TAG_MODEL) idRenderModelStatic;
    356 		smodel->InitEmpty( canonical );
    357 		smodel->MakeDefaultModel();
    358 
    359 		model = smodel;
    360 	}
    361 
    362 	if ( cvarSystem->GetCVarBool( "fs_buildresources" ) ) {
    363 		fileSystem->AddModelPreload( canonical );
    364 	}
    365 	model->SetLevelLoadReferenced( true );
    366 
    367 	if ( !createIfNotFound && model->IsDefaultModel() ) {
    368 		delete model;
    369 		model = NULL;
    370 
    371 		return NULL;
    372 	}
    373 
    374 	if ( cvarSystem->GetCVarBool( "fs_buildgame" ) ) {
    375 		fileSystem->AddModelPreload( model->Name() );
    376 	}
    377 
    378 	AddModel( model );
    379 
    380 	return model;
    381 }
    382 
    383 /*
    384 =================
    385 idRenderModelManagerLocal::AllocModel
    386 =================
    387 */
    388 idRenderModel *idRenderModelManagerLocal::AllocModel() {
    389 	return new (TAG_MODEL) idRenderModelStatic();
    390 }
    391 
    392 /*
    393 =================
    394 idRenderModelManagerLocal::FreeModel
    395 =================
    396 */
    397 void idRenderModelManagerLocal::FreeModel( idRenderModel *model ) {
    398 	if ( !model ) {
    399 		return;
    400 	}
    401 	if ( !dynamic_cast<idRenderModelStatic *>( model ) ) {
    402 		common->Error( "idRenderModelManager::FreeModel: model '%s' is not a static model", model->Name() );
    403 		return;
    404 	}
    405 	if ( model == defaultModel ) {
    406 		common->Error( "idRenderModelManager::FreeModel: can't free the default model" );
    407 		return;
    408 	}
    409 	if ( model == beamModel ) {
    410 		common->Error( "idRenderModelManager::FreeModel: can't free the beam model" );
    411 		return;
    412 	}
    413 	if ( model == spriteModel ) { 
    414 		common->Error( "idRenderModelManager::FreeModel: can't free the sprite model" );
    415 		return;
    416 	}
    417 
    418 	R_CheckForEntityDefsUsingModel( model );
    419 
    420 	delete model;
    421 }
    422 
    423 /*
    424 =================
    425 idRenderModelManagerLocal::FindModel
    426 =================
    427 */
    428 idRenderModel *idRenderModelManagerLocal::FindModel( const char *modelName ) {
    429 	return GetModel( modelName, true );
    430 }
    431 
    432 /*
    433 =================
    434 idRenderModelManagerLocal::CheckModel
    435 =================
    436 */
    437 idRenderModel *idRenderModelManagerLocal::CheckModel( const char *modelName ) {
    438 	return GetModel( modelName, false );
    439 }
    440 
    441 /*
    442 =================
    443 idRenderModelManagerLocal::DefaultModel
    444 =================
    445 */
    446 idRenderModel *idRenderModelManagerLocal::DefaultModel() {
    447 	return defaultModel;
    448 }
    449 
    450 /*
    451 =================
    452 idRenderModelManagerLocal::AddModel
    453 =================
    454 */
    455 void idRenderModelManagerLocal::AddModel( idRenderModel *model ) {
    456 	hash.Add( hash.GenerateKey( model->Name(), false ), models.Append( model ) );
    457 }
    458 
    459 /*
    460 =================
    461 idRenderModelManagerLocal::RemoveModel
    462 =================
    463 */
    464 void idRenderModelManagerLocal::RemoveModel( idRenderModel *model ) {
    465 	int index = models.FindIndex( model );
    466 	if ( index != -1 ) {
    467 		hash.RemoveIndex( hash.GenerateKey( model->Name(), false ), index );
    468 		models.RemoveIndex( index );
    469 	}
    470 }
    471 
    472 /*
    473 =================
    474 idRenderModelManagerLocal::ReloadModels
    475 =================
    476 */
    477 void idRenderModelManagerLocal::ReloadModels( bool forceAll ) {
    478 	if ( forceAll ) {
    479 		common->Printf( "Reloading all model files...\n" );
    480 	} else {
    481 		common->Printf( "Checking for changed model files...\n" );
    482 	}
    483 
    484 	R_FreeDerivedData();
    485 
    486 	// skip the default model at index 0
    487 	for ( int i = 1; i < models.Num(); i++ ) {
    488 		idRenderModel	*model = models[i];
    489 
    490 		// we may want to allow world model reloading in the future, but we don't now
    491 		if ( !model->IsReloadable() ) {
    492 			continue;
    493 		}
    494 
    495 		if ( !forceAll ) {
    496 			// check timestamp
    497 			ID_TIME_T current;
    498 
    499 			fileSystem->ReadFile( model->Name(), NULL, &current );
    500 			if ( current <= model->Timestamp() ) {
    501 				continue;
    502 			}
    503 		}
    504 
    505 		common->DPrintf( "reloading %s.\n", model->Name() );
    506 
    507 		model->LoadModel();
    508 	}
    509 
    510 	// we must force the world to regenerate, because models may
    511 	// have changed size, making their references invalid
    512 	R_ReCreateWorldReferences();
    513 }
    514 
    515 /*
    516 =================
    517 idRenderModelManagerLocal::FreeModelVertexCaches
    518 =================
    519 */
    520 void idRenderModelManagerLocal::FreeModelVertexCaches() {
    521 	for ( int i = 0; i < models.Num(); i++ ) {
    522 		idRenderModel *model = models[i];
    523 		model->FreeVertexCache();
    524 	}
    525 }
    526 
    527 /*
    528 =================
    529 idRenderModelManagerLocal::BeginLevelLoad
    530 =================
    531 */
    532 void idRenderModelManagerLocal::BeginLevelLoad() {
    533 	insideLevelLoad = true;
    534 
    535 	for ( int i = 0; i < models.Num(); i++ ) {
    536 		idRenderModel *model = models[i];
    537 
    538 		// always reload all models 
    539 		if ( model->IsReloadable() ) {
    540 			R_CheckForEntityDefsUsingModel( model );
    541 			model->PurgeModel();
    542 		}
    543 
    544 		model->SetLevelLoadReferenced( false );
    545 	}
    546 
    547 	vertexCache.FreeStaticData();
    548 }
    549 
    550 /*
    551 =================
    552 idRenderModelManagerLocal::Preload
    553 =================
    554 */
    555 void idRenderModelManagerLocal::Preload( const idPreloadManifest &manifest  ) {
    556 	if ( preload_MapModels.GetBool() ) {
    557 		// preload this levels images
    558 		int	start = Sys_Milliseconds();
    559 		int numLoaded = 0;
    560 		idList< preloadSort_t > preloadSort;
    561 		preloadSort.Resize( manifest.NumResources() );
    562 		for ( int i = 0; i < manifest.NumResources(); i++ ) {
    563 			const preloadEntry_s & p = manifest.GetPreloadByIndex( i );
    564 			idResourceCacheEntry rc;
    565 			idStrStatic< MAX_OSPATH > filename;
    566 			if ( p.resType == PRELOAD_MODEL ) {
    567 				filename = "generated/rendermodels/";
    568 				filename += p.resourceName;
    569 				idStrStatic< 16 > ext;
    570 				filename.ExtractFileExtension( ext );
    571 				filename.SetFileExtension( va( "b%s", ext.c_str() ) );
    572 			}
    573 			if ( p.resType == PRELOAD_PARTICLE ) {
    574 				filename = "generated/particles/";
    575 				filename += p.resourceName;
    576 				filename += ".bprt";
    577 			}
    578 			if ( !filename.IsEmpty() ) {
    579 				if ( fileSystem->GetResourceCacheEntry( filename, rc ) ) {
    580 					preloadSort_t ps = {};
    581 					ps.idx = i;
    582 					ps.ofs = rc.offset;
    583 					preloadSort.Append( ps );
    584 				}
    585 			}
    586 		}
    587 		
    588 		preloadSort.SortWithTemplate( idSort_Preload() );
    589 
    590 		for ( int i = 0; i < preloadSort.Num(); i++ ) {
    591 			const preloadSort_t & ps = preloadSort[ i ];
    592 			const preloadEntry_s & p = manifest.GetPreloadByIndex( ps.idx );
    593 			if ( p.resType == PRELOAD_MODEL ) {
    594 				idRenderModel * model = FindModel( p.resourceName );
    595 				if ( model != NULL ) {
    596 					model->SetLevelLoadReferenced( true );
    597 				}
    598 			} else if ( p.resType == PRELOAD_PARTICLE ) {
    599 				declManager->FindType( DECL_PARTICLE, p.resourceName );
    600 			}
    601 			numLoaded++;
    602 		}
    603 
    604 		int	end = Sys_Milliseconds();
    605 		common->Printf( "%05d models preloaded ( or were already loaded ) in %5.1f seconds\n", numLoaded, ( end - start ) * 0.001 );
    606 		common->Printf( "----------------------------------------\n" );
    607 	}
    608 }
    609 
    610 
    611 
    612 /*
    613 =================
    614 idRenderModelManagerLocal::EndLevelLoad
    615 =================
    616 */
    617 void idRenderModelManagerLocal::EndLevelLoad() {
    618 	common->Printf( "----- idRenderModelManagerLocal::EndLevelLoad -----\n" );
    619 
    620 	int start = Sys_Milliseconds();
    621 
    622 	insideLevelLoad = false;
    623 	int	purgeCount = 0;
    624 	int	keepCount = 0;
    625 	int	loadCount = 0;
    626 
    627 	// purge any models not touched
    628 	for ( int i = 0; i < models.Num(); i++ ) {
    629 		idRenderModel *model = models[i];
    630 
    631 		if ( !model->IsLevelLoadReferenced() && model->IsLoaded() && model->IsReloadable() ) {
    632 
    633 //			common->Printf( "purging %s\n", model->Name() );
    634 
    635 			purgeCount++;
    636 
    637 			R_CheckForEntityDefsUsingModel( model );
    638 
    639 			model->PurgeModel();
    640 
    641 		} else {
    642 
    643 //			common->Printf( "keeping %s\n", model->Name() );
    644 
    645 			keepCount++;
    646 		}
    647 
    648 		common->UpdateLevelLoadPacifier();
    649 	}
    650 
    651 	// load any new ones
    652 	for ( int i = 0; i < models.Num(); i++ ) {
    653 		common->UpdateLevelLoadPacifier();
    654 
    655 
    656 		idRenderModel *model = models[i];
    657 
    658 		if ( model->IsLevelLoadReferenced() && !model->IsLoaded() && model->IsReloadable() ) {
    659 			loadCount++;
    660 			model->LoadModel();
    661 		}
    662 	}
    663 
    664 	// create static vertex/index buffers for all models
    665 	for ( int i = 0; i < models.Num(); i++ ) {
    666 		common->UpdateLevelLoadPacifier();
    667 
    668 
    669 		idRenderModel *model = models[i];
    670 		if ( model->IsLoaded() ) {
    671 			for ( int j = 0; j < model->NumSurfaces(); j++ ) {
    672 				R_CreateStaticBuffersForTri( *(model->Surface( j )->geometry) );
    673 			}
    674 		}
    675 	}
    676 
    677 
    678 	// _D3XP added this
    679 	int	end = Sys_Milliseconds();
    680 	common->Printf( "%5i models purged from previous level, ", purgeCount );
    681 	common->Printf( "%5i models kept.\n", keepCount );
    682 	if ( loadCount ) {
    683 		common->Printf( "%5i new models loaded in %5.1f seconds\n", loadCount, (end-start) * 0.001 );
    684 	}
    685 	common->Printf( "---------------------------------------------------\n" );
    686 }
    687 
    688 /*
    689 =================
    690 idRenderModelManagerLocal::PrintMemInfo
    691 =================
    692 */
    693 void idRenderModelManagerLocal::PrintMemInfo( MemInfo_t *mi ) {
    694 	int i, j, totalMem = 0;
    695 	int *sortIndex;
    696 	idFile *f;
    697 
    698 	f = fileSystem->OpenFileWrite( mi->filebase + "_models.txt" );
    699 	if ( !f ) {
    700 		return;
    701 	}
    702 
    703 	// sort first
    704 	sortIndex = new (TAG_MODEL) int[ localModelManager.models.Num()];
    705 
    706 	for ( i = 0; i <  localModelManager.models.Num(); i++ ) {
    707 		sortIndex[i] = i;
    708 	}
    709 
    710 	for ( i = 0; i <  localModelManager.models.Num() - 1; i++ ) {
    711 		for ( j = i + 1; j <  localModelManager.models.Num(); j++ ) {
    712 			if (  localModelManager.models[sortIndex[i]]->Memory() <  localModelManager.models[sortIndex[j]]->Memory() ) {
    713 				int temp = sortIndex[i];
    714 				sortIndex[i] = sortIndex[j];
    715 				sortIndex[j] = temp;
    716 			}
    717 		}
    718 	}
    719 
    720 	// print next
    721 	for ( int i = 0; i < localModelManager.models.Num(); i++ ) {
    722 		idRenderModel	*model = localModelManager.models[sortIndex[i]];
    723 		int mem;
    724 
    725 		if ( !model->IsLoaded() ) {
    726 			continue;
    727 		}
    728 
    729 		mem = model->Memory();
    730 		totalMem += mem;
    731 		f->Printf( "%s %s\n", idStr::FormatNumber( mem ).c_str(), model->Name() );
    732 	}
    733 
    734 	delete [] sortIndex;
    735 	mi->modelAssetsTotal = totalMem;
    736 
    737 	f->Printf( "\nTotal model bytes allocated: %s\n", idStr::FormatNumber( totalMem ).c_str() );
    738 	fileSystem->CloseFile( f );
    739 }