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, ¤t ); 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 }