]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/renderer/ModelManager.cpp
hello world
[icculus/iodoom3.git] / neo / renderer / ModelManager.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 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 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 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 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 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 #include "../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "Model_local.h"
33 #include "tr_local.h"   // just for R_FreeWorldInteractions and R_CreateWorldInteractions
34
35
36 class idRenderModelManagerLocal : public idRenderModelManager {
37 public:
38                                                         idRenderModelManagerLocal();
39         virtual                                 ~idRenderModelManagerLocal() {}
40
41         virtual void                    Init();
42         virtual void                    Shutdown();
43         virtual idRenderModel * AllocModel();
44         virtual void                    FreeModel( idRenderModel *model );
45         virtual idRenderModel * FindModel( const char *modelName );
46         virtual idRenderModel * CheckModel( const char *modelName );
47         virtual idRenderModel * DefaultModel();
48         virtual void                    AddModel( idRenderModel *model );
49         virtual void                    RemoveModel( idRenderModel *model );
50         virtual void                    ReloadModels( bool forceAll = false );
51         virtual void                    FreeModelVertexCaches();
52         virtual void                    WritePrecacheCommands( idFile *file );
53         virtual void                    BeginLevelLoad();
54         virtual void                    EndLevelLoad();
55
56         virtual void                    PrintMemInfo( MemInfo_t *mi );
57
58 private:
59         idList<idRenderModel*>  models;
60         idHashIndex                             hash;
61         idRenderModel *                 defaultModel;
62         idRenderModel *                 beamModel;
63         idRenderModel *                 spriteModel;
64         idRenderModel *                 trailModel;
65         bool                                    insideLevelLoad;                // don't actually load now
66
67         idRenderModel *                 GetModel( const char *modelName, bool createIfNotFound );
68
69         static void                             PrintModel_f( const idCmdArgs &args );
70         static void                             ListModels_f( const idCmdArgs &args );
71         static void                             ReloadModels_f( const idCmdArgs &args );
72         static void                             TouchModel_f( const idCmdArgs &args );
73 };
74
75
76 idRenderModelManagerLocal       localModelManager;
77 idRenderModelManager *          renderModelManager = &localModelManager;
78
79 /*
80 ==============
81 idRenderModelManagerLocal::idRenderModelManagerLocal
82 ==============
83 */
84 idRenderModelManagerLocal::idRenderModelManagerLocal() {
85         defaultModel = NULL;
86         beamModel = NULL;
87         spriteModel = NULL;
88         insideLevelLoad = false;
89         trailModel = NULL;
90 }
91
92 /*
93 ==============
94 idRenderModelManagerLocal::PrintModel_f
95 ==============
96 */
97 void idRenderModelManagerLocal::PrintModel_f( const idCmdArgs &args ) {
98         idRenderModel   *model;
99
100         if ( args.Argc() != 2 ) {
101                 common->Printf( "usage: printModel <modelName>\n" );
102                 return;
103         }
104
105         model = renderModelManager->CheckModel( args.Argv( 1 ) );
106         if ( !model ) {
107                 common->Printf( "model \"%s\" not found\n", args.Argv( 1 ) );
108                 return;
109         }
110
111         model->Print();
112 }
113
114 /*
115 ==============
116 idRenderModelManagerLocal::ListModels_f
117 ==============
118 */
119 void idRenderModelManagerLocal::ListModels_f( const idCmdArgs &args ) {
120         int             totalMem = 0;
121         int             inUse = 0;
122
123         common->Printf( " mem   srf verts tris\n" );
124         common->Printf( " ---   --- ----- ----\n" );
125
126         for ( int i = 0 ; i < localModelManager.models.Num() ; i++ ) {
127                 idRenderModel   *model = localModelManager.models[i];
128
129                 if ( !model->IsLoaded() ) {
130                         continue;
131                 }
132                 model->List();
133                 totalMem += model->Memory();
134                 inUse++;
135         }
136
137         common->Printf( " ---   --- ----- ----\n" );
138         common->Printf( " mem   srf verts tris\n" );
139
140         common->Printf( "%i loaded models\n", inUse );
141         common->Printf( "total memory: %4.1fM\n", (float)totalMem / (1024*1024) );
142 }
143
144 /*
145 ==============
146 idRenderModelManagerLocal::ReloadModels_f
147 ==============
148 */
149 void idRenderModelManagerLocal::ReloadModels_f( const idCmdArgs &args ) {
150         if ( idStr::Icmp( args.Argv(1), "all" ) == 0 ) {
151                 localModelManager.ReloadModels( true );
152         } else {
153                 localModelManager.ReloadModels( false );
154         }
155 }
156
157 /*
158 ==============
159 idRenderModelManagerLocal::TouchModel_f
160
161 Precache a specific model
162 ==============
163 */
164 void idRenderModelManagerLocal::TouchModel_f( const idCmdArgs &args ) {
165         const char      *model = args.Argv( 1 );
166
167         if ( !model[0] ) {
168                 common->Printf( "usage: touchModel <modelName>\n" );
169                 return;
170         }
171
172         common->Printf( "touchModel %s\n", model );
173         session->UpdateScreen();
174         idRenderModel *m = renderModelManager->CheckModel( model );
175         if ( !m ) {
176                 common->Printf( "...not found\n" );
177         }
178 }
179
180 /*
181 =================
182 idRenderModelManagerLocal::WritePrecacheCommands
183 =================
184 */
185 void idRenderModelManagerLocal::WritePrecacheCommands( idFile *f ) {
186         for ( int i = 0 ; i < models.Num() ; i++ ) {
187                 idRenderModel   *model = models[i];
188
189                 if ( !model ) {
190                         continue;
191                 }
192                 if ( !model->IsReloadable() ) {
193                         continue;
194                 }
195
196                 char    str[1024];
197                 sprintf( str, "touchModel %s\n", model->Name() );
198                 common->Printf( "%s", str );
199                 f->Printf( "%s", str );
200         }
201 }
202
203 /*
204 =================
205 idRenderModelManagerLocal::Init
206 =================
207 */
208 void idRenderModelManagerLocal::Init() {
209         cmdSystem->AddCommand( "listModels", ListModels_f, CMD_FL_RENDERER, "lists all models" );
210         cmdSystem->AddCommand( "printModel", PrintModel_f, CMD_FL_RENDERER, "prints model info", idCmdSystem::ArgCompletion_ModelName );
211         cmdSystem->AddCommand( "reloadModels", ReloadModels_f, CMD_FL_RENDERER|CMD_FL_CHEAT, "reloads models" );
212         cmdSystem->AddCommand( "touchModel", TouchModel_f, CMD_FL_RENDERER, "touches a model", idCmdSystem::ArgCompletion_ModelName );
213
214         insideLevelLoad = false;
215
216         // create a default model
217         idRenderModelStatic *model = new idRenderModelStatic;
218         model->InitEmpty( "_DEFAULT" );
219         model->MakeDefaultModel();
220         model->SetLevelLoadReferenced( true );
221         defaultModel = model;
222         AddModel( model );
223
224         // create the beam model
225         idRenderModelStatic *beam = new idRenderModelBeam;
226         beam->InitEmpty( "_BEAM" );
227         beam->SetLevelLoadReferenced( true );
228         beamModel = beam;
229         AddModel( beam );
230
231         idRenderModelStatic *sprite = new idRenderModelSprite;
232         sprite->InitEmpty( "_SPRITE" );
233         sprite->SetLevelLoadReferenced( true );
234         spriteModel = sprite;
235         AddModel( sprite );
236 }
237
238 /*
239 =================
240 idRenderModelManagerLocal::Shutdown
241 =================
242 */
243 void idRenderModelManagerLocal::Shutdown() {
244         models.DeleteContents( true );
245         hash.Free();
246 }
247
248 /*
249 =================
250 idRenderModelManagerLocal::GetModel
251 =================
252 */
253 idRenderModel *idRenderModelManagerLocal::GetModel( const char *modelName, bool createIfNotFound ) {
254         idStr           canonical;
255         idStr           extension;
256
257         if ( !modelName || !modelName[0] ) {
258                 return NULL;
259         }
260
261         canonical = modelName;
262         canonical.ToLower();
263
264         // see if it is already present
265         int key = hash.GenerateKey( modelName, false );
266         for ( int i = hash.First( key ); i != -1; i = hash.Next( i ) ) {
267                 idRenderModel *model = models[i];
268
269                 if ( canonical.Icmp( model->Name() ) == 0 ) {
270                         if ( !model->IsLoaded() ) {
271                                 // reload it if it was purged
272                                 model->LoadModel();
273                         } else if ( insideLevelLoad && !model->IsLevelLoadReferenced() ) {
274                                 // we are reusing a model already in memory, but
275                                 // touch all the materials to make sure they stay
276                                 // in memory as well
277                                 model->TouchData();
278                         }
279                         model->SetLevelLoadReferenced( true );
280                         return model;
281                 }
282         }
283
284         // see if we can load it
285
286         // determine which subclass of idRenderModel to initialize
287
288         idRenderModel   *model;
289
290         canonical.ExtractFileExtension( extension );
291
292         if ( ( extension.Icmp( "ase" ) == 0 ) || ( extension.Icmp( "lwo" ) == 0 ) || ( extension.Icmp( "flt" ) == 0 ) ) {
293                 model = new idRenderModelStatic;
294                 model->InitFromFile( modelName );
295         } else if ( extension.Icmp( "ma" ) == 0 ) {
296                 model = new idRenderModelStatic;
297                 model->InitFromFile( modelName );
298         } else if ( extension.Icmp( MD5_MESH_EXT ) == 0 ) {
299                 model = new idRenderModelMD5;
300                 model->InitFromFile( modelName );
301         } else if ( extension.Icmp( "md3" ) == 0 ) {
302                 model = new idRenderModelMD3;
303                 model->InitFromFile( modelName );
304         } else if ( extension.Icmp( "prt" ) == 0  ) {
305                 model = new idRenderModelPrt;
306                 model->InitFromFile( modelName );
307         } else if ( extension.Icmp( "liquid" ) == 0  ) {
308                 model = new idRenderModelLiquid;
309                 model->InitFromFile( modelName );
310         } else {
311
312                 if ( extension.Length() ) {
313                         common->Warning( "unknown model type '%s'", canonical.c_str() );
314                 }
315
316                 if ( !createIfNotFound ) {
317                         return NULL;
318                 }
319
320                 idRenderModelStatic     *smodel = new idRenderModelStatic;
321                 smodel->InitEmpty( modelName );
322                 smodel->MakeDefaultModel();
323
324                 model = smodel;
325         }
326
327         model->SetLevelLoadReferenced( true );
328
329         if ( !createIfNotFound && model->IsDefaultModel() ) {
330                 delete model;
331                 model = NULL;
332
333                 return NULL;
334         }
335
336         AddModel( model );
337
338         return model;
339 }
340
341 /*
342 =================
343 idRenderModelManagerLocal::AllocModel
344 =================
345 */
346 idRenderModel *idRenderModelManagerLocal::AllocModel() {
347         return new idRenderModelStatic();
348 }
349
350 /*
351 =================
352 idRenderModelManagerLocal::FreeModel
353 =================
354 */
355 void idRenderModelManagerLocal::FreeModel( idRenderModel *model ) {
356         if ( !model ) {
357                 return;
358         }
359         if ( !dynamic_cast<idRenderModelStatic *>( model ) ) {
360                 common->Error( "idRenderModelManager::FreeModel: model '%s' is not a static model", model->Name() );
361                 return;
362         }
363         if ( model == defaultModel ) {
364                 common->Error( "idRenderModelManager::FreeModel: can't free the default model" );
365                 return;
366         }
367         if ( model == beamModel ) {
368                 common->Error( "idRenderModelManager::FreeModel: can't free the beam model" );
369                 return;
370         }
371         if ( model == spriteModel ) { 
372                 common->Error( "idRenderModelManager::FreeModel: can't free the sprite model" );
373                 return;
374         }
375
376         R_CheckForEntityDefsUsingModel( model );
377
378         delete model;
379 }
380
381 /*
382 =================
383 idRenderModelManagerLocal::FindModel
384 =================
385 */
386 idRenderModel *idRenderModelManagerLocal::FindModel( const char *modelName ) {
387         return GetModel( modelName, true );
388 }
389
390 /*
391 =================
392 idRenderModelManagerLocal::CheckModel
393 =================
394 */
395 idRenderModel *idRenderModelManagerLocal::CheckModel( const char *modelName ) {
396         return GetModel( modelName, false );
397 }
398
399 /*
400 =================
401 idRenderModelManagerLocal::DefaultModel
402 =================
403 */
404 idRenderModel *idRenderModelManagerLocal::DefaultModel() {
405         return defaultModel;
406 }
407
408 /*
409 =================
410 idRenderModelManagerLocal::AddModel
411 =================
412 */
413 void idRenderModelManagerLocal::AddModel( idRenderModel *model ) {
414         hash.Add( hash.GenerateKey( model->Name(), false ), models.Append( model ) );
415 }
416
417 /*
418 =================
419 idRenderModelManagerLocal::RemoveModel
420 =================
421 */
422 void idRenderModelManagerLocal::RemoveModel( idRenderModel *model ) {
423         int index = models.FindIndex( model );
424         hash.RemoveIndex( hash.GenerateKey( model->Name(), false ), index );
425         models.RemoveIndex( index );
426 }
427
428 /*
429 =================
430 idRenderModelManagerLocal::ReloadModels
431 =================
432 */
433 void idRenderModelManagerLocal::ReloadModels( bool forceAll ) {
434         if ( forceAll ) {
435                 common->Printf( "Reloading all model files...\n" );
436         } else {
437                 common->Printf( "Checking for changed model files...\n" );
438         }
439
440         R_FreeDerivedData();
441
442         // skip the default model at index 0
443         for ( int i = 1 ; i < models.Num() ; i++ ) {
444                 idRenderModel   *model = models[i];
445
446                 // we may want to allow world model reloading in the future, but we don't now
447                 if ( !model->IsReloadable() ) {
448                         continue;
449                 }
450
451                 if ( !forceAll ) {
452                         // check timestamp
453                         ID_TIME_T current;
454
455                         fileSystem->ReadFile( model->Name(), NULL, &current );
456                         if ( current <= model->Timestamp() ) {
457                                 continue;
458                         }
459                 }
460
461                 common->DPrintf( "reloading %s.\n", model->Name() );
462
463                 model->LoadModel();
464         }
465
466         // we must force the world to regenerate, because models may
467         // have changed size, making their references invalid
468         R_ReCreateWorldReferences();
469 }
470
471 /*
472 =================
473 idRenderModelManagerLocal::FreeModelVertexCaches
474 =================
475 */
476 void idRenderModelManagerLocal::FreeModelVertexCaches() {
477         for ( int i = 0 ; i < models.Num() ; i++ ) {
478                 idRenderModel *model = models[i];
479                 model->FreeVertexCache();
480         }
481 }
482
483 /*
484 =================
485 idRenderModelManagerLocal::BeginLevelLoad
486 =================
487 */
488 void idRenderModelManagerLocal::BeginLevelLoad() {
489         insideLevelLoad = true;
490
491         for ( int i = 0 ; i < models.Num() ; i++ ) {
492                 idRenderModel *model = models[i];
493
494                 if ( com_purgeAll.GetBool() && model->IsReloadable() ) {
495                         R_CheckForEntityDefsUsingModel( model );
496                         model->PurgeModel();
497                 }
498
499                 model->SetLevelLoadReferenced( false );
500         }
501
502         // purge unused triangle surface memory
503         R_PurgeTriSurfData( frameData );
504 }
505
506 /*
507 =================
508 idRenderModelManagerLocal::EndLevelLoad
509 =================
510 */
511 void idRenderModelManagerLocal::EndLevelLoad() {
512         common->Printf( "----- idRenderModelManagerLocal::EndLevelLoad -----\n" );
513
514         int start = Sys_Milliseconds();
515
516         insideLevelLoad = false;
517         int     purgeCount = 0;
518         int     keepCount = 0;
519         int     loadCount = 0;
520
521         // purge any models not touched
522         for ( int i = 0 ; i < models.Num() ; i++ ) {
523                 idRenderModel *model = models[i];
524
525                 if ( !model->IsLevelLoadReferenced() && model->IsLoaded() && model->IsReloadable() ) {
526
527 //                      common->Printf( "purging %s\n", model->Name() );
528
529                         purgeCount++;
530
531                         R_CheckForEntityDefsUsingModel( model );
532
533                         model->PurgeModel();
534
535                 } else {
536
537 //                      common->Printf( "keeping %s\n", model->Name() );
538
539                         keepCount++;
540                 }
541         }
542
543         // purge unused triangle surface memory
544         R_PurgeTriSurfData( frameData );
545
546         // load any new ones
547         for ( int i = 0 ; i < models.Num() ; i++ ) {
548                 idRenderModel *model = models[i];
549
550                 if ( model->IsLevelLoadReferenced() && !model->IsLoaded() && model->IsReloadable() ) {
551
552                         loadCount++;
553                         model->LoadModel();
554
555                         if ( ( loadCount & 15 ) == 0 ) {
556                                 session->PacifierUpdate();
557                         }
558                 }
559         }
560
561         // _D3XP added this
562         int     end = Sys_Milliseconds();
563         common->Printf( "%5i models purged from previous level, ", purgeCount );
564         common->Printf( "%5i models kept.\n", keepCount );
565         if ( loadCount ) {
566                 common->Printf( "%5i new models loaded in %5.1f seconds\n", loadCount, (end-start) * 0.001 );
567         }
568         common->Printf( "---------------------------------------------------\n" );
569 }
570
571 /*
572 =================
573 idRenderModelManagerLocal::PrintMemInfo
574 =================
575 */
576 void idRenderModelManagerLocal::PrintMemInfo( MemInfo_t *mi ) {
577         int i, j, totalMem = 0;
578         int *sortIndex;
579         idFile *f;
580
581         f = fileSystem->OpenFileWrite( mi->filebase + "_models.txt" );
582         if ( !f ) {
583                 return;
584         }
585
586         // sort first
587         sortIndex = new int[ localModelManager.models.Num()];
588
589         for ( i = 0; i <  localModelManager.models.Num(); i++ ) {
590                 sortIndex[i] = i;
591         }
592
593         for ( i = 0; i <  localModelManager.models.Num() - 1; i++ ) {
594                 for ( j = i + 1; j <  localModelManager.models.Num(); j++ ) {
595                         if (  localModelManager.models[sortIndex[i]]->Memory() <  localModelManager.models[sortIndex[j]]->Memory() ) {
596                                 int temp = sortIndex[i];
597                                 sortIndex[i] = sortIndex[j];
598                                 sortIndex[j] = temp;
599                         }
600                 }
601         }
602
603         // print next
604         for ( int i = 0 ; i < localModelManager.models.Num() ; i++ ) {
605                 idRenderModel   *model = localModelManager.models[sortIndex[i]];
606                 int mem;
607
608                 if ( !model->IsLoaded() ) {
609                         continue;
610                 }
611
612                 mem = model->Memory();
613                 totalMem += mem;
614                 f->Printf( "%s %s\n", idStr::FormatNumber( mem ).c_str(), model->Name() );
615         }
616
617         delete sortIndex;
618         mi->modelAssetsTotal = totalMem;
619
620         f->Printf( "\nTotal model bytes allocated: %s\n", idStr::FormatNumber( totalMem ).c_str() );
621         fileSystem->CloseFile( f );
622 }