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