Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

6232 lines
184 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Model loading / unloading interface
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #include "render_pch.h"
  8. #include "common.h"
  9. #include "modelloader.h"
  10. #include "sysexternal.h"
  11. #include "cmd.h"
  12. #include "istudiorender.h"
  13. #include "engine/ivmodelinfo.h"
  14. #include "draw.h"
  15. #include "zone.h"
  16. #include "edict.h"
  17. #include "cmodel_engine.h"
  18. #include "cdll_engine_int.h"
  19. #include "iscratchpad3d.h"
  20. #include "materialsystem/imaterialsystemhardwareconfig.h"
  21. #include "materialsystem/materialsystem_config.h"
  22. #include "gl_rsurf.h"
  23. #include "video/ivideoservices.h"
  24. #include "materialsystem/itexture.h"
  25. #include "Overlay.h"
  26. #include "utldict.h"
  27. #include "mempool.h"
  28. #include "r_decal.h"
  29. #include "l_studio.h"
  30. #include "gl_drawlights.h"
  31. #include "tier0/icommandline.h"
  32. #include "MapReslistGenerator.h"
  33. #ifndef SWDS
  34. #include "vgui_baseui_interface.h"
  35. #endif
  36. #include "engine/ivmodelrender.h"
  37. #include "host.h"
  38. #include "datacache/idatacache.h"
  39. #include "sys_dll.h"
  40. #include "datacache/imdlcache.h"
  41. #include "gl_cvars.h"
  42. #include "vphysics_interface.h"
  43. #include "filesystem/IQueuedLoader.h"
  44. #include "tier2/tier2.h"
  45. #include "lightcache.h"
  46. #include "lumpfiles.h"
  47. #include "tier2/fileutils.h"
  48. #include "UtlSortVector.h"
  49. #include "utlhashtable.h"
  50. #include "tier1/lzmaDecoder.h"
  51. #include "eiface.h"
  52. #include "server.h"
  53. #include "ifilelist.h"
  54. #include "LoadScreenUpdate.h"
  55. #include "optimize.h"
  56. #include "networkstringtable.h"
  57. #include "tier1/callqueue.h"
  58. // memdbgon must be the last include file in a .cpp file!!!
  59. #include "tier0/memdbgon.h"
  60. ConVar mat_loadtextures( "mat_loadtextures", "1", FCVAR_CHEAT );
  61. // OS X and Linux are blowing up right now due to this. Benefits vs possible regressions on DX less clear.
  62. #if defined( DX_TO_GL_ABSTRACTION ) || defined( STAGING_ONLY )
  63. #define CONVAR_DEFAULT_MOD_OFFLINE_HDR_SWITCH "1"
  64. #else
  65. #define CONVAR_DEFAULT_MOD_OFFLINE_HDR_SWITCH "0"
  66. #endif
  67. static ConVar mod_offline_hdr_switch( "mod_offline_hdr_switch", CONVAR_DEFAULT_MOD_OFFLINE_HDR_SWITCH, FCVAR_INTERNAL_USE,
  68. "Re-order the HDR/LDR mode switch to do most of the material system "
  69. "reloading with the device offline. This reduces unnecessary device "
  70. "resource uploads and may drastically reduce load time and memory pressure "
  71. "on certain drivers, but may trigger bugs in some very old source engine "
  72. "pathways." );
  73. static ConVar mod_touchalldata( "mod_touchalldata", "1", 0, "Touch model data during level startup" );
  74. static ConVar mod_forcetouchdata( "mod_forcetouchdata", "1", 0, "Forces all model file data into cache on model load." );
  75. ConVar mat_excludetextures( "mat_excludetextures", "0", FCVAR_CHEAT );
  76. ConVar r_unloadlightmaps( "r_unloadlightmaps", "0", FCVAR_CHEAT );
  77. ConVar r_hunkalloclightmaps( "r_hunkalloclightmaps", "1" );
  78. extern ConVar r_lightcache_zbuffercache;
  79. static ConVar mod_dynamicunloadtime( "mod_dynamicunloadtime", "150", FCVAR_HIDDEN | FCVAR_DONTRECORD );
  80. static ConVar mod_dynamicunloadtextures( "mod_dynamicunloadtex", "1", FCVAR_HIDDEN | FCVAR_DONTRECORD );
  81. static ConVar mod_dynamicloadpause( "mod_dynamicloadpause", "0", FCVAR_CHEAT | FCVAR_HIDDEN | FCVAR_DONTRECORD );
  82. static ConVar mod_dynamicloadthrottle( "mod_dynamicloadthrottle", "0", FCVAR_CHEAT | FCVAR_HIDDEN | FCVAR_DONTRECORD );
  83. static ConVar mod_dynamicloadspew( "mod_dynamicloadspew", "0", FCVAR_HIDDEN | FCVAR_DONTRECORD );
  84. #define DynamicModelDebugMsg(...) ( mod_dynamicloadspew.GetBool() ? Msg(__VA_ARGS__) : (void)0 )
  85. bool g_bHunkAllocLightmaps;
  86. extern CGlobalVars g_ServerGlobalVariables;
  87. extern IMaterial *g_materialEmpty;
  88. extern ConVar r_rootlod;
  89. bool g_bLoadedMapHasBakedPropLighting = false;
  90. bool g_bBakedPropLightingNoSeparateHDR = false; // Some maps only have HDR lighting on props, contained in the file for non-hdr light data
  91. double g_flAccumulatedModelLoadTime;
  92. double g_flAccumulatedModelLoadTimeStudio;
  93. double g_flAccumulatedModelLoadTimeStaticMesh;
  94. double g_flAccumulatedModelLoadTimeBrush;
  95. double g_flAccumulatedModelLoadTimeSprite;
  96. double g_flAccumulatedModelLoadTimeVCollideSync;
  97. double g_flAccumulatedModelLoadTimeVCollideAsync;
  98. double g_flAccumulatedModelLoadTimeVirtualModel;
  99. double g_flAccumulatedModelLoadTimeMaterialNamesOnly;
  100. //-----------------------------------------------------------------------------
  101. // A dictionary used to store where to find game lump data in the .bsp file
  102. //-----------------------------------------------------------------------------
  103. // Extended from the on-disk struct to include uncompressed size and stop propagation of bogus signed values
  104. struct dgamelump_internal_t
  105. {
  106. dgamelump_internal_t( dgamelump_t &other, unsigned int nCompressedSize )
  107. : id( other.id )
  108. , flags( other.flags )
  109. , version( other.version )
  110. , offset( Max( other.fileofs, 0 ) )
  111. , uncompressedSize( Max( other.filelen, 0 ) )
  112. , compressedSize( nCompressedSize )
  113. {}
  114. GameLumpId_t id;
  115. unsigned short flags;
  116. unsigned short version;
  117. unsigned int offset;
  118. unsigned int uncompressedSize;
  119. unsigned int compressedSize;
  120. };
  121. static CUtlVector< dgamelump_internal_t > g_GameLumpDict;
  122. static char g_GameLumpFilename[128] = { 0 };
  123. //void NotifyHunkBeginMapLoad( const char *pszMapName )
  124. //{
  125. // Hunk_OnMapStart( 32*1024*1024 );
  126. //}
  127. // FIXME/TODO: Right now Host_FreeToLowMark unloads all models including studio
  128. // models that have Cache_Alloc data, too. This needs to be fixed before shipping
  129. BEGIN_BYTESWAP_DATADESC( lump_t )
  130. DEFINE_FIELD( fileofs, FIELD_INTEGER ),
  131. DEFINE_FIELD( filelen, FIELD_INTEGER ),
  132. DEFINE_FIELD( version, FIELD_INTEGER ),
  133. DEFINE_FIELD( uncompressedSize, FIELD_INTEGER ),
  134. END_BYTESWAP_DATADESC()
  135. BEGIN_BYTESWAP_DATADESC( dheader_t )
  136. DEFINE_FIELD( ident, FIELD_INTEGER ),
  137. DEFINE_FIELD( version, FIELD_INTEGER ),
  138. DEFINE_EMBEDDED_ARRAY( lumps, HEADER_LUMPS ),
  139. DEFINE_FIELD( mapRevision, FIELD_INTEGER ),
  140. END_BYTESWAP_DATADESC()
  141. bool Model_LessFunc( FileNameHandle_t const &a, FileNameHandle_t const &b )
  142. {
  143. return a < b;
  144. }
  145. //-----------------------------------------------------------------------------
  146. // Purpose: Implements IModelLoader
  147. //-----------------------------------------------------------------------------
  148. class CModelLoader : public IModelLoader
  149. {
  150. // Implement IModelLoader interface
  151. public:
  152. CModelLoader() : m_ModelPool( sizeof( model_t ), MAX_KNOWN_MODELS, CUtlMemoryPool::GROW_FAST, "CModelLoader::m_ModelPool" ),
  153. m_Models( 0, 0, Model_LessFunc )
  154. {
  155. }
  156. void Init( void );
  157. void Shutdown( void );
  158. int GetCount( void );
  159. model_t *GetModelForIndex( int i );
  160. // Look up name for model
  161. const char *GetName( model_t const *model );
  162. // Check cache for data, reload model if needed
  163. void *GetExtraData( model_t *model );
  164. int GetModelFileSize( char const *name );
  165. // Finds the model, and loads it if it isn't already present. Updates reference flags
  166. model_t *GetModelForName( const char *name, REFERENCETYPE referencetype );
  167. // Mark as referenced by name
  168. model_t *ReferenceModel( const char *name, REFERENCETYPE referencetype );
  169. // Unmasks the referencetype field for the model
  170. void UnreferenceModel( model_t *model, REFERENCETYPE referencetype );
  171. // Unmasks the specified reference type across all models
  172. void UnreferenceAllModels( REFERENCETYPE referencetype );
  173. // Set all models to last loaded on server count -1
  174. void ResetModelServerCounts();
  175. // For any models with referencetype blank, frees all memory associated with the model
  176. // and frees up the models slot
  177. void UnloadUnreferencedModels( void );
  178. void PurgeUnusedModels( void );
  179. bool Map_GetRenderInfoAllocated( void );
  180. void Map_SetRenderInfoAllocated( bool allocated );
  181. virtual void Map_LoadDisplacements( model_t *pModel, bool bRestoring );
  182. // Validate version/header of a .bsp file
  183. bool Map_IsValid( char const *mapname, bool bQuiet = false );
  184. virtual void RecomputeSurfaceFlags( model_t *mod );
  185. virtual void Studio_ReloadModels( ReloadType_t reloadType );
  186. void Print( void );
  187. // Is a model loaded?
  188. virtual bool IsLoaded( const model_t *mod );
  189. virtual bool LastLoadedMapHasHDRLighting(void);
  190. void DumpVCollideStats();
  191. // Returns the map model, otherwise NULL, no load or create
  192. model_t *FindModelNoCreate( const char *pModelName );
  193. // Finds the model, builds a model entry if not present
  194. model_t *FindModel( const char *name );
  195. modtype_t GetTypeFromName( const char *pModelName );
  196. // start with -1, list terminates with -1
  197. int FindNext( int iIndex, model_t **ppModel );
  198. virtual void UnloadModel( model_t *pModel );
  199. virtual void ReloadFilesInList( IFileList *pFilesToReload );
  200. virtual const char *GetActiveMapName( void );
  201. // Called by app system once per frame to poll and update dynamic models
  202. virtual void UpdateDynamicModels() { InternalUpdateDynamicModels(false); }
  203. // Called by server and client engine code to flush unreferenced dynamic models
  204. virtual void FlushDynamicModels() { InternalUpdateDynamicModels(true); }
  205. // Called by server and client to force-unload dynamic models regardless of refcount!
  206. virtual void ForceUnloadNonClientDynamicModels();
  207. // Called by client code to load dynamic models, instead of GetModelForName.
  208. virtual model_t *GetDynamicModel( const char *name, bool bClientOnly );
  209. // Called by client code to query dynamic model state
  210. virtual bool IsDynamicModelLoading( model_t *pModel, bool bClientOnly );
  211. // Called by client code to refcount dynamic models
  212. virtual void AddRefDynamicModel( model_t *pModel, bool bClientSideRef );
  213. virtual void ReleaseDynamicModel( model_t *pModel, bool bClientSideRef );
  214. // Called by client code or GetDynamicModel
  215. virtual bool RegisterModelLoadCallback( model_t *pModel, bool bClientOnly, IModelLoadCallback *pCallback, bool bCallImmediatelyIfLoaded );
  216. // Called by client code or IModelLoadCallback destructor
  217. virtual void UnregisterModelLoadCallback( model_t *pModel, bool bClientOnly, IModelLoadCallback *pCallback );
  218. virtual void Client_OnServerModelStateChanged( model_t *pModel, bool bServerLoaded );
  219. void DebugPrintDynamicModels();
  220. // Internal types
  221. private:
  222. // TODO, flag these and allow for UnloadUnreferencedModels to check for allocation type
  223. // so we don't have to flush all of the studio models when we free the hunk
  224. enum
  225. {
  226. FALLOC_USESHUNKALLOC = (1<<31),
  227. FALLOC_USESCACHEALLOC = (1<<30),
  228. };
  229. // Internal methods
  230. private:
  231. // Set reference flags and load model if it's not present already
  232. model_t *LoadModel( model_t *model, REFERENCETYPE *referencetype );
  233. // Unload models ( won't unload referenced models if checkreferences is true )
  234. void UnloadAllModels( bool checkreference );
  235. void SetupSubModels( model_t *model, CUtlVector<mmodel_t> &list );
  236. // World/map
  237. void Map_LoadModel( model_t *mod );
  238. void Map_UnloadModel( model_t *mod );
  239. void Map_UnloadCubemapSamples( model_t *mod );
  240. // World loading helper
  241. void SetWorldModel( model_t *mod );
  242. void ClearWorldModel( void );
  243. bool IsWorldModelSet( void );
  244. int GetNumWorldSubmodels( void );
  245. // Sprites
  246. void Sprite_LoadModel( model_t *mod );
  247. void Sprite_UnloadModel( model_t *mod );
  248. // Studio models
  249. void Studio_LoadModel( model_t *mod, bool bTouchAllData );
  250. void Studio_UnloadModel( model_t *mod );
  251. // Byteswap
  252. int UpdateOrCreate( const char *pSourceName, char *pTargetName, int maxLen, bool bForce );
  253. // Dynamic load queue
  254. class CDynamicModelInfo;
  255. void QueueDynamicModelLoad( CDynamicModelInfo *dyn, model_t *mod );
  256. bool CancelDynamicModelLoad( CDynamicModelInfo *dyn, model_t *mod );
  257. void UpdateDynamicModelLoadQueue();
  258. void FinishDynamicModelLoadIfReady( CDynamicModelInfo *dyn, model_t *mod );
  259. void InternalUpdateDynamicModels( bool bIgnoreUpdateTime );
  260. // Internal data
  261. private:
  262. enum
  263. {
  264. MAX_KNOWN_MODELS = 1024,
  265. };
  266. struct ModelEntry_t
  267. {
  268. model_t *modelpointer;
  269. };
  270. CUtlMap< FileNameHandle_t, ModelEntry_t > m_Models;
  271. CUtlMemoryPool m_ModelPool;
  272. CUtlVector<model_t> m_InlineModels;
  273. model_t *m_pWorldModel;
  274. public: // HACKHACK
  275. worldbrushdata_t m_worldBrushData;
  276. private:
  277. // local name of current loading model
  278. char m_szLoadName[64];
  279. bool m_bMapRenderInfoLoaded;
  280. bool m_bMapHasHDRLighting;
  281. char m_szActiveMapName[64];
  282. // Dynamic model support:
  283. class CDynamicModelInfo
  284. {
  285. public:
  286. enum { QUEUED = 0x01, LOADING = 0x02, CLIENTREADY = 0x04, SERVERLOADING = 0x08, ALLREADY = 0x10, INVALIDFLAG = 0x20 }; // flags
  287. CDynamicModelInfo() : m_iRefCount(0), m_iClientRefCount(0), m_nLoadFlags(INVALIDFLAG), m_uLastTouchedMS_Div256(0) { }
  288. int16 m_iRefCount;
  289. int16 m_iClientRefCount; // also doublecounted in m_iRefCount
  290. uint32 m_nLoadFlags : 8;
  291. uint32 m_uLastTouchedMS_Div256 : 24;
  292. CUtlVector< uintptr_t > m_Callbacks; // low bit = client only
  293. };
  294. CUtlHashtable< model_t * , CDynamicModelInfo > m_DynamicModels;
  295. CUtlHashtable< uintptr_t , int > m_RegisteredDynamicCallbacks;
  296. // Dynamic model load queue
  297. CUtlVector< model_t* > m_DynamicModelLoadQueue;
  298. bool m_bDynamicLoadQueueHeadActive;
  299. };
  300. // Expose interface
  301. static CModelLoader g_ModelLoader;
  302. IModelLoader *modelloader = ( IModelLoader * )&g_ModelLoader;
  303. //-----------------------------------------------------------------------------
  304. // Globals used by the CMapLoadHelper
  305. //-----------------------------------------------------------------------------
  306. static dheader_t s_MapHeader;
  307. static FileHandle_t s_MapFileHandle = FILESYSTEM_INVALID_HANDLE;
  308. static char s_szLoadName[128];
  309. static char s_szMapName[128];
  310. static worldbrushdata_t *s_pMap = NULL;
  311. static int s_nMapLoadRecursion = 0;
  312. static CUtlBuffer s_MapBuffer;
  313. // Lump files are patches for a shipped map
  314. // List of lump files found when map was loaded. Each entry is the lump file index for that lump id.
  315. struct lumpfiles_t
  316. {
  317. FileHandle_t file;
  318. int lumpfileindex;
  319. lumpfileheader_t header;
  320. };
  321. static lumpfiles_t s_MapLumpFiles[ HEADER_LUMPS ];
  322. CON_COMMAND( mem_vcollide, "Dumps the memory used by vcollides" )
  323. {
  324. g_ModelLoader.DumpVCollideStats();
  325. }
  326. //-----------------------------------------------------------------------------
  327. // Returns the ref count for this bsp
  328. //-----------------------------------------------------------------------------
  329. int CMapLoadHelper::GetRefCount()
  330. {
  331. return s_nMapLoadRecursion;
  332. }
  333. //-----------------------------------------------------------------------------
  334. // Setup a BSP loading context, maintains a ref count.
  335. //-----------------------------------------------------------------------------
  336. void CMapLoadHelper::Init( model_t *pMapModel, const char *loadname )
  337. {
  338. if ( ++s_nMapLoadRecursion > 1 )
  339. {
  340. return;
  341. }
  342. s_pMap = NULL;
  343. s_szLoadName[ 0 ] = 0;
  344. s_MapFileHandle = FILESYSTEM_INVALID_HANDLE;
  345. V_memset( &s_MapHeader, 0, sizeof( s_MapHeader ) );
  346. V_memset( &s_MapLumpFiles, 0, sizeof( s_MapLumpFiles ) );
  347. if ( !pMapModel )
  348. {
  349. V_strcpy_safe( s_szMapName, loadname );
  350. }
  351. else
  352. {
  353. V_strcpy_safe( s_szMapName, pMapModel->strName );
  354. }
  355. s_MapFileHandle = g_pFileSystem->OpenEx( s_szMapName, "rb", IsX360() ? FSOPEN_NEVERINPACK : 0, IsX360() ? "GAME" : NULL );
  356. if ( s_MapFileHandle == FILESYSTEM_INVALID_HANDLE )
  357. {
  358. Host_Error( "CMapLoadHelper::Init, unable to open %s\n", s_szMapName );
  359. return;
  360. }
  361. g_pFileSystem->Read( &s_MapHeader, sizeof( dheader_t ), s_MapFileHandle );
  362. if ( s_MapHeader.ident != IDBSPHEADER )
  363. {
  364. g_pFileSystem->Close( s_MapFileHandle );
  365. s_MapFileHandle = FILESYSTEM_INVALID_HANDLE;
  366. Host_Error( "CMapLoadHelper::Init, map %s has wrong identifier\n", s_szMapName );
  367. return;
  368. }
  369. if ( s_MapHeader.version < MINBSPVERSION || s_MapHeader.version > BSPVERSION )
  370. {
  371. g_pFileSystem->Close( s_MapFileHandle );
  372. s_MapFileHandle = FILESYSTEM_INVALID_HANDLE;
  373. Host_Error( "CMapLoadHelper::Init, map %s has wrong version (%i when expecting %i)\n", s_szMapName,
  374. s_MapHeader.version, BSPVERSION );
  375. return;
  376. }
  377. V_strcpy_safe( s_szLoadName, loadname );
  378. // Store map version, but only do it once so that the communication between the engine and Hammer isn't broken. The map version
  379. // is incremented whenever a Hammer to Engine session is established so resetting the global map version each time causes a problem.
  380. if ( 0 == g_ServerGlobalVariables.mapversion )
  381. {
  382. g_ServerGlobalVariables.mapversion = s_MapHeader.mapRevision;
  383. }
  384. #ifndef SWDS
  385. InitDLightGlobals( s_MapHeader.version );
  386. #endif
  387. s_pMap = &g_ModelLoader.m_worldBrushData;
  388. #if 0
  389. // XXX(johns): There are security issues with this system currently. sv_pure doesn't handle unexpected/mismatched
  390. // lumps, so players can create lumps for maps not using them to wallhack/etc.. Currently unused,
  391. // disabling until we have time to make a proper security pass.
  392. if ( IsPC() )
  393. {
  394. // Now find and open our lump files, and create the master list of them.
  395. for ( int iIndex = 0; iIndex < MAX_LUMPFILES; iIndex++ )
  396. {
  397. lumpfileheader_t lumpHeader;
  398. char lumpfilename[MAX_PATH];
  399. GenerateLumpFileName( s_szMapName, lumpfilename, MAX_PATH, iIndex );
  400. if ( !g_pFileSystem->FileExists( lumpfilename ) )
  401. break;
  402. // Open the lump file
  403. FileHandle_t lumpFile = g_pFileSystem->Open( lumpfilename, "rb" );
  404. if ( lumpFile == FILESYSTEM_INVALID_HANDLE )
  405. {
  406. Host_Error( "CMapLoadHelper::Init, failed to load lump file %s\n", lumpfilename );
  407. return;
  408. }
  409. // Read the lump header
  410. memset( &lumpHeader, 0, sizeof( lumpHeader ) );
  411. g_pFileSystem->Read( &lumpHeader, sizeof( lumpfileheader_t ), lumpFile );
  412. if ( lumpHeader.lumpID >= 0 && lumpHeader.lumpID < HEADER_LUMPS )
  413. {
  414. // We may find multiple lump files for the same lump ID. If so,
  415. // close the earlier lump file, because the later one overwrites it.
  416. if ( s_MapLumpFiles[lumpHeader.lumpID].file != FILESYSTEM_INVALID_HANDLE )
  417. {
  418. g_pFileSystem->Close( s_MapLumpFiles[lumpHeader.lumpID].file );
  419. }
  420. s_MapLumpFiles[lumpHeader.lumpID].file = lumpFile;
  421. s_MapLumpFiles[lumpHeader.lumpID].lumpfileindex = iIndex;
  422. memcpy( &(s_MapLumpFiles[lumpHeader.lumpID].header), &lumpHeader, sizeof(lumpHeader) );
  423. }
  424. else
  425. {
  426. Warning("Found invalid lump file '%s'. Lump Id: %d\n", lumpfilename, lumpHeader.lumpID );
  427. }
  428. }
  429. }
  430. #endif
  431. }
  432. //-----------------------------------------------------------------------------
  433. // Setup a BSP loading context from a supplied buffer
  434. //-----------------------------------------------------------------------------
  435. void CMapLoadHelper::InitFromMemory( model_t *pMapModel, const void *pData, int nDataSize )
  436. {
  437. // valid for 360 only
  438. // 360 has reorganized bsp format and no external lump files
  439. Assert( IsX360() && pData && nDataSize );
  440. if ( ++s_nMapLoadRecursion > 1 )
  441. {
  442. return;
  443. }
  444. s_pMap = NULL;
  445. s_MapFileHandle = FILESYSTEM_INVALID_HANDLE;
  446. V_memset( &s_MapHeader, 0, sizeof( s_MapHeader ) );
  447. V_memset( &s_MapLumpFiles, 0, sizeof( s_MapLumpFiles ) );
  448. V_strcpy_safe( s_szMapName, pMapModel->strName );
  449. V_FileBase( s_szMapName, s_szLoadName, sizeof( s_szLoadName ) );
  450. s_MapBuffer.SetExternalBuffer( (void *)pData, nDataSize, nDataSize );
  451. V_memcpy( &s_MapHeader, pData, sizeof( dheader_t ) );
  452. if ( s_MapHeader.ident != IDBSPHEADER )
  453. {
  454. Host_Error( "CMapLoadHelper::Init, map %s has wrong identifier\n", s_szMapName );
  455. return;
  456. }
  457. if ( s_MapHeader.version < MINBSPVERSION || s_MapHeader.version > BSPVERSION )
  458. {
  459. Host_Error( "CMapLoadHelper::Init, map %s has wrong version (%i when expecting %i)\n", s_szMapName, s_MapHeader.version, BSPVERSION );
  460. return;
  461. }
  462. // Store map version
  463. g_ServerGlobalVariables.mapversion = s_MapHeader.mapRevision;
  464. #ifndef SWDS
  465. InitDLightGlobals( s_MapHeader.version );
  466. #endif
  467. s_pMap = &g_ModelLoader.m_worldBrushData;
  468. }
  469. //-----------------------------------------------------------------------------
  470. // Shutdown a BSP loading context.
  471. //-----------------------------------------------------------------------------
  472. void CMapLoadHelper::Shutdown( void )
  473. {
  474. if ( --s_nMapLoadRecursion > 0 )
  475. {
  476. return;
  477. }
  478. if ( s_MapFileHandle != FILESYSTEM_INVALID_HANDLE )
  479. {
  480. g_pFileSystem->Close( s_MapFileHandle );
  481. s_MapFileHandle = FILESYSTEM_INVALID_HANDLE;
  482. }
  483. if ( IsPC() )
  484. {
  485. // Close our open lump files
  486. for ( int i = 0; i < HEADER_LUMPS; i++ )
  487. {
  488. if ( s_MapLumpFiles[i].file != FILESYSTEM_INVALID_HANDLE )
  489. {
  490. g_pFileSystem->Close( s_MapLumpFiles[i].file );
  491. }
  492. }
  493. V_memset( &s_MapLumpFiles, 0, sizeof( s_MapLumpFiles ) );
  494. }
  495. s_szLoadName[ 0 ] = 0;
  496. V_memset( &s_MapHeader, 0, sizeof( s_MapHeader ) );
  497. s_pMap = NULL;
  498. // discard from memory
  499. if ( s_MapBuffer.Base() )
  500. {
  501. free( s_MapBuffer.Base() );
  502. s_MapBuffer.SetExternalBuffer( NULL, 0, 0 );
  503. }
  504. }
  505. //-----------------------------------------------------------------------------
  506. // Free the lighting lump (increases free memory during loading on 360)
  507. //-----------------------------------------------------------------------------
  508. void CMapLoadHelper::FreeLightingLump( void )
  509. {
  510. if ( IsX360() && ( s_MapFileHandle == FILESYSTEM_INVALID_HANDLE ) && s_MapBuffer.Base() )
  511. {
  512. int lightingLump = LumpSize( LUMP_LIGHTING_HDR ) ? LUMP_LIGHTING_HDR : LUMP_LIGHTING;
  513. // Should never have both lighting lumps on 360
  514. Assert( ( lightingLump == LUMP_LIGHTING ) || ( LumpSize( LUMP_LIGHTING ) == 0 ) );
  515. if ( LumpSize( lightingLump ) )
  516. {
  517. // Check that the lighting lump is the last one in the BSP
  518. int lightingOffset = LumpOffset( lightingLump );
  519. for ( int i = 0;i < HEADER_LUMPS; i++ )
  520. {
  521. if ( ( LumpOffset( i ) > lightingOffset ) && ( i != LUMP_PAKFILE ) )
  522. {
  523. Warning( "CMapLoadHelper: Cannot free lighting lump (should be last before the PAK lump). Regenerate the .360.bsp file with the latest version of makegamedata." );
  524. return;
  525. }
  526. }
  527. // Flag the lighting chunk as gone from the BSP (principally, this sets 'filelen' to 0)
  528. V_memset( &s_MapHeader.lumps[ lightingLump ], 0, sizeof( lump_t ) );
  529. // Shrink the buffer to free up the space that was used by the lighting lump
  530. void * shrunkBuffer = realloc( s_MapBuffer.Base(), lightingOffset );
  531. Assert( shrunkBuffer == s_MapBuffer.Base() ); // A shrink would surely never move!!!
  532. s_MapBuffer.SetExternalBuffer( shrunkBuffer, lightingOffset, lightingOffset );
  533. }
  534. }
  535. }
  536. //-----------------------------------------------------------------------------
  537. // Returns the size of a particular lump without loading it...
  538. //-----------------------------------------------------------------------------
  539. int CMapLoadHelper::LumpSize( int lumpId )
  540. {
  541. // If we have a lump file for this lump, return its length instead
  542. if ( IsPC() && s_MapLumpFiles[lumpId].file != FILESYSTEM_INVALID_HANDLE )
  543. {
  544. return s_MapLumpFiles[lumpId].header.lumpLength;
  545. }
  546. lump_t *pLump = &s_MapHeader.lumps[ lumpId ];
  547. Assert( pLump );
  548. // all knowledge of compression is private, they expect and get the original size
  549. int originalSize = s_MapHeader.lumps[lumpId].uncompressedSize;
  550. if ( originalSize != 0 )
  551. {
  552. return originalSize;
  553. }
  554. return pLump->filelen;
  555. }
  556. //-----------------------------------------------------------------------------
  557. // Returns the offset of a particular lump without loading it...
  558. //-----------------------------------------------------------------------------
  559. int CMapLoadHelper::LumpOffset( int lumpID )
  560. {
  561. // If we have a lump file for this lump, return
  562. // the offset to move past the lump file header.
  563. if ( IsPC() && s_MapLumpFiles[lumpID].file != FILESYSTEM_INVALID_HANDLE )
  564. {
  565. return s_MapLumpFiles[lumpID].header.lumpOffset;
  566. }
  567. lump_t *pLump = &s_MapHeader.lumps[ lumpID ];
  568. Assert( pLump );
  569. return pLump->fileofs;
  570. }
  571. //-----------------------------------------------------------------------------
  572. // Loads one element in a lump.
  573. //-----------------------------------------------------------------------------
  574. void CMapLoadHelper::LoadLumpElement( int nElemIndex, int nElemSize, void *pData )
  575. {
  576. if ( !nElemSize || !m_nLumpSize )
  577. {
  578. return;
  579. }
  580. // supply from memory
  581. if ( nElemIndex * nElemSize + nElemSize <= m_nLumpSize )
  582. {
  583. V_memcpy( pData, m_pData + nElemIndex * nElemSize, nElemSize );
  584. }
  585. else
  586. {
  587. // out of range
  588. Assert( 0 );
  589. }
  590. }
  591. //-----------------------------------------------------------------------------
  592. // Loads one element in a lump.
  593. //-----------------------------------------------------------------------------
  594. void CMapLoadHelper::LoadLumpData( int offset, int size, void *pData )
  595. {
  596. if ( !size || !m_nLumpSize )
  597. {
  598. return;
  599. }
  600. if ( offset + size <= m_nLumpSize )
  601. {
  602. V_memcpy( pData, m_pData + offset, size );
  603. }
  604. else
  605. {
  606. // out of range
  607. Assert( 0 );
  608. }
  609. }
  610. //-----------------------------------------------------------------------------
  611. // Purpose:
  612. // Input : mapfile -
  613. // lumpToLoad -
  614. //-----------------------------------------------------------------------------
  615. CMapLoadHelper::CMapLoadHelper( int lumpToLoad )
  616. {
  617. if ( lumpToLoad < 0 || lumpToLoad >= HEADER_LUMPS )
  618. {
  619. Sys_Error( "Can't load lump %i, range is 0 to %i!!!", lumpToLoad, HEADER_LUMPS - 1 );
  620. }
  621. m_nLumpID = lumpToLoad;
  622. m_nLumpSize = 0;
  623. m_nLumpOffset = -1;
  624. m_pData = NULL;
  625. m_pRawData = NULL;
  626. m_pUncompressedData = NULL;
  627. // Load raw lump from disk
  628. lump_t *lump = &s_MapHeader.lumps[ lumpToLoad ];
  629. Assert( lump );
  630. m_nLumpSize = lump->filelen;
  631. m_nLumpOffset = lump->fileofs;
  632. m_nLumpVersion = lump->version;
  633. FileHandle_t fileToUse = s_MapFileHandle;
  634. // If we have a lump file for this lump, use it instead
  635. if ( IsPC() && s_MapLumpFiles[lumpToLoad].file != FILESYSTEM_INVALID_HANDLE )
  636. {
  637. fileToUse = s_MapLumpFiles[lumpToLoad].file;
  638. m_nLumpSize = s_MapLumpFiles[lumpToLoad].header.lumpLength;
  639. m_nLumpOffset = s_MapLumpFiles[lumpToLoad].header.lumpOffset;
  640. m_nLumpVersion = s_MapLumpFiles[lumpToLoad].header.lumpVersion;
  641. // Store off the lump file name
  642. GenerateLumpFileName( s_szLoadName, m_szLumpFilename, MAX_PATH, s_MapLumpFiles[lumpToLoad].lumpfileindex );
  643. }
  644. if ( !m_nLumpSize )
  645. {
  646. // this lump has no data
  647. return;
  648. }
  649. if ( s_MapBuffer.Base() )
  650. {
  651. // bsp is in memory
  652. m_pData = (unsigned char*)s_MapBuffer.Base() + m_nLumpOffset;
  653. }
  654. else
  655. {
  656. if ( s_MapFileHandle == FILESYSTEM_INVALID_HANDLE )
  657. {
  658. Sys_Error( "Can't load map from invalid handle!!!" );
  659. }
  660. unsigned nOffsetAlign, nSizeAlign, nBufferAlign;
  661. g_pFileSystem->GetOptimalIOConstraints( fileToUse, &nOffsetAlign, &nSizeAlign, &nBufferAlign );
  662. bool bTryOptimal = ( m_nLumpOffset % 4 == 0 ); // Don't return badly aligned data
  663. unsigned int alignedOffset = m_nLumpOffset;
  664. unsigned int alignedBytesToRead = ( ( m_nLumpSize ) ? m_nLumpSize : 1 );
  665. if ( bTryOptimal )
  666. {
  667. alignedOffset = AlignValue( ( alignedOffset - nOffsetAlign ) + 1, nOffsetAlign );
  668. alignedBytesToRead = AlignValue( ( m_nLumpOffset - alignedOffset ) + alignedBytesToRead, nSizeAlign );
  669. }
  670. m_pRawData = (byte *)g_pFileSystem->AllocOptimalReadBuffer( fileToUse, alignedBytesToRead, alignedOffset );
  671. if ( !m_pRawData && m_nLumpSize )
  672. {
  673. Sys_Error( "Can't load lump %i, allocation of %i bytes failed!!!", lumpToLoad, m_nLumpSize + 1 );
  674. }
  675. if ( m_nLumpSize )
  676. {
  677. g_pFileSystem->Seek( fileToUse, alignedOffset, FILESYSTEM_SEEK_HEAD );
  678. g_pFileSystem->ReadEx( m_pRawData, alignedBytesToRead, alignedBytesToRead, fileToUse );
  679. m_pData = m_pRawData + ( m_nLumpOffset - alignedOffset );
  680. }
  681. }
  682. if ( lump->uncompressedSize != 0 )
  683. {
  684. // Handle compressed lump -- users of the class see the uncompressed data
  685. AssertMsg( CLZMA::IsCompressed( m_pData ),
  686. "Lump claims to be compressed but is not recognized as LZMA" );
  687. m_nLumpSize = CLZMA::GetActualSize( m_pData );
  688. AssertMsg( lump->uncompressedSize == m_nLumpSize,
  689. "Lump header disagrees with lzma header for compressed lump" );
  690. m_pUncompressedData = (unsigned char *)malloc( m_nLumpSize );
  691. CLZMA::Uncompress( m_pData, m_pUncompressedData );
  692. m_pData = m_pUncompressedData;
  693. }
  694. }
  695. //-----------------------------------------------------------------------------
  696. // Purpose:
  697. //-----------------------------------------------------------------------------
  698. CMapLoadHelper::~CMapLoadHelper( void )
  699. {
  700. if ( m_pUncompressedData )
  701. {
  702. free( m_pUncompressedData );
  703. }
  704. if ( m_pRawData )
  705. {
  706. g_pFileSystem->FreeOptimalReadBuffer( m_pRawData );
  707. }
  708. }
  709. //-----------------------------------------------------------------------------
  710. // Purpose:
  711. // Output : model_t
  712. //-----------------------------------------------------------------------------
  713. worldbrushdata_t *CMapLoadHelper::GetMap( void )
  714. {
  715. Assert( s_pMap );
  716. return s_pMap;
  717. }
  718. //-----------------------------------------------------------------------------
  719. // Purpose:
  720. // Output : const char
  721. //-----------------------------------------------------------------------------
  722. const char *CMapLoadHelper::GetMapName( void )
  723. {
  724. return s_szMapName;
  725. }
  726. //-----------------------------------------------------------------------------
  727. // Purpose:
  728. // Output : const char
  729. //-----------------------------------------------------------------------------
  730. char *CMapLoadHelper::GetLoadName( void )
  731. {
  732. // If we have a custom lump file for the lump this helper
  733. // is loading, return it instead.
  734. if ( IsPC() && s_MapLumpFiles[m_nLumpID].file != FILESYSTEM_INVALID_HANDLE )
  735. {
  736. return m_szLumpFilename;
  737. }
  738. return s_szLoadName;
  739. }
  740. //-----------------------------------------------------------------------------
  741. // Purpose:
  742. // Output : byte
  743. //-----------------------------------------------------------------------------
  744. byte *CMapLoadHelper::LumpBase( void )
  745. {
  746. return m_pData;
  747. }
  748. //-----------------------------------------------------------------------------
  749. // Purpose:
  750. // Output : int
  751. //-----------------------------------------------------------------------------
  752. int CMapLoadHelper::LumpSize()
  753. {
  754. return m_nLumpSize;
  755. }
  756. int CMapLoadHelper::LumpOffset()
  757. {
  758. return m_nLumpOffset;
  759. }
  760. int CMapLoadHelper::LumpVersion() const
  761. {
  762. return m_nLumpVersion;
  763. }
  764. void EnableHDR( bool bEnable )
  765. {
  766. if ( g_pMaterialSystemHardwareConfig->GetHDREnabled() == bEnable )
  767. return;
  768. g_pMaterialSystemHardwareConfig->SetHDREnabled( bEnable );
  769. if ( IsX360() )
  770. {
  771. // cannot do what the pc does and ditch resources, we're loading!
  772. // can safely do the state update only, knowing that the state change won't affect 360 resources
  773. ((MaterialSystem_Config_t *)g_pMaterialSystemConfig)->SetFlag( MATSYS_VIDCFG_FLAGS_ENABLE_HDR, bEnable );
  774. return;
  775. }
  776. ShutdownWellKnownRenderTargets();
  777. InitWellKnownRenderTargets();
  778. /// XXX(JohnS): This works around part of the terribleness the comments below discuss by performing
  779. /// UpdateMaterialSystemConfig with the device offline, removing its need to do multiple re-uploads of
  780. /// things. I am not positive my changes to allow that won't introduce terrible regressions or awaken
  781. /// ancient bugs, hence the kill switch.
  782. bool bUpdateOffline = mod_offline_hdr_switch.GetBool();
  783. #ifndef DEDICATED
  784. extern void V_RenderVGuiOnly();
  785. #endif
  786. if ( bUpdateOffline )
  787. {
  788. #ifndef DEDICATED
  789. V_RenderVGuiOnly();
  790. #endif
  791. materials->ReleaseResources();
  792. }
  793. // Grah. This is terrible. changin mat_hdr_enabled at the commandline
  794. // will by definition break because the release/restore methods don't call
  795. // ShutdownWellKnownRenderTargets/InitWellKnownRenderTargets.
  796. // Also, this forces two alt-tabs, one for InitWellKnownRenderTargets, one
  797. // for UpdateMaterialSystemConfig.
  798. UpdateMaterialSystemConfig();
  799. // Worse, since we need to init+shutdown render targets here, we can't
  800. // rely on UpdateMaterialSystemConfig to release + reacquire resources
  801. // because it could be called at any time. We have to precisely control
  802. // when hdr is changed since this is the only time the code can handle it.
  803. if ( !bUpdateOffline )
  804. {
  805. materials->ReleaseResources();
  806. }
  807. materials->ReacquireResources();
  808. #ifndef DEDICATED
  809. if ( bUpdateOffline )
  810. {
  811. V_RenderVGuiOnly();
  812. }
  813. #endif
  814. }
  815. //-----------------------------------------------------------------------------
  816. // Determine feature flags
  817. //-----------------------------------------------------------------------------
  818. void Map_CheckFeatureFlags()
  819. {
  820. g_bLoadedMapHasBakedPropLighting = false;
  821. g_bBakedPropLightingNoSeparateHDR = false;
  822. if ( CMapLoadHelper::LumpSize( LUMP_MAP_FLAGS ) > 0 )
  823. {
  824. CMapLoadHelper lh( LUMP_MAP_FLAGS );
  825. dflagslump_t flags_lump;
  826. flags_lump = *( (dflagslump_t *)( lh.LumpBase() ) );
  827. // check if loaded map has baked static prop lighting
  828. g_bLoadedMapHasBakedPropLighting =
  829. ( flags_lump.m_LevelFlags & LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_NONHDR ) != 0 ||
  830. ( flags_lump.m_LevelFlags & LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_HDR ) != 0;
  831. g_bBakedPropLightingNoSeparateHDR =
  832. ( flags_lump.m_LevelFlags & LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_HDR ) == 0;
  833. }
  834. }
  835. //-----------------------------------------------------------------------------
  836. // Parse the map header for HDR ability. Returns the presence of HDR data only,
  837. // not the HDR enable state.
  838. //-----------------------------------------------------------------------------
  839. bool Map_CheckForHDR( model_t *pModel, const char *pLoadName )
  840. {
  841. // parse the map header only
  842. CMapLoadHelper::Init( pModel, pLoadName );
  843. bool bHasHDR = false;
  844. if ( IsX360() )
  845. {
  846. // If this is true, the 360 MUST use HDR, because the LDR data gets stripped out.
  847. bHasHDR = CMapLoadHelper::LumpSize( LUMP_LIGHTING_HDR ) > 0;
  848. }
  849. else
  850. {
  851. // might want to also consider the game lumps GAMELUMP_DETAIL_PROP_LIGHTING_HDR
  852. bHasHDR = CMapLoadHelper::LumpSize( LUMP_LIGHTING_HDR ) > 0 &&
  853. CMapLoadHelper::LumpSize( LUMP_WORLDLIGHTS_HDR ) > 0;
  854. // Mod_GameLumpSize( GAMELUMP_DETAIL_PROP_LIGHTING_HDR ) > 0 // fixme
  855. }
  856. if ( s_MapHeader.version >= 20 && CMapLoadHelper::LumpSize( LUMP_LEAF_AMBIENT_LIGHTING_HDR ) == 0 )
  857. {
  858. // This lump only exists in version 20 and greater, so don't bother checking for it on earlier versions.
  859. bHasHDR = false;
  860. }
  861. bool bEnableHDR = ( IsX360() && bHasHDR ) ||
  862. ( bHasHDR && ( mat_hdr_level.GetInt() >= 2 ) &&
  863. ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 90 ) );
  864. EnableHDR( bEnableHDR );
  865. // this data really should have been in the header, but it isn't
  866. // establish the features now, before the real bsp load commences
  867. Map_CheckFeatureFlags();
  868. CMapLoadHelper::Shutdown();
  869. return bHasHDR;
  870. }
  871. //-----------------------------------------------------------------------------
  872. // Allocates, frees lighting data
  873. //-----------------------------------------------------------------------------
  874. static void AllocateLightingData( worldbrushdata_t *pBrushData, int nSize )
  875. {
  876. g_bHunkAllocLightmaps = ( !r_unloadlightmaps.GetBool() && r_hunkalloclightmaps.GetBool() );
  877. if ( g_bHunkAllocLightmaps )
  878. {
  879. pBrushData->lightdata = (ColorRGBExp32 *)Hunk_Alloc( nSize, false );
  880. }
  881. else
  882. {
  883. // Specifically *not* adding it to the hunk.
  884. // If this malloc changes, also change the free in CacheAndUnloadLightmapData()
  885. pBrushData->lightdata = (ColorRGBExp32 *)malloc( nSize );
  886. }
  887. pBrushData->unloadedlightmaps = false;
  888. }
  889. static void DeallocateLightingData( worldbrushdata_t *pBrushData )
  890. {
  891. if ( pBrushData && pBrushData->lightdata )
  892. {
  893. if ( !g_bHunkAllocLightmaps )
  894. {
  895. free( pBrushData->lightdata );
  896. }
  897. pBrushData->lightdata = NULL;
  898. }
  899. }
  900. static int ComputeLightmapSize( dface_t *pFace, mtexinfo_t *pTexInfo )
  901. {
  902. bool bNeedsBumpmap = false;
  903. if( pTexInfo[pFace->texinfo].flags & SURF_BUMPLIGHT )
  904. {
  905. bNeedsBumpmap = true;
  906. }
  907. int lightstyles;
  908. for (lightstyles=0; lightstyles < MAXLIGHTMAPS; lightstyles++ )
  909. {
  910. if ( pFace->styles[lightstyles] == 255 )
  911. break;
  912. }
  913. int nLuxels = (pFace->m_LightmapTextureSizeInLuxels[0]+1) * (pFace->m_LightmapTextureSizeInLuxels[1]+1);
  914. if( bNeedsBumpmap )
  915. {
  916. return nLuxels * 4 * lightstyles * ( NUM_BUMP_VECTS + 1 );
  917. }
  918. return nLuxels * 4 * lightstyles;
  919. }
  920. //-----------------------------------------------------------------------------
  921. // Purpose:
  922. //-----------------------------------------------------------------------------
  923. void Mod_LoadLighting( CMapLoadHelper &lh )
  924. {
  925. if ( !lh.LumpSize() )
  926. {
  927. lh.GetMap()->lightdata = NULL;
  928. return;
  929. }
  930. Assert( lh.LumpSize() % sizeof( ColorRGBExp32 ) == 0 );
  931. Assert ( lh.LumpVersion() != 0 );
  932. AllocateLightingData( lh.GetMap(), lh.LumpSize() );
  933. memcpy( lh.GetMap()->lightdata, lh.LumpBase(), lh.LumpSize());
  934. if ( IsX360() )
  935. {
  936. // Free the lighting lump, to increase the amount of memory free during the rest of loading
  937. CMapLoadHelper::FreeLightingLump();
  938. }
  939. }
  940. //-----------------------------------------------------------------------------
  941. // Purpose:
  942. //-----------------------------------------------------------------------------
  943. void Mod_LoadWorldlights( CMapLoadHelper &lh, bool bIsHDR )
  944. {
  945. lh.GetMap()->shadowzbuffers = NULL;
  946. if (!lh.LumpSize())
  947. {
  948. lh.GetMap()->numworldlights = 0;
  949. lh.GetMap()->worldlights = NULL;
  950. return;
  951. }
  952. lh.GetMap()->numworldlights = lh.LumpSize() / sizeof( dworldlight_t );
  953. lh.GetMap()->worldlights = (dworldlight_t *)Hunk_AllocName( lh.LumpSize(), va( "%s [%s]", lh.GetLoadName(), "worldlights" ) );
  954. memcpy (lh.GetMap()->worldlights, lh.LumpBase(), lh.LumpSize());
  955. #if !defined( SWDS )
  956. if ( r_lightcache_zbuffercache.GetInt() )
  957. {
  958. size_t zbufSize = lh.GetMap()->numworldlights * sizeof( lightzbuffer_t );
  959. lh.GetMap()->shadowzbuffers = ( lightzbuffer_t *) Hunk_AllocName( zbufSize, va( "%s [%s]", lh.GetLoadName(), "shadowzbuffers" ) );
  960. memset( lh.GetMap()->shadowzbuffers, 0, zbufSize ); // mark empty
  961. }
  962. #endif
  963. // Fixup for backward compatability
  964. for ( int i = 0; i < lh.GetMap()->numworldlights; i++ )
  965. {
  966. if( lh.GetMap()->worldlights[i].type == emit_spotlight)
  967. {
  968. if ((lh.GetMap()->worldlights[i].constant_attn == 0.0) &&
  969. (lh.GetMap()->worldlights[i].linear_attn == 0.0) &&
  970. (lh.GetMap()->worldlights[i].quadratic_attn == 0.0))
  971. {
  972. lh.GetMap()->worldlights[i].quadratic_attn = 1.0;
  973. }
  974. if (lh.GetMap()->worldlights[i].exponent == 0.0)
  975. lh.GetMap()->worldlights[i].exponent = 1.0;
  976. }
  977. else if( lh.GetMap()->worldlights[i].type == emit_point)
  978. {
  979. // To match earlier lighting, use quadratic...
  980. if ((lh.GetMap()->worldlights[i].constant_attn == 0.0) &&
  981. (lh.GetMap()->worldlights[i].linear_attn == 0.0) &&
  982. (lh.GetMap()->worldlights[i].quadratic_attn == 0.0))
  983. {
  984. lh.GetMap()->worldlights[i].quadratic_attn = 1.0;
  985. }
  986. }
  987. // I replaced the cuttoff_dot field (which took a value from 0 to 1)
  988. // with a max light radius. Radius of less than 1 will never happen,
  989. // so I can get away with this. When I set radius to 0, it'll
  990. // run the old code which computed a radius
  991. if (lh.GetMap()->worldlights[i].radius < 1)
  992. {
  993. lh.GetMap()->worldlights[i].radius = ComputeLightRadius( &lh.GetMap()->worldlights[i], bIsHDR );
  994. }
  995. }
  996. }
  997. //-----------------------------------------------------------------------------
  998. // Purpose:
  999. //-----------------------------------------------------------------------------
  1000. void Mod_LoadVertices( void )
  1001. {
  1002. dvertex_t *in;
  1003. mvertex_t *out;
  1004. int i, count;
  1005. CMapLoadHelper lh( LUMP_VERTEXES );
  1006. in = (dvertex_t *)lh.LumpBase();
  1007. if ( lh.LumpSize() % sizeof(*in) )
  1008. {
  1009. Host_Error( "Mod_LoadVertices: funny lump size in %s", lh.GetMapName() );
  1010. }
  1011. count = lh.LumpSize() / sizeof(*in);
  1012. out = (mvertex_t *)Hunk_AllocName( count*sizeof(*out), va( "%s [%s]", lh.GetLoadName(), "vertexes" ) );
  1013. lh.GetMap()->vertexes = out;
  1014. lh.GetMap()->numvertexes = count;
  1015. for ( i=0 ; i<count ; i++, in++, out++)
  1016. {
  1017. out->position[0] = in->point[0];
  1018. out->position[1] = in->point[1];
  1019. out->position[2] = in->point[2];
  1020. }
  1021. }
  1022. //-----------------------------------------------------------------------------
  1023. // Purpose:
  1024. // Input : mins -
  1025. // maxs -
  1026. // Output : float
  1027. //-----------------------------------------------------------------------------
  1028. static float RadiusFromBounds (Vector& mins, Vector& maxs)
  1029. {
  1030. int i;
  1031. Vector corner;
  1032. for (i=0 ; i<3 ; i++)
  1033. {
  1034. corner[i] = fabs(mins[i]) > fabs(maxs[i]) ? fabs(mins[i]) : fabs(maxs[i]);
  1035. }
  1036. return VectorLength( corner );
  1037. }
  1038. //-----------------------------------------------------------------------------
  1039. // Purpose:
  1040. //-----------------------------------------------------------------------------
  1041. void Mod_LoadSubmodels( CUtlVector<mmodel_t> &submodelList )
  1042. {
  1043. dmodel_t *in;
  1044. int i, j, count;
  1045. CMapLoadHelper lh( LUMP_MODELS );
  1046. in = (dmodel_t *)lh.LumpBase();
  1047. if (lh.LumpSize() % sizeof(*in))
  1048. Host_Error("Mod_LoadSubmodels: funny lump size in %s",lh.GetMapName());
  1049. count = lh.LumpSize() / sizeof(*in);
  1050. submodelList.SetCount( count );
  1051. lh.GetMap()->numsubmodels = count;
  1052. for ( i=0 ; i<count ; i++, in++)
  1053. {
  1054. for (j=0 ; j<3 ; j++)
  1055. { // spread the mins / maxs by a pixel
  1056. submodelList[i].mins[j] = in->mins[j] - 1;
  1057. submodelList[i].maxs[j] = in->maxs[j] + 1;
  1058. submodelList[i].origin[j] = in->origin[j];
  1059. }
  1060. submodelList[i].radius = RadiusFromBounds (submodelList[i].mins, submodelList[i].maxs);
  1061. submodelList[i].headnode = in->headnode;
  1062. submodelList[i].firstface = in->firstface;
  1063. submodelList[i].numfaces = in->numfaces;
  1064. }
  1065. }
  1066. //-----------------------------------------------------------------------------
  1067. // Purpose:
  1068. // Output : medge_t *Mod_LoadEdges
  1069. //-----------------------------------------------------------------------------
  1070. medge_t *Mod_LoadEdges ( void )
  1071. {
  1072. dedge_t *in;
  1073. medge_t *out;
  1074. int i, count;
  1075. CMapLoadHelper lh( LUMP_EDGES );
  1076. in = (dedge_t *)lh.LumpBase();
  1077. if (lh.LumpSize() % sizeof(*in))
  1078. Host_Error ("Mod_LoadEdges: funny lump size in %s",lh.GetMapName());
  1079. count = lh.LumpSize() / sizeof(*in);
  1080. medge_t *pedges = new medge_t[count];
  1081. out = pedges;
  1082. for ( i=0 ; i<count ; i++, in++, out++)
  1083. {
  1084. out->v[0] = in->v[0];
  1085. out->v[1] = in->v[1];
  1086. }
  1087. // delete this in the loader
  1088. return pedges;
  1089. }
  1090. //-----------------------------------------------------------------------------
  1091. // Purpose:
  1092. //-----------------------------------------------------------------------------
  1093. void Mod_LoadOcclusion( void )
  1094. {
  1095. CMapLoadHelper lh( LUMP_OCCLUSION );
  1096. worldbrushdata_t *b = lh.GetMap();
  1097. b->numoccluders = 0;
  1098. b->occluders = NULL;
  1099. b->numoccluderpolys = 0;
  1100. b->occluderpolys = NULL;
  1101. b->numoccludervertindices = 0;
  1102. b->occludervertindices = NULL;
  1103. if ( !lh.LumpSize() )
  1104. {
  1105. return;
  1106. }
  1107. CUtlBuffer buf( lh.LumpBase(), lh.LumpSize(), CUtlBuffer::READ_ONLY );
  1108. switch( lh.LumpVersion() )
  1109. {
  1110. case LUMP_OCCLUSION_VERSION:
  1111. {
  1112. b->numoccluders = buf.GetInt();
  1113. if (b->numoccluders)
  1114. {
  1115. int nSize = b->numoccluders * sizeof(doccluderdata_t);
  1116. b->occluders = (doccluderdata_t*)Hunk_AllocName( nSize, "occluder data" );
  1117. buf.Get( b->occluders, nSize );
  1118. }
  1119. b->numoccluderpolys = buf.GetInt();
  1120. if (b->numoccluderpolys)
  1121. {
  1122. int nSize = b->numoccluderpolys * sizeof(doccluderpolydata_t);
  1123. b->occluderpolys = (doccluderpolydata_t*)Hunk_AllocName( nSize, "occluder poly data" );
  1124. buf.Get( b->occluderpolys, nSize );
  1125. }
  1126. b->numoccludervertindices = buf.GetInt();
  1127. if (b->numoccludervertindices)
  1128. {
  1129. int nSize = b->numoccludervertindices * sizeof(int);
  1130. b->occludervertindices = (int*)Hunk_AllocName( nSize, "occluder vertices" );
  1131. buf.Get( b->occludervertindices, nSize );
  1132. }
  1133. }
  1134. break;
  1135. case 1:
  1136. {
  1137. b->numoccluders = buf.GetInt();
  1138. if (b->numoccluders)
  1139. {
  1140. int nSize = b->numoccluders * sizeof(doccluderdata_t);
  1141. b->occluders = (doccluderdata_t*)Hunk_AllocName( nSize, "occluder data" );
  1142. doccluderdataV1_t temp;
  1143. for ( int i = 0; i < b->numoccluders; ++i )
  1144. {
  1145. buf.Get( &temp, sizeof(doccluderdataV1_t) );
  1146. memcpy( &b->occluders[i], &temp, sizeof(doccluderdataV1_t) );
  1147. b->occluders[i].area = 1;
  1148. }
  1149. }
  1150. b->numoccluderpolys = buf.GetInt();
  1151. if (b->numoccluderpolys)
  1152. {
  1153. int nSize = b->numoccluderpolys * sizeof(doccluderpolydata_t);
  1154. b->occluderpolys = (doccluderpolydata_t*)Hunk_AllocName( nSize, "occluder poly data" );
  1155. buf.Get( b->occluderpolys, nSize );
  1156. }
  1157. b->numoccludervertindices = buf.GetInt();
  1158. if (b->numoccludervertindices)
  1159. {
  1160. int nSize = b->numoccludervertindices * sizeof(int);
  1161. b->occludervertindices = (int*)Hunk_AllocName( nSize, "occluder vertices" );
  1162. buf.Get( b->occludervertindices, nSize );
  1163. }
  1164. }
  1165. break;
  1166. case 0:
  1167. break;
  1168. default:
  1169. Host_Error("Invalid occlusion lump version!\n");
  1170. break;
  1171. }
  1172. }
  1173. // UNDONE: Really, it's stored 2 times because the texture system keeps a
  1174. // copy of the name too. I guess we'll get rid of this when we have a material
  1175. // system that works without a graphics context. At that point, everyone can
  1176. // reference the name in the material, or just the material itself.
  1177. //-----------------------------------------------------------------------------
  1178. // Purpose:
  1179. //-----------------------------------------------------------------------------
  1180. void Mod_LoadTexdata( void )
  1181. {
  1182. // Don't bother loading these again; they're already stored in the collision model
  1183. // which is guaranteed to be loaded at this point
  1184. s_pMap->numtexdata = GetCollisionBSPData()->numtextures;
  1185. s_pMap->texdata = GetCollisionBSPData()->map_surfaces.Base();
  1186. }
  1187. //-----------------------------------------------------------------------------
  1188. // Purpose:
  1189. //-----------------------------------------------------------------------------
  1190. void Mod_LoadTexinfo( void )
  1191. {
  1192. texinfo_t *in;
  1193. mtexinfo_t *out;
  1194. int i, j, count;
  1195. // UNDONE: Fix this
  1196. CMapLoadHelper lh( LUMP_TEXINFO );
  1197. in = (texinfo_t *)lh.LumpBase();
  1198. if (lh.LumpSize() % sizeof(*in))
  1199. Host_Error ("Mod_LoadTexinfo: funny lump size in %s",lh.GetMapName());
  1200. count = lh.LumpSize() / sizeof(*in);
  1201. out = (mtexinfo_t *)Hunk_AllocName( count*sizeof(*out), va( "%s [%s]", lh.GetLoadName(), "texinfo" ) );
  1202. s_pMap->texinfo = out;
  1203. s_pMap->numtexinfo = count;
  1204. bool loadtextures = mat_loadtextures.GetBool();
  1205. for ( i=0 ; i<count ; ++i, ++in, ++out )
  1206. {
  1207. for (j=0; j<2; ++j)
  1208. {
  1209. for (int k=0 ; k<4 ; ++k)
  1210. {
  1211. out->textureVecsTexelsPerWorldUnits[j][k] = in->textureVecsTexelsPerWorldUnits[j][k];
  1212. out->lightmapVecsLuxelsPerWorldUnits[j][k] = in->lightmapVecsLuxelsPerWorldUnits[j][k] ;
  1213. }
  1214. }
  1215. // assume that the scale is the same on both s and t.
  1216. out->luxelsPerWorldUnit = VectorLength( out->lightmapVecsLuxelsPerWorldUnits[0].AsVector3D() );
  1217. out->worldUnitsPerLuxel = 1.0f / out->luxelsPerWorldUnit;
  1218. out->flags = in->flags;
  1219. out->texinfoFlags = 0;
  1220. if ( loadtextures )
  1221. {
  1222. if ( in->texdata >= 0 )
  1223. {
  1224. out->material = GL_LoadMaterial( lh.GetMap()->texdata[ in->texdata ].name, TEXTURE_GROUP_WORLD );
  1225. }
  1226. else
  1227. {
  1228. DevMsg( "Mod_LoadTexinfo: texdata < 0 (index==%i/%i)\n", i, count );
  1229. out->material = NULL;
  1230. }
  1231. if ( !out->material )
  1232. {
  1233. out->material = g_materialEmpty;
  1234. g_materialEmpty->IncrementReferenceCount();
  1235. }
  1236. }
  1237. else
  1238. {
  1239. out->material = g_materialEmpty;
  1240. g_materialEmpty->IncrementReferenceCount();
  1241. }
  1242. }
  1243. }
  1244. // code to scan the lightmaps for empty lightstyles
  1245. static void LinearToGamma( unsigned char *pDstRGB, const float *pSrcRGB )
  1246. {
  1247. pDstRGB[0] = LinearToScreenGamma( pSrcRGB[0] );
  1248. pDstRGB[1] = LinearToScreenGamma( pSrcRGB[1] );
  1249. pDstRGB[2] = LinearToScreenGamma( pSrcRGB[2] );
  1250. }
  1251. static void CheckSurfaceLighting( SurfaceHandle_t surfID, worldbrushdata_t *pBrushData )
  1252. {
  1253. #if !defined( SWDS )
  1254. host_state.worldbrush = pBrushData;
  1255. msurfacelighting_t *pLighting = SurfaceLighting( surfID, pBrushData );
  1256. if( !pLighting->m_pSamples )
  1257. return;
  1258. int smax = ( pLighting->m_LightmapExtents[0] ) + 1;
  1259. int tmax = ( pLighting->m_LightmapExtents[1] ) + 1;
  1260. int offset = smax * tmax;
  1261. if ( SurfHasBumpedLightmaps( surfID ) )
  1262. {
  1263. offset *= ( NUM_BUMP_VECTS + 1 );
  1264. }
  1265. // how many lightmaps does this surface have?
  1266. int maxLightmapIndex = 0;
  1267. for (int maps = 1 ; maps < MAXLIGHTMAPS && pLighting->m_nStyles[maps] != 255 ; ++maps)
  1268. {
  1269. maxLightmapIndex = maps;
  1270. }
  1271. if ( maxLightmapIndex < 1 )
  1272. return;
  1273. // iterate and test each lightmap
  1274. for ( int maps = maxLightmapIndex; maps != 0; maps-- )
  1275. {
  1276. ColorRGBExp32 *pLightmap = pLighting->m_pSamples + (maps * offset);
  1277. float maxLen = -1;
  1278. Vector maxLight;
  1279. maxLight.Init();
  1280. for ( int i = 0; i < offset; i++ )
  1281. {
  1282. Vector c;
  1283. ColorRGBExp32ToVector( pLightmap[i], c );
  1284. if ( c.Length() > maxLen )
  1285. {
  1286. maxLight = c;
  1287. maxLen = c.Length();
  1288. }
  1289. }
  1290. unsigned char color[4];
  1291. LinearToGamma( color, maxLight.Base() );
  1292. const int minLightVal = 1;
  1293. if ( color[0] <= minLightVal && color[1] <= minLightVal && color[2] <= minLightVal )
  1294. {
  1295. // found a lightmap that is too dark, remove it and shift over the subsequent maps/styles
  1296. for ( int i = maps; i < maxLightmapIndex; i++ )
  1297. {
  1298. ColorRGBExp32 *pLightmapOverwrite = pLighting->m_pSamples + (i * offset);
  1299. memcpy( pLightmapOverwrite, pLightmapOverwrite+offset, offset * sizeof(*pLightmapOverwrite) );
  1300. pLighting->m_nStyles[i] = pLighting->m_nStyles[i+1];
  1301. }
  1302. // mark end lightstyle as removed, decrement max index
  1303. pLighting->m_nStyles[maxLightmapIndex] = 255;
  1304. maxLightmapIndex--;
  1305. }
  1306. }
  1307. // we removed all of the lightstyle maps so clear the flag
  1308. if ( maxLightmapIndex == 0 )
  1309. {
  1310. surfID->flags &= ~SURFDRAW_HASLIGHTSYTLES;
  1311. }
  1312. #endif
  1313. }
  1314. //-----------------------------------------------------------------------------
  1315. // Purpose:
  1316. // Input : *loadmodel -
  1317. // *s -
  1318. // Output : void CalcSurfaceExtents
  1319. //-----------------------------------------------------------------------------
  1320. static void CalcSurfaceExtents ( CMapLoadHelper& lh, SurfaceHandle_t surfID )
  1321. {
  1322. float textureMins[2], textureMaxs[2], val;
  1323. int i,j, e;
  1324. mvertex_t *v;
  1325. mtexinfo_t *tex;
  1326. int bmins[2], bmaxs[2];
  1327. textureMins[0] = textureMins[1] = 999999;
  1328. textureMaxs[0] = textureMaxs[1] = -99999;
  1329. worldbrushdata_t *pBrushData = lh.GetMap();
  1330. tex = MSurf_TexInfo( surfID, pBrushData );
  1331. for (i=0 ; i<MSurf_VertCount( surfID ); i++)
  1332. {
  1333. e = pBrushData->vertindices[MSurf_FirstVertIndex( surfID )+i];
  1334. v = &pBrushData->vertexes[e];
  1335. for (j=0 ; j<2 ; j++)
  1336. {
  1337. val = v->position[0] * tex->textureVecsTexelsPerWorldUnits[j][0] +
  1338. v->position[1] * tex->textureVecsTexelsPerWorldUnits[j][1] +
  1339. v->position[2] * tex->textureVecsTexelsPerWorldUnits[j][2] +
  1340. tex->textureVecsTexelsPerWorldUnits[j][3];
  1341. if (val < textureMins[j])
  1342. textureMins[j] = val;
  1343. if (val > textureMaxs[j])
  1344. textureMaxs[j] = val;
  1345. }
  1346. }
  1347. for (i=0 ; i<2 ; i++)
  1348. {
  1349. if( MSurf_LightmapExtents( surfID, pBrushData )[i] == 0 && !MSurf_Samples( surfID, pBrushData ) )
  1350. {
  1351. MSurf_Flags( surfID ) |= SURFDRAW_NOLIGHT;
  1352. }
  1353. bmins[i] = Float2Int( textureMins[i] );
  1354. bmaxs[i] = Ceil2Int( textureMaxs[i] );
  1355. MSurf_TextureMins( surfID, pBrushData )[i] = bmins[i];
  1356. MSurf_TextureExtents( surfID, pBrushData )[i] = ( bmaxs[i] - bmins[i] );
  1357. if ( !(tex->flags & SURF_NOLIGHT) && MSurf_LightmapExtents( surfID, pBrushData )[i] > MSurf_MaxLightmapSizeWithBorder( surfID ) )
  1358. {
  1359. Sys_Error ("Bad surface extents on texture %s", tex->material->GetName() );
  1360. }
  1361. }
  1362. CheckSurfaceLighting( surfID, pBrushData );
  1363. }
  1364. //-----------------------------------------------------------------------------
  1365. // Input : *pModel -
  1366. // *pLump -
  1367. // *loadname -
  1368. //-----------------------------------------------------------------------------
  1369. void Mod_LoadVertNormals( void )
  1370. {
  1371. CMapLoadHelper lh( LUMP_VERTNORMALS );
  1372. // get a pointer to the vertex normal data.
  1373. Vector *pVertNormals = ( Vector * )lh.LumpBase();
  1374. //
  1375. // verify vertnormals data size
  1376. //
  1377. if( lh.LumpSize() % sizeof( *pVertNormals ) )
  1378. Host_Error( "Mod_LoadVertNormals: funny lump size in %s!\n", lh.GetMapName() );
  1379. int count = lh.LumpSize() / sizeof(*pVertNormals);
  1380. Vector *out = (Vector *)Hunk_AllocName( lh.LumpSize(), va( "%s [%s]", lh.GetLoadName(), "vertnormals" ) );
  1381. memcpy( out, pVertNormals, lh.LumpSize() );
  1382. lh.GetMap()->vertnormals = out;
  1383. lh.GetMap()->numvertnormals = count;
  1384. }
  1385. //-----------------------------------------------------------------------------
  1386. // Purpose:
  1387. //-----------------------------------------------------------------------------
  1388. void Mod_LoadVertNormalIndices( void )
  1389. {
  1390. CMapLoadHelper lh( LUMP_VERTNORMALINDICES );
  1391. // get a pointer to the vertex normal data.
  1392. unsigned short *pIndices = ( unsigned short * )lh.LumpBase();
  1393. int count = lh.LumpSize() / sizeof(*pIndices);
  1394. unsigned short *out = (unsigned short *)Hunk_AllocName( lh.LumpSize(), va( "%s [%s]", lh.GetLoadName(), "vertnormalindices" ) );
  1395. memcpy( out, pIndices, lh.LumpSize() );
  1396. lh.GetMap()->vertnormalindices = out;
  1397. lh.GetMap()->numvertnormalindices = count;
  1398. // OPTIMIZE: Water surfaces don't need vertex normals?
  1399. int normalIndex = 0;
  1400. for( int i = 0; i < lh.GetMap()->numsurfaces; i++ )
  1401. {
  1402. SurfaceHandle_t surfID = SurfaceHandleFromIndex( i, lh.GetMap() );
  1403. MSurf_FirstVertNormal( surfID, lh.GetMap() ) = normalIndex;
  1404. normalIndex += MSurf_VertCount( surfID );
  1405. }
  1406. }
  1407. //-----------------------------------------------------------------------------
  1408. // Purpose:
  1409. // Input : *loadmodel -
  1410. // *l -
  1411. // *loadname -
  1412. //-----------------------------------------------------------------------------
  1413. void Mod_LoadPrimitives( void )
  1414. {
  1415. dprimitive_t *in;
  1416. mprimitive_t *out;
  1417. int i, count;
  1418. CMapLoadHelper lh( LUMP_PRIMITIVES );
  1419. in = (dprimitive_t *)lh.LumpBase();
  1420. if (lh.LumpSize() % sizeof(*in))
  1421. Host_Error ("Mod_LoadPrimitives: funny lump size in %s",lh.GetMapName());
  1422. count = lh.LumpSize() / sizeof(*in);
  1423. out = (mprimitive_t *)Hunk_AllocName( count*sizeof(*out), va( "%s [%s]", lh.GetLoadName(), "primitives" ) );
  1424. memset( out, 0, count * sizeof( mprimitive_t ) );
  1425. lh.GetMap()->primitives = out;
  1426. lh.GetMap()->numprimitives = count;
  1427. for ( i=0 ; i<count ; i++, in++, out++)
  1428. {
  1429. out->firstIndex = in->firstIndex;
  1430. out->firstVert = in->firstVert;
  1431. out->indexCount = in->indexCount;
  1432. out->type = in->type;
  1433. out->vertCount = in->vertCount;
  1434. }
  1435. }
  1436. //-----------------------------------------------------------------------------
  1437. // Purpose:
  1438. // Input : *loadmodel -
  1439. // *l -
  1440. // *loadname -
  1441. //-----------------------------------------------------------------------------
  1442. void Mod_LoadPrimVerts( void )
  1443. {
  1444. dprimvert_t *in;
  1445. mprimvert_t *out;
  1446. int i, count;
  1447. CMapLoadHelper lh( LUMP_PRIMVERTS );
  1448. in = (dprimvert_t *)lh.LumpBase();
  1449. if (lh.LumpSize() % sizeof(*in))
  1450. Host_Error ("Mod_LoadPrimVerts: funny lump size in %s",lh.GetMapName());
  1451. count = lh.LumpSize() / sizeof(*in);
  1452. out = (mprimvert_t *)Hunk_AllocName( count*sizeof(*out), va( "%s [%s]", lh.GetLoadName(), "primverts" ) );
  1453. memset( out, 0, count * sizeof( mprimvert_t ) );
  1454. lh.GetMap()->primverts = out;
  1455. lh.GetMap()->numprimverts = count;
  1456. for ( i=0 ; i<count ; i++, in++, out++)
  1457. {
  1458. out->pos = in->pos;
  1459. }
  1460. }
  1461. //-----------------------------------------------------------------------------
  1462. // Purpose:
  1463. // Input : *loadmodel -
  1464. // *l -
  1465. // *loadname -
  1466. //-----------------------------------------------------------------------------
  1467. void Mod_LoadPrimIndices( void )
  1468. {
  1469. unsigned short *in;
  1470. unsigned short *out;
  1471. int count;
  1472. CMapLoadHelper lh( LUMP_PRIMINDICES );
  1473. in = (unsigned short *)lh.LumpBase();
  1474. if (lh.LumpSize() % sizeof(*in))
  1475. Host_Error ("Mod_LoadPrimIndices: funny lump size in %s",lh.GetMapName());
  1476. count = lh.LumpSize() / sizeof(*in);
  1477. out = (unsigned short *)Hunk_AllocName( count*sizeof(*out), va("%s [%s]", lh.GetLoadName(), "primindices" ) );
  1478. memset( out, 0, count * sizeof( unsigned short ) );
  1479. lh.GetMap()->primindices = out;
  1480. lh.GetMap()->numprimindices = count;
  1481. memcpy( out, in, count * sizeof( unsigned short ) );
  1482. }
  1483. // This allocates memory for a lump and copies the lump data in.
  1484. void Mod_LoadLump(
  1485. model_t *loadmodel,
  1486. int iLump,
  1487. char *loadname,
  1488. int elementSize,
  1489. void **ppData,
  1490. int *nElements )
  1491. {
  1492. CMapLoadHelper lh( iLump );
  1493. if ( lh.LumpSize() % elementSize )
  1494. {
  1495. Host_Error( "Mod_LoadLump: funny lump size in %s", loadmodel->strName.String() );
  1496. }
  1497. // How many elements?
  1498. *nElements = lh.LumpSize() / elementSize;
  1499. // Make room for the data and copy the data in.
  1500. *ppData = Hunk_AllocName( lh.LumpSize(), loadname );
  1501. memcpy( *ppData, lh.LumpBase(), lh.LumpSize() );
  1502. }
  1503. //-----------------------------------------------------------------------------
  1504. // Sets up the msurfacelighting_t structure
  1505. //-----------------------------------------------------------------------------
  1506. bool Mod_LoadSurfaceLightingV1( msurfacelighting_t *pLighting, dface_t *in, ColorRGBExp32 *pBaseLightData )
  1507. {
  1508. // Get lightmap extents from the file.
  1509. pLighting->m_LightmapExtents[0] = in->m_LightmapTextureSizeInLuxels[0];
  1510. pLighting->m_LightmapExtents[1] = in->m_LightmapTextureSizeInLuxels[1];
  1511. pLighting->m_LightmapMins[0] = in->m_LightmapTextureMinsInLuxels[0];
  1512. pLighting->m_LightmapMins[1] = in->m_LightmapTextureMinsInLuxels[1];
  1513. int i = in->lightofs;
  1514. if ( (i == -1) || (!pBaseLightData) )
  1515. {
  1516. pLighting->m_pSamples = NULL;
  1517. // Can't have *any* lightstyles if we have no samples....
  1518. for ( i=0; i<MAXLIGHTMAPS; ++i)
  1519. {
  1520. pLighting->m_nStyles[i] = 255;
  1521. }
  1522. }
  1523. else
  1524. {
  1525. pLighting->m_pSamples = (ColorRGBExp32 *)( ((byte *)pBaseLightData) + i );
  1526. for (i=0 ; i<MAXLIGHTMAPS; ++i)
  1527. {
  1528. pLighting->m_nStyles[i] = in->styles[i];
  1529. }
  1530. }
  1531. return ((pLighting->m_nStyles[0] != 0) && (pLighting->m_nStyles[0] != 255)) || (pLighting->m_nStyles[1] != 255);
  1532. }
  1533. void *Hunk_AllocNameAlignedClear_( int size, int alignment, const char *pHunkName )
  1534. {
  1535. Assert(IsPowerOfTwo(alignment));
  1536. void *pMem = Hunk_AllocName( alignment + size, pHunkName );
  1537. memset( pMem, 0, size + alignment );
  1538. pMem = (void *)( ( ( ( unsigned long )pMem ) + (alignment-1) ) & ~(alignment-1) );
  1539. return pMem;
  1540. }
  1541. // Allocates a block of T from the hunk. Aligns as specified and clears the memory
  1542. template< typename T >
  1543. T *Hunk_AllocNameAlignedClear( int count, int alignment, const char *pHunkName )
  1544. {
  1545. return (T *)Hunk_AllocNameAlignedClear_( alignment + count * sizeof(T), alignment, pHunkName );
  1546. }
  1547. //-----------------------------------------------------------------------------
  1548. // Purpose:
  1549. // Input : *loadmodel -
  1550. // *l -
  1551. // *loadname -
  1552. //-----------------------------------------------------------------------------
  1553. void Mod_LoadFaces( void )
  1554. {
  1555. dface_t *in;
  1556. int count, surfnum;
  1557. int planenum;
  1558. int ti, di;
  1559. int face_lump_to_load = LUMP_FACES;
  1560. if ( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE &&
  1561. CMapLoadHelper::LumpSize( LUMP_FACES_HDR ) > 0 )
  1562. {
  1563. face_lump_to_load = LUMP_FACES_HDR;
  1564. }
  1565. CMapLoadHelper lh( face_lump_to_load );
  1566. in = (dface_t *)lh.LumpBase();
  1567. if (lh.LumpSize() % sizeof(*in))
  1568. Host_Error ("Mod_LoadFaces: funny lump size in %s",lh.GetMapName());
  1569. count = lh.LumpSize() / sizeof(*in);
  1570. // align these allocations
  1571. // If you trip one of these, you need to rethink the alignment of the struct
  1572. Assert( sizeof(msurface1_t) == 16 );
  1573. Assert( sizeof(msurface2_t) == 32 );
  1574. Assert( sizeof(msurfacelighting_t) == 32 );
  1575. msurface1_t *out1 = Hunk_AllocNameAlignedClear< msurface1_t >( count, 16, va( "%s [%s]", lh.GetLoadName(), "surface1" ) );
  1576. msurface2_t *out2 = Hunk_AllocNameAlignedClear< msurface2_t >( count, 32, va( "%s [%s]", lh.GetLoadName(), "surface2" ) );
  1577. msurfacelighting_t *pLighting = Hunk_AllocNameAlignedClear< msurfacelighting_t >( count, 32, va( "%s [%s]", lh.GetLoadName(), "surfacelighting" ) );
  1578. lh.GetMap()->surfaces1 = out1;
  1579. lh.GetMap()->surfaces2 = out2;
  1580. lh.GetMap()->surfacelighting = pLighting;
  1581. lh.GetMap()->surfacenormals = Hunk_AllocNameAlignedClear< msurfacenormal_t >( count, 2, va( "%s [%s]", lh.GetLoadName(), "surfacenormal" ) );
  1582. lh.GetMap()->numsurfaces = count;
  1583. worldbrushdata_t *pBrushData = lh.GetMap();
  1584. for ( surfnum=0 ; surfnum<count ; ++surfnum, ++in, ++out1, ++out2, ++pLighting )
  1585. {
  1586. SurfaceHandle_t surfID = SurfaceHandleFromIndex( surfnum, pBrushData );
  1587. MSurf_FirstVertIndex( surfID ) = in->firstedge;
  1588. int vertCount = in->numedges;
  1589. MSurf_Flags( surfID ) = 0;
  1590. Assert( vertCount <= 255 );
  1591. MSurf_SetVertCount( surfID, vertCount );
  1592. planenum = in->planenum;
  1593. if ( in->onNode )
  1594. {
  1595. MSurf_Flags( surfID ) |= SURFDRAW_NODE;
  1596. }
  1597. if ( in->side )
  1598. {
  1599. MSurf_Flags( surfID ) |= SURFDRAW_PLANEBACK;
  1600. }
  1601. out2->plane = lh.GetMap()->planes + planenum;
  1602. ti = in->texinfo;
  1603. if (ti < 0 || ti >= lh.GetMap()->numtexinfo)
  1604. {
  1605. Host_Error( "Mod_LoadFaces: bad texinfo number" );
  1606. }
  1607. surfID->texinfo = ti;
  1608. surfID->m_bDynamicShadowsEnabled = in->AreDynamicShadowsEnabled();
  1609. mtexinfo_t *pTex = lh.GetMap()->texinfo + ti;
  1610. // big hack!
  1611. if ( !pTex->material )
  1612. {
  1613. pTex->material = g_materialEmpty;
  1614. g_materialEmpty->IncrementReferenceCount();
  1615. }
  1616. // lighting info
  1617. if ( Mod_LoadSurfaceLightingV1( pLighting, in, lh.GetMap()->lightdata ) )
  1618. {
  1619. MSurf_Flags( surfID ) |= SURFDRAW_HASLIGHTSYTLES;
  1620. }
  1621. // set the drawing flags flag
  1622. if ( pTex->flags & SURF_NOLIGHT )
  1623. {
  1624. MSurf_Flags( surfID ) |= SURFDRAW_NOLIGHT;
  1625. }
  1626. if ( pTex->flags & SURF_NOSHADOWS )
  1627. {
  1628. MSurf_Flags( surfID ) |= SURFDRAW_NOSHADOWS;
  1629. }
  1630. if ( pTex->flags & SURF_WARP )
  1631. {
  1632. MSurf_Flags( surfID ) |= SURFDRAW_WATERSURFACE;
  1633. }
  1634. if ( pTex->flags & SURF_SKY )
  1635. {
  1636. MSurf_Flags( surfID ) |= SURFDRAW_SKY;
  1637. }
  1638. di = in->dispinfo;
  1639. out2->pDispInfo = NULL;
  1640. if( di != -1 )
  1641. {
  1642. // out->origSurfaceID = in->origFace;
  1643. MSurf_Flags( surfID ) |= SURFDRAW_HAS_DISP;
  1644. }
  1645. else
  1646. {
  1647. // non-displacement faces shouldn't come out of VBSP if they have nodraw.
  1648. Assert( !(pTex->flags & SURF_NODRAW) );
  1649. out1->prims.numPrims = in->GetNumPrims();
  1650. out1->prims.firstPrimID = in->firstPrimID;
  1651. if ( in->GetNumPrims() )
  1652. {
  1653. MSurf_Flags( surfID ) |= SURFDRAW_HAS_PRIMS;
  1654. mprimitive_t *pPrim = &pBrushData->primitives[in->firstPrimID];
  1655. if ( pPrim->vertCount > 0 )
  1656. {
  1657. MSurf_Flags( surfID ) |= SURFDRAW_DYNAMIC;
  1658. }
  1659. }
  1660. }
  1661. // No shadows on the surface to start with
  1662. out2->m_ShadowDecals = SHADOW_DECAL_HANDLE_INVALID;
  1663. out2->decals = WORLD_DECAL_HANDLE_INVALID;
  1664. // No overlays on the surface to start with
  1665. out2->m_nFirstOverlayFragment = OVERLAY_FRAGMENT_INVALID;
  1666. CalcSurfaceExtents( lh, surfID );
  1667. }
  1668. }
  1669. //-----------------------------------------------------------------------------
  1670. // Purpose:
  1671. // Input : *node -
  1672. // *parent -
  1673. // Output : void Mod_SetParent
  1674. //-----------------------------------------------------------------------------
  1675. void Mod_SetParent (mnode_t *node, mnode_t *parent)
  1676. {
  1677. node->parent = parent;
  1678. if (node->contents >= 0)
  1679. return;
  1680. Mod_SetParent (node->children[0], node);
  1681. Mod_SetParent (node->children[1], node);
  1682. }
  1683. //-----------------------------------------------------------------------------
  1684. // Mark an entire subtree as being too small to bother with
  1685. //-----------------------------------------------------------------------------
  1686. static void MarkSmallNode( mnode_t *node )
  1687. {
  1688. if (node->contents >= 0)
  1689. return;
  1690. node->contents = -2;
  1691. MarkSmallNode (node->children[0]);
  1692. MarkSmallNode (node->children[1]);
  1693. }
  1694. static void CheckSmallVolumeDifferences( mnode_t *pNode, const Vector &parentSize )
  1695. {
  1696. if (pNode->contents >= 0)
  1697. return;
  1698. Vector delta;
  1699. VectorSubtract( parentSize, pNode->m_vecHalfDiagonal, delta );
  1700. if ((delta.x < 5) && (delta.y < 5) && (delta.z < 5))
  1701. {
  1702. pNode->contents = -3;
  1703. CheckSmallVolumeDifferences( pNode->children[0], parentSize );
  1704. CheckSmallVolumeDifferences( pNode->children[1], parentSize );
  1705. }
  1706. }
  1707. //-----------------------------------------------------------------------------
  1708. // Purpose:
  1709. // Input : *loadmodel -
  1710. // *l -
  1711. // *loadname -
  1712. //-----------------------------------------------------------------------------
  1713. void Mod_LoadNodes( void )
  1714. {
  1715. Vector mins( 0, 0, 0 ), maxs( 0, 0, 0 );
  1716. int i, j, count, p;
  1717. dnode_t *in;
  1718. mnode_t *out;
  1719. CMapLoadHelper lh( LUMP_NODES );
  1720. in = (dnode_t *)lh.LumpBase();
  1721. if (lh.LumpSize() % sizeof(*in))
  1722. Host_Error ("Mod_LoadNodes: funny lump size in %s",lh.GetMapName());
  1723. count = lh.LumpSize() / sizeof(*in);
  1724. out = (mnode_t *)Hunk_AllocName( count*sizeof(*out), va( "%s [%s]", lh.GetLoadName(), "nodes" ) );
  1725. lh.GetMap()->nodes = out;
  1726. lh.GetMap()->numnodes = count;
  1727. for ( i=0 ; i<count ; i++, in++, out++)
  1728. {
  1729. for (j=0 ; j<3 ; j++)
  1730. {
  1731. mins[j] = in->mins[j];
  1732. maxs[j] = in->maxs[j];
  1733. }
  1734. VectorAdd( mins, maxs, out->m_vecCenter );
  1735. out->m_vecCenter *= 0.5f;
  1736. VectorSubtract( maxs, out->m_vecCenter, out->m_vecHalfDiagonal );
  1737. p = in->planenum;
  1738. out->plane = lh.GetMap()->planes + p;
  1739. out->firstsurface = in->firstface;
  1740. out->numsurfaces = in->numfaces;
  1741. out->area = in->area;
  1742. out->contents = -1; // differentiate from leafs
  1743. for (j=0 ; j<2 ; j++)
  1744. {
  1745. p = in->children[j];
  1746. if (p >= 0)
  1747. out->children[j] = lh.GetMap()->nodes + p;
  1748. else
  1749. out->children[j] = (mnode_t *)(lh.GetMap()->leafs + (-1 - p));
  1750. }
  1751. }
  1752. Mod_SetParent (lh.GetMap()->nodes, NULL); // sets nodes and leafs
  1753. // Check for small-area parents... no culling below them...
  1754. mnode_t *pNode = lh.GetMap()->nodes;
  1755. for ( i=0 ; i<count ; ++i, ++pNode)
  1756. {
  1757. if (pNode->contents == -1)
  1758. {
  1759. if ((pNode->m_vecHalfDiagonal.x <= 50) && (pNode->m_vecHalfDiagonal.y <= 50) &&
  1760. (pNode->m_vecHalfDiagonal.z <= 50))
  1761. {
  1762. // Mark all children as being too small to bother with...
  1763. MarkSmallNode( pNode->children[0] );
  1764. MarkSmallNode( pNode->children[1] );
  1765. }
  1766. else
  1767. {
  1768. CheckSmallVolumeDifferences( pNode->children[0], pNode->m_vecHalfDiagonal );
  1769. CheckSmallVolumeDifferences( pNode->children[1], pNode->m_vecHalfDiagonal );
  1770. }
  1771. }
  1772. }
  1773. }
  1774. //-----------------------------------------------------------------------------
  1775. // Purpose:
  1776. // Input : *loadmodel -
  1777. // *l -
  1778. // *loadname -
  1779. //-----------------------------------------------------------------------------
  1780. void Mod_LoadLeafs_Version_0( CMapLoadHelper &lh )
  1781. {
  1782. Vector mins( 0, 0, 0 ), maxs( 0, 0, 0 );
  1783. dleaf_version_0_t *in;
  1784. mleaf_t *out;
  1785. int i, j, count, p;
  1786. in = (dleaf_version_0_t *)lh.LumpBase();
  1787. if (lh.LumpSize() % sizeof(*in))
  1788. Host_Error ("Mod_LoadLeafs: funny lump size in %s",lh.GetMapName());
  1789. count = lh.LumpSize() / sizeof(*in);
  1790. out = (mleaf_t *)Hunk_AllocName( count*sizeof(*out), va( "%s [%s]", lh.GetLoadName(), "leafs" ) );
  1791. lh.GetMap()->leafs = out;
  1792. lh.GetMap()->numleafs = count;
  1793. // one sample per leaf
  1794. lh.GetMap()->m_pLeafAmbient = (mleafambientindex_t *)Hunk_AllocName( count * sizeof(*lh.GetMap()->m_pLeafAmbient), "LeafAmbient" );
  1795. lh.GetMap()->m_pAmbientSamples = (mleafambientlighting_t *)Hunk_AllocName( count * sizeof(*lh.GetMap()->m_pAmbientSamples), "LeafAmbientSamples" );
  1796. mleafambientindex_t *pTable = lh.GetMap()->m_pLeafAmbient;
  1797. mleafambientlighting_t *pSamples = lh.GetMap()->m_pAmbientSamples;
  1798. for ( i=0 ; i<count ; i++, in++, out++)
  1799. {
  1800. for (j=0 ; j<3 ; j++)
  1801. {
  1802. mins[j] = in->mins[j];
  1803. maxs[j] = in->maxs[j];
  1804. }
  1805. VectorAdd( mins, maxs, out->m_vecCenter );
  1806. out->m_vecCenter *= 0.5f;
  1807. VectorSubtract( maxs, out->m_vecCenter, out->m_vecHalfDiagonal );
  1808. pTable[i].ambientSampleCount = 1;
  1809. pTable[i].firstAmbientSample = i;
  1810. pSamples[i].x = pSamples[i].y = pSamples[i].z = 128;
  1811. pSamples[i].pad = 0;
  1812. Q_memcpy( &pSamples[i].cube, &in->m_AmbientLighting, sizeof(pSamples[i].cube) );
  1813. p = in->contents;
  1814. out->contents = p;
  1815. out->cluster = in->cluster;
  1816. out->area = in->area;
  1817. out->flags = in->flags;
  1818. /*
  1819. out->firstmarksurface = lh.GetMap()->marksurfaces + in->firstleafface;
  1820. */
  1821. out->firstmarksurface = in->firstleafface;
  1822. out->nummarksurfaces = in->numleaffaces;
  1823. out->parent = NULL;
  1824. out->dispCount = 0;
  1825. out->leafWaterDataID = in->leafWaterDataID;
  1826. }
  1827. }
  1828. //-----------------------------------------------------------------------------
  1829. // Purpose:
  1830. // Input : *loadmodel -
  1831. // *l -
  1832. // *loadname -
  1833. //-----------------------------------------------------------------------------
  1834. void Mod_LoadLeafs_Version_1( CMapLoadHelper &lh, CMapLoadHelper &ambientLightingLump, CMapLoadHelper &ambientLightingTable )
  1835. {
  1836. Vector mins( 0, 0, 0 ), maxs( 0, 0, 0 );
  1837. dleaf_t *in;
  1838. mleaf_t *out;
  1839. int i, j, count, p;
  1840. in = (dleaf_t *)lh.LumpBase();
  1841. if (lh.LumpSize() % sizeof(*in))
  1842. Host_Error ("Mod_LoadLeafs: funny lump size in %s",lh.GetMapName());
  1843. count = lh.LumpSize() / sizeof(*in);
  1844. out = (mleaf_t *)Hunk_AllocName( count*sizeof(*out), va( "%s [%s]", lh.GetLoadName(), "leafs" ) );
  1845. lh.GetMap()->leafs = out;
  1846. lh.GetMap()->numleafs = count;
  1847. if ( ambientLightingLump.LumpVersion() != LUMP_LEAF_AMBIENT_LIGHTING_VERSION || ambientLightingTable.LumpSize() == 0 )
  1848. {
  1849. // convert from previous version
  1850. CompressedLightCube *inLightCubes = NULL;
  1851. if ( ambientLightingLump.LumpSize() )
  1852. {
  1853. inLightCubes = ( CompressedLightCube * )ambientLightingLump.LumpBase();
  1854. Assert( ambientLightingLump.LumpSize() % sizeof( CompressedLightCube ) == 0 );
  1855. Assert( ambientLightingLump.LumpSize() / sizeof( CompressedLightCube ) == lh.LumpSize() / sizeof( dleaf_t ) );
  1856. }
  1857. lh.GetMap()->m_pLeafAmbient = (mleafambientindex_t *)Hunk_AllocName( count * sizeof(*lh.GetMap()->m_pLeafAmbient), "LeafAmbient" );
  1858. lh.GetMap()->m_pAmbientSamples = (mleafambientlighting_t *)Hunk_AllocName( count * sizeof(*lh.GetMap()->m_pAmbientSamples), "LeafAmbientSamples" );
  1859. mleafambientindex_t *pTable = lh.GetMap()->m_pLeafAmbient;
  1860. mleafambientlighting_t *pSamples = lh.GetMap()->m_pAmbientSamples;
  1861. Vector gray(0.5, 0.5, 0.5);
  1862. ColorRGBExp32 grayColor;
  1863. VectorToColorRGBExp32( gray, grayColor );
  1864. for ( i = 0; i < count; i++ )
  1865. {
  1866. pTable[i].ambientSampleCount = 1;
  1867. pTable[i].firstAmbientSample = i;
  1868. pSamples[i].x = pSamples[i].y = pSamples[i].z = 128;
  1869. pSamples[i].pad = 0;
  1870. if ( inLightCubes )
  1871. {
  1872. Q_memcpy( &pSamples[i].cube, &inLightCubes[i], sizeof(pSamples[i].cube) );
  1873. }
  1874. else
  1875. {
  1876. for ( j = 0; j < 6; j++ )
  1877. {
  1878. pSamples[i].cube.m_Color[j] = grayColor;
  1879. }
  1880. }
  1881. }
  1882. }
  1883. else
  1884. {
  1885. Assert( ambientLightingLump.LumpSize() % sizeof( dleafambientlighting_t ) == 0 );
  1886. Assert( ambientLightingTable.LumpSize() % sizeof( dleafambientindex_t ) == 0 );
  1887. Assert((ambientLightingTable.LumpSize() / sizeof(dleafambientindex_t)) == (unsigned)count); // should have one of these per leaf
  1888. lh.GetMap()->m_pLeafAmbient = (mleafambientindex_t *)Hunk_AllocName( ambientLightingTable.LumpSize(), "LeafAmbient" );
  1889. lh.GetMap()->m_pAmbientSamples = (mleafambientlighting_t *)Hunk_AllocName( ambientLightingLump.LumpSize(), "LeafAmbientSamples" );
  1890. Q_memcpy( lh.GetMap()->m_pLeafAmbient, ambientLightingTable.LumpBase(), ambientLightingTable.LumpSize() );
  1891. Q_memcpy( lh.GetMap()->m_pAmbientSamples, ambientLightingLump.LumpBase(), ambientLightingLump.LumpSize() );
  1892. }
  1893. for ( i=0 ; i<count ; i++, in++, out++ )
  1894. {
  1895. for (j=0 ; j<3 ; j++)
  1896. {
  1897. mins[j] = in->mins[j];
  1898. maxs[j] = in->maxs[j];
  1899. }
  1900. VectorAdd( mins, maxs, out->m_vecCenter );
  1901. out->m_vecCenter *= 0.5f;
  1902. VectorSubtract( maxs, out->m_vecCenter, out->m_vecHalfDiagonal );
  1903. p = in->contents;
  1904. out->contents = p;
  1905. out->cluster = in->cluster;
  1906. out->area = in->area;
  1907. out->flags = in->flags;
  1908. /*
  1909. out->firstmarksurface = lh.GetMap()->marksurfaces + in->firstleafface;
  1910. */
  1911. out->firstmarksurface = in->firstleafface;
  1912. out->nummarksurfaces = in->numleaffaces;
  1913. out->parent = NULL;
  1914. out->dispCount = 0;
  1915. out->leafWaterDataID = in->leafWaterDataID;
  1916. }
  1917. }
  1918. void Mod_LoadLeafs( void )
  1919. {
  1920. CMapLoadHelper lh( LUMP_LEAFS );
  1921. switch( lh.LumpVersion() )
  1922. {
  1923. case 0:
  1924. Mod_LoadLeafs_Version_0( lh );
  1925. break;
  1926. case 1:
  1927. if( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE &&
  1928. CMapLoadHelper::LumpSize( LUMP_LEAF_AMBIENT_LIGHTING_HDR ) > 0 )
  1929. {
  1930. CMapLoadHelper mlh( LUMP_LEAF_AMBIENT_LIGHTING_HDR );
  1931. CMapLoadHelper mlhTable( LUMP_LEAF_AMBIENT_INDEX_HDR );
  1932. Mod_LoadLeafs_Version_1( lh, mlh, mlhTable );
  1933. }
  1934. else
  1935. {
  1936. CMapLoadHelper mlh( LUMP_LEAF_AMBIENT_LIGHTING );
  1937. CMapLoadHelper mlhTable( LUMP_LEAF_AMBIENT_INDEX );
  1938. Mod_LoadLeafs_Version_1( lh, mlh, mlhTable );
  1939. }
  1940. break;
  1941. default:
  1942. Assert( 0 );
  1943. Error( "Unknown LUMP_LEAFS version\n" );
  1944. break;
  1945. }
  1946. worldbrushdata_t *pMap = lh.GetMap();
  1947. cleaf_t *pCLeaf = GetCollisionBSPData()->map_leafs.Base();
  1948. for ( int i = 0; i < pMap->numleafs; i++ )
  1949. {
  1950. pMap->leafs[i].dispCount = pCLeaf[i].dispCount;
  1951. pMap->leafs[i].dispListStart = pCLeaf[i].dispListStart;
  1952. }
  1953. // HACKHACK: Copy over the shared global list here. Hunk_Alloc a copy?
  1954. pMap->m_pDispInfoReferences = GetCollisionBSPData()->map_dispList.Base();
  1955. pMap->m_nDispInfoReferences = GetCollisionBSPData()->numdisplist;
  1956. }
  1957. //-----------------------------------------------------------------------------
  1958. // Purpose:
  1959. //-----------------------------------------------------------------------------
  1960. void Mod_LoadLeafWaterData( void )
  1961. {
  1962. dleafwaterdata_t *in;
  1963. mleafwaterdata_t *out;
  1964. int count, i;
  1965. CMapLoadHelper lh( LUMP_LEAFWATERDATA );
  1966. in = (dleafwaterdata_t *)lh.LumpBase();
  1967. if (lh.LumpSize() % sizeof(*in))
  1968. Host_Error ("Mod_LoadLeafs: funny lump size in %s",lh.GetMapName());
  1969. count = lh.LumpSize() / sizeof(*in);
  1970. out = (mleafwaterdata_t *)Hunk_AllocName( count*sizeof(*out), va( "%s [%s]", lh.GetLoadName(), "leafwaterdata" ) );
  1971. lh.GetMap()->leafwaterdata = out;
  1972. lh.GetMap()->numleafwaterdata = count;
  1973. for ( i=0 ; i<count ; i++, in++, out++)
  1974. {
  1975. out->minZ = in->minZ;
  1976. out->surfaceTexInfoID = in->surfaceTexInfoID;
  1977. out->surfaceZ = in->surfaceZ;
  1978. out->firstLeafIndex = -1;
  1979. }
  1980. if ( count == 1 )
  1981. {
  1982. worldbrushdata_t *brush = lh.GetMap();
  1983. for ( i = 0; i < brush->numleafs; i++ )
  1984. {
  1985. if ( brush->leafs[i].leafWaterDataID >= 0 )
  1986. {
  1987. brush->leafwaterdata[0].firstLeafIndex = i;
  1988. break;
  1989. }
  1990. }
  1991. }
  1992. }
  1993. //-----------------------------------------------------------------------------
  1994. // Purpose:
  1995. //-----------------------------------------------------------------------------
  1996. void Mod_LoadCubemapSamples( void )
  1997. {
  1998. char textureName[512];
  1999. char loadName[ MAX_PATH ];
  2000. dcubemapsample_t *in;
  2001. mcubemapsample_t *out;
  2002. int count, i;
  2003. CMapLoadHelper lh( LUMP_CUBEMAPS );
  2004. V_strcpy_safe( loadName, lh.GetLoadName() );
  2005. in = (dcubemapsample_t *)lh.LumpBase();
  2006. if (lh.LumpSize() % sizeof(*in))
  2007. Host_Error ("Mod_LoadCubemapSamples: funny lump size in %s",lh.GetMapName());
  2008. count = lh.LumpSize() / sizeof(*in);
  2009. out = (mcubemapsample_t *)Hunk_AllocName( count*sizeof(*out), va( "%s [%s]", lh.GetLoadName(), "cubemapsample" ) );
  2010. lh.GetMap()->m_pCubemapSamples = out;
  2011. lh.GetMap()->m_nCubemapSamples = count;
  2012. bool bHDR = g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE;
  2013. int nCreateFlags = bHDR ? 0 : TEXTUREFLAGS_SRGB;
  2014. // We have separate HDR versions of the textures. In order to deal with this,
  2015. // we have blahenvmap.hdr.vtf and blahenvmap.vtf.
  2016. char *pHDRExtension = "";
  2017. if( bHDR )
  2018. {
  2019. pHDRExtension = ".hdr";
  2020. }
  2021. for ( i=0 ; i<count ; i++, in++, out++)
  2022. {
  2023. out->origin.Init( ( float )in->origin[0], ( float )in->origin[1], ( float )in->origin[2] );
  2024. out->size = in->size;
  2025. Q_snprintf( textureName, sizeof( textureName ), "maps/%s/c%d_%d_%d%s", loadName, ( int )in->origin[0],
  2026. ( int )in->origin[1], ( int )in->origin[2], pHDRExtension );
  2027. out->pTexture = materials->FindTexture( textureName, TEXTURE_GROUP_CUBE_MAP, true, nCreateFlags );
  2028. if ( IsErrorTexture( out->pTexture ) )
  2029. {
  2030. if ( bHDR )
  2031. {
  2032. Warning( "Couldn't get HDR '%s' -- ", textureName );
  2033. // try non hdr version
  2034. Q_snprintf( textureName, sizeof( textureName ), "maps/%s/c%d_%d_%d", loadName, ( int )in->origin[0],
  2035. ( int )in->origin[1], ( int )in->origin[2]);
  2036. Warning( "Trying non HDR '%s'\n", textureName);
  2037. out->pTexture = materials->FindTexture( textureName, TEXTURE_GROUP_CUBE_MAP, true );
  2038. }
  2039. if ( IsErrorTexture( out->pTexture ) )
  2040. {
  2041. Q_snprintf( textureName, sizeof( textureName ), "maps/%s/cubemapdefault", loadName );
  2042. out->pTexture = materials->FindTexture( textureName, TEXTURE_GROUP_CUBE_MAP, true, nCreateFlags );
  2043. if ( IsErrorTexture( out->pTexture ) )
  2044. {
  2045. out->pTexture = materials->FindTexture( "engine/defaultcubemap", TEXTURE_GROUP_CUBE_MAP, true, nCreateFlags );
  2046. }
  2047. Warning( "Failed, using default cubemap '%s'\n", out->pTexture->GetName() );
  2048. }
  2049. }
  2050. out->pTexture->IncrementReferenceCount();
  2051. }
  2052. CMatRenderContextPtr pRenderContext( materials );
  2053. if ( count )
  2054. {
  2055. pRenderContext->BindLocalCubemap( lh.GetMap()->m_pCubemapSamples[0].pTexture );
  2056. }
  2057. else
  2058. {
  2059. if ( CommandLine()->CheckParm( "-requirecubemaps" ) )
  2060. {
  2061. Sys_Error( "Map \"%s\" does not have cubemaps!", lh.GetMapName() );
  2062. }
  2063. ITexture *pTexture;
  2064. Q_snprintf( textureName, sizeof( textureName ), "maps/%s/cubemapdefault", loadName );
  2065. pTexture = materials->FindTexture( textureName, TEXTURE_GROUP_CUBE_MAP, true, nCreateFlags );
  2066. if ( IsErrorTexture( pTexture ) )
  2067. {
  2068. pTexture = materials->FindTexture( "engine/defaultcubemap", TEXTURE_GROUP_CUBE_MAP, true, nCreateFlags );
  2069. }
  2070. pTexture->IncrementReferenceCount();
  2071. pRenderContext->BindLocalCubemap( pTexture );
  2072. }
  2073. }
  2074. //-----------------------------------------------------------------------------
  2075. // Purpose:
  2076. //-----------------------------------------------------------------------------
  2077. void Mod_LoadLeafMinDistToWater( void )
  2078. {
  2079. CMapLoadHelper lh( LUMP_LEAFMINDISTTOWATER );
  2080. unsigned short *pTmp = ( unsigned short * )lh.LumpBase();
  2081. int i;
  2082. bool foundOne = false;
  2083. for( i = 0; i < ( int )( lh.LumpSize() / sizeof( *pTmp ) ); i++ )
  2084. {
  2085. if( pTmp[i] != 65535 ) // FIXME: make a marcro for this.
  2086. {
  2087. foundOne = true;
  2088. break;
  2089. }
  2090. }
  2091. if( !foundOne || lh.LumpSize() == 0 || !g_pMaterialSystemHardwareConfig || !g_pMaterialSystemHardwareConfig->SupportsVertexAndPixelShaders())
  2092. {
  2093. // We don't bother keeping this if:
  2094. // 1) there is no water in the map
  2095. // 2) we don't have this lump in the bsp file (old bsp file)
  2096. // 3) we aren't going to use it because we are on old hardware.
  2097. lh.GetMap()->m_LeafMinDistToWater = NULL;
  2098. }
  2099. else
  2100. {
  2101. int count;
  2102. unsigned short *in;
  2103. unsigned short *out;
  2104. in = (unsigned short *)lh.LumpBase();
  2105. if (lh.LumpSize() % sizeof(*in))
  2106. Host_Error ("Mod_LoadLeafMinDistToWater: funny lump size in %s",lh.GetMapName());
  2107. count = lh.LumpSize() / sizeof(*in);
  2108. out = (unsigned short *)Hunk_AllocName( count*sizeof(*out), va( "%s [%s]", lh.GetLoadName(), "leafmindisttowater" ) );
  2109. memcpy( out, in, sizeof( out[0] ) * count );
  2110. lh.GetMap()->m_LeafMinDistToWater = out;
  2111. }
  2112. }
  2113. //-----------------------------------------------------------------------------
  2114. // Purpose:
  2115. //-----------------------------------------------------------------------------
  2116. void Mod_LoadMarksurfaces( void )
  2117. {
  2118. int i, j, count;
  2119. unsigned short *in;
  2120. CMapLoadHelper lh( LUMP_LEAFFACES );
  2121. in = (unsigned short *)lh.LumpBase();
  2122. if (lh.LumpSize() % sizeof(*in))
  2123. Host_Error ("Mod_LoadMarksurfaces: funny lump size in %s",lh.GetMapName());
  2124. count = lh.LumpSize() / sizeof(*in);
  2125. SurfaceHandle_t *tempDiskData = new SurfaceHandle_t[count];
  2126. worldbrushdata_t *pBrushData = lh.GetMap();
  2127. pBrushData->marksurfaces = tempDiskData;
  2128. pBrushData->nummarksurfaces = count;
  2129. // read in the mark surfaces, count out how many we'll actually need to store
  2130. int realCount = 0;
  2131. for ( i=0 ; i<count ; i++)
  2132. {
  2133. j = in[i];
  2134. if (j >= lh.GetMap()->numsurfaces)
  2135. Host_Error ("Mod_LoadMarksurfaces: bad surface number");
  2136. SurfaceHandle_t surfID = SurfaceHandleFromIndex( j, pBrushData );
  2137. tempDiskData[i] = surfID;
  2138. if ( !SurfaceHasDispInfo( surfID ) && !(MSurf_Flags(surfID) & SURFDRAW_NODRAW) )
  2139. {
  2140. realCount++;
  2141. }
  2142. }
  2143. // now allocate the permanent list, and copy the non-terrain, non-nodraw surfs into it
  2144. SurfaceHandle_t *surfList = (SurfaceHandle_t *)Hunk_AllocName( realCount*sizeof(SurfaceHandle_t), va( "%s [%s]", lh.GetLoadName(), "surfacehandle" ) );
  2145. int outCount = 0;
  2146. mleaf_t *pLeaf = pBrushData->leafs;
  2147. for ( i = 0; i < pBrushData->numleafs; i++ )
  2148. {
  2149. int firstMark = outCount;
  2150. int numMark = 0;
  2151. bool foundDetail = false;
  2152. int numMarkNode = 0;
  2153. for ( j = 0; j < pLeaf[i].nummarksurfaces; j++ )
  2154. {
  2155. // write a new copy of the mark surfaces for this leaf, strip out the nodraw & terrain
  2156. SurfaceHandle_t surfID = tempDiskData[pLeaf[i].firstmarksurface+j];
  2157. if ( !SurfaceHasDispInfo( surfID ) && !(MSurf_Flags(surfID) & SURFDRAW_NODRAW) )
  2158. {
  2159. surfList[outCount++] = surfID;
  2160. numMark++;
  2161. Assert(outCount<=realCount);
  2162. if ( MSurf_Flags(surfID) & SURFDRAW_NODE )
  2163. {
  2164. // this assert assures that all SURFDRAW_NODE surfs appear coherently
  2165. Assert( !foundDetail );
  2166. numMarkNode++;
  2167. }
  2168. else
  2169. {
  2170. foundDetail = true;
  2171. }
  2172. }
  2173. }
  2174. // update the leaf count
  2175. pLeaf[i].nummarksurfaces = numMark;
  2176. pLeaf[i].firstmarksurface = firstMark;
  2177. pLeaf[i].nummarknodesurfaces = numMarkNode;
  2178. }
  2179. // write out the compacted array
  2180. pBrushData->marksurfaces = surfList;
  2181. pBrushData->nummarksurfaces = realCount;
  2182. // remove the temp copy of the disk data
  2183. delete[] tempDiskData;
  2184. //Msg("Must check %d / %d faces\n", checkCount, pModel->brush.numsurfaces );
  2185. }
  2186. //-----------------------------------------------------------------------------
  2187. // Purpose:
  2188. // Input : *pedges -
  2189. // *loadmodel -
  2190. // *l -
  2191. // *loadname -
  2192. //-----------------------------------------------------------------------------
  2193. void Mod_LoadSurfedges( medge_t *pedges )
  2194. {
  2195. int i, count;
  2196. int *in;
  2197. unsigned short *out;
  2198. CMapLoadHelper lh( LUMP_SURFEDGES );
  2199. in = (int *)lh.LumpBase();
  2200. if (lh.LumpSize() % sizeof(*in))
  2201. Host_Error ("Mod_LoadSurfedges: funny lump size in %s",lh.GetMapName());
  2202. count = lh.LumpSize() / sizeof(*in);
  2203. if (count < 1 || count >= MAX_MAP_SURFEDGES)
  2204. Host_Error ("Mod_LoadSurfedges: bad surfedges count in %s: %i",
  2205. lh.GetMapName(), count);
  2206. out = (unsigned short *)Hunk_AllocName( count*sizeof(*out), va( "%s [%s]", lh.GetLoadName(), "surfedges" ) );
  2207. lh.GetMap()->vertindices = out;
  2208. lh.GetMap()->numvertindices = count;
  2209. for ( i=0 ; i<count ; i++)
  2210. {
  2211. int edge = in[i];
  2212. int index = 0;
  2213. if ( edge < 0 )
  2214. {
  2215. edge = -edge;
  2216. index = 1;
  2217. }
  2218. out[i] = pedges[edge].v[index];
  2219. }
  2220. delete[] pedges;
  2221. }
  2222. //-----------------------------------------------------------------------------
  2223. // Purpose:
  2224. // Input : *loadmodel -
  2225. // *l -
  2226. // *loadname -
  2227. //-----------------------------------------------------------------------------
  2228. void Mod_LoadPlanes( void )
  2229. {
  2230. // Don't bother loading them, they're already stored
  2231. s_pMap->planes = GetCollisionBSPData()->map_planes.Base();
  2232. s_pMap->numplanes = GetCollisionBSPData()->numplanes;
  2233. }
  2234. //-----------------------------------------------------------------------------
  2235. // Returns game lump version
  2236. //-----------------------------------------------------------------------------
  2237. int Mod_GameLumpVersion( int lumpId )
  2238. {
  2239. for ( int i = g_GameLumpDict.Size(); --i >= 0; )
  2240. {
  2241. if ( g_GameLumpDict[i].id == lumpId )
  2242. {
  2243. return g_GameLumpDict[i].version;
  2244. }
  2245. }
  2246. return 0;
  2247. }
  2248. //-----------------------------------------------------------------------------
  2249. // Returns game lump size
  2250. //-----------------------------------------------------------------------------
  2251. int Mod_GameLumpSize( int lumpId )
  2252. {
  2253. for ( int i = g_GameLumpDict.Size(); --i >= 0; )
  2254. {
  2255. if ( g_GameLumpDict[i].id == lumpId )
  2256. {
  2257. return g_GameLumpDict[i].uncompressedSize;
  2258. }
  2259. }
  2260. return 0;
  2261. }
  2262. //-----------------------------------------------------------------------------
  2263. // Loads game lumps
  2264. //-----------------------------------------------------------------------------
  2265. bool Mod_LoadGameLump( int lumpId, void *pOutBuffer, int size )
  2266. {
  2267. int i;
  2268. for ( i = g_GameLumpDict.Size(); --i >= 0; )
  2269. {
  2270. if ( g_GameLumpDict[i].id == lumpId )
  2271. {
  2272. break;
  2273. }
  2274. }
  2275. if ( i < 0 )
  2276. {
  2277. // unknown
  2278. return false;
  2279. }
  2280. byte *pData;
  2281. bool bIsCompressed = ( g_GameLumpDict[i].flags & GAMELUMPFLAG_COMPRESSED );
  2282. int dataLength;
  2283. int outSize;
  2284. if ( bIsCompressed )
  2285. {
  2286. // lump data length is always original uncompressed size
  2287. // compressed lump data length is determined from next dictionary entry offset
  2288. dataLength = g_GameLumpDict[i].compressedSize;
  2289. outSize = g_GameLumpDict[i].uncompressedSize;
  2290. }
  2291. else
  2292. {
  2293. dataLength = outSize = g_GameLumpDict[i].uncompressedSize;
  2294. }
  2295. if ( size < 0 || size < outSize )
  2296. {
  2297. // caller must supply a buffer that is large enough to hold the data
  2298. return false;
  2299. }
  2300. if ( s_MapBuffer.Base() )
  2301. {
  2302. // data is in memory
  2303. Assert( CMapLoadHelper::GetRefCount() );
  2304. if ( g_GameLumpDict[i].offset + dataLength > (unsigned int)s_MapBuffer.TellMaxPut() )
  2305. {
  2306. // out of range
  2307. Assert( 0 );
  2308. return false;
  2309. }
  2310. pData = (unsigned char *)s_MapBuffer.Base() + g_GameLumpDict[i].offset;
  2311. if ( !bIsCompressed )
  2312. {
  2313. V_memcpy( pOutBuffer, pData, outSize );
  2314. return true;
  2315. }
  2316. }
  2317. else
  2318. {
  2319. // Load file into buffer
  2320. FileHandle_t fileHandle = g_pFileSystem->Open( g_GameLumpFilename, "rb" );
  2321. if ( fileHandle == FILESYSTEM_INVALID_HANDLE )
  2322. {
  2323. return false;
  2324. }
  2325. g_pFileSystem->Seek( fileHandle, g_GameLumpDict[i].offset, FILESYSTEM_SEEK_HEAD );
  2326. if ( !bIsCompressed )
  2327. {
  2328. // read directly into user's buffer
  2329. bool bOK = ( g_pFileSystem->Read( pOutBuffer, outSize, fileHandle ) > 0 );
  2330. g_pFileSystem->Close( fileHandle );
  2331. return bOK;
  2332. }
  2333. else
  2334. {
  2335. // data is compressed, read into temporary
  2336. pData = (byte *)malloc( dataLength );
  2337. bool bOK = ( g_pFileSystem->Read( pData, dataLength, fileHandle ) > 0 );
  2338. g_pFileSystem->Close( fileHandle );
  2339. if ( !bOK )
  2340. {
  2341. free( pData );
  2342. return false;
  2343. }
  2344. }
  2345. }
  2346. // We'll fall though to here through here if we're compressed
  2347. bool bResult = false;
  2348. if ( !CLZMA::IsCompressed( pData ) || CLZMA::GetActualSize( (unsigned char *)pData ) != g_GameLumpDict[i].uncompressedSize )
  2349. {
  2350. Warning( "Failed loading game lump %i: lump claims to be compressed but metadata does not match\n", lumpId );
  2351. }
  2352. else
  2353. {
  2354. // uncompress directly into caller's buffer
  2355. int outputLength = CLZMA::Uncompress( pData, (unsigned char *)pOutBuffer );
  2356. bResult = ( outputLength > 0 && (unsigned int)outputLength == g_GameLumpDict[i].uncompressedSize );
  2357. }
  2358. if ( !s_MapBuffer.Base() )
  2359. {
  2360. // done with temporary buffer
  2361. free( pData );
  2362. }
  2363. return bResult;
  2364. }
  2365. //-----------------------------------------------------------------------------
  2366. // Loads game lump dictionary
  2367. //-----------------------------------------------------------------------------
  2368. void Mod_LoadGameLumpDict( void )
  2369. {
  2370. CMapLoadHelper lh( LUMP_GAME_LUMP );
  2371. // FIXME: This is brittle. If we ever try to load two game lumps
  2372. // (say, in multiple BSP files), the dictionary info I store here will get whacked
  2373. g_GameLumpDict.RemoveAll();
  2374. V_strcpy_safe( g_GameLumpFilename, lh.GetMapName() );
  2375. unsigned int lhSize = (unsigned int)Max( lh.LumpSize(), 0 );
  2376. if ( lhSize >= sizeof( dgamelumpheader_t ) )
  2377. {
  2378. dgamelumpheader_t* pGameLumpHeader = (dgamelumpheader_t*)lh.LumpBase();
  2379. // Ensure (lumpsize * numlumps + headersize) doesn't overflow
  2380. const int nMaxGameLumps = ( INT_MAX - sizeof( dgamelumpheader_t ) ) / sizeof( dgamelump_t );
  2381. if ( pGameLumpHeader->lumpCount < 0 ||
  2382. pGameLumpHeader->lumpCount > nMaxGameLumps ||
  2383. sizeof( dgamelumpheader_t ) + sizeof( dgamelump_t ) * pGameLumpHeader->lumpCount > lhSize )
  2384. {
  2385. Warning( "Bogus gamelump header in map, rejecting\n" );
  2386. }
  2387. else
  2388. {
  2389. // Load in lumps
  2390. dgamelump_t* pGameLump = (dgamelump_t*)(pGameLumpHeader + 1);
  2391. for (int i = 0; i < pGameLumpHeader->lumpCount; ++i )
  2392. {
  2393. if ( pGameLump[i].fileofs >= 0 &&
  2394. (unsigned int)pGameLump[i].fileofs >= (unsigned int)lh.LumpOffset() &&
  2395. (unsigned int)pGameLump[i].fileofs < (unsigned int)lh.LumpOffset() + lhSize &&
  2396. pGameLump[i].filelen > 0 )
  2397. {
  2398. unsigned int compressedSize = 0;
  2399. if ( i + 1 < pGameLumpHeader->lumpCount &&
  2400. pGameLump[i+1].fileofs > pGameLump[i].fileofs &&
  2401. pGameLump[i+1].fileofs >= 0 &&
  2402. (unsigned int)pGameLump[i+1].fileofs <= (unsigned int)lh.LumpOffset() + lhSize )
  2403. {
  2404. compressedSize = (unsigned int)pGameLump[i+1].fileofs - (unsigned int)pGameLump[i].fileofs;
  2405. }
  2406. else
  2407. {
  2408. compressedSize = (unsigned int)lh.LumpOffset() + lhSize - (unsigned int)pGameLump[i].fileofs;
  2409. }
  2410. g_GameLumpDict.AddToTail( { pGameLump[i], compressedSize } );
  2411. }
  2412. }
  2413. }
  2414. }
  2415. }
  2416. //-----------------------------------------------------------------------------
  2417. // Re-Loads all of a model's peer data
  2418. //-----------------------------------------------------------------------------
  2419. void Mod_TouchAllData( model_t *pModel, int nServerCount )
  2420. {
  2421. double t1 = Plat_FloatTime();
  2422. MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
  2423. virtualmodel_t *pVirtualModel = g_pMDLCache->GetVirtualModel( pModel->studio );
  2424. double t2 = Plat_FloatTime();
  2425. g_flAccumulatedModelLoadTimeVirtualModel += ( t2 - t1 );
  2426. if ( pVirtualModel && nServerCount >= 1 )
  2427. {
  2428. // ensure all sub models get current count to avoid purge
  2429. // mark first to prevent re-entrant issues during possible reload
  2430. // skip self, start at children
  2431. for ( int i=1; i<pVirtualModel->m_group.Count(); ++i )
  2432. {
  2433. MDLHandle_t childHandle = (MDLHandle_t)(int)pVirtualModel->m_group[i].cache&0xffff;
  2434. model_t *pChildModel = (model_t *)g_pMDLCache->GetUserData( childHandle );
  2435. if ( pChildModel )
  2436. {
  2437. // child inherits parent reference
  2438. pChildModel->nLoadFlags |= ( pModel->nLoadFlags & IModelLoader::FMODELLOADER_REFERENCEMASK );
  2439. pChildModel->nLoadFlags |= IModelLoader::FMODELLOADER_LOADED;
  2440. pChildModel->nLoadFlags &= ~IModelLoader::FMODELLOADER_LOADED_BY_PRELOAD;
  2441. pChildModel->nServerCount = nServerCount;
  2442. }
  2443. }
  2444. }
  2445. // don't touch all the data
  2446. if ( !mod_forcetouchdata.GetBool() )
  2447. return;
  2448. g_pMDLCache->TouchAllData( pModel->studio );
  2449. }
  2450. //-----------------------------------------------------------------------------
  2451. // Callbacks to get called when various data is loaded or unloaded
  2452. //-----------------------------------------------------------------------------
  2453. class CMDLCacheNotify : public IMDLCacheNotify
  2454. {
  2455. public:
  2456. virtual void OnDataLoaded( MDLCacheDataType_t type, MDLHandle_t handle );
  2457. virtual void OnDataUnloaded( MDLCacheDataType_t type, MDLHandle_t handle );
  2458. private:
  2459. void ComputeModelFlags( model_t* mod, MDLHandle_t handle );
  2460. // Sets the bounds from the studiohdr
  2461. void SetBoundsFromStudioHdr( model_t *pModel, MDLHandle_t handle );
  2462. };
  2463. static CMDLCacheNotify s_MDLCacheNotify;
  2464. //-----------------------------------------------------------------------------
  2465. // Computes model flags
  2466. //-----------------------------------------------------------------------------
  2467. void CMDLCacheNotify::ComputeModelFlags( model_t* pModel, MDLHandle_t handle )
  2468. {
  2469. studiohdr_t *pStudioHdr = g_pMDLCache->GetStudioHdr( handle );
  2470. // Clear out those flags we set...
  2471. pModel->flags &= ~(MODELFLAG_TRANSLUCENT_TWOPASS | MODELFLAG_VERTEXLIT |
  2472. MODELFLAG_TRANSLUCENT | MODELFLAG_MATERIALPROXY | MODELFLAG_FRAMEBUFFER_TEXTURE |
  2473. MODELFLAG_STUDIOHDR_USES_FB_TEXTURE | MODELFLAG_STUDIOHDR_USES_BUMPMAPPING | MODELFLAG_STUDIOHDR_USES_ENV_CUBEMAP );
  2474. bool bForceOpaque = (pStudioHdr->flags & STUDIOHDR_FLAGS_FORCE_OPAQUE) != 0;
  2475. if ( pStudioHdr->flags & STUDIOHDR_FLAGS_TRANSLUCENT_TWOPASS )
  2476. {
  2477. pModel->flags |= MODELFLAG_TRANSLUCENT_TWOPASS;
  2478. }
  2479. if ( pStudioHdr->flags & STUDIOHDR_FLAGS_USES_FB_TEXTURE )
  2480. {
  2481. pModel->flags |= MODELFLAG_STUDIOHDR_USES_FB_TEXTURE;
  2482. }
  2483. if ( pStudioHdr->flags & STUDIOHDR_FLAGS_USES_BUMPMAPPING )
  2484. {
  2485. pModel->flags |= MODELFLAG_STUDIOHDR_USES_BUMPMAPPING;
  2486. }
  2487. if ( pStudioHdr->flags & STUDIOHDR_FLAGS_USES_ENV_CUBEMAP )
  2488. {
  2489. pModel->flags |= MODELFLAG_STUDIOHDR_USES_ENV_CUBEMAP;
  2490. }
  2491. if ( pStudioHdr->flags & STUDIOHDR_FLAGS_AMBIENT_BOOST )
  2492. {
  2493. pModel->flags |= MODELFLAG_STUDIOHDR_AMBIENT_BOOST;
  2494. }
  2495. if ( pStudioHdr->flags & STUDIOHDR_FLAGS_DO_NOT_CAST_SHADOWS )
  2496. {
  2497. pModel->flags |= MODELFLAG_STUDIOHDR_DO_NOT_CAST_SHADOWS;
  2498. }
  2499. IMaterial *pMaterials[ 128 ];
  2500. int materialCount = Mod_GetModelMaterials( pModel, ARRAYSIZE( pMaterials ), pMaterials );
  2501. for ( int i = 0; i < materialCount; ++i )
  2502. {
  2503. IMaterial *pMaterial = pMaterials[ i ];
  2504. if ( !pMaterial )
  2505. continue;
  2506. if ( pMaterial->IsVertexLit() )
  2507. {
  2508. pModel->flags |= MODELFLAG_VERTEXLIT;
  2509. }
  2510. if ( !bForceOpaque && pMaterial->IsTranslucent() )
  2511. {
  2512. //Msg("Translucent material %s for model %s\n", pLODData->ppMaterials[i]->GetName(), pModel->name );
  2513. pModel->flags |= MODELFLAG_TRANSLUCENT;
  2514. }
  2515. if ( pMaterial->HasProxy() )
  2516. {
  2517. pModel->flags |= MODELFLAG_MATERIALPROXY;
  2518. }
  2519. if ( pMaterial->NeedsPowerOfTwoFrameBufferTexture( false ) ) // The false checks if it will ever need the frame buffer, not just this frame
  2520. {
  2521. pModel->flags |= MODELFLAG_FRAMEBUFFER_TEXTURE;
  2522. }
  2523. }
  2524. }
  2525. //-----------------------------------------------------------------------------
  2526. // Sets the bounds from the studiohdr
  2527. //-----------------------------------------------------------------------------
  2528. void CMDLCacheNotify::SetBoundsFromStudioHdr( model_t *pModel, MDLHandle_t handle )
  2529. {
  2530. studiohdr_t *pStudioHdr = g_pMDLCache->GetStudioHdr( handle );
  2531. VectorCopy( pStudioHdr->hull_min, pModel->mins );
  2532. VectorCopy( pStudioHdr->hull_max, pModel->maxs );
  2533. pModel->radius = 0.0f;
  2534. for ( int i = 0; i < 3; i++ )
  2535. {
  2536. if ( fabs(pModel->mins[i]) > pModel->radius )
  2537. {
  2538. pModel->radius = fabs(pModel->mins[i]);
  2539. }
  2540. if ( fabs(pModel->maxs[i]) > pModel->radius )
  2541. {
  2542. pModel->radius = fabs(pModel->maxs[i]);
  2543. }
  2544. }
  2545. }
  2546. //-----------------------------------------------------------------------------
  2547. // Callbacks to get called when various data is loaded or unloaded
  2548. //-----------------------------------------------------------------------------
  2549. void CMDLCacheNotify::OnDataLoaded( MDLCacheDataType_t type, MDLHandle_t handle )
  2550. {
  2551. model_t *pModel = (model_t*)g_pMDLCache->GetUserData( handle );
  2552. // NOTE: A NULL model can occur for dependent MDLHandle_ts (like .ani files)
  2553. if ( !pModel )
  2554. return;
  2555. switch( type )
  2556. {
  2557. case MDLCACHE_STUDIOHDR:
  2558. {
  2559. // FIXME: This code only works because it assumes StudioHdr
  2560. // is loaded before VCollide.
  2561. SetBoundsFromStudioHdr( pModel, handle );
  2562. }
  2563. break;
  2564. case MDLCACHE_VCOLLIDE:
  2565. {
  2566. SetBoundsFromStudioHdr( pModel, handle );
  2567. // Expand the model bounds to enclose the collision model (should be done in studiomdl)
  2568. vcollide_t *pCollide = g_pMDLCache->GetVCollide( handle );
  2569. if ( pCollide )
  2570. {
  2571. Vector mins, maxs;
  2572. physcollision->CollideGetAABB( &mins, &maxs, pCollide->solids[0], vec3_origin, vec3_angle );
  2573. AddPointToBounds( mins, pModel->mins, pModel->maxs );
  2574. AddPointToBounds( maxs, pModel->mins, pModel->maxs );
  2575. }
  2576. }
  2577. break;
  2578. case MDLCACHE_STUDIOHWDATA:
  2579. ComputeModelFlags( pModel, handle );
  2580. break;
  2581. }
  2582. }
  2583. void CMDLCacheNotify::OnDataUnloaded( MDLCacheDataType_t type, MDLHandle_t handle )
  2584. {
  2585. }
  2586. //-----------------------------------------------------------------------------
  2587. // Hooks the cache notify into the MDL cache system
  2588. //-----------------------------------------------------------------------------
  2589. void ConnectMDLCacheNotify( )
  2590. {
  2591. g_pMDLCache->SetCacheNotify( &s_MDLCacheNotify );
  2592. }
  2593. void DisconnectMDLCacheNotify( )
  2594. {
  2595. g_pMDLCache->SetCacheNotify( NULL );
  2596. }
  2597. //-----------------------------------------------------------------------------
  2598. // Initialize studiomdl state
  2599. //-----------------------------------------------------------------------------
  2600. void InitStudioModelState( model_t *pModel )
  2601. {
  2602. Assert( pModel->type == mod_studio );
  2603. if ( g_pMDLCache->IsDataLoaded( pModel->studio, MDLCACHE_STUDIOHDR ) )
  2604. {
  2605. s_MDLCacheNotify.OnDataLoaded( MDLCACHE_STUDIOHDR, pModel->studio );
  2606. }
  2607. if ( g_pMDLCache->IsDataLoaded( pModel->studio, MDLCACHE_STUDIOHWDATA ) )
  2608. {
  2609. s_MDLCacheNotify.OnDataLoaded( MDLCACHE_STUDIOHWDATA, pModel->studio );
  2610. }
  2611. if ( g_pMDLCache->IsDataLoaded( pModel->studio, MDLCACHE_VCOLLIDE ) )
  2612. {
  2613. s_MDLCacheNotify.OnDataLoaded( MDLCACHE_VCOLLIDE, pModel->studio );
  2614. }
  2615. }
  2616. //-----------------------------------------------------------------------------
  2617. // Resource loading for models
  2618. //-----------------------------------------------------------------------------
  2619. class CResourcePreloadModel : public CResourcePreload
  2620. {
  2621. static void QueuedLoaderMapCallback( void *pContext, void *pContext2, const void *pData, int nSize, LoaderError_t loaderError )
  2622. {
  2623. if ( loaderError == LOADERERROR_NONE )
  2624. {
  2625. // 360 mounts its bsp entirely into memory
  2626. // this data is discarded at the conclusion of the entire load process
  2627. Assert( CMapLoadHelper::GetRefCount() == 0 );
  2628. CMapLoadHelper::InitFromMemory( (model_t *)pContext, pData, nSize );
  2629. }
  2630. }
  2631. virtual bool CreateResource( const char *pName )
  2632. {
  2633. modtype_t modType = g_ModelLoader.GetTypeFromName( pName );
  2634. // each model type resource has entirely differnt schemes for loading/creating
  2635. if ( modType == mod_brush )
  2636. {
  2637. // expect to be the map bsp model
  2638. MEM_ALLOC_CREDIT_( "CResourcePreloadModel(BSP)" );
  2639. model_t *pMapModel = g_ModelLoader.FindModelNoCreate( pName );
  2640. if ( pMapModel )
  2641. {
  2642. Assert( CMapLoadHelper::GetRefCount() == 0 );
  2643. // 360 reads its specialized bsp into memory,
  2644. // up to the pack lump, which is guranateed last
  2645. char szLoadName[MAX_PATH];
  2646. V_FileBase( pMapModel->strName, szLoadName, sizeof( szLoadName ) );
  2647. CMapLoadHelper::Init( pMapModel, szLoadName );
  2648. int nBytesToRead = CMapLoadHelper::LumpOffset( LUMP_PAKFILE );
  2649. CMapLoadHelper::Shutdown();
  2650. // create a loader job to perform i/o operation to mount the .bsp
  2651. LoaderJob_t loaderJobBSP;
  2652. loaderJobBSP.m_pFilename = pMapModel->strName;
  2653. loaderJobBSP.m_pPathID = "GAME";
  2654. loaderJobBSP.m_pCallback = QueuedLoaderMapCallback;
  2655. loaderJobBSP.m_pContext = (void *)pMapModel;
  2656. loaderJobBSP.m_pTargetData = malloc( nBytesToRead );
  2657. loaderJobBSP.m_nBytesToRead = nBytesToRead;
  2658. loaderJobBSP.m_Priority = LOADERPRIORITY_DURINGPRELOAD;
  2659. g_pQueuedLoader->AddJob( &loaderJobBSP );
  2660. // create an anonymous job to perform i/o operation to mount the .ain
  2661. // the .ain gets claimed later
  2662. char szAINName[MAX_PATH] = { 0 };
  2663. V_snprintf( szAINName, sizeof( szAINName ), "maps/graphs/%s.360.ain", szLoadName );
  2664. LoaderJob_t loaderJobAIN;
  2665. loaderJobAIN.m_pFilename = szAINName;
  2666. loaderJobAIN.m_pPathID = "GAME";
  2667. loaderJobAIN.m_Priority = LOADERPRIORITY_DURINGPRELOAD;
  2668. g_pQueuedLoader->AddJob( &loaderJobAIN );
  2669. return true;
  2670. }
  2671. }
  2672. else if ( modType == mod_studio )
  2673. {
  2674. MEM_ALLOC_CREDIT_( "CResourcePreloadModel(MDL)" );
  2675. char szFilename[MAX_PATH];
  2676. V_ComposeFileName( "models", pName, szFilename, sizeof( szFilename ) );
  2677. // find model or create empty entry
  2678. model_t *pModel = g_ModelLoader.FindModel( szFilename );
  2679. // mark as touched
  2680. pModel->nLoadFlags |= IModelLoader::FMODELLOADER_TOUCHED_BY_PRELOAD;
  2681. if ( pModel->nLoadFlags & ( IModelLoader::FMODELLOADER_LOADED|IModelLoader::FMODELLOADER_LOADED_BY_PRELOAD ) )
  2682. {
  2683. // already loaded or preloaded
  2684. return true;
  2685. }
  2686. // the model in not supposed to be in memory
  2687. Assert( pModel->type == mod_bad );
  2688. // set its type
  2689. pModel->type = mod_studio;
  2690. // mark the model so that the normal studio load path can perform a final fixup
  2691. pModel->nLoadFlags |= IModelLoader::FMODELLOADER_LOADED_BY_PRELOAD;
  2692. // setup the new entry for preload to operate
  2693. pModel->studio = g_pMDLCache->FindMDL( pModel->strName );
  2694. // the model is not supposed to be in memory
  2695. // if this hits, the mdlcache is out of sync with the modelloder
  2696. // if this hits, the mdlcache has the model, but the modelloader doesn't think so
  2697. // if the refcounts go haywire, bad evil bugs will occur
  2698. Assert( g_pMDLCache->GetRef( pModel->studio ) == 1 );
  2699. g_pMDLCache->SetUserData( pModel->studio, pModel );
  2700. // get it into the cache
  2701. g_pMDLCache->PreloadModel( pModel->studio );
  2702. return true;
  2703. }
  2704. // unknown
  2705. return false;
  2706. }
  2707. //-----------------------------------------------------------------------------
  2708. // Called before queued loader i/o jobs are actually performed. Must free up memory
  2709. // to ensure i/o requests have enough memory to succeed. The models that were
  2710. // touched by the CreateResource() are the ones to keep, all others get purged.
  2711. //-----------------------------------------------------------------------------
  2712. virtual void PurgeUnreferencedResources()
  2713. {
  2714. bool bSpew = ( g_pQueuedLoader->GetSpewDetail() & LOADER_DETAIL_PURGES ) != 0;
  2715. // purge any model that was not touched by the preload process
  2716. int iIndex = -1;
  2717. CUtlVector< model_t* > firstList;
  2718. CUtlVector< model_t* > otherList;
  2719. for ( ;; )
  2720. {
  2721. model_t *pModel;
  2722. iIndex = g_ModelLoader.FindNext( iIndex, &pModel );
  2723. if ( iIndex == -1 || !pModel )
  2724. {
  2725. // end of list
  2726. break;
  2727. }
  2728. if ( pModel->type == mod_studio )
  2729. {
  2730. // models that were touched during the preload stay, otherwise purged
  2731. if ( pModel->nLoadFlags & IModelLoader::FMODELLOADER_TOUCHED_BY_PRELOAD )
  2732. {
  2733. pModel->nLoadFlags &= ~IModelLoader::FMODELLOADER_TOUCHED_BY_PRELOAD;
  2734. }
  2735. else
  2736. {
  2737. if ( bSpew )
  2738. {
  2739. Msg( "CResourcePreloadModel: Purging: %s\n", pModel->strName.String() );
  2740. }
  2741. // Models that have virtual models have to unload first to
  2742. // ensure they properly unreference their virtual models.
  2743. if ( g_pMDLCache->IsDataLoaded( pModel->studio, MDLCACHE_VIRTUALMODEL ) )
  2744. {
  2745. firstList.AddToTail( pModel );
  2746. }
  2747. else
  2748. {
  2749. otherList.AddToTail( pModel );
  2750. }
  2751. }
  2752. }
  2753. }
  2754. for ( int i=0; i<firstList.Count(); i++ )
  2755. {
  2756. g_ModelLoader.UnloadModel( firstList[i] );
  2757. }
  2758. for ( int i=0; i<otherList.Count(); i++ )
  2759. {
  2760. g_ModelLoader.UnloadModel( otherList[i] );
  2761. }
  2762. if ( !g_pQueuedLoader->IsSameMapLoading() )
  2763. {
  2764. g_pMDLCache->Flush( MDLCACHE_FLUSH_ANIMBLOCK );
  2765. }
  2766. }
  2767. virtual void PurgeAll()
  2768. {
  2769. bool bSpew = ( g_pQueuedLoader->GetSpewDetail() & LOADER_DETAIL_PURGES ) != 0;
  2770. // purge any model that was not touched by the preload process
  2771. int iIndex = -1;
  2772. CUtlVector< model_t* > firstList;
  2773. CUtlVector< model_t* > otherList;
  2774. for ( ;; )
  2775. {
  2776. model_t *pModel;
  2777. iIndex = g_ModelLoader.FindNext( iIndex, &pModel );
  2778. if ( iIndex == -1 || !pModel )
  2779. {
  2780. // end of list
  2781. break;
  2782. }
  2783. if ( pModel->type == mod_studio )
  2784. {
  2785. pModel->nLoadFlags &= ~IModelLoader::FMODELLOADER_TOUCHED_BY_PRELOAD;
  2786. if ( bSpew )
  2787. {
  2788. Msg( "CResourcePreloadModel: Purging: %s\n", pModel->strName.String() );
  2789. }
  2790. // Models that have virtual models have to unload first to
  2791. // ensure they properly unreference their virtual models.
  2792. if ( g_pMDLCache->IsDataLoaded( pModel->studio, MDLCACHE_VIRTUALMODEL ) )
  2793. {
  2794. firstList.AddToTail( pModel );
  2795. }
  2796. else
  2797. {
  2798. otherList.AddToTail( pModel );
  2799. }
  2800. }
  2801. }
  2802. for ( int i=0; i<firstList.Count(); i++ )
  2803. {
  2804. g_ModelLoader.UnloadModel( firstList[i] );
  2805. }
  2806. for ( int i=0; i<otherList.Count(); i++ )
  2807. {
  2808. g_ModelLoader.UnloadModel( otherList[i] );
  2809. }
  2810. g_pMDLCache->Flush( MDLCACHE_FLUSH_ANIMBLOCK );
  2811. }
  2812. virtual void OnEndMapLoading( bool bAbort )
  2813. {
  2814. // discard the memory mounted bsp
  2815. CMapLoadHelper::Shutdown();
  2816. Assert( CMapLoadHelper::GetRefCount() == 0 );
  2817. }
  2818. };
  2819. static CResourcePreloadModel s_ResourcePreloadModel;
  2820. //-----------------------------------------------------------------------------
  2821. // Purpose:
  2822. //-----------------------------------------------------------------------------
  2823. void CModelLoader::Init( void )
  2824. {
  2825. m_Models.RemoveAll();
  2826. m_InlineModels.Purge();
  2827. m_pWorldModel = NULL;
  2828. m_bMapRenderInfoLoaded = false;
  2829. m_bMapHasHDRLighting = false;
  2830. g_bLoadedMapHasBakedPropLighting = false;
  2831. // Make sure we have physcollision and physprop interfaces
  2832. CollisionBSPData_LinkPhysics();
  2833. m_szActiveMapName[0] = '\0';
  2834. g_pQueuedLoader->InstallLoader( RESOURCEPRELOAD_MODEL, &s_ResourcePreloadModel );
  2835. }
  2836. //-----------------------------------------------------------------------------
  2837. // Purpose:
  2838. //-----------------------------------------------------------------------------
  2839. void CModelLoader::Shutdown( void )
  2840. {
  2841. m_pWorldModel = NULL;
  2842. ForceUnloadNonClientDynamicModels();
  2843. UnloadAllModels( false );
  2844. m_ModelPool.Clear();
  2845. }
  2846. int CModelLoader::GetCount( void )
  2847. {
  2848. Assert( m_Models.Count() == m_Models.MaxElement() );
  2849. return m_Models.Count();
  2850. }
  2851. model_t *CModelLoader::GetModelForIndex( int i )
  2852. {
  2853. if ( i < 0 || (unsigned)i >= m_Models.Count() )
  2854. {
  2855. Assert( !m_Models.IsValidIndex( i ) );
  2856. return NULL;
  2857. }
  2858. Assert( m_Models.IsValidIndex( i ) );
  2859. return m_Models[i].modelpointer;
  2860. }
  2861. //-----------------------------------------------------------------------------
  2862. // Purpose: Look up name for model
  2863. // Input : *model -
  2864. // Output : const char
  2865. //-----------------------------------------------------------------------------
  2866. const char *CModelLoader::GetName( const model_t *pModel )
  2867. {
  2868. if ( pModel )
  2869. {
  2870. return pModel->strName;
  2871. }
  2872. return NULL;
  2873. }
  2874. //-----------------------------------------------------------------------------
  2875. // Purpose: Finds the model, builds entry if not present, always returns a model
  2876. // Input : *name -
  2877. // referencetype -
  2878. // Output : model_t
  2879. //-----------------------------------------------------------------------------
  2880. model_t *CModelLoader::FindModel( const char *pName )
  2881. {
  2882. if ( !pName || !pName[0] )
  2883. {
  2884. Sys_Error( "CModelLoader::FindModel: NULL name" );
  2885. }
  2886. // inline models are grabbed only from worldmodel
  2887. if ( pName[0] == '*' )
  2888. {
  2889. int modelNum = atoi( pName + 1 );
  2890. if ( !IsWorldModelSet() )
  2891. {
  2892. Sys_Error( "bad inline model number %i, worldmodel not yet setup", modelNum );
  2893. }
  2894. if ( modelNum < 1 || modelNum >= GetNumWorldSubmodels() )
  2895. {
  2896. Sys_Error( "bad inline model number %i", modelNum );
  2897. }
  2898. return &m_InlineModels[modelNum];
  2899. }
  2900. model_t *pModel = NULL;
  2901. // get a handle suitable to use as the model key
  2902. // handles are insensitive to case and slashes
  2903. FileNameHandle_t fnHandle = g_pFileSystem->FindOrAddFileName( pName );
  2904. int i = m_Models.Find( fnHandle );
  2905. if ( i == m_Models.InvalidIndex() )
  2906. {
  2907. pModel = (model_t *)m_ModelPool.Alloc();
  2908. Assert( pModel );
  2909. memset( pModel, 0, sizeof( model_t ) );
  2910. pModel->fnHandle = fnHandle;
  2911. // Mark that we should load from disk
  2912. pModel->nLoadFlags = FMODELLOADER_NOTLOADEDORREFERENCED;
  2913. // Copy in name and normalize!
  2914. // Various other subsystems fetch this 'object' name to do dictionary lookups,
  2915. // which are usually case insensitive, but not to slashes or dotslashes.
  2916. pModel->strName = pName;
  2917. V_RemoveDotSlashes( pModel->strName.GetForModify(), '/' );
  2918. ModelEntry_t entry;
  2919. entry.modelpointer = pModel;
  2920. m_Models.Insert( fnHandle, entry );
  2921. }
  2922. else
  2923. {
  2924. pModel = m_Models[i].modelpointer;
  2925. }
  2926. // notify the reslist generator that this model may be referenced later in the level
  2927. // (does nothing if reslist generation is not enabled)
  2928. MapReslistGenerator().OnModelPrecached( pName );
  2929. Assert( pModel );
  2930. return pModel;
  2931. }
  2932. //-----------------------------------------------------------------------------
  2933. // Purpose: Finds the model, and loads it if it isn't already present. Updates reference flags
  2934. // Input : *name -
  2935. // referencetype -
  2936. // Output : model_t
  2937. //-----------------------------------------------------------------------------
  2938. model_t *CModelLoader::GetModelForName( const char *name, REFERENCETYPE referencetype )
  2939. {
  2940. AssertMsg( !(referencetype & FMODELLOADER_DYNAMIC), "GetModelForName: dynamic models must use GetDynamicModel" );
  2941. // find or build new entry
  2942. model_t *model = FindModel( name );
  2943. // touch and load if not present
  2944. model_t *retval = LoadModel( model, &referencetype );
  2945. return retval;
  2946. }
  2947. //-----------------------------------------------------------------------------
  2948. // Purpose: Add a reference to the model in question
  2949. // Input : *name -
  2950. // referencetype -
  2951. //-----------------------------------------------------------------------------
  2952. model_t *CModelLoader::ReferenceModel( const char *name, REFERENCETYPE referencetype )
  2953. {
  2954. AssertMsg( !(referencetype & FMODELLOADER_DYNAMIC), "ReferenceModel: do not use for dynamic models" );
  2955. model_t *model = FindModel( name );
  2956. model->nLoadFlags |= referencetype;
  2957. return model;
  2958. }
  2959. //-----------------------------------------------------------------------------
  2960. // Purpose:
  2961. // Input : *entry -
  2962. // referencetype -
  2963. //-----------------------------------------------------------------------------
  2964. model_t *CModelLoader::LoadModel( model_t *mod, REFERENCETYPE *pReferencetype )
  2965. {
  2966. if ( pReferencetype )
  2967. {
  2968. mod->nLoadFlags |= *pReferencetype;
  2969. }
  2970. // during initial load mark the model with an unique session ticket
  2971. // at load end, models that have a mismatch count are considered candidates for purge
  2972. // models that get marked, touch *all* their sub data to ensure the cache is pre-populated
  2973. // and hitches less during gameplay
  2974. bool bTouchAllData = false;
  2975. int nServerCount = Host_GetServerCount();
  2976. if ( mod->nServerCount != nServerCount )
  2977. {
  2978. // server has changed
  2979. mod->nServerCount = nServerCount;
  2980. bTouchAllData = true;
  2981. }
  2982. // Check if the studio model is in cache.
  2983. // The model type will not be set for first time models that need to fall through to the load path.
  2984. // A model that needs a post precache fixup will fall through to the load path.
  2985. if ( mod->type == mod_studio && !( mod->nLoadFlags & FMODELLOADER_LOADED_BY_PRELOAD ) )
  2986. {
  2987. // in cache
  2988. Verify( g_pMDLCache->GetStudioHdr( mod->studio ) != 0 );
  2989. Assert( FMODELLOADER_LOADED & mod->nLoadFlags );
  2990. if ( bTouchAllData )
  2991. {
  2992. // Touch all related .ani files and sub/dependent models
  2993. // only touches once, when server changes
  2994. Mod_TouchAllData( mod, nServerCount );
  2995. }
  2996. return mod;
  2997. }
  2998. // Check if brushes or sprites are loaded
  2999. if ( FMODELLOADER_LOADED & mod->nLoadFlags )
  3000. {
  3001. return mod;
  3002. }
  3003. // model needs to be loaded
  3004. double st = Plat_FloatTime();
  3005. // Set the name of the current model we are loading
  3006. Q_FileBase( mod->strName, m_szLoadName, sizeof( m_szLoadName ) );
  3007. // load the file
  3008. if ( developer.GetInt() > 1 )
  3009. {
  3010. DevMsg( "Loading: %s\n", mod->strName.String() );
  3011. }
  3012. mod->type = GetTypeFromName( mod->strName );
  3013. if ( mod->type == mod_bad )
  3014. {
  3015. mod->type = mod_studio;
  3016. }
  3017. // finalize the model data
  3018. switch ( mod->type )
  3019. {
  3020. case mod_sprite:
  3021. {
  3022. MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
  3023. double t1 = Plat_FloatTime();
  3024. Sprite_LoadModel( mod );
  3025. double t2 = Plat_FloatTime();
  3026. g_flAccumulatedModelLoadTimeSprite += ( t2 - t1 );
  3027. }
  3028. break;
  3029. case mod_studio:
  3030. {
  3031. MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
  3032. double t1 = Plat_FloatTime();
  3033. Studio_LoadModel( mod, bTouchAllData );
  3034. double t2 = Plat_FloatTime();
  3035. g_flAccumulatedModelLoadTimeStudio += ( t2 - t1 );
  3036. }
  3037. break;
  3038. case mod_brush:
  3039. {
  3040. double t1 = Plat_FloatTime();
  3041. // This is necessary on dedicated clients. On listen + dedicated servers, it's called twice.
  3042. // The second invocation is harmless.
  3043. // Add to file system before loading so referenced objects in map can use the filename.
  3044. g_pFileSystem->AddSearchPath( mod->strName, "GAME", PATH_ADD_TO_HEAD );
  3045. // the map may have explicit texture exclusion
  3046. // the texture state needs to be established before any loading work
  3047. if ( IsX360() || mat_excludetextures.GetBool() )
  3048. {
  3049. char szExcludePath[MAX_PATH];
  3050. sprintf( szExcludePath, "//MOD/maps/%s_exclude.lst", m_szLoadName );
  3051. g_pMaterialSystem->SetExcludedTextures( szExcludePath );
  3052. }
  3053. // need this before queued loader starts, various systems use this as a cheap map changed state
  3054. V_strncpy( m_szActiveMapName, mod->strName, sizeof( m_szActiveMapName ) );
  3055. //NotifyHunkBeginMapLoad( m_szActiveMapName );
  3056. bool bQueuedLoader = false;
  3057. if ( IsX360() )
  3058. {
  3059. // must establish the bsp feature set first to ensure proper state during queued loading
  3060. Map_CheckForHDR( mod, m_szLoadName );
  3061. // Do not optimize map-to-same-map loading in TF
  3062. // FIXME/HACK: this fixes a bug (when shipping Orange Box) where static props would sometimes
  3063. // disappear when a client disconnects and reconnects to the same map+server
  3064. // (static prop lighting data persists when loading map A after map A)
  3065. bool bIsTF = !V_stricmp( COM_GetModDirectory(), "tf" );
  3066. bool bOptimizeMapReload = !bIsTF;
  3067. // start the queued loading process
  3068. bQueuedLoader = g_pQueuedLoader->BeginMapLoading( mod->strName, g_pMaterialSystemHardwareConfig->GetHDREnabled(), bOptimizeMapReload );
  3069. }
  3070. // the queued loader process needs to own the actual texture update
  3071. if ( !bQueuedLoader && ( IsX360() || mat_excludetextures.GetBool() ) )
  3072. {
  3073. g_pMaterialSystem->UpdateExcludedTextures();
  3074. }
  3075. BeginLoadingUpdates( MATERIAL_NON_INTERACTIVE_MODE_LEVEL_LOAD );
  3076. g_pFileSystem->BeginMapAccess();
  3077. Map_LoadModel( mod );
  3078. g_pFileSystem->EndMapAccess();
  3079. double t2 = Plat_FloatTime();
  3080. g_flAccumulatedModelLoadTimeBrush += (t2 - t1);
  3081. }
  3082. break;
  3083. default:
  3084. Assert( 0 );
  3085. break;
  3086. };
  3087. float dt = ( Plat_FloatTime() - st );
  3088. COM_TimestampedLog( "Load of %s took %.3f msec", mod->strName.String(), 1000.0f * dt );
  3089. g_flAccumulatedModelLoadTime += dt;
  3090. return mod;
  3091. }
  3092. //-----------------------------------------------------------------------------
  3093. // Purpose: Creates the name of the sprite
  3094. //-----------------------------------------------------------------------------
  3095. //static void BuildSpriteLoadName( const char *pName, char *pOut, int outLen, bool &bIsAVI, bool &bIsBIK )
  3096. static void BuildSpriteLoadName( const char *pName, char *pOut, int outLen, bool &bIsVideo )
  3097. {
  3098. // If it's a .vmt and they put a path in there, then use the path.
  3099. // Otherwise, use the old method of prepending the sprites directory.
  3100. Assert( pName != NULL && pOut != NULL );
  3101. bIsVideo = false;
  3102. bool bIsVMT = false;
  3103. const char *pExt = V_GetFileExtension( pName );
  3104. if ( pExt != NULL )
  3105. {
  3106. bIsVMT = !Q_stricmp( pExt, "vmt" );
  3107. if ( !bIsVMT )
  3108. {
  3109. if ( g_pVideo )
  3110. {
  3111. bIsVideo = ( g_pVideo->LocateVideoSystemForPlayingFile( pName ) != VideoSystem::NONE );
  3112. }
  3113. }
  3114. }
  3115. if ( ( bIsVideo || bIsVMT ) && ( strchr( pName, '/' ) || strchr( pName, '\\' ) ) )
  3116. {
  3117. // The material system cannot handle a prepended "materials" dir
  3118. // Keep .avi extensions on the material to load avi-based materials
  3119. if ( bIsVMT )
  3120. {
  3121. const char *pNameStart = pName;
  3122. if ( Q_stristr( pName, "materials/" ) == pName ||
  3123. Q_stristr( pName, "materials\\" ) == pName )
  3124. {
  3125. // skip past materials/
  3126. pNameStart = &pName[10];
  3127. }
  3128. Q_StripExtension( pNameStart, pOut, outLen );
  3129. }
  3130. else
  3131. {
  3132. // name is good as is
  3133. Q_strncpy( pOut, pName, outLen );
  3134. }
  3135. }
  3136. else
  3137. {
  3138. char szBase[MAX_PATH];
  3139. Q_FileBase( pName, szBase, sizeof( szBase ) );
  3140. Q_snprintf( pOut, outLen, "sprites/%s", szBase );
  3141. }
  3142. return;
  3143. }
  3144. //-----------------------------------------------------------------------------
  3145. // Purpose:
  3146. // Input : *name -
  3147. // Output : int
  3148. //-----------------------------------------------------------------------------
  3149. int CModelLoader::GetModelFileSize( char const *name )
  3150. {
  3151. if ( !name || !name[ 0 ] )
  3152. return -1;
  3153. model_t *model = FindModel( name );
  3154. int size = -1;
  3155. if ( Q_stristr( model->strName, ".spr" ) || Q_stristr( model->strName, ".vmt" ) )
  3156. {
  3157. char spritename[ MAX_PATH ];
  3158. Q_StripExtension( va( "materials/%s", model->strName.String() ), spritename, MAX_PATH );
  3159. Q_DefaultExtension( spritename, ".vmt", sizeof( spritename ) );
  3160. size = COM_FileSize( spritename );
  3161. }
  3162. else
  3163. {
  3164. size = COM_FileSize( name );
  3165. }
  3166. return size;
  3167. }
  3168. //-----------------------------------------------------------------------------
  3169. // Purpose: Unmasks the referencetype field for the model
  3170. // Input : *model -
  3171. // referencetype -
  3172. //-----------------------------------------------------------------------------
  3173. void CModelLoader::UnreferenceModel( model_t *model, REFERENCETYPE referencetype )
  3174. {
  3175. AssertMsg( !(referencetype & FMODELLOADER_DYNAMIC), "UnreferenceModel: do not use for dynamic models" );
  3176. model->nLoadFlags &= ~referencetype;
  3177. }
  3178. //-----------------------------------------------------------------------------
  3179. // Purpose: Unmasks the specified reference type across all models
  3180. // Input : referencetype -
  3181. //-----------------------------------------------------------------------------
  3182. void CModelLoader::UnreferenceAllModels( REFERENCETYPE referencetype )
  3183. {
  3184. AssertMsg( !(referencetype & FMODELLOADER_DYNAMIC), "UnreferenceAllModels: do not use for dynamic models" );
  3185. // UNDONE: If we ever free a studio model, write code to free the collision data
  3186. // UNDONE: Reference count collision data?
  3187. FOR_EACH_MAP_FAST( m_Models, i )
  3188. {
  3189. m_Models[ i ].modelpointer->nLoadFlags &= ~referencetype;
  3190. }
  3191. }
  3192. //-----------------------------------------------------------------------------
  3193. // Purpose: When changing servers the old servercount number is bogus. This
  3194. // marks all models as loaded from -1 (e.g. a server count from the
  3195. // before time.)
  3196. //-----------------------------------------------------------------------------
  3197. void CModelLoader::ResetModelServerCounts()
  3198. {
  3199. FOR_EACH_MAP_FAST( m_Models, i )
  3200. {
  3201. model_t *pModel = m_Models[i].modelpointer;
  3202. pModel->nServerCount = -1;
  3203. }
  3204. }
  3205. void CModelLoader::ReloadFilesInList( IFileList *pFilesToReload )
  3206. {
  3207. FOR_EACH_MAP_FAST( m_Models, i )
  3208. {
  3209. model_t *pModel = m_Models[i].modelpointer;
  3210. if ( pModel->type != mod_studio )
  3211. continue;
  3212. if ( !IsLoaded( pModel ) )
  3213. continue;
  3214. if ( pModel->type != mod_studio )
  3215. continue;
  3216. if ( pFilesToReload->IsFileInList( pModel->strName ) )
  3217. {
  3218. #ifdef PURE_SERVER_DEBUG_SPEW
  3219. Msg( "Reloading model %s\n", pModel->strName.String() );
  3220. #endif
  3221. // Flush out the model cache
  3222. // Don't flush vcollides since the vphysics system currently
  3223. // has no way of indicating they refer to vcollides
  3224. g_pMDLCache->Flush( pModel->studio, (int)(MDLCACHE_FLUSH_ALL & (~MDLCACHE_FLUSH_VCOLLIDE)) );
  3225. MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
  3226. // Get the studiohdr into the cache
  3227. g_pMDLCache->GetStudioHdr( pModel->studio );
  3228. #ifndef _XBOX
  3229. // force the collision to load
  3230. g_pMDLCache->GetVCollide( pModel->studio );
  3231. #endif
  3232. }
  3233. else
  3234. {
  3235. if ( g_pMDLCache->IsDataLoaded( pModel->studio, MDLCACHE_STUDIOHWDATA ) )
  3236. {
  3237. studiohdr_t *pStudioHdr = g_pMDLCache->GetStudioHdr( pModel->studio );
  3238. if ( pStudioHdr )
  3239. {
  3240. // Ok, we didn't have to do a full reload, but if any of our materials changed, flush out the studiohwdata because the
  3241. // vertex format may have changed.
  3242. IMaterial *pMaterials[128];
  3243. int nMaterials = g_pStudioRender->GetMaterialList( pStudioHdr, ARRAYSIZE( pMaterials ), &pMaterials[0] );
  3244. for ( int iMat=0; iMat < nMaterials; iMat++ )
  3245. {
  3246. if ( pMaterials[iMat] && pMaterials[iMat]->WasReloadedFromWhitelist() )
  3247. {
  3248. #ifdef PURE_SERVER_DEBUG_SPEW
  3249. Msg( "Reloading model %s because material %s was reloaded\n", pModel->strName.String(), pMaterials[iMat]->GetName() );
  3250. #endif
  3251. g_pMDLCache->Flush( pModel->studio, MDLCACHE_FLUSH_STUDIOHWDATA );
  3252. break;
  3253. }
  3254. }
  3255. }
  3256. }
  3257. }
  3258. }
  3259. }
  3260. //-----------------------------------------------------------------------------
  3261. // Purpose: For any models with referencetype blank (if checking), frees all memory associated with the model
  3262. // and frees up the models slot
  3263. //-----------------------------------------------------------------------------
  3264. void CModelLoader::UnloadAllModels( bool bCheckReference )
  3265. {
  3266. model_t *model;
  3267. FOR_EACH_MAP_FAST( m_Models, i )
  3268. {
  3269. model = m_Models[ i ].modelpointer;
  3270. if ( bCheckReference )
  3271. {
  3272. if ( model->nLoadFlags & FMODELLOADER_REFERENCEMASK )
  3273. {
  3274. if ( model->type == mod_studio )
  3275. {
  3276. g_pMDLCache->MarkAsLoaded(model->studio);
  3277. }
  3278. continue;
  3279. }
  3280. }
  3281. else
  3282. {
  3283. // Wipe current flags
  3284. model->nLoadFlags &= ~FMODELLOADER_REFERENCEMASK;
  3285. }
  3286. if ( IsX360() && g_pQueuedLoader->IsMapLoading() && ( model->nLoadFlags & FMODELLOADER_LOADED_BY_PRELOAD ) )
  3287. {
  3288. // models preloaded by the queued loader are not initially claimed and MUST remain until the end of the load process
  3289. // unclaimed models get unloaded during the post load purge
  3290. continue;
  3291. }
  3292. if ( model->nLoadFlags & ( FMODELLOADER_LOADED | FMODELLOADER_LOADED_BY_PRELOAD ) )
  3293. {
  3294. UnloadModel( model );
  3295. }
  3296. }
  3297. }
  3298. //-----------------------------------------------------------------------------
  3299. // Purpose: For any models with referencetype blank (if checking), frees all memory associated with the model
  3300. // and frees up the models slot
  3301. //-----------------------------------------------------------------------------
  3302. void CModelLoader::UnloadUnreferencedModels( void )
  3303. {
  3304. // unload all unreferenced models
  3305. UnloadAllModels( true );
  3306. }
  3307. //-----------------------------------------------------------------------------
  3308. // Called at the conclusion of loading.
  3309. // Frees all memory associated with models (and their materials) that are not
  3310. // marked with the current session.
  3311. //-----------------------------------------------------------------------------
  3312. void CModelLoader::PurgeUnusedModels( void )
  3313. {
  3314. int nServerCount = Host_GetServerCount();
  3315. FOR_EACH_MAP_FAST( m_Models, i )
  3316. {
  3317. model_t *pModel = m_Models[i].modelpointer;
  3318. if ( ( pModel->nLoadFlags & FMODELLOADER_LOADED ) && ( pModel->nServerCount != nServerCount ) )
  3319. {
  3320. // mark as unreferenced
  3321. // do not unload dynamic models
  3322. pModel->nLoadFlags &= (~FMODELLOADER_REFERENCEMASK) | FMODELLOADER_DYNAMIC;
  3323. }
  3324. }
  3325. // flush dynamic models that have no refcount
  3326. FlushDynamicModels();
  3327. // unload unreferenced models only
  3328. UnloadAllModels( true );
  3329. // now purge unreferenced materials
  3330. materials->UncacheUnusedMaterials( true );
  3331. }
  3332. //-----------------------------------------------------------------------------
  3333. // Compute whether this submodel uses material proxies or not
  3334. //-----------------------------------------------------------------------------
  3335. static void Mod_ComputeBrushModelFlags( model_t *mod )
  3336. {
  3337. Assert( mod );
  3338. worldbrushdata_t *pBrushData = mod->brush.pShared;
  3339. // Clear out flags we're going to set
  3340. mod->flags &= ~(MODELFLAG_MATERIALPROXY | MODELFLAG_TRANSLUCENT | MODELFLAG_FRAMEBUFFER_TEXTURE | MODELFLAG_TRANSLUCENT_TWOPASS );
  3341. mod->flags = MODELFLAG_HAS_DLIGHT; // force this check the first time
  3342. int i;
  3343. int scount = mod->brush.nummodelsurfaces;
  3344. bool bHasOpaqueSurfaces = false;
  3345. bool bHasTranslucentSurfaces = false;
  3346. for ( i = 0; i < scount; ++i )
  3347. {
  3348. SurfaceHandle_t surfID = SurfaceHandleFromIndex( mod->brush.firstmodelsurface + i, pBrushData );
  3349. // Clear out flags we're going to set
  3350. MSurf_Flags( surfID ) &= ~(SURFDRAW_NOCULL | SURFDRAW_TRANS | SURFDRAW_ALPHATEST | SURFDRAW_NODECALS);
  3351. mtexinfo_t *pTex = MSurf_TexInfo( surfID, pBrushData );
  3352. IMaterial* pMaterial = pTex->material;
  3353. if ( pMaterial->HasProxy() )
  3354. {
  3355. mod->flags |= MODELFLAG_MATERIALPROXY;
  3356. }
  3357. if ( pMaterial->NeedsPowerOfTwoFrameBufferTexture( false ) ) // The false checks if it will ever need the frame buffer, not just this frame
  3358. {
  3359. mod->flags |= MODELFLAG_FRAMEBUFFER_TEXTURE;
  3360. }
  3361. // Deactivate culling if the material is two sided
  3362. if ( pMaterial->IsTwoSided() )
  3363. {
  3364. MSurf_Flags( surfID ) |= SURFDRAW_NOCULL;
  3365. }
  3366. if ( (pTex->flags & SURF_TRANS) || pMaterial->IsTranslucent() )
  3367. {
  3368. mod->flags |= MODELFLAG_TRANSLUCENT;
  3369. MSurf_Flags( surfID ) |= SURFDRAW_TRANS;
  3370. bHasTranslucentSurfaces = true;
  3371. }
  3372. else
  3373. {
  3374. bHasOpaqueSurfaces = true;
  3375. }
  3376. // Certain surfaces don't want decals at all
  3377. if ( (pTex->flags & SURF_NODECALS) || pMaterial->GetMaterialVarFlag( MATERIAL_VAR_SUPPRESS_DECALS ) || pMaterial->IsAlphaTested() )
  3378. {
  3379. MSurf_Flags( surfID ) |= SURFDRAW_NODECALS;
  3380. }
  3381. if ( pMaterial->IsAlphaTested() )
  3382. {
  3383. MSurf_Flags( surfID ) |= SURFDRAW_ALPHATEST;
  3384. }
  3385. }
  3386. if ( bHasOpaqueSurfaces && bHasTranslucentSurfaces )
  3387. {
  3388. mod->flags |= MODELFLAG_TRANSLUCENT_TWOPASS;
  3389. }
  3390. }
  3391. //-----------------------------------------------------------------------------
  3392. // Recomputes translucency for the model...
  3393. //-----------------------------------------------------------------------------
  3394. void Mod_RecomputeTranslucency( model_t* mod, int nSkin, int nBody, void /*IClientRenderable*/ *pClientRenderable, float fInstanceAlphaModulate )
  3395. {
  3396. if (fInstanceAlphaModulate < 1.0f)
  3397. {
  3398. mod->flags |= MODELFLAG_TRANSLUCENT;
  3399. return;
  3400. }
  3401. mod->flags &= ~MODELFLAG_TRANSLUCENT;
  3402. switch( mod->type )
  3403. {
  3404. case mod_brush:
  3405. {
  3406. for (int i = 0; i < mod->brush.nummodelsurfaces; ++i)
  3407. {
  3408. SurfaceHandle_t surfID = SurfaceHandleFromIndex( mod->brush.firstmodelsurface+i, mod->brush.pShared );
  3409. if ( MSurf_Flags( surfID ) & SURFDRAW_NODRAW )
  3410. continue;
  3411. IMaterial* material = MSurf_TexInfo( surfID, mod->brush.pShared )->material;
  3412. if ( material->IsTranslucent() )
  3413. {
  3414. mod->flags |= MODELFLAG_TRANSLUCENT;
  3415. break;
  3416. }
  3417. }
  3418. }
  3419. break;
  3420. case mod_studio:
  3421. {
  3422. studiohdr_t *pStudioHdr = g_pMDLCache->GetStudioHdr( mod->studio );
  3423. if ( pStudioHdr->flags & STUDIOHDR_FLAGS_FORCE_OPAQUE )
  3424. return;
  3425. IMaterial *pMaterials[ 128 ];
  3426. int materialCount = g_pStudioRender->GetMaterialListFromBodyAndSkin( mod->studio, nSkin, nBody, ARRAYSIZE( pMaterials ), pMaterials );
  3427. for ( int i = 0; i < materialCount; i++ )
  3428. {
  3429. if ( pMaterials[i] != NULL )
  3430. {
  3431. // Bind material first so all material proxies execute
  3432. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  3433. pRenderContext->Bind( pMaterials[i], pClientRenderable );
  3434. bool bIsTranslucent = pMaterials[i]->IsTranslucent();
  3435. if ( bIsTranslucent )
  3436. {
  3437. mod->flags |= MODELFLAG_TRANSLUCENT;
  3438. break;
  3439. }
  3440. }
  3441. }
  3442. }
  3443. break;
  3444. }
  3445. }
  3446. //-----------------------------------------------------------------------------
  3447. // returns the material count...
  3448. //-----------------------------------------------------------------------------
  3449. int Mod_GetMaterialCount( model_t* mod )
  3450. {
  3451. switch( mod->type )
  3452. {
  3453. case mod_brush:
  3454. {
  3455. CUtlVector<IMaterial*> uniqueMaterials( 0, 32 );
  3456. for (int i = 0; i < mod->brush.nummodelsurfaces; ++i)
  3457. {
  3458. SurfaceHandle_t surfID = SurfaceHandleFromIndex( mod->brush.firstmodelsurface + i, mod->brush.pShared );
  3459. if ( MSurf_Flags( surfID ) & SURFDRAW_NODRAW )
  3460. continue;
  3461. IMaterial* pMaterial = MSurf_TexInfo( surfID, mod->brush.pShared )->material;
  3462. // Try to find the material in the unique list of materials
  3463. // if it's not there, then add it
  3464. if (uniqueMaterials.Find(pMaterial) < 0)
  3465. uniqueMaterials.AddToTail(pMaterial);
  3466. }
  3467. return uniqueMaterials.Size();
  3468. }
  3469. break;
  3470. case mod_studio:
  3471. {
  3472. // FIXME: This should return the list of all materials
  3473. // across all LODs if we every decide to implement this
  3474. Assert(0);
  3475. }
  3476. break;
  3477. default:
  3478. // unimplemented
  3479. Assert(0);
  3480. break;
  3481. }
  3482. return 0;
  3483. }
  3484. //-----------------------------------------------------------------------------
  3485. // returns the first n materials.
  3486. //-----------------------------------------------------------------------------
  3487. int Mod_GetModelMaterials( model_t* pModel, int count, IMaterial** ppMaterials )
  3488. {
  3489. studiohdr_t *pStudioHdr;
  3490. int found = 0;
  3491. int i;
  3492. switch( pModel->type )
  3493. {
  3494. case mod_brush:
  3495. {
  3496. for ( i = 0; i < pModel->brush.nummodelsurfaces; ++i)
  3497. {
  3498. SurfaceHandle_t surfID = SurfaceHandleFromIndex( pModel->brush.firstmodelsurface + i, pModel->brush.pShared );
  3499. if ( MSurf_Flags( surfID ) & SURFDRAW_NODRAW )
  3500. continue;
  3501. IMaterial* pMaterial = MSurf_TexInfo( surfID, pModel->brush.pShared )->material;
  3502. // Try to find the material in the unique list of materials
  3503. // if it's not there, then add it
  3504. int j = found;
  3505. while ( --j >= 0 )
  3506. {
  3507. if ( ppMaterials[j] == pMaterial )
  3508. break;
  3509. }
  3510. if (j < 0)
  3511. ppMaterials[found++] = pMaterial;
  3512. // Stop when we've gotten count materials
  3513. if ( found >= count )
  3514. return found;
  3515. }
  3516. }
  3517. break;
  3518. case mod_studio:
  3519. if ( pModel->ppMaterials )
  3520. {
  3521. int nMaterials = ((intptr_t*)(pModel->ppMaterials))[-1];
  3522. found = MIN( count, nMaterials );
  3523. memcpy( ppMaterials, pModel->ppMaterials, found * sizeof( IMaterial* ) );
  3524. }
  3525. else
  3526. {
  3527. // Get the studiohdr into the cache
  3528. pStudioHdr = g_pMDLCache->GetStudioHdr( pModel->studio );
  3529. // Get the list of materials
  3530. found = g_pStudioRender->GetMaterialList( pStudioHdr, count, ppMaterials );
  3531. }
  3532. break;
  3533. default:
  3534. // unimplemented
  3535. Assert( 0 );
  3536. break;
  3537. }
  3538. return found;
  3539. }
  3540. void Mod_SetMaterialVarFlag( model_t *pModel, unsigned int uiFlag, bool on )
  3541. {
  3542. MaterialVarFlags_t flag = (MaterialVarFlags_t)uiFlag;
  3543. IMaterial *pMaterials[ 128 ];
  3544. if ( pModel )
  3545. {
  3546. int materialCount = Mod_GetModelMaterials( pModel, ARRAYSIZE( pMaterials ), pMaterials );
  3547. for ( int i = 0; i < materialCount; ++i )
  3548. {
  3549. IMaterial *pMaterial = pMaterials[ i ];
  3550. if ( pMaterial )
  3551. {
  3552. pMaterial->SetMaterialVarFlag( flag, on );
  3553. }
  3554. }
  3555. }
  3556. }
  3557. //-----------------------------------------------------------------------------
  3558. // Used to compute which surfaces are in water or not
  3559. //-----------------------------------------------------------------------------
  3560. static void MarkWaterSurfaces_ProcessLeafNode( mleaf_t *pLeaf )
  3561. {
  3562. int i;
  3563. int flags = ( pLeaf->leafWaterDataID == -1 ) ? SURFDRAW_ABOVEWATER : SURFDRAW_UNDERWATER;
  3564. SurfaceHandle_t *pHandle = &host_state.worldbrush->marksurfaces[pLeaf->firstmarksurface];
  3565. for( i = 0; i < pLeaf->nummarksurfaces; i++ )
  3566. {
  3567. SurfaceHandle_t surfID = pHandle[i];
  3568. ASSERT_SURF_VALID( surfID );
  3569. if( MSurf_Flags( surfID ) & SURFDRAW_WATERSURFACE )
  3570. continue;
  3571. if (SurfaceHasDispInfo( surfID ))
  3572. continue;
  3573. MSurf_Flags( surfID ) |= flags;
  3574. }
  3575. // FIXME: This is somewhat bogus, but I can do it quickly, and it's
  3576. // not clear I need to solve the harder problem.
  3577. // If any portion of a displacement surface hits a water surface,
  3578. // I'm going to mark it as being in water, and vice versa.
  3579. for ( i = 0; i < pLeaf->dispCount; i++ )
  3580. {
  3581. IDispInfo *pDispInfo = MLeaf_Disaplcement( pLeaf, i );
  3582. if ( pDispInfo )
  3583. {
  3584. SurfaceHandle_t parentSurfID = pDispInfo->GetParent();
  3585. MSurf_Flags( parentSurfID ) |= flags;
  3586. }
  3587. }
  3588. }
  3589. void MarkWaterSurfaces_r( mnode_t *node )
  3590. {
  3591. // no polygons in solid nodes
  3592. if (node->contents == CONTENTS_SOLID)
  3593. return; // solid
  3594. // if a leaf node, . .mark all the polys as to whether or not they are in water.
  3595. if (node->contents >= 0)
  3596. {
  3597. MarkWaterSurfaces_ProcessLeafNode( (mleaf_t *)node );
  3598. return;
  3599. }
  3600. MarkWaterSurfaces_r( node->children[0] );
  3601. MarkWaterSurfaces_r( node->children[1] );
  3602. }
  3603. //-----------------------------------------------------------------------------
  3604. // Computes the sort group for a particular face
  3605. //-----------------------------------------------------------------------------
  3606. static int SurfFlagsToSortGroup( SurfaceHandle_t surfID, int flags )
  3607. {
  3608. // If we're on the low end, stick everything into the same sort group
  3609. if ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() < 80 )
  3610. return MAT_SORT_GROUP_STRICTLY_ABOVEWATER;
  3611. if( flags & SURFDRAW_WATERSURFACE )
  3612. return MAT_SORT_GROUP_WATERSURFACE;
  3613. if( ( flags & ( SURFDRAW_UNDERWATER | SURFDRAW_ABOVEWATER ) ) == ( SURFDRAW_UNDERWATER | SURFDRAW_ABOVEWATER ) )
  3614. return MAT_SORT_GROUP_INTERSECTS_WATER_SURFACE;
  3615. if( flags & SURFDRAW_UNDERWATER )
  3616. return MAT_SORT_GROUP_STRICTLY_UNDERWATER;
  3617. if( flags & SURFDRAW_ABOVEWATER )
  3618. return MAT_SORT_GROUP_STRICTLY_ABOVEWATER;
  3619. static int warningcount = 0;
  3620. if ( ++warningcount < 10 )
  3621. {
  3622. Vector vecCenter;
  3623. Surf_ComputeCentroid( surfID, &vecCenter );
  3624. DevWarning( "SurfFlagsToSortGroup: unhandled flags (%X) (%s)!\n", flags, MSurf_TexInfo(surfID)->material->GetName() );
  3625. DevWarning( "- This implies you have a surface (usually a displacement) embedded in solid.\n" );
  3626. DevWarning( "- Look near (%.1f, %.1f, %.1f)\n", vecCenter.x, vecCenter.y, vecCenter.z );
  3627. }
  3628. //Assert( 0 );
  3629. return MAT_SORT_GROUP_STRICTLY_ABOVEWATER;
  3630. }
  3631. //-----------------------------------------------------------------------------
  3632. // Computes sort group
  3633. //-----------------------------------------------------------------------------
  3634. bool Mod_MarkWaterSurfaces( model_t *pModel )
  3635. {
  3636. bool bHasWaterSurfaces = false;
  3637. model_t *pSaveModel = host_state.worldmodel;
  3638. // garymcthack!!!!!!!!
  3639. // host_state.worldmodel isn't set at this point, so. . . .
  3640. host_state.SetWorldModel( pModel );
  3641. MarkWaterSurfaces_r( pModel->brush.pShared->nodes );
  3642. for ( int i = 0; i < pModel->brush.pShared->numsurfaces; i++ )
  3643. {
  3644. SurfaceHandle_t surfID = SurfaceHandleFromIndex( i, pModel->brush.pShared );
  3645. int sortGroup = SurfFlagsToSortGroup( surfID, MSurf_Flags( surfID ) );
  3646. if ( sortGroup == MAT_SORT_GROUP_WATERSURFACE )
  3647. {
  3648. bHasWaterSurfaces = true;
  3649. }
  3650. MSurf_SetSortGroup( surfID, sortGroup );
  3651. }
  3652. host_state.SetWorldModel( pSaveModel );
  3653. return bHasWaterSurfaces;
  3654. }
  3655. //-----------------------------------------------------------------------------
  3656. // Marks identity brushes as being in fog volumes or not
  3657. //-----------------------------------------------------------------------------
  3658. class CBrushBSPIterator : public ISpatialLeafEnumerator
  3659. {
  3660. public:
  3661. CBrushBSPIterator( model_t *pWorld, model_t *pBrush )
  3662. {
  3663. m_pWorld = pWorld;
  3664. m_pBrush = pBrush;
  3665. m_pShared = pBrush->brush.pShared;
  3666. m_count = 0;
  3667. }
  3668. bool EnumerateLeaf( int leaf, int )
  3669. {
  3670. // garymcthack - need to test identity brush models
  3671. int flags = ( m_pShared->leafs[leaf].leafWaterDataID == -1 ) ? SURFDRAW_ABOVEWATER : SURFDRAW_UNDERWATER;
  3672. MarkModelSurfaces( flags );
  3673. m_count++;
  3674. return true;
  3675. }
  3676. void MarkModelSurfaces( int flags )
  3677. {
  3678. // Iterate over all this models surfaces
  3679. int surfaceCount = m_pBrush->brush.nummodelsurfaces;
  3680. for (int i = 0; i < surfaceCount; ++i)
  3681. {
  3682. SurfaceHandle_t surfID = SurfaceHandleFromIndex( m_pBrush->brush.firstmodelsurface + i, m_pShared );
  3683. MSurf_Flags( surfID ) &= ~(SURFDRAW_ABOVEWATER | SURFDRAW_UNDERWATER);
  3684. MSurf_Flags( surfID ) |= flags;
  3685. }
  3686. }
  3687. void CheckSurfaces()
  3688. {
  3689. if ( !m_count )
  3690. {
  3691. MarkModelSurfaces( SURFDRAW_ABOVEWATER );
  3692. }
  3693. }
  3694. model_t* m_pWorld;
  3695. model_t* m_pBrush;
  3696. worldbrushdata_t *m_pShared;
  3697. int m_count;
  3698. };
  3699. static void MarkBrushModelWaterSurfaces( model_t* world,
  3700. Vector const& mins, Vector const& maxs, model_t* brush )
  3701. {
  3702. // HACK: This is a totally brutal hack dealing with initialization order issues.
  3703. // I want to use the same box enumeration code so I don't have multiple
  3704. // copies, but I want to use it from modelloader. host_state.worldmodel isn't
  3705. // set up at that time however, so I have to fly through these crazy hoops.
  3706. // Massive suckage.
  3707. model_t* pTemp = host_state.worldmodel;
  3708. CBrushBSPIterator brushIterator( world, brush );
  3709. host_state.SetWorldModel( world );
  3710. g_pToolBSPTree->EnumerateLeavesInBox( mins, maxs, &brushIterator, (int)brush );
  3711. brushIterator.CheckSurfaces();
  3712. host_state.SetWorldModel( pTemp );
  3713. }
  3714. int g_nMapLoadCount = 0;
  3715. //-----------------------------------------------------------------------------
  3716. // Purpose:
  3717. // Input : *mod -
  3718. // *buffer -
  3719. //-----------------------------------------------------------------------------
  3720. void CModelLoader::Map_LoadModel( model_t *mod )
  3721. {
  3722. ++g_nMapLoadCount;
  3723. MEM_ALLOC_CREDIT();
  3724. #ifndef SWDS
  3725. EngineVGui()->UpdateProgressBar(PROGRESS_LOADWORLDMODEL);
  3726. #endif
  3727. Assert( !( mod->nLoadFlags & FMODELLOADER_LOADED ) );
  3728. COM_TimestampedLog( "Map_LoadModel: Start" );
  3729. double startTime = Plat_FloatTime();
  3730. SetWorldModel( mod );
  3731. // point at the shared world/brush data
  3732. mod->brush.pShared = &m_worldBrushData;
  3733. mod->brush.renderHandle = 0;
  3734. // HDR and features must be established first
  3735. COM_TimestampedLog( " Map_CheckForHDR" );
  3736. m_bMapHasHDRLighting = Map_CheckForHDR( mod, m_szLoadName );
  3737. if ( IsX360() && !m_bMapHasHDRLighting )
  3738. {
  3739. Warning( "Map '%s' lacks exepected HDR data! 360 does not support accurate LDR visuals.", m_szLoadName );
  3740. }
  3741. // Load the collision model
  3742. COM_TimestampedLog( " CM_LoadMap" );
  3743. unsigned int checksum;
  3744. CM_LoadMap( mod->strName, false, &checksum );
  3745. // Load the map
  3746. mod->type = mod_brush;
  3747. mod->nLoadFlags |= FMODELLOADER_LOADED;
  3748. CMapLoadHelper::Init( mod, m_szLoadName );
  3749. COM_TimestampedLog( " Mod_LoadVertices" );
  3750. Mod_LoadVertices();
  3751. COM_TimestampedLog( " Mod_LoadEdges" );
  3752. medge_t *pedges = Mod_LoadEdges();
  3753. COM_TimestampedLog( " Mod_LoadSurfedges" );
  3754. Mod_LoadSurfedges( pedges );
  3755. COM_TimestampedLog( " Mod_LoadPlanes" );
  3756. Mod_LoadPlanes();
  3757. COM_TimestampedLog( " Mod_LoadOcclusion" );
  3758. Mod_LoadOcclusion();
  3759. // texdata needs to load before texinfo
  3760. COM_TimestampedLog( " Mod_LoadTexdata" );
  3761. Mod_LoadTexdata();
  3762. COM_TimestampedLog( " Mod_LoadTexinfo" );
  3763. Mod_LoadTexinfo();
  3764. #ifndef SWDS
  3765. EngineVGui()->UpdateProgressBar(PROGRESS_LOADWORLDMODEL);
  3766. #endif
  3767. // Until BSP version 19, this must occur after loading texinfo
  3768. COM_TimestampedLog( " Mod_LoadLighting" );
  3769. if ( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE &&
  3770. CMapLoadHelper::LumpSize( LUMP_LIGHTING_HDR ) > 0 )
  3771. {
  3772. CMapLoadHelper mlh( LUMP_LIGHTING_HDR );
  3773. Mod_LoadLighting( mlh );
  3774. }
  3775. else
  3776. {
  3777. CMapLoadHelper mlh( LUMP_LIGHTING );
  3778. Mod_LoadLighting( mlh );
  3779. }
  3780. COM_TimestampedLog( " Mod_LoadPrimitives" );
  3781. Mod_LoadPrimitives();
  3782. COM_TimestampedLog( " Mod_LoadPrimVerts" );
  3783. Mod_LoadPrimVerts();
  3784. COM_TimestampedLog( " Mod_LoadPrimIndices" );
  3785. Mod_LoadPrimIndices();
  3786. #ifndef SWDS
  3787. EngineVGui()->UpdateProgressBar(PROGRESS_LOADWORLDMODEL);
  3788. #endif
  3789. // faces need to be loaded before vertnormals
  3790. COM_TimestampedLog( " Mod_LoadFaces" );
  3791. Mod_LoadFaces();
  3792. COM_TimestampedLog( " Mod_LoadVertNormals" );
  3793. Mod_LoadVertNormals();
  3794. COM_TimestampedLog( " Mod_LoadVertNormalIndices" );
  3795. Mod_LoadVertNormalIndices();
  3796. #ifndef SWDS
  3797. EngineVGui()->UpdateProgressBar(PROGRESS_LOADWORLDMODEL);
  3798. #endif
  3799. // note leafs must load befor marksurfaces
  3800. COM_TimestampedLog( " Mod_LoadLeafs" );
  3801. Mod_LoadLeafs();
  3802. COM_TimestampedLog( " Mod_LoadMarksurfaces" );
  3803. Mod_LoadMarksurfaces();
  3804. COM_TimestampedLog( " Mod_LoadNodes" );
  3805. Mod_LoadNodes();
  3806. COM_TimestampedLog( " Mod_LoadLeafWaterData" );
  3807. Mod_LoadLeafWaterData();
  3808. COM_TimestampedLog( " Mod_LoadCubemapSamples" );
  3809. Mod_LoadCubemapSamples();
  3810. #ifndef SWDS
  3811. // UNDONE: Does the cmodel need worldlights?
  3812. COM_TimestampedLog( " OverlayMgr()->LoadOverlays" );
  3813. OverlayMgr()->LoadOverlays();
  3814. #endif
  3815. COM_TimestampedLog( " Mod_LoadLeafMinDistToWater" );
  3816. Mod_LoadLeafMinDistToWater();
  3817. #ifndef SWDS
  3818. EngineVGui()->UpdateProgressBar(PROGRESS_LOADWORLDMODEL);
  3819. #endif
  3820. COM_TimestampedLog( " LUMP_CLIPPORTALVERTS" );
  3821. Mod_LoadLump( mod,
  3822. LUMP_CLIPPORTALVERTS,
  3823. va( "%s [%s]", m_szLoadName, "clipportalverts" ),
  3824. sizeof(m_worldBrushData.m_pClipPortalVerts[0]),
  3825. (void**)&m_worldBrushData.m_pClipPortalVerts,
  3826. &m_worldBrushData.m_nClipPortalVerts );
  3827. COM_TimestampedLog( " LUMP_AREAPORTALS" );
  3828. Mod_LoadLump( mod,
  3829. LUMP_AREAPORTALS,
  3830. va( "%s [%s]", m_szLoadName, "areaportals" ),
  3831. sizeof(m_worldBrushData.m_pAreaPortals[0]),
  3832. (void**)&m_worldBrushData.m_pAreaPortals,
  3833. &m_worldBrushData.m_nAreaPortals );
  3834. COM_TimestampedLog( " LUMP_AREAS" );
  3835. Mod_LoadLump( mod,
  3836. LUMP_AREAS,
  3837. va( "%s [%s]", m_szLoadName, "areas" ),
  3838. sizeof(m_worldBrushData.m_pAreas[0]),
  3839. (void**)&m_worldBrushData.m_pAreas,
  3840. &m_worldBrushData.m_nAreas );
  3841. COM_TimestampedLog( " Mod_LoadWorldlights" );
  3842. if ( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE &&
  3843. CMapLoadHelper::LumpSize( LUMP_WORLDLIGHTS_HDR ) > 0 )
  3844. {
  3845. CMapLoadHelper mlh( LUMP_WORLDLIGHTS_HDR );
  3846. Mod_LoadWorldlights( mlh, true );
  3847. }
  3848. else
  3849. {
  3850. CMapLoadHelper mlh( LUMP_WORLDLIGHTS );
  3851. Mod_LoadWorldlights( mlh, false );
  3852. }
  3853. COM_TimestampedLog( " Mod_LoadGameLumpDict" );
  3854. Mod_LoadGameLumpDict();
  3855. // load the portal information
  3856. // JAY: Disabled until we need this information.
  3857. #if 0
  3858. Mod_LoadPortalVerts();
  3859. Mod_LoadClusterPortals();
  3860. Mod_LoadClusters();
  3861. Mod_LoadPortals();
  3862. #endif
  3863. #ifndef SWDS
  3864. EngineVGui()->UpdateProgressBar(PROGRESS_LOADWORLDMODEL);
  3865. #endif
  3866. COM_TimestampedLog( " Mod_LoadSubmodels" );
  3867. CUtlVector<mmodel_t> submodelList;
  3868. Mod_LoadSubmodels( submodelList );
  3869. #ifndef SWDS
  3870. EngineVGui()->UpdateProgressBar(PROGRESS_LOADWORLDMODEL);
  3871. #endif
  3872. COM_TimestampedLog( " SetupSubModels" );
  3873. SetupSubModels( mod, submodelList );
  3874. COM_TimestampedLog( " RecomputeSurfaceFlags" );
  3875. RecomputeSurfaceFlags( mod );
  3876. #ifndef SWDS
  3877. EngineVGui()->UpdateProgressBar(PROGRESS_LOADWORLDMODEL);
  3878. #endif
  3879. COM_TimestampedLog( " Map_VisClear" );
  3880. Map_VisClear();
  3881. COM_TimestampedLog( " Map_SetRenderInfoAllocated" );
  3882. Map_SetRenderInfoAllocated( false );
  3883. // Close map file, etc.
  3884. CMapLoadHelper::Shutdown();
  3885. double elapsed = Plat_FloatTime() - startTime;
  3886. COM_TimestampedLog( "Map_LoadModel: Finish - loading took %.4f seconds", elapsed );
  3887. }
  3888. void CModelLoader::Map_UnloadCubemapSamples( model_t *mod )
  3889. {
  3890. int i;
  3891. for ( i=0 ; i < mod->brush.pShared->m_nCubemapSamples ; i++ )
  3892. {
  3893. mcubemapsample_t *pSample = &mod->brush.pShared->m_pCubemapSamples[i];
  3894. pSample->pTexture->DecrementReferenceCount();
  3895. }
  3896. }
  3897. //-----------------------------------------------------------------------------
  3898. // Recomputes surface flags
  3899. //-----------------------------------------------------------------------------
  3900. void CModelLoader::RecomputeSurfaceFlags( model_t *mod )
  3901. {
  3902. for (int i=0 ; i<mod->brush.pShared->numsubmodels ; i++)
  3903. {
  3904. model_t *pSubModel = &m_InlineModels[i];
  3905. // Compute whether this submodel uses material proxies or not
  3906. Mod_ComputeBrushModelFlags( pSubModel );
  3907. // Mark if brush models are in water or not; we'll use this
  3908. // for identity brushes. If the brush is not an identity brush,
  3909. // then we'll not have to worry.
  3910. if ( i != 0 )
  3911. {
  3912. MarkBrushModelWaterSurfaces( mod, pSubModel->mins, pSubModel->maxs, pSubModel );
  3913. }
  3914. }
  3915. }
  3916. //-----------------------------------------------------------------------------
  3917. // Setup sub models
  3918. //-----------------------------------------------------------------------------
  3919. void CModelLoader::SetupSubModels( model_t *mod, CUtlVector<mmodel_t> &list )
  3920. {
  3921. int i;
  3922. m_InlineModels.SetCount( m_worldBrushData.numsubmodels );
  3923. for (i=0 ; i<m_worldBrushData.numsubmodels ; i++)
  3924. {
  3925. model_t *starmod;
  3926. mmodel_t *bm;
  3927. bm = &list[i];
  3928. starmod = &m_InlineModels[i];
  3929. *starmod = *mod;
  3930. starmod->brush.firstmodelsurface = bm->firstface;
  3931. starmod->brush.nummodelsurfaces = bm->numfaces;
  3932. starmod->brush.firstnode = bm->headnode;
  3933. if ( starmod->brush.firstnode >= m_worldBrushData.numnodes )
  3934. {
  3935. Sys_Error( "Inline model %i has bad firstnode", i );
  3936. }
  3937. VectorCopy(bm->maxs, starmod->maxs);
  3938. VectorCopy(bm->mins, starmod->mins);
  3939. starmod->radius = bm->radius;
  3940. if (i == 0)
  3941. {
  3942. *mod = *starmod;
  3943. }
  3944. else
  3945. {
  3946. starmod->strName.Format( "*%d", i );
  3947. starmod->fnHandle = g_pFileSystem->FindOrAddFileName( starmod->strName );
  3948. }
  3949. }
  3950. }
  3951. //-----------------------------------------------------------------------------
  3952. // Purpose:
  3953. // Input : *mod -
  3954. //-----------------------------------------------------------------------------
  3955. void CModelLoader::Map_UnloadModel( model_t *mod )
  3956. {
  3957. Assert( !( mod->nLoadFlags & FMODELLOADER_REFERENCEMASK ) );
  3958. mod->nLoadFlags &= ~FMODELLOADER_LOADED;
  3959. #ifndef SWDS
  3960. OverlayMgr()->UnloadOverlays();
  3961. #endif
  3962. DeallocateLightingData( &m_worldBrushData );
  3963. #ifndef SWDS
  3964. DispInfo_ReleaseMaterialSystemObjects( mod );
  3965. #endif
  3966. Map_UnloadCubemapSamples( mod );
  3967. #ifndef SWDS
  3968. // Free decals in displacements.
  3969. R_DecalTerm( &m_worldBrushData, true );
  3970. #endif
  3971. if ( m_worldBrushData.hDispInfos )
  3972. {
  3973. DispInfo_DeleteArray( m_worldBrushData.hDispInfos );
  3974. m_worldBrushData.hDispInfos = NULL;
  3975. }
  3976. // Model loader loads world model materials, unload them here
  3977. for( int texinfoID = 0; texinfoID < m_worldBrushData.numtexinfo; texinfoID++ )
  3978. {
  3979. mtexinfo_t *pTexinfo = &m_worldBrushData.texinfo[texinfoID];
  3980. if ( pTexinfo )
  3981. {
  3982. GL_UnloadMaterial( pTexinfo->material );
  3983. }
  3984. }
  3985. MaterialSystem_DestroySortinfo();
  3986. // Don't store any reference to it here
  3987. ClearWorldModel();
  3988. Map_SetRenderInfoAllocated( false );
  3989. }
  3990. //-----------------------------------------------------------------------------
  3991. // Computes dimensions + frame count of a material
  3992. //-----------------------------------------------------------------------------
  3993. static void GetSpriteInfo( const char *pName, bool bIsVideo, int &nWidth, int &nHeight, int &nFrameCount )
  3994. {
  3995. nFrameCount = 1;
  3996. nWidth = nHeight = 1;
  3997. // FIXME: The reason we are putting logic related to AVIs here,
  3998. // logic which is duplicated in the client DLL related to loading sprites,
  3999. // is that this code gets run on dedicated servers also.
  4000. IMaterial *pMaterial = NULL;
  4001. IVideoMaterial *pVideoMaterial = NULL;
  4002. if ( bIsVideo && g_pVideo != NULL )
  4003. {
  4004. pVideoMaterial = g_pVideo->CreateVideoMaterial( pName, pName, "GAME", VideoPlaybackFlags::DEFAULT_MATERIAL_OPTIONS, VideoSystem::DETERMINE_FROM_FILE_EXTENSION, false );
  4005. if ( pVideoMaterial )
  4006. {
  4007. pVideoMaterial->GetVideoImageSize( &nWidth, &nHeight );
  4008. nFrameCount = pVideoMaterial->GetFrameCount();
  4009. pMaterial = pVideoMaterial->GetMaterial();
  4010. g_pVideo->DestroyVideoMaterial( pVideoMaterial );
  4011. }
  4012. }
  4013. else
  4014. {
  4015. pMaterial = GL_LoadMaterial( pName, TEXTURE_GROUP_OTHER );
  4016. if ( pMaterial )
  4017. {
  4018. // Store off our source height, width, frame count
  4019. nWidth = pMaterial->GetMappingWidth();
  4020. nHeight = pMaterial->GetMappingHeight();
  4021. nFrameCount = pMaterial->GetNumAnimationFrames();
  4022. }
  4023. }
  4024. if ( pMaterial == g_materialEmpty )
  4025. {
  4026. DevMsg( "Missing sprite material %s\n", pName );
  4027. }
  4028. }
  4029. //-----------------------------------------------------------------------------
  4030. // Purpose:
  4031. //-----------------------------------------------------------------------------
  4032. void CModelLoader::Sprite_LoadModel( model_t *mod )
  4033. {
  4034. Assert( !( mod->nLoadFlags & FMODELLOADER_LOADED ) );
  4035. mod->nLoadFlags |= FMODELLOADER_LOADED;
  4036. // The hunk data is not used on the server
  4037. byte* pSprite = NULL;
  4038. #ifndef SWDS
  4039. if ( g_ClientDLL )
  4040. {
  4041. int nSize = g_ClientDLL->GetSpriteSize();
  4042. if ( nSize )
  4043. {
  4044. pSprite = ( byte * )new byte[ nSize ];
  4045. }
  4046. }
  4047. #endif
  4048. mod->type = mod_sprite;
  4049. mod->sprite.sprite = (CEngineSprite *)pSprite;
  4050. // Fake the bounding box. We need it for PVS culling, and we don't
  4051. // know the scale at which the sprite is going to be rendered at
  4052. // when we load it
  4053. mod->mins = mod->maxs = Vector(0,0,0);
  4054. // Figure out the real load name..
  4055. char loadName[MAX_PATH];
  4056. bool bIsVideo;
  4057. BuildSpriteLoadName( mod->strName, loadName, MAX_PATH, bIsVideo );
  4058. GetSpriteInfo( loadName, bIsVideo, mod->sprite.width, mod->sprite.height, mod->sprite.numframes );
  4059. #ifndef SWDS
  4060. if ( g_ClientDLL && mod->sprite.sprite )
  4061. {
  4062. g_ClientDLL->InitSprite( mod->sprite.sprite, loadName );
  4063. }
  4064. #endif
  4065. }
  4066. //-----------------------------------------------------------------------------
  4067. // Purpose:
  4068. //-----------------------------------------------------------------------------
  4069. void CModelLoader::Sprite_UnloadModel( model_t *mod )
  4070. {
  4071. Assert( !( mod->nLoadFlags & FMODELLOADER_REFERENCEMASK ) );
  4072. mod->nLoadFlags &= ~FMODELLOADER_LOADED;
  4073. char loadName[MAX_PATH];
  4074. bool bIsVideo;
  4075. BuildSpriteLoadName( mod->strName, loadName, sizeof( loadName ), bIsVideo );
  4076. IMaterial *mat = materials->FindMaterial( loadName, TEXTURE_GROUP_OTHER );
  4077. if ( !IsErrorMaterial( mat ) )
  4078. {
  4079. GL_UnloadMaterial( mat );
  4080. }
  4081. #ifndef SWDS
  4082. if ( g_ClientDLL && mod->sprite.sprite )
  4083. {
  4084. g_ClientDLL->ShutdownSprite( mod->sprite.sprite );
  4085. }
  4086. #endif
  4087. delete[] (byte *)mod->sprite.sprite;
  4088. mod->sprite.sprite = 0;
  4089. mod->sprite.numframes = 0;
  4090. }
  4091. //-----------------------------------------------------------------------------
  4092. // Purpose: Flush and reload models. Intended for use when lod changes.
  4093. //-----------------------------------------------------------------------------
  4094. void CModelLoader::Studio_ReloadModels( CModelLoader::ReloadType_t reloadType )
  4095. {
  4096. #if !defined( SWDS )
  4097. if ( g_ClientDLL )
  4098. g_ClientDLL->InvalidateMdlCache();
  4099. #endif // SWDS
  4100. if ( serverGameDLL )
  4101. serverGameDLL->InvalidateMdlCache();
  4102. // ensure decals have no stale references to invalid lods
  4103. modelrender->RemoveAllDecalsFromAllModels();
  4104. // ensure static props have no stale references to invalid lods
  4105. modelrender->ReleaseAllStaticPropColorData();
  4106. // Flush out the model cache
  4107. // Don't flush vcollides since the vphysics system currently
  4108. // has no way of indicating they refer to vcollides
  4109. g_pMDLCache->Flush( (MDLCacheFlush_t) (MDLCACHE_FLUSH_ALL & (~MDLCACHE_FLUSH_VCOLLIDE)) );
  4110. // Load the critical pieces now
  4111. // The model cache will re-populate as models render
  4112. FOR_EACH_MAP_FAST( m_Models, i )
  4113. {
  4114. model_t *pModel = m_Models[ i ].modelpointer;
  4115. if ( !IsLoaded( pModel ) )
  4116. continue;
  4117. if ( pModel->type != mod_studio )
  4118. continue;
  4119. MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
  4120. // Get the studiohdr into the cache
  4121. g_pMDLCache->GetStudioHdr( pModel->studio );
  4122. // force the collision to load
  4123. g_pMDLCache->GetVCollide( pModel->studio );
  4124. }
  4125. }
  4126. struct modelsize_t
  4127. {
  4128. const char *pName;
  4129. int size;
  4130. };
  4131. class CModelsize_Less
  4132. {
  4133. public:
  4134. bool Less( const modelsize_t& src1, const modelsize_t& src2, void *pCtx )
  4135. {
  4136. return ( src1.size < src2.size );
  4137. }
  4138. };
  4139. void CModelLoader::DumpVCollideStats()
  4140. {
  4141. int i;
  4142. CUtlSortVector< modelsize_t, CModelsize_Less > list;
  4143. for ( i = 0; (m_Models).IsUtlMap && i < (m_Models).MaxElement(); ++i ) if ( !(m_Models).IsValidIndex( i ) ) continue; else
  4144. {
  4145. model_t *pModel = m_Models[ i ].modelpointer;
  4146. if ( pModel && pModel->type == mod_studio )
  4147. {
  4148. int size = 0;
  4149. bool loaded = g_pMDLCache->GetVCollideSize( pModel->studio, &size );
  4150. if ( loaded && size )
  4151. {
  4152. modelsize_t elem;
  4153. elem.pName = pModel->strName;
  4154. elem.size = size;
  4155. list.Insert( elem );
  4156. }
  4157. }
  4158. }
  4159. for ( i = m_InlineModels.Count(); --i >= 0; )
  4160. {
  4161. vcollide_t *pCollide = CM_VCollideForModel( i+1, &m_InlineModels[i] );
  4162. if ( pCollide )
  4163. {
  4164. int size = 0;
  4165. for ( int j = 0; j < pCollide->solidCount; j++ )
  4166. {
  4167. size += physcollision->CollideSize( pCollide->solids[j] );
  4168. }
  4169. size += pCollide->descSize;
  4170. if ( size )
  4171. {
  4172. modelsize_t elem;
  4173. elem.pName = m_InlineModels[i].strName;
  4174. elem.size = size;
  4175. list.Insert( elem );
  4176. }
  4177. }
  4178. }
  4179. Msg("VCollides loaded: %d\n", list.Count() );
  4180. int totalVCollideMemory = 0;
  4181. for ( i = 0; i < list.Count(); i++ )
  4182. {
  4183. Msg("%8d bytes:%s\n", list[i].size, list[i].pName);
  4184. totalVCollideMemory += list[i].size;
  4185. }
  4186. int bboxCount, bboxSize;
  4187. physcollision->GetBBoxCacheSize( &bboxSize, &bboxCount );
  4188. Msg( "%8d bytes BBox physics: %d boxes\n", bboxSize, bboxCount );
  4189. totalVCollideMemory += bboxSize;
  4190. Msg( "--------------\n%8d bytes total VCollide Memory\n", totalVCollideMemory );
  4191. }
  4192. //-----------------------------------------------------------------------------
  4193. // Is the model loaded?
  4194. //-----------------------------------------------------------------------------
  4195. bool CModelLoader::IsLoaded( const model_t *mod )
  4196. {
  4197. return (mod->nLoadFlags & FMODELLOADER_LOADED) != 0;
  4198. }
  4199. bool CModelLoader::LastLoadedMapHasHDRLighting(void)
  4200. {
  4201. return m_bMapHasHDRLighting;
  4202. }
  4203. //-----------------------------------------------------------------------------
  4204. // Loads a studio model
  4205. //-----------------------------------------------------------------------------
  4206. void CModelLoader::Studio_LoadModel( model_t *pModel, bool bTouchAllData )
  4207. {
  4208. if ( !mod_touchalldata.GetBool() )
  4209. {
  4210. bTouchAllData = false;
  4211. }
  4212. // a preloaded model requires specific fixup behavior
  4213. bool bPreLoaded = ( pModel->nLoadFlags & FMODELLOADER_LOADED_BY_PRELOAD ) != 0;
  4214. bool bLoadPhysics = true;
  4215. if ( pModel->nLoadFlags == FMODELLOADER_STATICPROP )
  4216. {
  4217. // this is the first call in loading as a static prop (load bit not set), don't load physics yet
  4218. // the next call in causes the physics to load
  4219. bLoadPhysics = false;
  4220. }
  4221. // mark as loaded and fixed up
  4222. pModel->nLoadFlags |= FMODELLOADER_LOADED;
  4223. pModel->nLoadFlags &= ~FMODELLOADER_LOADED_BY_PRELOAD;
  4224. if ( !bPreLoaded )
  4225. {
  4226. pModel->studio = g_pMDLCache->FindMDL( pModel->strName );
  4227. g_pMDLCache->SetUserData( pModel->studio, pModel );
  4228. InitStudioModelState( pModel );
  4229. }
  4230. // Get the studiohdr into the cache
  4231. studiohdr_t *pStudioHdr = g_pMDLCache->GetStudioHdr( pModel->studio );
  4232. (void) pStudioHdr;
  4233. // a preloaded model alrady has its physics data resident
  4234. if ( bLoadPhysics && !bPreLoaded )
  4235. {
  4236. // load the collision data now
  4237. bool bSynchronous = bTouchAllData;
  4238. double t1 = Plat_FloatTime();
  4239. g_pMDLCache->GetVCollideEx( pModel->studio, bSynchronous );
  4240. double t2 = Plat_FloatTime();
  4241. if ( bSynchronous )
  4242. {
  4243. g_flAccumulatedModelLoadTimeVCollideSync += ( t2 - t1 );
  4244. }
  4245. else
  4246. {
  4247. g_flAccumulatedModelLoadTimeVCollideAsync += ( t2 - t1 );
  4248. }
  4249. }
  4250. // this forces sync setup operations (materials/shaders) to build out now during load and not at runtime
  4251. double t1 = Plat_FloatTime();
  4252. // should already be NULL, but better safe than sorry
  4253. if ( pModel->ppMaterials )
  4254. {
  4255. free( pModel->ppMaterials - 1 );
  4256. pModel->ppMaterials = NULL;
  4257. }
  4258. IMaterial *pMaterials[128];
  4259. int nMaterials = Mod_GetModelMaterials( pModel, ARRAYSIZE( pMaterials ), pMaterials );
  4260. if ( pModel->nLoadFlags & FMODELLOADER_DYNAMIC )
  4261. {
  4262. // Cache the material pointers so that we don't re-scan all the VMTs on dynamic unload
  4263. COMPILE_TIME_ASSERT( sizeof( intptr_t ) == sizeof( IMaterial * ) );
  4264. IMaterial **pMem = (IMaterial**) malloc( (1 + nMaterials) * sizeof( IMaterial* ) );
  4265. *(intptr_t*)pMem = nMaterials;
  4266. pModel->ppMaterials = pMem + 1;
  4267. for ( int i=0; i<nMaterials; i++ )
  4268. {
  4269. pModel->ppMaterials[i] = pMaterials[i];
  4270. }
  4271. }
  4272. if ( nMaterials )
  4273. {
  4274. for ( int i=0; i<nMaterials; i++ )
  4275. {
  4276. pMaterials[i]->IncrementReferenceCount();
  4277. }
  4278. // track the refcount bump
  4279. pModel->nLoadFlags |= FMODELLOADER_TOUCHED_MATERIALS;
  4280. }
  4281. double t2 = Plat_FloatTime();
  4282. g_flAccumulatedModelLoadTimeMaterialNamesOnly += ( t2 - t1 );
  4283. // a preloaded model must touch its children
  4284. if ( bTouchAllData || bPreLoaded )
  4285. {
  4286. Mod_TouchAllData( pModel, Host_GetServerCount() );
  4287. }
  4288. }
  4289. //-----------------------------------------------------------------------------
  4290. // Purpose:
  4291. // Input : *mod -
  4292. //-----------------------------------------------------------------------------
  4293. void CModelLoader::Studio_UnloadModel( model_t *pModel )
  4294. {
  4295. // Do not unload models that are still referenced by the dynamic system
  4296. if ( pModel->nLoadFlags & FMODELLOADER_DYNAMIC )
  4297. {
  4298. return;
  4299. }
  4300. if ( pModel->nLoadFlags & FMODELLOADER_TOUCHED_MATERIALS )
  4301. {
  4302. IMaterial *pMaterials[128];
  4303. int nMaterials = Mod_GetModelMaterials( pModel, ARRAYSIZE( pMaterials ), &pMaterials[0] );
  4304. for ( int j=0; j<nMaterials; j++ )
  4305. {
  4306. pMaterials[j]->DecrementReferenceCount();
  4307. }
  4308. pModel->nLoadFlags &= ~FMODELLOADER_TOUCHED_MATERIALS;
  4309. }
  4310. // leave these flags alone since we are going to return from alt-tab at some point.
  4311. // Assert( !( mod->needload & FMODELLOADER_REFERENCEMASK ) );
  4312. pModel->nLoadFlags &= ~( FMODELLOADER_LOADED | FMODELLOADER_LOADED_BY_PRELOAD );
  4313. if ( IsX360() )
  4314. {
  4315. // 360 doesn't need to keep the reference flags, but the PC does
  4316. pModel->nLoadFlags &= ~FMODELLOADER_REFERENCEMASK;
  4317. }
  4318. #ifdef DBGFLAG_ASSERT
  4319. int nRef =
  4320. #endif
  4321. g_pMDLCache->Release( pModel->studio );
  4322. // the refcounts must be as expected, or evil latent bugs will occur
  4323. Assert( InEditMode() || ( nRef == 0 ) );
  4324. if ( pModel->ppMaterials )
  4325. {
  4326. free( pModel->ppMaterials - 1 );
  4327. pModel->ppMaterials = NULL;
  4328. }
  4329. pModel->studio = MDLHANDLE_INVALID;
  4330. pModel->type = mod_bad;
  4331. }
  4332. //-----------------------------------------------------------------------------
  4333. // Purpose:
  4334. // Input : *mod -
  4335. //-----------------------------------------------------------------------------
  4336. void CModelLoader::SetWorldModel( model_t *mod )
  4337. {
  4338. Assert( mod );
  4339. m_pWorldModel = mod;
  4340. // host_state.SetWorldModel( mod ); // garymcthack
  4341. }
  4342. //-----------------------------------------------------------------------------
  4343. // Purpose:
  4344. //-----------------------------------------------------------------------------
  4345. void CModelLoader::ClearWorldModel( void )
  4346. {
  4347. m_pWorldModel = NULL;
  4348. memset( &m_worldBrushData, 0, sizeof(m_worldBrushData) );
  4349. m_InlineModels.Purge();
  4350. }
  4351. //-----------------------------------------------------------------------------
  4352. // Purpose:
  4353. // Output : Returns true on success, false on failure.
  4354. //-----------------------------------------------------------------------------
  4355. bool CModelLoader::IsWorldModelSet( void )
  4356. {
  4357. return m_pWorldModel ? true : false;
  4358. }
  4359. //-----------------------------------------------------------------------------
  4360. // Purpose:
  4361. // Output : int
  4362. //-----------------------------------------------------------------------------
  4363. int CModelLoader::GetNumWorldSubmodels( void )
  4364. {
  4365. if ( !IsWorldModelSet() )
  4366. return 0;
  4367. return m_worldBrushData.numsubmodels;
  4368. }
  4369. //-----------------------------------------------------------------------------
  4370. // Purpose: Check cache or union data for info, reload studio model if needed
  4371. // Input : *model -
  4372. //-----------------------------------------------------------------------------
  4373. void *CModelLoader::GetExtraData( model_t *model )
  4374. {
  4375. if ( !model )
  4376. {
  4377. return NULL;
  4378. }
  4379. switch ( model->type )
  4380. {
  4381. case mod_sprite:
  4382. {
  4383. // sprites don't use the real cache yet
  4384. if ( model->type == mod_sprite )
  4385. {
  4386. // The sprite got unloaded.
  4387. if ( !( FMODELLOADER_LOADED & model->nLoadFlags ) )
  4388. {
  4389. return NULL;
  4390. }
  4391. return model->sprite.sprite;
  4392. }
  4393. }
  4394. break;
  4395. case mod_studio:
  4396. return g_pMDLCache->GetStudioHdr( model->studio );
  4397. default:
  4398. case mod_brush:
  4399. // Should never happen
  4400. Assert( 0 );
  4401. break;
  4402. };
  4403. return NULL;
  4404. }
  4405. //-----------------------------------------------------------------------------
  4406. // Purpose:
  4407. // Output : Returns true on success, false on failure.
  4408. //-----------------------------------------------------------------------------
  4409. bool CModelLoader::Map_GetRenderInfoAllocated( void )
  4410. {
  4411. return m_bMapRenderInfoLoaded;
  4412. }
  4413. //-----------------------------------------------------------------------------
  4414. // Purpose:
  4415. //-----------------------------------------------------------------------------
  4416. void CModelLoader::Map_SetRenderInfoAllocated( bool allocated )
  4417. {
  4418. m_bMapRenderInfoLoaded = allocated;
  4419. }
  4420. //-----------------------------------------------------------------------------
  4421. // Purpose:
  4422. // Input : *mod -
  4423. //-----------------------------------------------------------------------------
  4424. void CModelLoader::Map_LoadDisplacements( model_t *pModel, bool bRestoring )
  4425. {
  4426. if ( !pModel )
  4427. {
  4428. Assert( false );
  4429. return;
  4430. }
  4431. Q_FileBase( pModel->strName, m_szLoadName, sizeof( m_szLoadName ) );
  4432. CMapLoadHelper::Init( pModel, m_szLoadName );
  4433. DispInfo_LoadDisplacements( pModel, bRestoring );
  4434. CMapLoadHelper::Shutdown();
  4435. }
  4436. //-----------------------------------------------------------------------------
  4437. // Purpose: List the model dictionary
  4438. //-----------------------------------------------------------------------------
  4439. void CModelLoader::Print( void )
  4440. {
  4441. ConMsg( "Models:\n" );
  4442. FOR_EACH_MAP_FAST( m_Models, i )
  4443. {
  4444. model_t *pModel = m_Models[i].modelpointer;
  4445. if ( pModel->type == mod_studio || pModel->type == mod_bad )
  4446. {
  4447. // studio models have ref counts
  4448. // bad models are unloaded models which need to be listed
  4449. int refCount = ( pModel->type == mod_studio ) ? g_pMDLCache->GetRef( pModel->studio ) : 0;
  4450. ConMsg( "%4d: Flags:0x%8.8x RefCount:%2d %s\n", i, pModel->nLoadFlags, refCount, pModel->strName.String() );
  4451. }
  4452. else
  4453. {
  4454. ConMsg( "%4d: Flags:0x%8.8x %s\n", i, pModel->nLoadFlags, pModel->strName.String() );
  4455. }
  4456. }
  4457. }
  4458. //-----------------------------------------------------------------------------
  4459. // Callback for UpdateOrCreate utility function - swaps a bsp.
  4460. //-----------------------------------------------------------------------------
  4461. #if defined( _X360 )
  4462. static bool BSPCreateCallback( const char *pSourceName, const char *pTargetName, const char *pPathID, void *pExtraData )
  4463. {
  4464. // load the bsppack dll
  4465. IBSPPack *iBSPPack = NULL;
  4466. CSysModule *pmodule = g_pFullFileSystem->LoadModule( "bsppack" );
  4467. if ( pmodule )
  4468. {
  4469. CreateInterfaceFn factory = Sys_GetFactory( pmodule );
  4470. if ( factory )
  4471. {
  4472. iBSPPack = ( IBSPPack * )factory( IBSPPACK_VERSION_STRING, NULL );
  4473. }
  4474. }
  4475. if( !iBSPPack )
  4476. {
  4477. Warning( "Can't load bsppack.dll - unable to swap bsp.\n" );
  4478. return false;
  4479. }
  4480. bool bOk = true;
  4481. if ( !iBSPPack->SwapBSPFile( g_pFileSystem, pSourceName, pTargetName, IsX360(), ConvertVTFTo360Format, NULL, NULL ) )
  4482. {
  4483. bOk = false;
  4484. Warning( "Failed to create %s\n", pTargetName );
  4485. }
  4486. Sys_UnloadModule( pmodule );
  4487. return bOk;
  4488. }
  4489. #endif
  4490. //-----------------------------------------------------------------------------
  4491. // Calls utility function to create .360 version of a file.
  4492. //-----------------------------------------------------------------------------
  4493. int CModelLoader::UpdateOrCreate( const char *pSourceName, char *pTargetName, int targetLen, bool bForce )
  4494. {
  4495. #if defined( _X360 )
  4496. return ::UpdateOrCreate( pSourceName, pTargetName, targetLen, NULL, BSPCreateCallback, bForce );
  4497. #else
  4498. return UOC_NOT_CREATED;
  4499. #endif
  4500. }
  4501. //-----------------------------------------------------------------------------
  4502. // Purpose: Determine if specified .bsp is valid
  4503. // Input : *mapname -
  4504. // Output : Returns true on success, false on failure.
  4505. //-----------------------------------------------------------------------------
  4506. bool CModelLoader::Map_IsValid( char const *pMapFile, bool bQuiet /* = false */ )
  4507. {
  4508. static char s_szLastMapFile[MAX_PATH] = { 0 };
  4509. if ( !pMapFile || !pMapFile[0] )
  4510. {
  4511. if ( !bQuiet )
  4512. {
  4513. ConMsg( "CModelLoader::Map_IsValid: Empty mapname!!!\n" );
  4514. }
  4515. return false;
  4516. }
  4517. char szMapFile[MAX_PATH] = { 0 };
  4518. V_strncpy( szMapFile, pMapFile, sizeof( szMapFile ) );
  4519. if ( IsX360() && !V_stricmp( szMapFile, s_szLastMapFile ) )
  4520. {
  4521. // already been checked, no reason to do multiple i/o validations
  4522. return true;
  4523. }
  4524. // Blacklist some characters
  4525. // - Don't allow characters not allowed on all supported platforms for consistency
  4526. // - Don't allow quotes or ;"' as defense-in-depth against script abuses (and, no real reason for mapnames to use these)
  4527. const char *pBaseFileName = V_UnqualifiedFileName( pMapFile );
  4528. bool bIllegalChar = false;
  4529. for (; pBaseFileName && *pBaseFileName; pBaseFileName++ )
  4530. {
  4531. // ASCII control characters (codepoints <= 31) illegal in windows filenames
  4532. if ( *pBaseFileName <= (char)31 )
  4533. bIllegalChar = true;
  4534. switch ( *pBaseFileName )
  4535. {
  4536. // Illegal in windows filenames, don't allow on any platform
  4537. case '<': case '>': case ':': case '"': case '/': case '\\':
  4538. case '|': case '?': case '*':
  4539. bIllegalChar = true;
  4540. // Additional special characters in source engine commands, defense-in-depth against things that might be
  4541. // composing commands with map names (though they really shouldn't be)
  4542. case ';': case '\'':
  4543. bIllegalChar = true;
  4544. default: break;
  4545. }
  4546. }
  4547. if ( bIllegalChar )
  4548. {
  4549. Assert( !"Map with illegal characters in filename" );
  4550. Warning( "Map with illegal characters in filename\n" );
  4551. return false;
  4552. }
  4553. FileHandle_t mapfile;
  4554. if ( IsX360() )
  4555. {
  4556. char szMapName360[MAX_PATH];
  4557. UpdateOrCreate( szMapFile, szMapName360, sizeof( szMapName360 ), false );
  4558. V_strcpy_safe( szMapFile, szMapName360 );
  4559. }
  4560. mapfile = g_pFileSystem->OpenEx( szMapFile, "rb", IsX360() ? FSOPEN_NEVERINPACK : 0, "GAME" );
  4561. if ( mapfile != FILESYSTEM_INVALID_HANDLE )
  4562. {
  4563. dheader_t header;
  4564. memset( &header, 0, sizeof( header ) );
  4565. g_pFileSystem->Read( &header, sizeof( dheader_t ), mapfile );
  4566. g_pFileSystem->Close( mapfile );
  4567. if ( header.ident == IDBSPHEADER )
  4568. {
  4569. if ( header.version >= MINBSPVERSION && header.version <= BSPVERSION )
  4570. {
  4571. V_strncpy( s_szLastMapFile, szMapFile, sizeof( s_szLastMapFile ) );
  4572. return true;
  4573. }
  4574. else
  4575. {
  4576. if ( !bQuiet )
  4577. {
  4578. Warning( "CModelLoader::Map_IsValid: Map '%s' bsp version %i, expecting %i\n", szMapFile, header.version, BSPVERSION );
  4579. }
  4580. }
  4581. }
  4582. else
  4583. {
  4584. if ( !bQuiet )
  4585. {
  4586. Warning( "CModelLoader::Map_IsValid: '%s' is not a valid BSP file\n", szMapFile );
  4587. }
  4588. }
  4589. }
  4590. else
  4591. {
  4592. if ( !bQuiet )
  4593. {
  4594. Warning( "CModelLoader::Map_IsValid: No such map '%s'\n", szMapFile );
  4595. }
  4596. }
  4597. // Get outta here if we are checking vidmemstats.
  4598. if ( CommandLine()->CheckParm( "-dumpvidmemstats" ) )
  4599. {
  4600. Cbuf_AddText( "quit\n" );
  4601. }
  4602. return false;
  4603. }
  4604. model_t *CModelLoader::FindModelNoCreate( const char *pModelName )
  4605. {
  4606. FileNameHandle_t fnHandle = g_pFileSystem->FindOrAddFileName( pModelName );
  4607. int i = m_Models.Find( fnHandle );
  4608. if ( i != m_Models.InvalidIndex() )
  4609. {
  4610. return m_Models[i].modelpointer;
  4611. }
  4612. // not found
  4613. return NULL;
  4614. }
  4615. modtype_t CModelLoader::GetTypeFromName( const char *pModelName )
  4616. {
  4617. // HACK HACK, force sprites to correctly
  4618. const char *pExt = V_GetFileExtension( pModelName );
  4619. if ( pExt )
  4620. {
  4621. if ( !V_stricmp( pExt, "spr" ) || !V_stricmp( pExt, "vmt" ) )
  4622. {
  4623. return mod_sprite;
  4624. }
  4625. else if ( !V_stricmp( pExt, "bsp" ) )
  4626. {
  4627. return mod_brush;
  4628. }
  4629. else if ( !V_stricmp( pExt, "mdl" ) )
  4630. {
  4631. return mod_studio;
  4632. }
  4633. else if ( g_pVideo != NULL && g_pVideo->LocateVideoSystemForPlayingFile( pModelName) != VideoSystem::NONE ) // video sprite
  4634. {
  4635. return mod_sprite;
  4636. }
  4637. }
  4638. return mod_bad;
  4639. }
  4640. int CModelLoader::FindNext( int iIndex, model_t **ppModel )
  4641. {
  4642. if ( iIndex == -1 && m_Models.Count() )
  4643. {
  4644. iIndex = m_Models.FirstInorder();
  4645. }
  4646. else if ( !m_Models.Count() || !m_Models.IsValidIndex( iIndex ) )
  4647. {
  4648. *ppModel = NULL;
  4649. return -1;
  4650. }
  4651. *ppModel = m_Models[iIndex].modelpointer;
  4652. iIndex = m_Models.NextInorder( iIndex );
  4653. if ( iIndex == m_Models.InvalidIndex() )
  4654. {
  4655. // end of list
  4656. iIndex = -1;
  4657. }
  4658. return iIndex;
  4659. }
  4660. void CModelLoader::UnloadModel( model_t *pModel )
  4661. {
  4662. switch ( pModel->type )
  4663. {
  4664. case mod_brush:
  4665. // Let it free data or call destructors..
  4666. Map_UnloadModel( pModel );
  4667. // Remove from file system
  4668. g_pFileSystem->RemoveSearchPath( pModel->strName, "GAME" );
  4669. m_szActiveMapName[0] = '\0';
  4670. break;
  4671. case mod_studio:
  4672. Studio_UnloadModel( pModel );
  4673. break;
  4674. case mod_sprite:
  4675. Sprite_UnloadModel( pModel );
  4676. break;
  4677. }
  4678. }
  4679. const char *CModelLoader::GetActiveMapName( void )
  4680. {
  4681. return m_szActiveMapName;
  4682. }
  4683. model_t *CModelLoader::GetDynamicModel( const char *name, bool bClientOnly )
  4684. {
  4685. if ( !name || !name[0] )
  4686. {
  4687. name = "models/empty.mdl";
  4688. }
  4689. Assert( V_strnicmp( name, "models/", 7 ) == 0 && V_strstr( name, ".mdl" ) != NULL );
  4690. model_t *pModel = FindModel( name );
  4691. Assert( pModel );
  4692. CDynamicModelInfo &dyn = m_DynamicModels[ m_DynamicModels.Insert( pModel ) ]; // Insert returns existing if key is already set
  4693. if ( dyn.m_nLoadFlags == CDynamicModelInfo::INVALIDFLAG )
  4694. {
  4695. dyn.m_nLoadFlags = 0;
  4696. DynamicModelDebugMsg( "model %p [%s] registered\n", pModel, pModel->strName.String() );
  4697. }
  4698. dyn.m_uLastTouchedMS_Div256 = Plat_MSTime() >> 8;
  4699. return pModel;
  4700. }
  4701. void CModelLoader::UpdateDynamicModelLoadQueue()
  4702. {
  4703. if ( mod_dynamicloadpause.GetBool() )
  4704. return;
  4705. static double s_LastDynamicLoadTime = 0.0;
  4706. if ( mod_dynamicloadthrottle.GetFloat() > 0 && Plat_FloatTime() < s_LastDynamicLoadTime + mod_dynamicloadthrottle.GetFloat() )
  4707. return;
  4708. if ( m_bDynamicLoadQueueHeadActive )
  4709. {
  4710. Assert( m_DynamicModelLoadQueue.Count() >= 1 );
  4711. MaterialLock_t matLock = g_pMaterialSystem->Lock(); // ASDFADFASFASEGAafliejsfjaslaslgsaigas
  4712. bool bComplete = g_pQueuedLoader->CompleteDynamicLoad();
  4713. g_pMaterialSystem->Unlock(matLock);
  4714. if ( bComplete )
  4715. {
  4716. model_t *pModel = m_DynamicModelLoadQueue[0];
  4717. m_DynamicModelLoadQueue.Remove(0);
  4718. m_bDynamicLoadQueueHeadActive = false;
  4719. Assert( pModel->nLoadFlags & FMODELLOADER_DYNAMIC );
  4720. Assert( pModel->type == mod_bad || ( pModel->nLoadFlags & (FMODELLOADER_LOADED | FMODELLOADER_LOADED_BY_PRELOAD) ) );
  4721. (void) LoadModel( pModel, NULL );
  4722. Assert( pModel->type == mod_studio );
  4723. UtlHashHandle_t hDyn = m_DynamicModels.Find( pModel );
  4724. Assert( hDyn != m_DynamicModels.InvalidHandle() );
  4725. if ( hDyn != m_DynamicModels.InvalidHandle() )
  4726. {
  4727. CDynamicModelInfo &dyn = m_DynamicModels[hDyn];
  4728. Assert( dyn.m_nLoadFlags & CDynamicModelInfo::QUEUED );
  4729. Assert( dyn.m_nLoadFlags & CDynamicModelInfo::LOADING );
  4730. dyn.m_nLoadFlags &= ~( CDynamicModelInfo::QUEUED | CDynamicModelInfo::LOADING );
  4731. g_pMDLCache->LockStudioHdr( pModel->studio );
  4732. dyn.m_nLoadFlags |= CDynamicModelInfo::CLIENTREADY;
  4733. dyn.m_uLastTouchedMS_Div256 = Plat_MSTime() >> 8;
  4734. FinishDynamicModelLoadIfReady( &dyn, pModel );
  4735. }
  4736. // do the clean up after we're actually done
  4737. // we keep some file cache around to make sure that LoadModel doesn't do blocking load
  4738. g_pQueuedLoader->CleanupDynamicLoad();
  4739. s_LastDynamicLoadTime = Plat_FloatTime();
  4740. }
  4741. }
  4742. // If we're not working, and we have work to do, and the queued loader is open for business...
  4743. if ( !m_bDynamicLoadQueueHeadActive && m_DynamicModelLoadQueue.Count() > 0 && g_pQueuedLoader->IsFinished() )
  4744. {
  4745. model_t *pModel = m_DynamicModelLoadQueue[0];
  4746. UtlHashHandle_t hDyn = m_DynamicModels.Find( pModel );
  4747. Assert( hDyn != m_DynamicModels.InvalidHandle() );
  4748. if ( hDyn != m_DynamicModels.InvalidHandle() )
  4749. {
  4750. m_bDynamicLoadQueueHeadActive = true;
  4751. CDynamicModelInfo &dyn = m_DynamicModels[hDyn];
  4752. Assert( dyn.m_nLoadFlags & CDynamicModelInfo::QUEUED );
  4753. Assert( !(dyn.m_nLoadFlags & CDynamicModelInfo::LOADING) );
  4754. Assert( !(dyn.m_nLoadFlags & CDynamicModelInfo::CLIENTREADY) );
  4755. dyn.m_nLoadFlags |= CDynamicModelInfo::LOADING;
  4756. // the queued loader is very ... particular about path names. it doesn't like leading "models/"
  4757. const char* pName = pModel->strName;
  4758. if ( V_strnicmp( pName, "models", 6 ) == 0 && ( pName[6] == '/' || pName[6] == '\\' ) )
  4759. {
  4760. pName += 7;
  4761. }
  4762. MaterialLock_t matLock = g_pMaterialSystem->Lock();
  4763. g_pQueuedLoader->DynamicLoadMapResource( pName, NULL, NULL, NULL );
  4764. g_pMaterialSystem->Unlock(matLock);
  4765. }
  4766. else
  4767. {
  4768. m_DynamicModelLoadQueue.Remove(0);
  4769. }
  4770. }
  4771. }
  4772. void CModelLoader::FinishDynamicModelLoadIfReady( CDynamicModelInfo *pDyn, model_t *pModel )
  4773. {
  4774. CDynamicModelInfo &dyn = *pDyn;
  4775. if ( ( dyn.m_nLoadFlags & CDynamicModelInfo::CLIENTREADY ) )
  4776. {
  4777. if ( !( dyn.m_nLoadFlags & CDynamicModelInfo::SERVERLOADING ) )
  4778. {
  4779. // There ought to be a better way to plumb this through, but this should be ok...
  4780. if ( sv.GetDynamicModelsTable() )
  4781. {
  4782. int netidx = sv.GetDynamicModelsTable()->FindStringIndex( pModel->strName );
  4783. if ( netidx != INVALID_STRING_INDEX )
  4784. {
  4785. char nIsLoaded = 1;
  4786. sv.GetDynamicModelsTable()->SetStringUserData( netidx, 1, &nIsLoaded );
  4787. }
  4788. }
  4789. DynamicModelDebugMsg( "model %p [%s] loaded\n", pModel, pModel->strName.String() );
  4790. dyn.m_nLoadFlags |= CDynamicModelInfo::ALLREADY;
  4791. // Reverse order; UnregisterModelLoadCallback does a FastRemove that swaps from back
  4792. for ( int i = dyn.m_Callbacks.Count()-1; i >= 0; --i )
  4793. {
  4794. uintptr_t callbackID = dyn.m_Callbacks[ i ];
  4795. bool bClientOnly = (bool)(callbackID & 1);
  4796. IModelLoadCallback* pCallback = ( IModelLoadCallback* )( callbackID & ~1 );
  4797. UnregisterModelLoadCallback( pModel, bClientOnly, pCallback );
  4798. pCallback->OnModelLoadComplete( pModel );
  4799. }
  4800. }
  4801. else
  4802. {
  4803. // Reverse order; UnregisterModelLoadCallback does a FastRemove that swaps from back
  4804. for ( int i = dyn.m_Callbacks.Count()-1; i >= 0; --i )
  4805. {
  4806. uintptr_t callbackID = dyn.m_Callbacks[ i ];
  4807. bool bClientOnly = (bool)(callbackID & 1);
  4808. IModelLoadCallback* pCallback = ( IModelLoadCallback* )( callbackID & ~1 );
  4809. if ( bClientOnly )
  4810. {
  4811. UnregisterModelLoadCallback( pModel, true, pCallback );
  4812. pCallback->OnModelLoadComplete( pModel );
  4813. }
  4814. }
  4815. }
  4816. }
  4817. }
  4818. bool CModelLoader::RegisterModelLoadCallback( model_t *pModel, bool bClientOnly, IModelLoadCallback *pCallback, bool bCallImmediatelyIfLoaded )
  4819. {
  4820. UtlHashHandle_t hDyn = m_DynamicModels.Find( pModel );
  4821. Assert( hDyn != m_DynamicModels.InvalidHandle() );
  4822. if ( hDyn == m_DynamicModels.InvalidHandle() )
  4823. return false;
  4824. Assert( ((uintptr_t)pCallback & 1) == 0 );
  4825. uintptr_t callbackID = (uintptr_t)pCallback | (uintptr_t)bClientOnly;
  4826. int readyFlag = bClientOnly ? CDynamicModelInfo::CLIENTREADY : CDynamicModelInfo::ALLREADY;
  4827. CDynamicModelInfo &dyn = m_DynamicModels[ hDyn ];
  4828. AssertMsg( dyn.m_iRefCount > 0, "RegisterModelLoadCallback requires non-zero model refcount" );
  4829. if ( dyn.m_nLoadFlags & readyFlag )
  4830. {
  4831. if ( !bCallImmediatelyIfLoaded )
  4832. return false;
  4833. pCallback->OnModelLoadComplete( pModel );
  4834. }
  4835. else
  4836. {
  4837. if ( !dyn.m_Callbacks.HasElement( callbackID ) )
  4838. {
  4839. dyn.m_Callbacks.AddToTail( callbackID );
  4840. // Set registration count for callback pointer
  4841. m_RegisteredDynamicCallbacks[ m_RegisteredDynamicCallbacks.Insert( callbackID, 0 ) ]++;
  4842. }
  4843. }
  4844. return true;
  4845. }
  4846. bool CModelLoader::IsDynamicModelLoading( model_t *pModel, bool bClientOnly )
  4847. {
  4848. Assert( pModel->nLoadFlags & FMODELLOADER_DYNAMIC );
  4849. UtlHashHandle_t hDyn = m_DynamicModels.Find( pModel );
  4850. Assert( hDyn != m_DynamicModels.InvalidHandle() );
  4851. if ( hDyn != m_DynamicModels.InvalidHandle() )
  4852. {
  4853. CDynamicModelInfo &dyn = m_DynamicModels[ hDyn ];
  4854. AssertMsg( dyn.m_iRefCount > 0, "dynamic model state cannot be queried with zero refcount" );
  4855. if ( dyn.m_iRefCount > 0 )
  4856. {
  4857. int readyFlag = bClientOnly ? CDynamicModelInfo::CLIENTREADY : CDynamicModelInfo::ALLREADY;
  4858. return !( dyn.m_nLoadFlags & readyFlag );
  4859. }
  4860. }
  4861. return false;
  4862. }
  4863. void CModelLoader::AddRefDynamicModel( model_t *pModel, bool bClientSideRef )
  4864. {
  4865. extern IVModelInfo* modelinfo;
  4866. UtlHashHandle_t hDyn = m_DynamicModels.Insert( pModel );
  4867. CDynamicModelInfo& dyn = m_DynamicModels[ hDyn ];
  4868. dyn.m_iRefCount++;
  4869. dyn.m_iClientRefCount += ( bClientSideRef ? 1 : 0 );
  4870. Assert( dyn.m_iRefCount > 0 );
  4871. DynamicModelDebugMsg( "model %p [%s] addref %d (%d)\n", pModel, pModel->strName.String(), dyn.m_iRefCount, dyn.m_iClientRefCount );
  4872. if ( !( dyn.m_nLoadFlags & ( CDynamicModelInfo::QUEUED | CDynamicModelInfo::CLIENTREADY ) ) )
  4873. {
  4874. QueueDynamicModelLoad( &dyn, pModel );
  4875. // Try to kick it off asap if we aren't already busy.
  4876. if ( !m_bDynamicLoadQueueHeadActive )
  4877. {
  4878. UpdateDynamicModelLoadQueue();
  4879. }
  4880. }
  4881. }
  4882. void CModelLoader::ReleaseDynamicModel( model_t *pModel, bool bClientSideRef )
  4883. {
  4884. Assert( pModel->nLoadFlags & FMODELLOADER_DYNAMIC );
  4885. UtlHashHandle_t hDyn = m_DynamicModels.Find( pModel );
  4886. Assert( hDyn != m_DynamicModels.InvalidHandle() );
  4887. if ( hDyn != m_DynamicModels.InvalidHandle() )
  4888. {
  4889. CDynamicModelInfo &dyn = m_DynamicModels[ hDyn ];
  4890. Assert( dyn.m_iRefCount > 0 );
  4891. if ( dyn.m_iRefCount > 0 )
  4892. {
  4893. DynamicModelDebugMsg( "model %p [%s] release %d (%dc)\n", pModel, pModel->strName.String(), dyn.m_iRefCount, dyn.m_iClientRefCount );
  4894. dyn.m_iRefCount--;
  4895. dyn.m_iClientRefCount -= ( bClientSideRef ? 1 : 0 );
  4896. Assert( dyn.m_iClientRefCount >= 0 );
  4897. if ( dyn.m_iClientRefCount < 0 )
  4898. dyn.m_iClientRefCount = 0;
  4899. dyn.m_uLastTouchedMS_Div256 = Plat_MSTime() >> 8;
  4900. }
  4901. }
  4902. }
  4903. void CModelLoader::UnregisterModelLoadCallback( model_t *pModel, bool bClientOnly, IModelLoadCallback *pCallback )
  4904. {
  4905. Assert( ((uintptr_t)pCallback & 1) == 0 );
  4906. uintptr_t callbackID = (uintptr_t)pCallback | (uintptr_t)bClientOnly;
  4907. if ( int *pCallbackRegistrationCount = m_RegisteredDynamicCallbacks.GetPtr( callbackID ) )
  4908. {
  4909. if ( pModel )
  4910. {
  4911. UtlHashHandle_t i = m_DynamicModels.Find( pModel );
  4912. if ( i != m_DynamicModels.InvalidHandle() )
  4913. {
  4914. CDynamicModelInfo &dyn = m_DynamicModels[ i ];
  4915. if ( dyn.m_Callbacks.FindAndFastRemove( callbackID ) )
  4916. {
  4917. if ( dyn.m_Callbacks.Count() == 0 )
  4918. {
  4919. dyn.m_Callbacks.Purge();
  4920. }
  4921. if ( --(*pCallbackRegistrationCount) == 0 )
  4922. {
  4923. m_RegisteredDynamicCallbacks.Remove( callbackID );
  4924. return;
  4925. }
  4926. }
  4927. }
  4928. }
  4929. else
  4930. {
  4931. for ( UtlHashHandle_t i = m_DynamicModels.FirstHandle(); i != m_DynamicModels.InvalidHandle(); i = m_DynamicModels.NextHandle(i) )
  4932. {
  4933. CDynamicModelInfo &dyn = m_DynamicModels[ i ];
  4934. if ( dyn.m_Callbacks.FindAndFastRemove( callbackID ) )
  4935. {
  4936. if ( dyn.m_Callbacks.Count() == 0 )
  4937. {
  4938. dyn.m_Callbacks.Purge();
  4939. }
  4940. if ( --(*pCallbackRegistrationCount) == 0 )
  4941. {
  4942. m_RegisteredDynamicCallbacks.Remove( callbackID );
  4943. return;
  4944. }
  4945. }
  4946. }
  4947. }
  4948. }
  4949. }
  4950. void CModelLoader::QueueDynamicModelLoad( CDynamicModelInfo *dyn, model_t *mod )
  4951. {
  4952. Assert( !(dyn->m_nLoadFlags & CDynamicModelInfo::QUEUED) );
  4953. // Client-side entities have priority over server-side entities
  4954. // because they are more likely to be used in UI elements. --henryg
  4955. if ( dyn->m_iClientRefCount > 0 && m_DynamicModelLoadQueue.Count() > 1 )
  4956. {
  4957. m_DynamicModelLoadQueue.InsertAfter( 0, mod );
  4958. }
  4959. else
  4960. {
  4961. m_DynamicModelLoadQueue.AddToTail( mod );
  4962. }
  4963. dyn->m_nLoadFlags |= CDynamicModelInfo::QUEUED;
  4964. mod->nLoadFlags |= ( dyn->m_iClientRefCount > 0 ? FMODELLOADER_DYNCLIENT : FMODELLOADER_DYNSERVER );
  4965. }
  4966. bool CModelLoader::CancelDynamicModelLoad( CDynamicModelInfo *dyn, model_t *mod )
  4967. {
  4968. int i = m_DynamicModelLoadQueue.Find( mod );
  4969. Assert( (i < 0) == !(dyn->m_nLoadFlags & CDynamicModelInfo::QUEUED) );
  4970. if ( i >= 0 )
  4971. {
  4972. if ( i == 0 && m_bDynamicLoadQueueHeadActive )
  4973. {
  4974. Assert( dyn->m_nLoadFlags & CDynamicModelInfo::LOADING );
  4975. // can't remove head of queue
  4976. return false;
  4977. }
  4978. else
  4979. {
  4980. Assert( dyn->m_nLoadFlags & CDynamicModelInfo::QUEUED );
  4981. Assert( !(dyn->m_nLoadFlags & CDynamicModelInfo::LOADING) );
  4982. m_DynamicModelLoadQueue.Remove( i );
  4983. dyn->m_nLoadFlags &= ~CDynamicModelInfo::QUEUED;
  4984. mod->nLoadFlags &= ~FMODELLOADER_DYNAMIC;
  4985. return true;
  4986. }
  4987. }
  4988. return false;
  4989. }
  4990. void CModelLoader::InternalUpdateDynamicModels( bool bIgnoreTime )
  4991. {
  4992. const uint now = Plat_MSTime();
  4993. const uint delay = bIgnoreTime ? 0 : (int)( clamp( mod_dynamicunloadtime.GetFloat(), 1.f, 600.f ) * 1000 );
  4994. UpdateDynamicModelLoadQueue();
  4995. #ifdef _DEBUG
  4996. extern CNetworkStringTableContainer *networkStringTableContainerServer;
  4997. bool bPrevStringTableLockState = networkStringTableContainerServer->Lock( false );
  4998. #endif
  4999. // Scan for models to unload. TODO: accelerate with a "models to potentially unload" list?
  5000. UtlHashHandle_t i = m_DynamicModels.FirstHandle();
  5001. while ( i != m_DynamicModels.InvalidHandle() )
  5002. {
  5003. model_t *pModel = m_DynamicModels.Key( i );
  5004. CDynamicModelInfo& dyn = m_DynamicModels[ i ];
  5005. // UNLOAD THIS MODEL if zero refcount and not currently loading, and either timed out or never loaded
  5006. if ( dyn.m_iRefCount <= 0 && !(dyn.m_nLoadFlags & CDynamicModelInfo::LOADING) &&
  5007. ( ( now - (dyn.m_uLastTouchedMS_Div256 << 8) ) >= delay || !( dyn.m_nLoadFlags & CDynamicModelInfo::CLIENTREADY ) ) )
  5008. {
  5009. // Remove from load queue
  5010. if ( dyn.m_nLoadFlags & CDynamicModelInfo::QUEUED )
  5011. {
  5012. if ( !CancelDynamicModelLoad( &dyn, pModel ) )
  5013. {
  5014. // Couldn't remove from queue, advance to next entry and do not remove
  5015. i = m_DynamicModels.NextHandle(i);
  5016. continue;
  5017. }
  5018. }
  5019. // Unlock studiohdr_t
  5020. if ( dyn.m_nLoadFlags & CDynamicModelInfo::CLIENTREADY )
  5021. {
  5022. g_pMDLCache->UnlockStudioHdr( pModel->studio );
  5023. }
  5024. // There ought to be a better way to plumb this through, but this should be ok...
  5025. if ( sv.GetDynamicModelsTable() )
  5026. {
  5027. int netidx = sv.GetDynamicModelsTable()->FindStringIndex( pModel->strName );
  5028. if ( netidx != INVALID_STRING_INDEX )
  5029. {
  5030. char nIsLoaded = 0;
  5031. sv.GetDynamicModelsTable()->SetStringUserData( netidx, 1, &nIsLoaded );
  5032. }
  5033. }
  5034. if ( pModel->nLoadFlags & FMODELLOADER_DYNAMIC )
  5035. {
  5036. pModel->nLoadFlags &= ~FMODELLOADER_DYNAMIC;
  5037. // Actually unload the model if all system references are gone
  5038. if ( pModel->nLoadFlags & FMODELLOADER_REFERENCEMASK )
  5039. {
  5040. DynamicModelDebugMsg( "model %p [%s] unload - deferred: non-dynamic reference\n", pModel, pModel->strName.String() );
  5041. }
  5042. else
  5043. {
  5044. DynamicModelDebugMsg( "model %p [%s] unload\n", pModel, pModel->strName.String() );
  5045. Studio_UnloadModel( pModel );
  5046. if ( mod_dynamicunloadtextures.GetBool() )
  5047. {
  5048. materials->UncacheUnusedMaterials( false );
  5049. }
  5050. }
  5051. }
  5052. // Remove from table, advance to next entry
  5053. i = m_DynamicModels.RemoveAndAdvance(i);
  5054. continue;
  5055. }
  5056. // Advance to next entry in table
  5057. i = m_DynamicModels.NextHandle(i);
  5058. }
  5059. #ifdef _DEBUG
  5060. networkStringTableContainerServer->Lock( bPrevStringTableLockState );
  5061. #endif
  5062. }
  5063. void CModelLoader::Client_OnServerModelStateChanged( model_t *pModel, bool bServerLoaded )
  5064. {
  5065. #ifndef SWDS
  5066. // Listen server don't distinguish between server and client ready, never use SERVERLOADING flag
  5067. if ( sv.IsActive() )
  5068. return;
  5069. UtlHashHandle_t i = m_DynamicModels.Find( pModel );
  5070. if ( i != m_DynamicModels.InvalidHandle() )
  5071. {
  5072. CDynamicModelInfo &dyn = m_DynamicModels[i];
  5073. if ( !bServerLoaded )
  5074. {
  5075. if ( dyn.m_nLoadFlags & CDynamicModelInfo::ALLREADY )
  5076. DynamicModelDebugMsg( "dynamic model [%s] loaded on client but not server! is this bad? unknown...", pModel->strName.String() );
  5077. dyn.m_nLoadFlags &= ~CDynamicModelInfo::ALLREADY;
  5078. dyn.m_nLoadFlags |= CDynamicModelInfo::SERVERLOADING;
  5079. }
  5080. else
  5081. {
  5082. dyn.m_nLoadFlags &= ~CDynamicModelInfo::SERVERLOADING;
  5083. FinishDynamicModelLoadIfReady( &dyn, pModel );
  5084. }
  5085. }
  5086. #endif
  5087. }
  5088. void CModelLoader::ForceUnloadNonClientDynamicModels()
  5089. {
  5090. UtlHashHandle_t i = m_DynamicModels.FirstHandle();
  5091. while ( i != m_DynamicModels.InvalidHandle() )
  5092. {
  5093. CDynamicModelInfo &dyn = m_DynamicModels[i];
  5094. dyn.m_iRefCount = dyn.m_iClientRefCount;
  5095. i = m_DynamicModels.NextHandle( i );
  5096. }
  5097. // Flush everything
  5098. InternalUpdateDynamicModels( true );
  5099. }
  5100. // reconstruct the ambient lighting for a leaf at the given position in worldspace
  5101. void Mod_LeafAmbientColorAtPos( Vector *pOut, const Vector &pos, int leafIndex )
  5102. {
  5103. for ( int i = 0; i < 6; i++ )
  5104. {
  5105. pOut[i].Init();
  5106. }
  5107. mleafambientindex_t *pAmbient = &host_state.worldbrush->m_pLeafAmbient[leafIndex];
  5108. if ( !pAmbient->ambientSampleCount && pAmbient->firstAmbientSample )
  5109. {
  5110. // this leaf references another leaf, move there (this leaf is a solid leaf so it borrows samples from a neighbor)
  5111. leafIndex = pAmbient->firstAmbientSample;
  5112. pAmbient = &host_state.worldbrush->m_pLeafAmbient[leafIndex];
  5113. }
  5114. int count = pAmbient->ambientSampleCount;
  5115. if ( count > 0 )
  5116. {
  5117. int start = host_state.worldbrush->m_pLeafAmbient[leafIndex].firstAmbientSample;
  5118. mleafambientlighting_t *pSamples = host_state.worldbrush->m_pAmbientSamples + start;
  5119. mleaf_t *pLeaf = &host_state.worldbrush->leafs[leafIndex];
  5120. float totalFactor = 0;
  5121. for ( int i = 0; i < count; i++ )
  5122. {
  5123. // do an inverse squared distance weighted average of the samples to reconstruct
  5124. // the original function
  5125. // the sample positions are packed as leaf bounds fractions, compute
  5126. Vector samplePos = pLeaf->m_vecCenter - pLeaf->m_vecHalfDiagonal;
  5127. samplePos.x += float(pSamples[i].x) * pLeaf->m_vecHalfDiagonal.x * (2.0f / 255.0f);
  5128. samplePos.y += float(pSamples[i].y) * pLeaf->m_vecHalfDiagonal.y * (2.0f / 255.0f);
  5129. samplePos.z += float(pSamples[i].z) * pLeaf->m_vecHalfDiagonal.z * (2.0f / 255.0f);
  5130. float dist = (samplePos - pos).LengthSqr();
  5131. float factor = 1.0f / (dist + 1.0f);
  5132. totalFactor += factor;
  5133. for ( int j = 0; j < 6; j++ )
  5134. {
  5135. Vector v;
  5136. ColorRGBExp32ToVector( pSamples[i].cube.m_Color[j], v );
  5137. pOut[j] += v * factor;
  5138. }
  5139. }
  5140. for ( int i = 0; i < 6; i++ )
  5141. {
  5142. pOut[i] *= (1.0f / totalFactor);
  5143. }
  5144. }
  5145. }
  5146. #if defined( WIN32 )
  5147. int ComputeSize( studiohwdata_t *hwData, int *numVerts, int *pTriCount, bool onlyTopLod = false )
  5148. {
  5149. unsigned size = 0;
  5150. Assert(hwData && numVerts);
  5151. int max_lod = (onlyTopLod ? 1 : hwData->m_NumLODs);
  5152. *pTriCount = 0;
  5153. for ( int i=0; i < max_lod; i++ )
  5154. {
  5155. studioloddata_t *pLOD = &hwData->m_pLODs[i];
  5156. for ( int j = 0; j < hwData->m_NumStudioMeshes; j++ )
  5157. {
  5158. studiomeshdata_t *pMeshData = &pLOD->m_pMeshData[j];
  5159. for ( int k = 0; k < pMeshData->m_NumGroup; k++ )
  5160. {
  5161. studiomeshgroup_t *pMeshGroup = &pMeshData->m_pMeshGroup[k];
  5162. IMesh* mesh = pMeshGroup->m_pMesh;
  5163. size += mesh->ComputeMemoryUsed(); // Size of VB and IB
  5164. size += 2*pMeshGroup->m_NumVertices; // Size of m_pGroupIndexToMeshIndex[] array
  5165. *numVerts += mesh->VertexCount();
  5166. Assert( mesh->VertexCount() == pMeshGroup->m_NumVertices );
  5167. for ( int l = 0; l < pMeshGroup->m_NumStrips; ++l )
  5168. {
  5169. OptimizedModel::StripHeader_t *pStripData = &pMeshGroup->m_pStripData[l];
  5170. *pTriCount += pStripData->numIndices / 3;
  5171. }
  5172. }
  5173. }
  5174. }
  5175. return size;
  5176. }
  5177. // APSFIXME: needs to only do models that are resident, sizes might be wrong, i.e lacking compressed vert state?
  5178. CON_COMMAND_F( model_list, "Dump model list to file", FCVAR_CHEAT | FCVAR_DONTRECORD )
  5179. {
  5180. // don't run this on dedicated servers
  5181. if ( sv.IsDedicated() )
  5182. return;
  5183. if ( g_pFileSystem )
  5184. {
  5185. FileHandle_t fileHandle = g_pFileSystem->Open( "model_list.csv", "wt", "GAME" );
  5186. if ( fileHandle )
  5187. {
  5188. const char *substring = NULL;
  5189. if ( args.ArgC() > 1 )
  5190. {
  5191. substring = args[1];
  5192. }
  5193. g_pFileSystem->FPrintf( fileHandle, "name,dataSize,numVerts,nTriCount,dataSizeLod0,numVertsLod0,nTriCountLod0,numBones,numParts,numLODs,numMeshes\n" );
  5194. for ( int i = 0; i < modelloader->GetCount(); i++ )
  5195. {
  5196. const char* name = "Unknown";
  5197. int dataSizeLod0 = 0;
  5198. int dataSize = 0;
  5199. int numParts = 0;
  5200. int numBones = 0;
  5201. int numVertsLod0 = 0;
  5202. int numVerts = 0;
  5203. int numLODs = 0;
  5204. int numMeshes = 0;
  5205. int nTriCount = 0;
  5206. int nTriCountLod0 = 0;
  5207. model_t* model = modelloader->GetModelForIndex( i );
  5208. if ( model )
  5209. {
  5210. // other model types are not interesting
  5211. if ( model->type != mod_studio )
  5212. continue;
  5213. name = model->strName;
  5214. if ( substring && substring[0] )
  5215. {
  5216. if ( Q_stristr( name, substring ) == NULL )
  5217. continue;
  5218. }
  5219. studiohwdata_t *hwData = g_pMDLCache->GetHardwareData( model->studio );
  5220. if ( hwData )
  5221. {
  5222. numMeshes = hwData->m_NumStudioMeshes;
  5223. numLODs = hwData->m_NumLODs;
  5224. dataSize = ComputeSize( hwData, &numVerts, &nTriCount, false ); // Size of vertex data
  5225. dataSizeLod0 = ComputeSize( hwData, &numVertsLod0, &nTriCountLod0, true );
  5226. }
  5227. studiohdr_t *pStudioHdr = (studiohdr_t *)modelloader->GetExtraData( model );
  5228. dataSize += pStudioHdr->length; // Size of MDL file
  5229. numBones = pStudioHdr->numbones;
  5230. numParts = pStudioHdr->numbodyparts;
  5231. g_pFileSystem->FPrintf( fileHandle, "%s,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
  5232. name, dataSize, numVerts, nTriCount, dataSizeLod0, numVertsLod0, nTriCountLod0, numBones, numParts, numLODs, numMeshes );
  5233. }
  5234. }
  5235. g_pFileSystem->Close( fileHandle );
  5236. Msg( "Created \"model_list.csv\" in the game folder.\n" );
  5237. }
  5238. }
  5239. }
  5240. #endif // WIN32
  5241. CON_COMMAND_F( mod_dynamicmodeldebug, "debug spew for dynamic model loading", FCVAR_HIDDEN | FCVAR_DONTRECORD )
  5242. {
  5243. ((CModelLoader*)modelloader)->DebugPrintDynamicModels();
  5244. }
  5245. #include "server.h"
  5246. #ifndef SWDS
  5247. #include "client.h"
  5248. #endif
  5249. void CModelLoader::DebugPrintDynamicModels()
  5250. {
  5251. Msg( "network table (server):\n" );
  5252. if ( sv.GetDynamicModelsTable() )
  5253. {
  5254. for ( int i = 0; i < sv.GetDynamicModelsTable()->GetNumStrings(); ++i )
  5255. {
  5256. int dummy = 0;
  5257. char* data = (char*) sv.GetDynamicModelsTable()->GetStringUserData( i, &dummy );
  5258. bool bLoadedOnServer = !(data && dummy && data[0] == 0);
  5259. Msg( "%3i: %c %s\n", i, bLoadedOnServer ? '*' : ' ', sv.GetDynamicModelsTable()->GetString(i) );
  5260. }
  5261. }
  5262. #ifndef SWDS
  5263. Msg( "\nnetwork table (client):\n" );
  5264. if ( cl.m_pDynamicModelsTable )
  5265. {
  5266. for ( int i = 0; i < cl.m_pDynamicModelsTable->GetNumStrings(); ++i )
  5267. {
  5268. int dummy = 0;
  5269. char* data = (char*) cl.m_pDynamicModelsTable->GetStringUserData( i, &dummy );
  5270. bool bLoadedOnServer = !(data && dummy && data[0] == 0);
  5271. Msg( "%3i: %c %s\n", i, bLoadedOnServer ? '*' : ' ', cl.m_pDynamicModelsTable->GetString(i) );
  5272. }
  5273. }
  5274. #endif
  5275. extern IVModelInfo *modelinfo;
  5276. extern IVModelInfoClient *modelinfoclient;
  5277. Msg( "\ndynamic models:\n" );
  5278. for ( UtlHashHandle_t h = m_DynamicModels.FirstHandle(); h != m_DynamicModels.InvalidHandle(); h = m_DynamicModels.NextHandle(h) )
  5279. {
  5280. CDynamicModelInfo &dyn = m_DynamicModels[h];
  5281. int idx = modelinfo->GetModelIndex( m_DynamicModels.Key(h)->strName );
  5282. #ifndef SWDS
  5283. if ( idx == -1 ) idx = modelinfoclient->GetModelIndex( m_DynamicModels.Key(h)->strName );
  5284. #endif
  5285. Msg( "%d (%d%c): %s [ref: %d (%dc)] %s%s%s%s\n", idx, ((-2 - idx) >> 1), (idx & 1) ? 'c' : 's',
  5286. m_DynamicModels.Key(h)->strName.String(), dyn.m_iRefCount, dyn.m_iClientRefCount,
  5287. (dyn.m_nLoadFlags & CDynamicModelInfo::QUEUED) ? " QUEUED" : "",
  5288. (dyn.m_nLoadFlags & CDynamicModelInfo::LOADING) ? " LOADING" : "",
  5289. (dyn.m_nLoadFlags & CDynamicModelInfo::CLIENTREADY) ? " CLIENTREADY" : "",
  5290. (dyn.m_nLoadFlags & CDynamicModelInfo::ALLREADY) ? " ALLREADY" : "" );
  5291. }
  5292. }