1 /*
2 ===========================================================================
3 Copyright (C) 1999 - 2005, Id Software, Inc.
4 Copyright (C) 2000 - 2013, Raven Software, Inc.
5 Copyright (C) 2001 - 2013, Activision, Inc.
6 Copyright (C) 2005 - 2015, ioquake3 contributors
7 Copyright (C) 2013 - 2015, OpenJK contributors
8
9 This file is part of the OpenJK source code.
10
11 OpenJK is free software; you can redistribute it and/or modify it
12 under the terms of the GNU General Public License version 2 as
13 published by the Free Software Foundation.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, see <http://www.gnu.org/licenses/>.
22 ===========================================================================
23 */
24
25 // tr_models.c -- model loading and caching
26
27 #include "../server/exe_headers.h"
28
29 #include "tr_common.h"
30 #include "tr_local.h"
31 #include "qcommon/matcomp.h"
32 #include "../qcommon/sstring.h"
33
34 #define LL(x) x=LittleLong(x)
35 #define LS(x) x=LittleShort(x)
36 #define LF(x) x=LittleFloat(x)
37
38 void RE_LoadWorldMap_Actual( const char *name, world_t &worldData, int index ); //should only be called for sub-bsp instances
39
40 static qboolean R_LoadMD3 (model_t *mod, int lod, void *buffer, const char *name, qboolean &bAlreadyCached );
41
42 /*
43 Ghoul2 Insert Start
44 */
45
46 typedef struct modelHash_s
47 {
48 char name[MAX_QPATH];
49 qhandle_t handle;
50 struct modelHash_s *next;
51
52 }modelHash_t;
53
54 #define FILE_HASH_SIZE 1024
55 modelHash_t *mhHashTable[FILE_HASH_SIZE];
56
57
58 /*
59 Ghoul2 Insert End
60 */
61
62
63
64 // This stuff looks a bit messy, but it's kept here as black box, and nothing appears in any .H files for other
65 // modules to worry about. I may make another module for this sometime.
66 //
67 typedef std::pair<int,int> StringOffsetAndShaderIndexDest_t;
68 typedef std::vector <StringOffsetAndShaderIndexDest_t> ShaderRegisterData_t;
69 struct CachedEndianedModelBinary_s
70 {
71 void *pModelDiskImage;
72 int iAllocSize; // may be useful for mem-query, but I don't actually need it
73 ShaderRegisterData_t ShaderRegisterData;
74
75 int iLastLevelUsedOn;
76
CachedEndianedModelBinary_sCachedEndianedModelBinary_s77 CachedEndianedModelBinary_s()
78 {
79 pModelDiskImage = 0;
80 iLastLevelUsedOn = -1;
81 iAllocSize = 0;
82 ShaderRegisterData.clear();
83 }
84 };
85 typedef struct CachedEndianedModelBinary_s CachedEndianedModelBinary_t;
86 typedef std::map <sstring_t,CachedEndianedModelBinary_t> CachedModels_t;
87 CachedModels_t *CachedModels = NULL; // the important cache item.
88
RE_RegisterModels_StoreShaderRequest(const char * psModelFileName,const char * psShaderName,const int * piShaderIndexPoke)89 void RE_RegisterModels_StoreShaderRequest(const char *psModelFileName, const char *psShaderName, const int *piShaderIndexPoke)
90 {
91 char sModelName[MAX_QPATH];
92
93 Q_strncpyz(sModelName,psModelFileName,sizeof(sModelName));
94 Q_strlwr (sModelName);
95
96 CachedEndianedModelBinary_t &ModelBin = (*CachedModels)[sModelName];
97
98 if (ModelBin.pModelDiskImage == NULL)
99 {
100 assert(0); // should never happen, means that we're being called on a model that wasn't loaded
101 }
102 else
103 {
104 const int iNameOffset = psShaderName - (char *)ModelBin.pModelDiskImage;
105 const int iPokeOffset = (char*) piShaderIndexPoke - (char *)ModelBin.pModelDiskImage;
106
107 ModelBin.ShaderRegisterData.push_back( StringOffsetAndShaderIndexDest_t( iNameOffset,iPokeOffset) );
108 }
109 }
110
111
112 static const byte FakeGLAFile[] =
113 {
114 0x32, 0x4C, 0x47, 0x41, 0x06, 0x00, 0x00, 0x00, 0x2A, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74,
115 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
116 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
117 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
118 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x01, 0x00, 0x00, 0x00,
119 0x14, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00,
120 0x26, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x4D, 0x6F, 0x64, 0x56, 0x69, 0x65, 0x77, 0x20,
121 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74,
122 0x00, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD,
123 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD,
124 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
125 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
126 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
127 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00,
128 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
129 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
130 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00,
131 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xBF, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F,
132 0x00, 0x80, 0x00, 0x80, 0x00, 0x80
133 };
134
135
136 // returns qtrue if loaded, and sets the supplied qbool to true if it was from cache (instead of disk)
137 // (which we need to know to avoid LittleLong()ing everything again (well, the Mac needs to know anyway)...
138 //
RE_RegisterModels_GetDiskFile(const char * psModelFileName,void ** ppvBuffer,qboolean * pqbAlreadyCached)139 qboolean RE_RegisterModels_GetDiskFile( const char *psModelFileName, void **ppvBuffer, qboolean *pqbAlreadyCached)
140 {
141 char sModelName[MAX_QPATH];
142
143 Q_strncpyz(sModelName,psModelFileName,sizeof(sModelName));
144 Q_strlwr (sModelName);
145
146 CachedEndianedModelBinary_t &ModelBin = (*CachedModels)[sModelName];
147
148 if (ModelBin.pModelDiskImage == NULL)
149 {
150 // didn't have it cached, so try the disk...
151 //
152
153 // special case intercept first...
154 //
155 if (!strcmp(sDEFAULT_GLA_NAME ".gla" , psModelFileName))
156 {
157 // return fake params as though it was found on disk...
158 //
159 void *pvFakeGLAFile = R_Malloc( sizeof(FakeGLAFile), TAG_FILESYS, qfalse );
160 memcpy(pvFakeGLAFile, &FakeGLAFile[0], sizeof(FakeGLAFile));
161 *ppvBuffer = pvFakeGLAFile;
162 *pqbAlreadyCached = qfalse; // faking it like this should mean that it works fine on the Mac as well
163 return qtrue;
164 }
165
166 ri.FS_ReadFile( sModelName, ppvBuffer );
167 *pqbAlreadyCached = qfalse;
168
169 return (qboolean)(*ppvBuffer != 0);
170 }
171 else
172 {
173 *ppvBuffer = ModelBin.pModelDiskImage;
174 *pqbAlreadyCached = qtrue;
175 return qtrue;
176 }
177 }
178
179
180 // if return == true, no further action needed by the caller...
181 //
RE_RegisterModels_Malloc(int iSize,void * pvDiskBufferIfJustLoaded,const char * psModelFileName,qboolean * pqbAlreadyFound,memtag_t eTag)182 void *RE_RegisterModels_Malloc(int iSize, void *pvDiskBufferIfJustLoaded, const char *psModelFileName, qboolean *pqbAlreadyFound, memtag_t eTag)
183 {
184 char sModelName[MAX_QPATH];
185
186 Q_strncpyz(sModelName,psModelFileName,sizeof(sModelName));
187 Q_strlwr (sModelName);
188
189 CachedEndianedModelBinary_t &ModelBin = (*CachedModels)[sModelName];
190
191 if (ModelBin.pModelDiskImage == NULL)
192 {
193 // ... then this entry has only just been created, ie we need to load it fully...
194 //
195 // new, instead of doing a R_Malloc and assigning that we just morph the disk buffer alloc
196 // then don't thrown it away on return - cuts down on mem overhead
197 //
198 // ... groan, but not if doing a limb hierarchy creation (some VV stuff?), in which case it's NULL
199 //
200 if ( pvDiskBufferIfJustLoaded )
201 {
202 R_MorphMallocTag( pvDiskBufferIfJustLoaded, eTag );
203 }
204 else
205 {
206 pvDiskBufferIfJustLoaded = R_Malloc(iSize,eTag, qfalse );
207 }
208
209 ModelBin.pModelDiskImage= pvDiskBufferIfJustLoaded;
210 ModelBin.iAllocSize = iSize;
211 *pqbAlreadyFound = qfalse;
212 }
213 else
214 {
215 // if we already had this model entry, then re-register all the shaders it wanted...
216 //
217 const int iEntries = ModelBin.ShaderRegisterData.size();
218 for (int i=0; i<iEntries; i++)
219 {
220 int iShaderNameOffset = ModelBin.ShaderRegisterData[i].first;
221 int iShaderPokeOffset = ModelBin.ShaderRegisterData[i].second;
222
223 const char *const psShaderName = &((char*)ModelBin.pModelDiskImage)[iShaderNameOffset];
224 int *const piShaderPokePtr= (int *) &((char*)ModelBin.pModelDiskImage)[iShaderPokeOffset];
225
226 shader_t *sh = R_FindShader( psShaderName, lightmapsNone, stylesDefault, qtrue );
227
228 if ( sh->defaultShader )
229 {
230 *piShaderPokePtr = 0;
231 } else {
232 *piShaderPokePtr = sh->index;
233 }
234 }
235 *pqbAlreadyFound = qtrue; // tell caller not to re-Endian or re-Shader this binary
236 }
237
238 ModelBin.iLastLevelUsedOn = RE_RegisterMedia_GetLevel();
239
240 return ModelBin.pModelDiskImage;
241 }
242
243
244 // dump any models not being used by this level if we're running low on memory...
245 //
GetModelDataAllocSize(void)246 static int GetModelDataAllocSize(void)
247 {
248 return R_MemSize( TAG_MODEL_MD3) +
249 R_MemSize( TAG_MODEL_GLM) +
250 R_MemSize( TAG_MODEL_GLA);
251 }
252 extern cvar_t *r_modelpoolmegs;
253 //
254 // return qtrue if at least one cached model was freed (which tells z_malloc()-fail recovery code to try again)
255 //
256 extern qboolean gbInsideRegisterModel;
RE_RegisterModels_LevelLoadEnd(qboolean bDeleteEverythingNotUsedThisLevel)257 qboolean RE_RegisterModels_LevelLoadEnd(qboolean bDeleteEverythingNotUsedThisLevel /* = qfalse */)
258 {
259 qboolean bAtLeastoneModelFreed = qfalse;
260
261 if (gbInsideRegisterModel)
262 {
263 Com_DPrintf( "(Inside RE_RegisterModel (z_malloc recovery?), exiting...\n");
264 }
265 else
266 {
267 int iLoadedModelBytes = GetModelDataAllocSize();
268 const int iMaxModelBytes= r_modelpoolmegs->integer * 1024 * 1024;
269
270 for (CachedModels_t::iterator itModel = CachedModels->begin(); itModel != CachedModels->end() && ( bDeleteEverythingNotUsedThisLevel || iLoadedModelBytes > iMaxModelBytes ); )
271 {
272 CachedEndianedModelBinary_t &CachedModel = (*itModel).second;
273
274 qboolean bDeleteThis = qfalse;
275
276 if (bDeleteEverythingNotUsedThisLevel)
277 {
278 bDeleteThis = (qboolean)(CachedModel.iLastLevelUsedOn != RE_RegisterMedia_GetLevel());
279 }
280 else
281 {
282 bDeleteThis = (qboolean)(CachedModel.iLastLevelUsedOn < RE_RegisterMedia_GetLevel());
283 }
284
285 // if it wasn't used on this level, dump it...
286 //
287 if (bDeleteThis)
288 {
289 #ifdef _DEBUG
290 // LPCSTR psModelName = (*itModel).first.c_str();
291 // ri.Printf( PRINT_DEVELOPER, "Dumping \"%s\"", psModelName);
292 // ri.Printf( PRINT_DEVELOPER, ", used on lvl %d\n",CachedModel.iLastLevelUsedOn);
293 #endif
294
295 if (CachedModel.pModelDiskImage) {
296 R_Free(CachedModel.pModelDiskImage);
297 //CachedModel.pModelDiskImage = NULL; // REM for reference, erase() call below negates the need for it.
298 bAtLeastoneModelFreed = qtrue;
299 }
300 CachedModels->erase(itModel++);
301
302 iLoadedModelBytes = GetModelDataAllocSize();
303 }
304 else
305 {
306 ++itModel;
307 }
308 }
309 }
310
311 //ri.Printf( PRINT_DEVELOPER, "RE_RegisterModels_LevelLoadEnd(): Ok\n");
312
313 return bAtLeastoneModelFreed;
314 }
315
RE_RegisterModels_Info_f(void)316 void RE_RegisterModels_Info_f( void )
317 {
318 int iTotalBytes = 0;
319 if(!CachedModels) {
320 Com_Printf ("%d bytes total (%.2fMB)\n",iTotalBytes, (float)iTotalBytes / 1024.0f / 1024.0f);
321 return;
322 }
323
324 int iModels = CachedModels->size();
325 int iModel = 0;
326
327 for (CachedModels_t::iterator itModel = CachedModels->begin(); itModel != CachedModels->end(); ++itModel,iModel++)
328 {
329 CachedEndianedModelBinary_t &CachedModel = (*itModel).second;
330
331 ri.Printf( PRINT_ALL, "%d/%d: \"%s\" (%d bytes)",iModel,iModels,(*itModel).first.c_str(),CachedModel.iAllocSize );
332
333 #ifdef _DEBUG
334 ri.Printf( PRINT_ALL, ", lvl %d\n",CachedModel.iLastLevelUsedOn);
335 #endif
336
337 iTotalBytes += CachedModel.iAllocSize;
338 }
339 ri.Printf( PRINT_ALL, "%d bytes total (%.2fMB)\n",iTotalBytes, (float)iTotalBytes / 1024.0f / 1024.0f);
340 }
341
342
RE_RegisterModels_DeleteAll(void)343 static void RE_RegisterModels_DeleteAll(void)
344 {
345 if(!CachedModels) {
346 return; //argh!
347 }
348
349 for (CachedModels_t::iterator itModel = CachedModels->begin(); itModel != CachedModels->end(); )
350 {
351 CachedEndianedModelBinary_t &CachedModel = (*itModel).second;
352
353 if (CachedModel.pModelDiskImage) {
354 R_Free(CachedModel.pModelDiskImage);
355 }
356
357 CachedModels->erase(itModel++);
358 }
359
360 extern void RE_AnimationCFGs_DeleteAll(void);
361 RE_AnimationCFGs_DeleteAll();
362 }
363
364
365 static int giRegisterMedia_CurrentLevel=0;
366 static qboolean gbAllowScreenDissolve = qtrue;
367 //
368 // param "bAllowScreenDissolve" is just a convenient way of getting hold of a bool which can be checked by the code that
369 // issues the InitDissolve command later in RE_RegisterMedia_LevelLoadEnd()
370 //
RE_RegisterMedia_LevelLoadBegin(const char * psMapName,ForceReload_e eForceReload,qboolean bAllowScreenDissolve)371 void RE_RegisterMedia_LevelLoadBegin(const char *psMapName, ForceReload_e eForceReload, qboolean bAllowScreenDissolve)
372 {
373 gbAllowScreenDissolve = bAllowScreenDissolve;
374
375 tr.numBSPModels = 0;
376
377 // for development purposes we may want to ditch certain media just before loading a map...
378 //
379 switch (eForceReload)
380 {
381 case eForceReload_BSP:
382
383 ri.CM_DeleteCachedMap(qtrue);
384 R_Images_DeleteLightMaps();
385 break;
386
387 case eForceReload_MODELS:
388
389 RE_RegisterModels_DeleteAll();
390 break;
391
392 case eForceReload_ALL:
393
394 // BSP...
395 //
396 ri.CM_DeleteCachedMap(qtrue);
397 R_Images_DeleteLightMaps();
398 //
399 // models...
400 //
401 RE_RegisterModels_DeleteAll();
402 break;
403 default:
404 break;
405 }
406
407 // at some stage I'll probably want to put some special logic here, like not incrementing the level number
408 // when going into a map like "brig" or something, so returning to the previous level doesn't require an
409 // asset reload etc, but for now...
410 //
411 // only bump level number if we're not on the same level.
412 // Note that this will hide uncached models, which is perhaps a bad thing?...
413 //
414 static char sPrevMapName[MAX_QPATH]={0};
415 if (Q_stricmp( psMapName,sPrevMapName ))
416 {
417 Q_strncpyz( sPrevMapName, psMapName, sizeof(sPrevMapName) );
418 giRegisterMedia_CurrentLevel++;
419 }
420 }
421
RE_RegisterMedia_GetLevel(void)422 int RE_RegisterMedia_GetLevel(void)
423 {
424 return giRegisterMedia_CurrentLevel;
425 }
426
RE_RegisterMedia_LevelLoadEnd(void)427 void RE_RegisterMedia_LevelLoadEnd(void)
428 {
429 RE_RegisterModels_LevelLoadEnd(qfalse);
430 RE_RegisterImages_LevelLoadEnd();
431 ri.SND_RegisterAudio_LevelLoadEnd(qfalse);
432
433 if (gbAllowScreenDissolve)
434 {
435 RE_InitDissolve(qfalse);
436 }
437
438 ri.S_RestartMusic();
439
440 *(ri.gbAlreadyDoingLoad()) = qfalse;
441 }
442
443
444
445
446 /*
447 ** R_GetModelByHandle
448 */
R_GetModelByHandle(qhandle_t index)449 model_t *R_GetModelByHandle( qhandle_t index ) {
450 model_t *mod;
451
452 // out of range gets the defualt model
453 if ( index < 1 || index >= tr.numModels ) {
454 return tr.models[0];
455 }
456
457 mod = tr.models[index];
458
459 return mod;
460 }
461
462 //===============================================================================
463
464 /*
465 ** R_AllocModel
466 */
R_AllocModel(void)467 model_t *R_AllocModel( void ) {
468 model_t *mod;
469
470 if ( tr.numModels == MAX_MOD_KNOWN ) {
471 return NULL;
472 }
473
474 mod = (model_t*) R_Hunk_Alloc( sizeof( *tr.models[tr.numModels] ), qtrue );
475 mod->index= tr.numModels;
476 tr.models[tr.numModels] = mod;
477 tr.numModels++;
478
479 return mod;
480 }
481
482 /*
483 Ghoul2 Insert Start
484 */
485
486 /*
487 ================
488 return a hash value for the filename
489 ================
490 */
generateHashValue(const char * fname,const int size)491 static long generateHashValue( const char *fname, const int size ) {
492 int i;
493 long hash;
494 char letter;
495
496 hash = 0;
497 i = 0;
498 while (fname[i] != '\0') {
499 letter = tolower(fname[i]);
500 if (letter =='.') break; // don't include extension
501 if (letter =='\\') letter = '/'; // damn path names
502 hash+=(long)(letter)*(i+119);
503 i++;
504 }
505 hash &= (size-1);
506 return hash;
507 }
508
RE_InsertModelIntoHash(const char * name,model_t * mod)509 void RE_InsertModelIntoHash(const char *name, model_t *mod)
510 {
511 int hash;
512 modelHash_t *mh;
513
514 hash = generateHashValue(name, FILE_HASH_SIZE);
515
516 // insert this file into the hash table so we can look it up faster later
517 mh = (modelHash_t*)R_Hunk_Alloc( sizeof( modelHash_t ), qtrue );
518
519 mh->next = mhHashTable[hash]; // I have the breakpoint triggered here where mhHashTable[986] would be assigned
520 mh->handle = mod->index;
521 strcpy(mh->name, name);
522 mhHashTable[hash] = mh;
523 }
524 /*
525 Ghoul2 Insert End
526 */
527
528
529 /*
530 ====================
531 RE_RegisterModel
532
533 Loads in a model for the given name
534
535 Zero will be returned if the model fails to load.
536 An entry will be retained for failed models as an
537 optimization to prevent disk rescanning if they are
538 asked for again.
539 ====================
540 */
RE_RegisterModel_Actual(const char * name)541 static qhandle_t RE_RegisterModel_Actual( const char *name )
542 {
543 model_t *mod;
544 unsigned *buf;
545 int lod;
546 int ident;
547 qboolean loaded;
548 // qhandle_t hModel;
549 int numLoaded;
550 /*
551 Ghoul2 Insert Start
552 */
553 int hash;
554 modelHash_t *mh;
555 /*
556 Ghoul2 Insert End
557 */
558
559 if ( !name || !name[0] ) {
560 ri.Printf( PRINT_WARNING, "RE_RegisterModel: NULL name\n" );
561 return 0;
562 }
563
564 if ( strlen( name ) >= MAX_QPATH ) {
565 ri.Printf( PRINT_DEVELOPER, "Model name exceeds MAX_QPATH\n" );
566 return 0;
567 }
568
569 /*
570 Ghoul2 Insert Start
571 */
572 // if (!tr.registered) {
573 // ri.Printf( PRINT_WARNING, "RE_RegisterModel (%s) called before ready!\n",name );
574 // return 0;
575 // }
576 //
577 // search the currently loaded models
578 //
579
580 hash = generateHashValue(name, FILE_HASH_SIZE);
581
582 //
583 // see if the model is already loaded
584 //_
585 for (mh=mhHashTable[hash]; mh; mh=mh->next) {
586 if (Q_stricmp(mh->name, name) == 0) {
587 if (tr.models[mh->handle]->type == MOD_BAD)
588 {
589 return 0;
590 }
591 return mh->handle;
592 }
593 }
594
595 /*
596 Ghoul2 Insert End
597 */
598
599 if (name[0] == '#')
600 {
601 char temp[MAX_QPATH];
602
603 tr.numBSPModels++;
604 #ifndef DEDICATED
605 RE_LoadWorldMap_Actual(va("maps/%s.bsp", name + 1), tr.bspModels[tr.numBSPModels - 1], tr.numBSPModels); //this calls R_LoadSubmodels which will put them into the Hash
606 #endif
607 Com_sprintf(temp, MAX_QPATH, "*%d-0", tr.numBSPModels);
608 hash = generateHashValue(temp, FILE_HASH_SIZE);
609 for (mh=mhHashTable[hash]; mh; mh=mh->next)
610 {
611 if (Q_stricmp(mh->name, temp) == 0)
612 {
613 return mh->handle;
614 }
615 }
616
617 return 0;
618 }
619
620 // allocate a new model_t
621
622 if ( ( mod = R_AllocModel() ) == NULL ) {
623 ri.Printf( PRINT_WARNING, "RE_RegisterModel: R_AllocModel() failed for '%s'\n", name);
624 return 0;
625 }
626
627 // only set the name after the model has been successfully loaded
628 Q_strncpyz( mod->name, name, sizeof( mod->name ) );
629
630 // make sure the render thread is stopped
631 R_IssuePendingRenderCommands(); //
632
633 int iLODStart = 0;
634 if (strstr (name, ".md3")) {
635 iLODStart = MD3_MAX_LODS-1; //this loads the md3s in reverse so they can be biased
636 }
637 mod->numLods = 0;
638
639 //
640 // load the files
641 //
642 numLoaded = 0;
643
644 for ( lod = iLODStart; lod >= 0 ; lod-- ) {
645 char filename[1024];
646
647 strcpy( filename, name );
648
649 if ( lod != 0 ) {
650 char namebuf[80];
651
652 if ( strrchr( filename, '.' ) ) {
653 *strrchr( filename, '.' ) = 0;
654 }
655 sprintf( namebuf, "_%d.md3", lod );
656 strcat( filename, namebuf );
657 }
658
659 qboolean bAlreadyCached = qfalse;
660 if (!RE_RegisterModels_GetDiskFile(filename, (void **)&buf, &bAlreadyCached))
661 {
662 if (numLoaded) //we loaded one already, but a higher LOD is missing!
663 {
664 Com_Error (ERR_DROP, "R_LoadMD3: %s has LOD %d but is missing LOD %d ('%s')!", mod->name, lod+1, lod, filename);
665 }
666 continue;
667 }
668
669 //loadmodel = mod; // this seems to be fairly pointless
670
671 // important that from now on we pass 'filename' instead of 'name' to all model load functions,
672 // because 'filename' accounts for any LOD mangling etc so guarantees unique lookups for yet more
673 // internal caching...
674 //
675 ident = *(unsigned *)buf;
676 if (!bAlreadyCached)
677 {
678 ident = LittleLong(ident);
679 }
680
681 switch (ident)
682 {
683 // if you add any new types of model load in this switch-case, tell me,
684 // or copy what I've done with the cache scheme (-ste).
685 //
686 case MDXA_IDENT:
687
688 loaded = R_LoadMDXA( mod, buf, filename, bAlreadyCached );
689 break;
690
691 case MDXM_IDENT:
692
693 loaded = R_LoadMDXM( mod, buf, filename, bAlreadyCached );
694 break;
695
696 case MD3_IDENT:
697
698 loaded = R_LoadMD3( mod, lod, buf, filename, bAlreadyCached );
699 break;
700
701 default:
702
703 ri.Printf (PRINT_WARNING,"RE_RegisterModel: unknown fileid for %s\n", filename);
704 goto fail;
705 }
706
707 if (!bAlreadyCached){ // important to check!!
708 ri.FS_FreeFile (buf);
709 }
710
711 if ( !loaded ) {
712 if ( lod == 0 ) {
713 ri.Printf (PRINT_WARNING,"RE_RegisterModel: cannot load %s\n", filename);
714 goto fail;
715 } else {
716 break;
717 }
718 } else {
719 mod->numLods++;
720 numLoaded++;
721 // if we have a valid model and are biased
722 // so that we won't see any higher detail ones,
723 // stop loading them
724 if ( lod <= r_lodbias->integer ) {
725 break;
726 }
727 }
728 }
729
730 if ( numLoaded ) {
731 // duplicate into higher lod spots that weren't
732 // loaded, in case the user changes r_lodbias on the fly
733 for ( lod-- ; lod >= 0 ; lod-- ) {
734 mod->numLods++;
735 mod->md3[lod] = mod->md3[lod+1];
736 }
737 /*
738 Ghoul2 Insert Start
739 */
740
741 RE_InsertModelIntoHash(name, mod);
742 return mod->index;
743 /*
744 Ghoul2 Insert End
745 */
746
747 }
748
749
750 fail:
751 // we still keep the model_t around, so if the model name is asked for
752 // again, we won't bother scanning the filesystem
753 mod->type = MOD_BAD;
754 RE_InsertModelIntoHash(name, mod);
755 return 0;
756 }
757
758
759
760
761 // wrapper function needed to avoid problems with mid-function returns so I can safely use this bool to tell the
762 // z_malloc-fail recovery code whether it's safe to ditch any model caches...
763 //
764 qboolean gbInsideRegisterModel = qfalse;
RE_RegisterModel(const char * name)765 qhandle_t RE_RegisterModel( const char *name )
766 {
767 gbInsideRegisterModel = qtrue; // !!!!!!!!!!!!!!
768
769 qhandle_t q = RE_RegisterModel_Actual( name );
770
771 if (Q_stricmp(&name[strlen(name)-4],".gla")){
772 gbInsideRegisterModel = qfalse; // GLA files recursively call this, so don't turn off half way. A reference count would be nice, but if any ERR_DROP ever occurs within the load then the refcount will be knackered from then on
773 }
774
775 return q;
776 }
777
778
779 /*
780 =================
781 R_LoadMD3
782 =================
783 */
R_LoadMD3(model_t * mod,int lod,void * buffer,const char * mod_name,qboolean & bAlreadyCached)784 static qboolean R_LoadMD3 (model_t *mod, int lod, void *buffer, const char *mod_name, qboolean &bAlreadyCached ) {
785 int i, j;
786 md3Header_t *pinmodel;
787 md3Surface_t *surf;
788 md3Shader_t *shader;
789 int version;
790 int size;
791
792 #ifdef Q3_BIG_ENDIAN
793 md3Frame_t *frame;
794 md3Triangle_t *tri;
795 md3St_t *st;
796 md3XyzNormal_t *xyz;
797 md3Tag_t *tag;
798 #endif
799
800
801 pinmodel= (md3Header_t *)buffer;
802 //
803 // read some fields from the binary, but only LittleLong() them when we know this wasn't an already-cached model...
804 //
805 version = pinmodel->version;
806 size = pinmodel->ofsEnd;
807
808 if (!bAlreadyCached)
809 {
810 version = LittleLong(version);
811 size = LittleLong(size);
812 }
813
814 if (version != MD3_VERSION) {
815 ri.Printf( PRINT_WARNING, "R_LoadMD3: %s has wrong version (%i should be %i)\n",
816 mod_name, version, MD3_VERSION);
817 return qfalse;
818 }
819
820 mod->type = MOD_MESH;
821 mod->dataSize += size;
822
823 qboolean bAlreadyFound = qfalse;
824 mod->md3[lod] = (md3Header_t *) RE_RegisterModels_Malloc(size, buffer, mod_name, &bAlreadyFound, TAG_MODEL_MD3);
825
826 assert(bAlreadyCached == bAlreadyFound);
827
828 if (!bAlreadyFound)
829 {
830 // horrible new hackery, if !bAlreadyFound then we've just done a tag-morph, so we need to set the
831 // bool reference passed into this function to true, to tell the caller NOT to do an FS_Freefile since
832 // we've hijacked that memory block...
833 //
834 // Aaaargh. Kill me now...
835 //
836 bAlreadyCached = qtrue;
837 assert( mod->md3[lod] == buffer );
838 // memcpy( mod->md3[lod], buffer, size ); // and don't do this now, since it's the same thing
839
840 LL(mod->md3[lod]->ident);
841 LL(mod->md3[lod]->version);
842 LL(mod->md3[lod]->numFrames);
843 LL(mod->md3[lod]->numTags);
844 LL(mod->md3[lod]->numSurfaces);
845 LL(mod->md3[lod]->ofsFrames);
846 LL(mod->md3[lod]->ofsTags);
847 LL(mod->md3[lod]->ofsSurfaces);
848 LL(mod->md3[lod]->ofsEnd);
849 }
850
851 if ( mod->md3[lod]->numFrames < 1 ) {
852 ri.Printf( PRINT_WARNING, "R_LoadMD3: %s has no frames\n", mod_name );
853 return qfalse;
854 }
855
856 if (bAlreadyFound)
857 {
858 return qtrue; // All done. Stop, go no further, do not pass Go...
859 }
860
861 #ifdef Q3_BIG_ENDIAN
862 // swap all the frames
863 frame = (md3Frame_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsFrames );
864 for ( i = 0 ; i < mod->md3[lod]->numFrames ; i++, frame++) {
865 LF(frame->radius);
866 for ( j = 0 ; j < 3 ; j++ ) {
867 LF(frame->bounds[0][j]);
868 LF(frame->bounds[1][j]);
869 LF(frame->localOrigin[j]);
870 }
871 }
872
873 // swap all the tags
874 tag = (md3Tag_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsTags );
875 for ( i = 0 ; i < mod->md3[lod]->numTags * mod->md3[lod]->numFrames ; i++, tag++) {
876 for ( j = 0 ; j < 3 ; j++ ) {
877 LF(tag->origin[j]);
878 LF(tag->axis[0][j]);
879 LF(tag->axis[1][j]);
880 LF(tag->axis[2][j]);
881 }
882 }
883 #endif
884
885 // swap all the surfaces
886 surf = (md3Surface_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsSurfaces );
887 for ( i = 0 ; i < mod->md3[lod]->numSurfaces ; i++) {
888 LL(surf->flags);
889 LL(surf->numFrames);
890 LL(surf->numShaders);
891 LL(surf->numTriangles);
892 LL(surf->ofsTriangles);
893 LL(surf->numVerts);
894 LL(surf->ofsShaders);
895 LL(surf->ofsSt);
896 LL(surf->ofsXyzNormals);
897 LL(surf->ofsEnd);
898
899 if ( surf->numVerts > SHADER_MAX_VERTEXES ) {
900 Com_Error (ERR_DROP, "R_LoadMD3: %s has more than %i verts on a surface (%i)",
901 mod_name, SHADER_MAX_VERTEXES, surf->numVerts );
902 }
903 if ( surf->numTriangles*3 > SHADER_MAX_INDEXES ) {
904 Com_Error (ERR_DROP, "R_LoadMD3: %s has more than %i triangles on a surface (%i)",
905 mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles );
906 }
907
908 // change to surface identifier
909 surf->ident = SF_MD3;
910
911 // lowercase the surface name so skin compares are faster
912 Q_strlwr( surf->name );
913
914 // strip off a trailing _1 or _2
915 // this is a crutch for q3data being a mess
916 j = strlen( surf->name );
917 if ( j > 2 && surf->name[j-2] == '_' ) {
918 surf->name[j-2] = 0;
919 }
920
921 // register the shaders
922 shader = (md3Shader_t *) ( (byte *)surf + surf->ofsShaders );
923 for ( j = 0 ; j < surf->numShaders ; j++, shader++ ) {
924 shader_t *sh;
925
926 sh = R_FindShader( shader->name, lightmapsNone, stylesDefault, qtrue );
927 if ( sh->defaultShader ) {
928 shader->shaderIndex = 0;
929 } else {
930 shader->shaderIndex = sh->index;
931 }
932 RE_RegisterModels_StoreShaderRequest(mod_name, &shader->name[0], &shader->shaderIndex);
933 }
934
935
936 #ifdef Q3_BIG_ENDIAN
937 // swap all the triangles
938 tri = (md3Triangle_t *) ( (byte *)surf + surf->ofsTriangles );
939 for ( j = 0 ; j < surf->numTriangles ; j++, tri++ ) {
940 LL(tri->indexes[0]);
941 LL(tri->indexes[1]);
942 LL(tri->indexes[2]);
943 }
944
945 // swap all the ST
946 st = (md3St_t *) ( (byte *)surf + surf->ofsSt );
947 for ( j = 0 ; j < surf->numVerts ; j++, st++ ) {
948 LF(st->st[0]);
949 LF(st->st[1]);
950 }
951
952 // swap all the XyzNormals
953 xyz = (md3XyzNormal_t *) ( (byte *)surf + surf->ofsXyzNormals );
954 for ( j = 0 ; j < surf->numVerts * surf->numFrames ; j++, xyz++ )
955 {
956 LS(xyz->xyz[0]);
957 LS(xyz->xyz[1]);
958 LS(xyz->xyz[2]);
959
960 LS(xyz->normal);
961 }
962 #endif
963
964 // find the next surface
965 surf = (md3Surface_t *)( (byte *)surf + surf->ofsEnd );
966 }
967
968 return qtrue;
969 }
970
971
972 //=============================================================================
973
974 void CM_LoadShaderText(bool forceReload);
975 void CM_SetupShaderProperties(void);
976
977 /*
978 ** RE_BeginRegistration
979 */
RE_BeginRegistration(glconfig_t * glconfigOut)980 void RE_BeginRegistration( glconfig_t *glconfigOut ) {
981 ri.Hunk_ClearToMark();
982
983 R_Init();
984
985 *glconfigOut = glConfig;
986
987 R_IssuePendingRenderCommands();
988
989 tr.viewCluster = -1; // force markleafs to regenerate
990
991
992 RE_ClearScene();
993
994 tr.registered = qtrue;
995
996 }
997
998 //=============================================================================
999
1000 /*
1001 ===============
1002 R_ModelInit
1003 ===============
1004 */
R_ModelInit(void)1005 void R_ModelInit( void )
1006 {
1007 static CachedModels_t singleton; // sorry vv, your dynamic allocation was a (false) memory leak
1008 CachedModels = &singleton;
1009
1010 model_t *mod;
1011
1012 // leave a space for NULL model
1013 tr.numModels = 0;
1014 /*
1015 Ghoul2 Insert Start
1016 */
1017
1018 memset(mhHashTable, 0, sizeof(mhHashTable));
1019 /*
1020 Ghoul2 Insert End
1021 */
1022
1023 mod = R_AllocModel();
1024 mod->type = MOD_BAD;
1025
1026 }
1027
1028
1029 /*
1030 ================
1031 R_Modellist_f
1032 ================
1033 */
R_Modellist_f(void)1034 void R_Modellist_f( void ) {
1035 int i, j;
1036 model_t *mod;
1037 int total;
1038 int lods;
1039
1040 total = 0;
1041 for ( i = 1 ; i < tr.numModels; i++ ) {
1042 mod = tr.models[i];
1043 switch (mod->type)
1044 {
1045 default:
1046 assert(0);
1047 ri.Printf( PRINT_ALL, "UNKNOWN : %s\n", mod->name );
1048 break;
1049
1050 case MOD_BAD:
1051 ri.Printf( PRINT_ALL, "MOD_BAD : %s\n", mod->name );
1052 break;
1053
1054 case MOD_BRUSH:
1055 ri.Printf( PRINT_ALL, "%8i : (%i) %s\n", mod->dataSize, mod->numLods, mod->name );
1056 break;
1057
1058 case MOD_MDXA:
1059
1060 ri.Printf( PRINT_ALL, "%8i : (%i) %s\n", mod->dataSize, mod->numLods, mod->name );
1061 break;
1062
1063 case MOD_MDXM:
1064
1065 ri.Printf( PRINT_ALL, "%8i : (%i) %s\n", mod->dataSize, mod->numLods, mod->name );
1066 break;
1067
1068 case MOD_MESH:
1069
1070 lods = 1;
1071 for ( j = 1 ; j < MD3_MAX_LODS ; j++ ) {
1072 if ( mod->md3[j] && mod->md3[j] != mod->md3[j-1] ) {
1073 lods++;
1074 }
1075 }
1076 ri.Printf( PRINT_ALL, "%8i : (%i) %s\n",mod->dataSize, lods, mod->name );
1077 break;
1078 }
1079 total += mod->dataSize;
1080 }
1081 ri.Printf( PRINT_ALL, "%8i : Total models\n", total );
1082
1083 /* this doesn't work with the new hunks
1084 if ( tr.world ) {
1085 ri.Printf( PRINT_ALL, "%8i : %s\n", tr.world->dataSize, tr.world->name );
1086 } */
1087 }
1088
1089 //=============================================================================
1090
1091
1092 /*
1093 ================
1094 R_GetTag for MD3s
1095 ================
1096 */
R_GetTag(md3Header_t * mod,int frame,const char * tagName)1097 static md3Tag_t *R_GetTag( md3Header_t *mod, int frame, const char *tagName ) {
1098 md3Tag_t *tag;
1099 int i;
1100
1101 if ( frame >= mod->numFrames ) {
1102 // it is possible to have a bad frame while changing models, so don't error
1103 frame = mod->numFrames - 1;
1104 }
1105
1106 tag = (md3Tag_t *)((byte *)mod + mod->ofsTags) + frame * mod->numTags;
1107 for ( i = 0 ; i < mod->numTags ; i++, tag++ ) {
1108 if ( !strcmp( tag->name, tagName ) ) {
1109 return tag; // found it
1110 }
1111 }
1112
1113 return NULL;
1114 }
1115
1116 /*
1117 ================
1118 R_LerpTag
1119 ================
1120 */
R_LerpTag(orientation_t * tag,qhandle_t handle,int startFrame,int endFrame,float frac,const char * tagName)1121 void R_LerpTag( orientation_t *tag, qhandle_t handle, int startFrame, int endFrame,
1122 float frac, const char *tagName ) {
1123 md3Tag_t *start, *finish;
1124 int i;
1125 float frontLerp, backLerp;
1126 model_t *model;
1127
1128 model = R_GetModelByHandle( handle );
1129 if ( model->md3[0] )
1130 {
1131 start = R_GetTag( model->md3[0], startFrame, tagName );
1132 finish = R_GetTag( model->md3[0], endFrame, tagName );
1133 }
1134 else
1135 {
1136 AxisClear( tag->axis );
1137 VectorClear( tag->origin );
1138 return;
1139 }
1140
1141 if ( !start || !finish ) {
1142 AxisClear( tag->axis );
1143 VectorClear( tag->origin );
1144 return;
1145 }
1146
1147 frontLerp = frac;
1148 backLerp = 1.0 - frac;
1149
1150 for ( i = 0 ; i < 3 ; i++ ) {
1151 tag->origin[i] = start->origin[i] * backLerp + finish->origin[i] * frontLerp;
1152 tag->axis[0][i] = start->axis[0][i] * backLerp + finish->axis[0][i] * frontLerp;
1153 tag->axis[1][i] = start->axis[1][i] * backLerp + finish->axis[1][i] * frontLerp;
1154 tag->axis[2][i] = start->axis[2][i] * backLerp + finish->axis[2][i] * frontLerp;
1155 }
1156 VectorNormalize( tag->axis[0] );
1157 VectorNormalize( tag->axis[1] );
1158 VectorNormalize( tag->axis[2] );
1159 }
1160
1161
1162 /*
1163 ====================
1164 R_ModelBounds
1165 ====================
1166 */
R_ModelBounds(qhandle_t handle,vec3_t mins,vec3_t maxs)1167 void R_ModelBounds( qhandle_t handle, vec3_t mins, vec3_t maxs ) {
1168 model_t *model;
1169
1170 model = R_GetModelByHandle( handle );
1171
1172 if ( model->bmodel ) {
1173 VectorCopy( model->bmodel->bounds[0], mins );
1174 VectorCopy( model->bmodel->bounds[1], maxs );
1175 return;
1176 }
1177
1178 if ( model->md3[0] ) {
1179 md3Header_t *header;
1180 md3Frame_t *frame;
1181 header = model->md3[0];
1182
1183 frame = (md3Frame_t *)( (byte *)header + header->ofsFrames );
1184
1185 VectorCopy( frame->bounds[0], mins );
1186 VectorCopy( frame->bounds[1], maxs );
1187 }
1188 else
1189 {
1190 VectorClear( mins );
1191 VectorClear( maxs );
1192 return;
1193 }
1194 }
1195