Counter Strike : Global Offensive Source Code
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.

7578 lines
230 KiB

  1. //===== Copyright � 1996-2005, 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 "draw.h"
  14. #include "zone.h"
  15. #include "edict.h"
  16. #include "cmodel_engine.h"
  17. #include "cdll_engine_int.h"
  18. #include "iscratchpad3d.h"
  19. #include "materialsystem/imaterialsystemhardwareconfig.h"
  20. #include "materialsystem/materialsystem_config.h"
  21. #include "gl_rsurf.h"
  22. #include "avi/iavi.h"
  23. #include "avi/ibik.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 "optimize.h"
  31. #include "gl_drawlights.h"
  32. #include "tier0/icommandline.h"
  33. #include "MapReslistGenerator.h"
  34. #ifndef DEDICATED
  35. #include "vgui_baseui_interface.h"
  36. #endif
  37. #include "engine/ivmodelrender.h"
  38. #include "host.h"
  39. #include "datacache/idatacache.h"
  40. #include "sys_dll.h"
  41. #include "datacache/imdlcache.h"
  42. #include "gl_cvars.h"
  43. #include "vphysics_interface.h"
  44. #include "filesystem/IQueuedLoader.h"
  45. #include "tier2/tier2.h"
  46. #include "lightcache.h"
  47. #include "lumpfiles.h"
  48. #include "tier2/fileutils.h"
  49. #include "../utils/common/bsplib.h"
  50. #include "ibsppack.h"
  51. #include "utlsortvector.h"
  52. #include "utlhashtable.h"
  53. #include "UtlStringMap.h"
  54. #include "callqueue.h"
  55. #include "color.h"
  56. #include "tier1/lzmaDecoder.h"
  57. #include "eiface.h"
  58. #include "server.h"
  59. #include "ifilelist.h"
  60. #include "LoadScreenUpdate.h"
  61. #if defined( _X360 )
  62. #include "xbox/xbox_console.h"
  63. #elif defined( _PS3 )
  64. #include "ps3/ps3_console.h"
  65. #endif
  66. #include "materialsystem/imesh.h"
  67. #include "networkstringtable.h"
  68. #include "fmtstr.h"
  69. #include "engine_model_client.h"
  70. // memdbgon must be the last include file in a .cpp file!!!
  71. #include "tier0/memdbgon.h"
  72. // Uncomment this line to break down Map_LoadModel into
  73. // smaller, individual scopes.
  74. // #define MEM_DETAILED_ACCOUNTING_MAP_LOADMODEL
  75. ConVar mat_loadtextures( "mat_loadtextures", "1", FCVAR_CHEAT );
  76. static ConVar mod_touchalldata( "mod_touchalldata", "1", 0, "Touch model data during level startup" );
  77. static ConVar mod_forcetouchdata( "mod_forcetouchdata", "1", 0, "Forces all model file data into cache on model load." );
  78. ConVar mat_excludetextures( "mat_excludetextures", "0", 0 );
  79. ConVar r_unloadlightmaps( "r_unloadlightmaps", "0" );
  80. ConVar r_hunkalloclightmaps( "r_hunkalloclightmaps", "1" );
  81. // Not compatible for PC (due to ALT+TAB req's), mutually exclusive and similar to "unloadlightmaps", but keeps only
  82. // the styled lightmaps for animated light updates and discards the static portion of the lightmaps
  83. // (after lightmap page setup), so dlight support is severed when this is enabled.
  84. ConVar r_keepstyledlightmapsonly( "r_keepstyledlightmapsonly", IsGameConsole() ? "1" : "0" );
  85. // keep this many weapon view models resident, LRU purge others
  86. // clamped to minimum player inventory to prevent LRU and needing to
  87. ConVar mod_weaponviewmodelcache( "mod_WeaponViewModelCache", "8", 0, "", true, 0, false, 0 );
  88. // keep this many weapon world models resident, LRU purge others
  89. ConVar mod_weaponworldmodelcache( "mod_WeaponWorldModelCache", "10", 0, "", true, 0, false, 0 );
  90. ConVar mod_weaponworldmodelminage( "mod_WeaponWorldModelMinAge", "3000", 0, "", true, 0, false, 0 );
  91. #if !defined( DEDICATED )
  92. extern ConVar r_lightcache_zbuffercache;
  93. #endif
  94. bool g_bHunkAllocLightmaps;
  95. bool g_bClearingClientState = false;
  96. extern CGlobalVars g_ServerGlobalVariables;
  97. extern IMaterial *g_materialEmpty;
  98. extern ConVar r_rootlod;
  99. model_t *g_pSimpleWorldModel = NULL;
  100. model_t *g_pSimpleWorldModelWater = NULL;
  101. bool g_bLoadedMapHasBakedPropLighting = false;
  102. bool g_bBakedPropLightingNoSeparateHDR = false; // Some maps only have HDR lighting on props, contained in the file for non-hdr light data
  103. bool g_bHasLightmapAlphaData = false;
  104. bool g_bBakedPropLightingStreams3 = false;
  105. bool g_bHasLightmapAlphaData3 = false; // newer alpha data for CSM blending
  106. bool g_bHasIndirectOnlyInLightingStreams = false;
  107. bool g_bLightstylesWithCSM = false;
  108. double g_flAccumulatedModelLoadTime;
  109. double g_flAccumulatedModelLoadTimeStudio;
  110. double g_flAccumulatedModelLoadTimeStaticMesh;
  111. double g_flAccumulatedModelLoadTimeBrush;
  112. double g_flAccumulatedModelLoadTimeSprite;
  113. double g_flAccumulatedModelLoadTimeVCollideSync;
  114. double g_flAccumulatedModelLoadTimeVCollideAsync;
  115. double g_flAccumulatedModelLoadTimeVirtualModel;
  116. double g_flAccumulatedModelLoadTimeMaterialNamesOnly;
  117. static ConVar mod_dynamicunloadtime( "mod_dynamicunloadtime", "150", FCVAR_HIDDEN | FCVAR_DONTRECORD );
  118. static ConVar mod_dynamicunloadtextures( "mod_dynamicunloadtex", "1", FCVAR_HIDDEN | FCVAR_DONTRECORD );
  119. static ConVar mod_dynamicloadpause( "mod_dynamicloadpause", "0", FCVAR_CHEAT | FCVAR_HIDDEN | FCVAR_DONTRECORD );
  120. static ConVar mod_dynamicloadthrottle( "mod_dynamicloadthrottle", "0", FCVAR_CHEAT | FCVAR_HIDDEN | FCVAR_DONTRECORD );
  121. static ConVar mod_dynamicloadspew( "mod_dynamicloadspew", "0", FCVAR_HIDDEN | FCVAR_DONTRECORD );
  122. #define DynamicModelDebugMsg(...) ( mod_dynamicloadspew.GetBool() ? Msg(__VA_ARGS__) : (void)0 )
  123. //-----------------------------------------------------------------------------
  124. // A dictionary used to store where to find game lump data in the .bsp file
  125. //-----------------------------------------------------------------------------
  126. // Extended from the on-disk struct to include uncompressed size and stop propagation of bogus signed values
  127. struct dgamelump_internal_t
  128. {
  129. dgamelump_internal_t( dgamelump_t &other, unsigned int nCompressedSize )
  130. : id( other.id )
  131. , flags( other.flags )
  132. , version( other.version )
  133. , offset( Max( other.fileofs, 0 ) )
  134. , uncompressedSize( Max( other.filelen, 0 ) )
  135. , compressedSize( nCompressedSize )
  136. {}
  137. GameLumpId_t id;
  138. unsigned short flags;
  139. unsigned short version;
  140. unsigned int offset;
  141. unsigned int uncompressedSize;
  142. unsigned int compressedSize;
  143. };
  144. static CUtlVector< dgamelump_internal_t > g_GameLumpDict;
  145. static char g_GameLumpFilename[MAX_PATH];
  146. //-----------------------------------------------------------------------------
  147. void Con_ColorPrintf( const Color& clr, const char *fmt, ... );
  148. void NotifyHunkBeginMapLoad( const char *pszMapName )
  149. {
  150. // Set the estimated hunk size. For maps where there's versus versions, using the larger of the two
  151. struct EstimatedHunkSize_t
  152. {
  153. const char *pszMapRoot;
  154. int nBytes;
  155. };
  156. // These hunk sizes are used to set the initial commit amount.
  157. // They are an optimization and they don't need to be perfect. Setting
  158. // them a little bit low ensures that there is no wasted commit.
  159. static EstimatedHunkSize_t EstimatedHunkSizes[] =
  160. {
  161. // TODO: if Portal 2 map hunk sizes end up being highly variable, add entries here for maps
  162. // requiring > HUNK_COMMIT_FLOOR (defined in zone.cpp), to avoid fragmentation issues
  163. //{ "hospital01", 9568256 },
  164. { NULL, 1024*1024 },
  165. };
  166. for ( int i = 0; i < ARRAYSIZE(EstimatedHunkSizes); i++ )
  167. {
  168. if ( !EstimatedHunkSizes[i].pszMapRoot || V_stristr( pszMapName, EstimatedHunkSizes[i].pszMapRoot ) )
  169. {
  170. Hunk_OnMapStart( EstimatedHunkSizes[i].nBytes );
  171. break;
  172. }
  173. }
  174. }
  175. //-----------------------------------------------------------------------------
  176. // FIXME/TODO: Right now Host_FreeToLowMark unloads all models including studio
  177. // models that have Cache_Alloc data, too. This needs to be fixed before shipping
  178. BEGIN_BYTESWAP_DATADESC( lump_t )
  179. DEFINE_FIELD( fileofs, FIELD_INTEGER ),
  180. DEFINE_FIELD( filelen, FIELD_INTEGER ),
  181. DEFINE_FIELD( version, FIELD_INTEGER ),
  182. DEFINE_ARRAY( fourCC, FIELD_CHARACTER, 4 ),
  183. END_BYTESWAP_DATADESC()
  184. BEGIN_BYTESWAP_DATADESC( BSPHeader_t )
  185. DEFINE_FIELD( ident, FIELD_INTEGER ),
  186. DEFINE_FIELD( m_nVersion, FIELD_INTEGER ),
  187. DEFINE_EMBEDDED_ARRAY( lumps, HEADER_LUMPS ),
  188. DEFINE_FIELD( mapRevision, FIELD_INTEGER ),
  189. END_BYTESWAP_DATADESC()
  190. bool Model_LessFunc( FileNameHandle_t const &a, FileNameHandle_t const &b )
  191. {
  192. return a < b;
  193. }
  194. struct ViewWeaponEntry_t
  195. {
  196. ViewWeaponEntry_t( bool bIsViewModel )
  197. {
  198. m_nAgeTime = 0;
  199. m_bStudioHWDataResident = false;
  200. m_bViewModel = bIsViewModel;
  201. m_hAsyncVTXControl = NULL;
  202. m_hAsyncVVDControl = NULL;
  203. }
  204. CUtlVector< CUtlString > m_Materials;
  205. unsigned int m_nAgeTime;
  206. bool m_bStudioHWDataResident;
  207. bool m_bViewModel;
  208. FSAsyncControl_t m_hAsyncVTXControl;
  209. FSAsyncControl_t m_hAsyncVVDControl;
  210. };
  211. //-----------------------------------------------------------------------------
  212. // Purpose: Implements IModelLoader
  213. //-----------------------------------------------------------------------------
  214. class CModelLoader : public IModelLoader
  215. {
  216. friend class CMDLCacheNotify;
  217. // Implement IModelLoader interface
  218. public:
  219. CModelLoader() :
  220. m_ModelPool( sizeof( model_t ), MAX_KNOWN_MODELS, CUtlMemoryPool::GROW_FAST, "CModelLoader::m_ModelPool" ),
  221. m_Models( 0, 0, Model_LessFunc ),
  222. m_WeaponModelCache( 0, 0, DefLessFunc( model_t* ) )
  223. {
  224. }
  225. void Init( void );
  226. void Shutdown( void );
  227. int GetCount( void );
  228. model_t *GetModelForIndex( int i );
  229. // Look up name for model
  230. const char *GetName( model_t const *model );
  231. // Check cache for data, reload model if needed
  232. void *GetExtraData( model_t *model );
  233. int GetModelFileSize( char const *name );
  234. // Finds the model, and loads it if it isn't already present. Updates reference flags
  235. model_t *GetModelForName( const char *name, REFERENCETYPE referencetype );
  236. // Mark as referenced by name
  237. model_t *ReferenceModel( const char *name, REFERENCETYPE referencetype );
  238. // Unmasks the referencetype field for the model
  239. void UnreferenceModel( model_t *model, REFERENCETYPE referencetype );
  240. // Unmasks the specified reference type across all models
  241. void UnreferenceAllModels( REFERENCETYPE referencetype );
  242. // For any models with referencetype blank, frees all memory associated with the model
  243. // and frees up the models slot
  244. void UnloadUnreferencedModels( void );
  245. void PurgeUnusedModels( void );
  246. void UnMountCompatibilityPaths( void );
  247. void AddCompatibilityPath( const char *szNewCompatibilityPath );
  248. bool Map_GetRenderInfoAllocated( void );
  249. void Map_SetRenderInfoAllocated( bool allocated );
  250. virtual void Map_LoadDisplacements( model_t *pModel, bool bRestoring );
  251. // Validate version/header of a .bsp file
  252. bool Map_IsValid( char const *mapname, bool bQuiet = false );
  253. virtual void RecomputeSurfaceFlags( model_t *mod );
  254. virtual void Studio_ReloadModels( ReloadType_t reloadType );
  255. void Print( void );
  256. // Is a model loaded?
  257. virtual bool IsLoaded( const model_t *mod );
  258. virtual bool LastLoadedMapHasHDRLighting(void);
  259. virtual bool LastLoadedMapHasLightmapAlphaData( void );
  260. void DumpVCollideStats();
  261. // Returns the map model, otherwise NULL, no load or create
  262. model_t *FindModelNoCreate( const char *pModelName );
  263. // Finds the model, builds a model entry if not present
  264. model_t *FindModel( const char *name );
  265. modtype_t GetTypeFromName( const char *pModelName );
  266. // start with -1, list terminates with -1
  267. int FindNext( int iIndex, model_t **ppModel );
  268. void UnloadModel( model_t *pModel );
  269. virtual void ReloadFilesInList( IFileList *pFilesToReload );
  270. // Dynamic model loading
  271. virtual void UpdateDynamicModels() { InternalUpdateDynamicModels(false); }
  272. virtual void FlushDynamicModels() { InternalUpdateDynamicModels(true); }
  273. virtual void ForceUnloadNonClientDynamicModels();
  274. virtual model_t *GetDynamicModel( const char *name, bool bClientOnly );
  275. virtual model_t *GetDynamicCombinedModel( const char *name, bool bClientOnly );
  276. virtual void UpdateDynamicCombinedModel( model_t *pModel, MDLHandle_t Handle, bool bClientSide );
  277. virtual bool SetCombineModels( model_t* pModel, const CUtlVector< SCombinerModelInput_t > &vecModelsToCombine );
  278. virtual bool FinishCombinedModel( model_t *pModel, CombinedModelLoadedCallback pFunc, void *pUserData );
  279. virtual bool IsDynamicModelLoading( model_t *pModel );
  280. virtual void AddRefDynamicModel( model_t *pModel, bool bClientSideRef );
  281. virtual void ReleaseDynamicModel( model_t *pModel, bool bClientSideRef );
  282. virtual bool RegisterModelLoadCallback( model_t *pModel, IModelLoadCallback *pCallback, bool bCallImmediatelyIfLoaded );
  283. virtual void UnregisterModelLoadCallback( model_t *pModel, IModelLoadCallback *pCallback );
  284. virtual void Client_OnServerModelStateChanged( model_t *pModel, bool bServerLoaded );
  285. void DebugPrintDynamicModels();
  286. void DebugCombinerInfo( );
  287. byte *GetLightstyles( model_t *pModel );
  288. void AllocateLightstyles( model_t *pModel, byte *pStyles, int nStyleCount );
  289. virtual void UpdateViewWeaponModelCache( const char **ppWeaponModels, int nWeaponModels );
  290. virtual void TouchWorldWeaponModelCache( const char **ppWeaponModels, int nWeaponModels );
  291. void DumpWeaponModelCache( bool bViewModelsOnly );
  292. virtual bool IsModelInWeaponCache( const model_t *pModel );
  293. virtual void EvictAllWeaponsFromModelCache( bool bLoadingComplete );
  294. virtual bool IsViewWeaponModelResident( const model_t *pModel );
  295. bool ProcessWeaponModelCacheOperations();
  296. // Internal types
  297. private:
  298. // TODO, flag these and allow for UnloadUnreferencedModels to check for allocation type
  299. // so we don't have to flush all of the studio models when we free the hunk
  300. enum
  301. {
  302. FALLOC_USESHUNKALLOC = (1<<31),
  303. FALLOC_USESCACHEALLOC = (1<<30),
  304. };
  305. // Internal methods
  306. private:
  307. // Set reference flags and load model if it's not present already
  308. model_t *LoadModel( model_t *model, REFERENCETYPE *referencetype );
  309. // Unload models ( won't unload referenced models if checkreferences is true )
  310. void UnloadAllModels( bool checkreference );
  311. void SetupSubModels( model_t *model, CUtlVector<mmodel_t> &list );
  312. // World/map
  313. void Map_LoadModel( model_t *mod );
  314. void Map_LoadModelGuts( model_t *mod );
  315. void Map_UnloadModel( model_t *mod );
  316. void Map_UnloadCubemapSamples( model_t *mod );
  317. void Map_UnloadSimpleWorldModel( model_t *mod );
  318. // World loading helper
  319. void SetWorldModel( model_t *mod );
  320. void ClearWorldModel( void );
  321. bool IsWorldModelSet( void );
  322. int GetNumWorldSubmodels( void );
  323. // Sprites
  324. void Sprite_LoadModel( model_t *mod );
  325. void Sprite_UnloadModel( model_t *mod );
  326. // Studio models
  327. void Studio_LoadModel( model_t *mod, bool bTouchAllData );
  328. void Studio_UnloadModel( model_t *mod );
  329. // Byteswap
  330. int UpdateOrCreate( const char *pSourceName, char *pTargetName, int maxLen, bool bForce );
  331. // Dynamic model state
  332. void InternalUpdateDynamicModels( bool bForceFlushUnreferenced );
  333. // Dynamic load queue
  334. class CDynamicModelInfo;
  335. void QueueDynamicModelLoad( CDynamicModelInfo *dyn, model_t *mod );
  336. bool CancelDynamicModelLoad( CDynamicModelInfo *dyn, model_t *mod );
  337. void UpdateDynamicModelLoadQueue();
  338. void FinishDynamicModelLoadIfReady( CDynamicModelInfo *dyn, model_t *mod );
  339. void EvictWeaponModel( int CacheIndex, bool bForce );
  340. void RestoreWeaponModel( int CacheIndex );
  341. // Internal data
  342. private:
  343. enum
  344. {
  345. MAX_KNOWN_MODELS = 1024,
  346. };
  347. struct ModelEntry_t
  348. {
  349. model_t *modelpointer;
  350. };
  351. CUtlMap< FileNameHandle_t, ModelEntry_t > m_Models;
  352. CUtlMemoryPool m_ModelPool;
  353. CUtlVector<model_t> m_InlineModels;
  354. model_t *m_pWorldModel;
  355. public: // HACKHACK
  356. worldbrushdata_t m_worldBrushData;
  357. private:
  358. // local base name of current loading model
  359. // generally used for debugging spew where only the name is desired, not the disk path
  360. char m_szBaseName[64];
  361. bool m_bMapRenderInfoLoaded;
  362. bool m_bMapHasHDRLighting;
  363. // Dynamic model support:
  364. class CDynamicModelInfo
  365. {
  366. public:
  367. enum
  368. {
  369. QUEUED = 0x01,
  370. LOADING = 0x02,
  371. LOCKED = 0x04,
  372. SERVERLOADING = 0x08,
  373. READY = 0x10,
  374. INVALIDFLAG = 0x20,
  375. COMBINED = 0x40,
  376. }; // flags
  377. CDynamicModelInfo() : m_iRefCount(0), m_iClientRefCount(0), m_nLoadFlags(INVALIDFLAG), m_uLastTouchedMS_Div256(0) { }
  378. int16 m_iRefCount;
  379. int16 m_iClientRefCount; // also doublecounted in m_iRefCount
  380. uint32 m_nLoadFlags : 8;
  381. uint32 m_uLastTouchedMS_Div256 : 24;
  382. CUtlVector< IModelLoadCallback * > m_Callbacks;
  383. };
  384. CUtlHashtable< model_t * , CDynamicModelInfo > m_DynamicModels;
  385. CUtlHashtable< IModelLoadCallback * , int > m_RegisteredDynamicCallbacks;
  386. // Dynamic model load queue
  387. CUtlVector< model_t* > m_DynamicModelLoadQueue;
  388. bool m_bDynamicLoadQueueHeadActive;
  389. CUtlVector<byte> m_LightStyleList;
  390. CMemoryStack m_WorldLightingDataStack;
  391. bool m_bAllowWeaponModelCache;
  392. bool m_bAllowWeaponVertexEviction;
  393. bool m_bAllowWorldWeaponEviction;
  394. CUtlMap< model_t *, ViewWeaponEntry_t * > m_WeaponModelCache;
  395. int m_nNumWeaponsPartialResident;
  396. struct compatibility_path_t
  397. {
  398. CUtlString mPath;
  399. CUtlString mPathId;
  400. };
  401. CUtlVector< compatibility_path_t > m_vecSzCompatibilityPaths;
  402. };
  403. // Expose interface
  404. static CModelLoader g_ModelLoader;
  405. IModelLoader *modelloader = ( IModelLoader * )&g_ModelLoader;
  406. //-----------------------------------------------------------------------------
  407. // Globals used by the CMapLoadHelper
  408. //-----------------------------------------------------------------------------
  409. static BSPHeader_t s_MapHeader;
  410. static FileHandle_t s_MapFileHandle = FILESYSTEM_INVALID_HANDLE;
  411. static char s_szMapPathName[MAX_PATH];
  412. static char s_szMapPathNameOnDisk[MAX_PATH];
  413. static worldbrushdata_t *s_pMap = NULL;
  414. static int s_nMapLoadRecursion = 0;
  415. CMemoryStack s_MapBuffer;
  416. // Lump files are patches for a shipped map
  417. // List of lump files found when map was loaded. Each entry is the lump file index for that lump id.
  418. struct lumpfiles_t
  419. {
  420. FileHandle_t file;
  421. int lumpfileindex;
  422. lumpfileheader_t header;
  423. };
  424. static lumpfiles_t s_MapLumpFiles[ HEADER_LUMPS ];
  425. CON_COMMAND( mem_vcollide, "Dumps the memory used by vcollides" )
  426. {
  427. g_ModelLoader.DumpVCollideStats();
  428. }
  429. CON_COMMAND( mod_DumpWeaponWiewModelCache, "Dumps the weapon view model cache contents" )
  430. {
  431. g_ModelLoader.DumpWeaponModelCache( true );
  432. }
  433. CON_COMMAND( mod_DumpWeaponWorldModelCache, "Dumps the weapon world model cache contents" )
  434. {
  435. g_ModelLoader.DumpWeaponModelCache( false );
  436. }
  437. //-----------------------------------------------------------------------------
  438. // Get the map name with the appropriate platform extension. Used to hide
  439. // the expected platform suffix, which is desired to be private. The code
  440. // on all platforms generally expects the PC name and only changes it before i/o.
  441. //-----------------------------------------------------------------------------
  442. char *GetMapPathNameOnDisk( char *pDiskName, const char *pFullMapName, unsigned int nDiskNameSize )
  443. {
  444. if ( !IsGameConsole() )
  445. {
  446. // pc names are as is
  447. if ( pFullMapName != pDiskName )
  448. {
  449. V_strncpy( pDiskName, pFullMapName, nDiskNameSize );
  450. }
  451. }
  452. else
  453. {
  454. // expecting the input name to be maps/foo.bsp
  455. V_StripExtension( pFullMapName, pDiskName, nDiskNameSize );
  456. V_strncat( pDiskName, PLATFORM_EXT ".bsp", nDiskNameSize );
  457. }
  458. return pDiskName;
  459. }
  460. //-----------------------------------------------------------------------------
  461. // Returns the ref count for this bsp
  462. //-----------------------------------------------------------------------------
  463. int CMapLoadHelper::GetRefCount()
  464. {
  465. return s_nMapLoadRecursion;
  466. }
  467. //-----------------------------------------------------------------------------
  468. // Setup a BSP loading context, maintains a ref count.
  469. //-----------------------------------------------------------------------------
  470. void CMapLoadHelper::Init( model_t *pMapModel, const char *pPathName )
  471. {
  472. if ( ++s_nMapLoadRecursion > 1 )
  473. {
  474. return;
  475. }
  476. s_pMap = NULL;
  477. s_MapFileHandle = FILESYSTEM_INVALID_HANDLE;
  478. V_memset( &s_MapHeader, 0, sizeof( s_MapHeader ) );
  479. V_memset( &s_MapLumpFiles, 0, sizeof( s_MapLumpFiles ) );
  480. if ( !pMapModel )
  481. {
  482. Q_strncpy( s_szMapPathName, pPathName, sizeof( s_szMapPathName ) );
  483. }
  484. else
  485. {
  486. Q_strncpy( s_szMapPathName, pMapModel->szPathName, sizeof( s_szMapPathName ) );
  487. }
  488. char szNameOnDisk[MAX_PATH];
  489. GetMapPathNameOnDisk( szNameOnDisk, s_szMapPathName, sizeof( szNameOnDisk ) );
  490. s_MapFileHandle = g_pFileSystem->OpenEx( szNameOnDisk, "rb", IsGameConsole() ? FSOPEN_NEVERINPACK : 0, IsGameConsole() ? "GAME" : NULL );
  491. if ( s_MapFileHandle == FILESYSTEM_INVALID_HANDLE )
  492. {
  493. if ( !g_bClearingClientState )
  494. {
  495. Host_Error( "CMapLoadHelper::Init, unable to open %s\n", szNameOnDisk );
  496. }
  497. return;
  498. }
  499. g_pFileSystem->Read( &s_MapHeader, sizeof( BSPHeader_t ), s_MapFileHandle );
  500. if ( s_MapHeader.ident != IDBSPHEADER )
  501. {
  502. g_pFileSystem->Close( s_MapFileHandle );
  503. s_MapFileHandle = FILESYSTEM_INVALID_HANDLE;
  504. Host_Error( "CMapLoadHelper::Init, map %s has wrong identifier\n", szNameOnDisk );
  505. return;
  506. }
  507. if ( s_MapHeader.m_nVersion < MINBSPVERSION || s_MapHeader.m_nVersion > BSPVERSION )
  508. {
  509. g_pFileSystem->Close( s_MapFileHandle );
  510. s_MapFileHandle = FILESYSTEM_INVALID_HANDLE;
  511. Host_Error( "CMapLoadHelper::Init, map %s has wrong version (%i when expecting %i)\n", szNameOnDisk,
  512. s_MapHeader.m_nVersion, BSPVERSION );
  513. return;
  514. }
  515. // Store map version, but only do it once so that the communication between the engine and Hammer isn't broken. The map version
  516. // is incremented whenever a Hammer to Engine session is established so resetting the global map version each time causes a problem.
  517. if ( 0 == g_ServerGlobalVariables.mapversion )
  518. {
  519. g_ServerGlobalVariables.mapversion = s_MapHeader.mapRevision;
  520. }
  521. #ifndef DEDICATED
  522. InitDLightGlobals( s_MapHeader.m_nVersion );
  523. #endif
  524. s_pMap = &g_ModelLoader.m_worldBrushData;
  525. if ( IsPC() )
  526. {
  527. // Now find and open our lump files, and create the master list of them.
  528. for ( int iIndex = 0; iIndex < MAX_LUMPFILES; iIndex++ )
  529. {
  530. lumpfileheader_t lumpHeader;
  531. char lumpfilename[MAX_PATH];
  532. GenerateLumpFileName( s_szMapPathName, lumpfilename, MAX_PATH, iIndex );
  533. if ( !g_pFileSystem->FileExists( lumpfilename ) )
  534. break;
  535. // Open the lump file
  536. FileHandle_t lumpFile = g_pFileSystem->Open( lumpfilename, "rb" );
  537. if ( lumpFile == FILESYSTEM_INVALID_HANDLE )
  538. {
  539. Host_Error( "CMapLoadHelper::Init, failed to load lump file %s\n", lumpfilename );
  540. return;
  541. }
  542. // Read the lump header
  543. memset( &lumpHeader, 0, sizeof( lumpHeader ) );
  544. g_pFileSystem->Read( &lumpHeader, sizeof( lumpfileheader_t ), lumpFile );
  545. if ( lumpHeader.lumpID >= 0 && lumpHeader.lumpID < HEADER_LUMPS )
  546. {
  547. // We may find multiple lump files for the same lump ID. If so,
  548. // close the earlier lump file, because the later one overwrites it.
  549. if ( s_MapLumpFiles[lumpHeader.lumpID].file != FILESYSTEM_INVALID_HANDLE )
  550. {
  551. g_pFileSystem->Close( s_MapLumpFiles[lumpHeader.lumpID].file );
  552. }
  553. s_MapLumpFiles[lumpHeader.lumpID].file = lumpFile;
  554. s_MapLumpFiles[lumpHeader.lumpID].lumpfileindex = iIndex;
  555. memcpy( &(s_MapLumpFiles[lumpHeader.lumpID].header), &lumpHeader, sizeof(lumpHeader) );
  556. }
  557. else
  558. {
  559. Warning( "Found invalid lump file '%s'. Lump Id: %d\n", lumpfilename, lumpHeader.lumpID );
  560. }
  561. }
  562. }
  563. }
  564. //-----------------------------------------------------------------------------
  565. // Setup a BSP loading context from a supplied buffer
  566. //-----------------------------------------------------------------------------
  567. void CMapLoadHelper::InitFromMemory( model_t *pMapModel, const void *pData, int nDataSize )
  568. {
  569. // valid for consoles only
  570. // consoles have reorganized bsp format and no external lump files
  571. Assert( IsGameConsole() && pData && nDataSize );
  572. // the memory should be contained in s_MapBuffer
  573. Assert( ( pData == s_MapBuffer.GetBase() ) && ( nDataSize == s_MapBuffer.GetUsed() ) );
  574. if ( ++s_nMapLoadRecursion > 1 )
  575. {
  576. return;
  577. }
  578. s_pMap = NULL;
  579. s_MapFileHandle = FILESYSTEM_INVALID_HANDLE;
  580. V_memset( &s_MapHeader, 0, sizeof( s_MapHeader ) );
  581. V_memset( &s_MapLumpFiles, 0, sizeof( s_MapLumpFiles ) );
  582. // mimic the expected globals, as if loading from disk
  583. V_strncpy( s_szMapPathName, pMapModel->szPathName, sizeof( s_szMapPathName ) );
  584. char szNameOnDisk[MAX_PATH];
  585. GetMapPathNameOnDisk( szNameOnDisk, s_szMapPathName, sizeof( szNameOnDisk ) );
  586. g_ModelLoader.m_worldBrushData.m_nBSPFileSize = nDataSize;
  587. V_memcpy( &s_MapHeader, pData, sizeof( BSPHeader_t ) );
  588. if ( s_MapHeader.ident != IDBSPHEADER )
  589. {
  590. Host_Error( "CMapLoadHelper::Init, map %s has wrong identifier\n", szNameOnDisk );
  591. return;
  592. }
  593. if ( s_MapHeader.m_nVersion < MINBSPVERSION || s_MapHeader.m_nVersion > BSPVERSION )
  594. {
  595. Host_Error( "CMapLoadHelper::Init, map %s has wrong version (%i when expecting %i)\n", szNameOnDisk, s_MapHeader.m_nVersion, BSPVERSION );
  596. return;
  597. }
  598. // Store map version
  599. g_ServerGlobalVariables.mapversion = s_MapHeader.mapRevision;
  600. #ifndef DEDICATED
  601. InitDLightGlobals( s_MapHeader.m_nVersion );
  602. #endif
  603. s_pMap = &g_ModelLoader.m_worldBrushData;
  604. }
  605. //-----------------------------------------------------------------------------
  606. // Shutdown a BSP loading context.
  607. //-----------------------------------------------------------------------------
  608. void CMapLoadHelper::Shutdown( void )
  609. {
  610. if ( --s_nMapLoadRecursion > 0 )
  611. {
  612. return;
  613. }
  614. if ( s_MapFileHandle != FILESYSTEM_INVALID_HANDLE )
  615. {
  616. g_pFileSystem->Close( s_MapFileHandle );
  617. s_MapFileHandle = FILESYSTEM_INVALID_HANDLE;
  618. }
  619. if ( IsPC() )
  620. {
  621. // Close our open lump files
  622. for ( int i = 0; i < HEADER_LUMPS; i++ )
  623. {
  624. if ( s_MapLumpFiles[i].file != FILESYSTEM_INVALID_HANDLE )
  625. {
  626. g_pFileSystem->Close( s_MapLumpFiles[i].file );
  627. }
  628. }
  629. V_memset( &s_MapLumpFiles, 0, sizeof( s_MapLumpFiles ) );
  630. }
  631. s_szMapPathName[ 0 ] = '\0';
  632. V_memset( &s_MapHeader, 0, sizeof( s_MapHeader ) );
  633. s_pMap = NULL;
  634. // discard from memory
  635. if ( s_MapBuffer.GetUsed() )
  636. {
  637. s_MapBuffer.FreeAll();
  638. }
  639. }
  640. //-----------------------------------------------------------------------------
  641. // Free the lighting lump (increases free memory during loading on 360)
  642. //-----------------------------------------------------------------------------
  643. void CMapLoadHelper::FreeLightingLump( void )
  644. {
  645. if ( IsGameConsole() && ( s_MapFileHandle == FILESYSTEM_INVALID_HANDLE ) && s_MapBuffer.GetUsed() )
  646. {
  647. int lightingLump = LumpSize( LUMP_LIGHTING_HDR ) ? LUMP_LIGHTING_HDR : LUMP_LIGHTING;
  648. // Should never have both lighting lumps on 360
  649. Assert( ( lightingLump == LUMP_LIGHTING ) || ( LumpSize( LUMP_LIGHTING ) == 0 ) );
  650. if ( LumpSize( lightingLump ) )
  651. {
  652. // Check that the lighting lump is next to the last one in the BSP
  653. // The pak file is expected to be last
  654. int lightingOffset = LumpOffset( lightingLump );
  655. for ( int i = 0; i < HEADER_LUMPS; i++ )
  656. {
  657. if ( ( LumpOffset( i ) > lightingOffset ) && ( i != LUMP_PAKFILE ) )
  658. {
  659. Warning( "CMapLoadHelper: Cannot free lighting lump (should be last before the PAK lump).\n" );
  660. Warning( "Lumps may be now be incorrectly ordered. Regenerate the " PLATFORM_EXT ".bsp file with MakeGameData.\n" );
  661. return;
  662. }
  663. }
  664. // Flag the lighting chunk as gone from the BSP (principally, this sets 'filelen' to 0)
  665. V_memset( &s_MapHeader.lumps[ lightingLump ], 0, sizeof( lump_t ) );
  666. // Shrink the buffer to free up the space that was used by the lighting lump
  667. // The pak file is not part of the original allocation
  668. s_MapBuffer.FreeToAllocPoint( (MemoryStackMark_t)lightingOffset );
  669. }
  670. }
  671. }
  672. //-----------------------------------------------------------------------------
  673. // Returns the size of a particular lump without loading it...
  674. //-----------------------------------------------------------------------------
  675. int CMapLoadHelper::LumpSize( int lumpId )
  676. {
  677. // If we have a lump file for this lump, return its length instead
  678. if ( IsPC() && s_MapLumpFiles[lumpId].file != FILESYSTEM_INVALID_HANDLE )
  679. {
  680. return s_MapLumpFiles[lumpId].header.lumpLength;
  681. }
  682. lump_t *pLump = &s_MapHeader.lumps[ lumpId ];
  683. Assert( pLump );
  684. if ( IsGameConsole() )
  685. {
  686. // a compressed lump hides the uncompressed size in the unused fourCC
  687. // otherwise, the data has to be loaded to determine original size
  688. // all knowledge of compression is private, they expect and get the original size
  689. int originalSize = BigLong( *((int *)s_MapHeader.lumps[lumpId].fourCC) );
  690. if ( originalSize )
  691. {
  692. return originalSize;
  693. }
  694. }
  695. return pLump->filelen;
  696. }
  697. //-----------------------------------------------------------------------------
  698. // Returns the offset of a particular lump without loading it...
  699. //-----------------------------------------------------------------------------
  700. int CMapLoadHelper::LumpOffset( int lumpID )
  701. {
  702. // If we have a lump file for this lump, return
  703. // the offset to move past the lump file header.
  704. if ( IsPC() && s_MapLumpFiles[lumpID].file != FILESYSTEM_INVALID_HANDLE )
  705. {
  706. return s_MapLumpFiles[lumpID].header.lumpOffset;
  707. }
  708. lump_t *pLump = &s_MapHeader.lumps[ lumpID ];
  709. Assert( pLump );
  710. return pLump->fileofs;
  711. }
  712. //-----------------------------------------------------------------------------
  713. // Loads one element in a lump.
  714. //-----------------------------------------------------------------------------
  715. void CMapLoadHelper::LoadLumpElement( int nElemIndex, int nElemSize, void *pData )
  716. {
  717. if ( !nElemSize || !m_nLumpSize )
  718. {
  719. return;
  720. }
  721. // supply from memory
  722. if ( nElemIndex * nElemSize + nElemSize <= m_nLumpSize )
  723. {
  724. V_memcpy( pData, m_pData + nElemIndex * nElemSize, nElemSize );
  725. }
  726. else
  727. {
  728. // out of range
  729. Assert( 0 );
  730. }
  731. }
  732. //-----------------------------------------------------------------------------
  733. // Loads one element in a lump.
  734. //-----------------------------------------------------------------------------
  735. void CMapLoadHelper::LoadLumpData( int offset, int size, void *pData )
  736. {
  737. if ( !size || !m_nLumpSize )
  738. {
  739. return;
  740. }
  741. if ( offset + size <= m_nLumpSize )
  742. {
  743. V_memcpy( pData, m_pData + offset, size );
  744. }
  745. else
  746. {
  747. // out of range
  748. Assert( 0 );
  749. }
  750. }
  751. //-----------------------------------------------------------------------------
  752. // Purpose:
  753. // Input : mapfile -
  754. // lumpToLoad -
  755. //-----------------------------------------------------------------------------
  756. CMapLoadHelper::CMapLoadHelper( int lumpToLoad, bool bUncompress )
  757. {
  758. if ( lumpToLoad < 0 || lumpToLoad >= HEADER_LUMPS )
  759. {
  760. Sys_Error( "Can't load lump %i, range is 0 to %i!!!", lumpToLoad, HEADER_LUMPS - 1 );
  761. }
  762. m_nLumpID = lumpToLoad;
  763. m_nLumpSize = 0;
  764. m_nLumpOffset = -1;
  765. m_pData = NULL;
  766. m_pRawData = NULL;
  767. m_pUncompressedData = NULL;
  768. m_nUncompressedLumpSize = 0;
  769. m_bUncompressedDataExternal = false;
  770. // Load raw lump from disk
  771. lump_t *lump = &s_MapHeader.lumps[ lumpToLoad ];
  772. Assert( lump );
  773. m_nLumpSize = lump->filelen;
  774. m_nLumpOffset = lump->fileofs;
  775. m_nLumpVersion = lump->version;
  776. FileHandle_t fileToUse = s_MapFileHandle;
  777. // If we have a lump file for this lump, use it instead
  778. if ( IsPC() && s_MapLumpFiles[lumpToLoad].file != FILESYSTEM_INVALID_HANDLE )
  779. {
  780. fileToUse = s_MapLumpFiles[lumpToLoad].file;
  781. m_nLumpSize = s_MapLumpFiles[lumpToLoad].header.lumpLength;
  782. m_nLumpOffset = s_MapLumpFiles[lumpToLoad].header.lumpOffset;
  783. m_nLumpVersion = s_MapLumpFiles[lumpToLoad].header.lumpVersion;
  784. // Store off the lump file name
  785. GenerateLumpFileName( s_szMapPathName, m_szLumpFilename, sizeof( m_szLumpFilename ), s_MapLumpFiles[lumpToLoad].lumpfileindex );
  786. }
  787. if ( !m_nLumpSize )
  788. {
  789. // this lump has no data
  790. return;
  791. }
  792. if ( s_MapBuffer.GetUsed() )
  793. {
  794. // bsp is in memory
  795. m_pData = (unsigned char*)s_MapBuffer.GetBase() + m_nLumpOffset;
  796. }
  797. else
  798. {
  799. if ( s_MapFileHandle == FILESYSTEM_INVALID_HANDLE )
  800. {
  801. Sys_Error( "Can't load map from invalid handle!!!" );
  802. }
  803. unsigned nOffsetAlign, nSizeAlign, nBufferAlign;
  804. bool bTryOptimal = g_pFileSystem->GetOptimalIOConstraints( fileToUse, &nOffsetAlign, &nSizeAlign, &nBufferAlign );
  805. if ( bTryOptimal )
  806. {
  807. bTryOptimal = ( m_nLumpOffset % 4 == 0 ); // Don't return badly aligned data
  808. }
  809. unsigned int alignedOffset = m_nLumpOffset;
  810. unsigned int alignedBytesToRead = ( ( m_nLumpSize ) ? m_nLumpSize : 1 );
  811. if ( bTryOptimal )
  812. {
  813. alignedOffset = AlignValue( ( alignedOffset - nOffsetAlign ) + 1, nOffsetAlign );
  814. alignedBytesToRead = AlignValue( ( m_nLumpOffset - alignedOffset ) + alignedBytesToRead, nSizeAlign );
  815. }
  816. m_pRawData = (byte *)g_pFileSystem->AllocOptimalReadBuffer( fileToUse, alignedBytesToRead, alignedOffset );
  817. if ( !m_pRawData && m_nLumpSize )
  818. {
  819. Sys_Error( "Can't load lump %i, allocation of %i bytes failed!!!", lumpToLoad, m_nLumpSize + 1 );
  820. }
  821. if ( m_nLumpSize )
  822. {
  823. g_pFileSystem->Seek( fileToUse, alignedOffset, FILESYSTEM_SEEK_HEAD );
  824. g_pFileSystem->ReadEx( m_pRawData, alignedBytesToRead, alignedBytesToRead, fileToUse );
  825. m_pData = m_pRawData + ( m_nLumpOffset - alignedOffset );
  826. }
  827. }
  828. m_nUncompressedLumpSize = m_nLumpSize;
  829. if ( IsGameConsole() )
  830. {
  831. CLZMA lzma;
  832. if ( lzma.IsCompressed( m_pData ) )
  833. {
  834. m_nUncompressedLumpSize = lzma.GetActualSize( m_pData );
  835. if ( bUncompress )
  836. {
  837. UncompressLump();
  838. }
  839. }
  840. }
  841. }
  842. //-----------------------------------------------------------------------------
  843. // Purpose:
  844. //-----------------------------------------------------------------------------
  845. CMapLoadHelper::~CMapLoadHelper( void )
  846. {
  847. if ( IsGameConsole() && m_pUncompressedData && !m_bUncompressedDataExternal )
  848. {
  849. free( m_pUncompressedData );
  850. }
  851. if ( m_pRawData )
  852. {
  853. g_pFileSystem->FreeOptimalReadBuffer( m_pRawData );
  854. }
  855. }
  856. //-----------------------------------------------------------------------------
  857. // Purpose: decompress a compressed lump
  858. //-----------------------------------------------------------------------------
  859. void CMapLoadHelper::UncompressLump( void *pExternalBuffer )
  860. {
  861. Assert( !m_pUncompressedData );
  862. if ( m_pUncompressedData )
  863. return; // Already uncompressed!
  864. if ( pExternalBuffer )
  865. {
  866. m_pUncompressedData = (unsigned char *)pExternalBuffer;
  867. m_bUncompressedDataExternal = true;
  868. }
  869. else
  870. {
  871. m_pUncompressedData = (unsigned char *)malloc( m_nUncompressedLumpSize );
  872. m_bUncompressedDataExternal = false;
  873. }
  874. CLZMA lzma;
  875. int decodedLength;
  876. if ( IsGameConsole() && lzma.IsCompressed( m_pData ) )
  877. {
  878. // Uncompress into the dest buffer
  879. decodedLength = lzma.Uncompress( m_pData, m_pUncompressedData );
  880. Assert( decodedLength == m_nUncompressedLumpSize );
  881. }
  882. else
  883. {
  884. // Copy into the dest buffer
  885. Assert( m_nLumpSize == m_nUncompressedLumpSize );
  886. memcpy( m_pUncompressedData, m_pData, m_nUncompressedLumpSize );
  887. }
  888. // a user of the class sees the uncompressed data
  889. m_pData = m_pUncompressedData;
  890. m_nLumpSize = m_nUncompressedLumpSize;
  891. }
  892. //-----------------------------------------------------------------------------
  893. // Purpose:
  894. // Output : model_t
  895. //-----------------------------------------------------------------------------
  896. worldbrushdata_t *CMapLoadHelper::GetMap( void )
  897. {
  898. Assert( s_pMap );
  899. return s_pMap;
  900. }
  901. //-----------------------------------------------------------------------------
  902. // Purpose:
  903. // Output : const char
  904. //-----------------------------------------------------------------------------
  905. const char *CMapLoadHelper::GetMapPathName( void )
  906. {
  907. return s_szMapPathName;
  908. }
  909. //-----------------------------------------------------------------------------
  910. // Return the path for the lump, can be the map path or an atomic lump file
  911. // override.
  912. //-----------------------------------------------------------------------------
  913. char *CMapLoadHelper::GetLoadName( void )
  914. {
  915. // If we have a custom lump file for the lump this helper
  916. // is loading, return it instead.
  917. if ( IsPC() && s_MapLumpFiles[m_nLumpID].file != FILESYSTEM_INVALID_HANDLE )
  918. {
  919. return m_szLumpFilename;
  920. }
  921. return s_szMapPathName;
  922. }
  923. //-----------------------------------------------------------------------------
  924. // Hides possible platform extension.
  925. //-----------------------------------------------------------------------------
  926. char *CMapLoadHelper::GetDiskName( void )
  927. {
  928. return GetMapPathNameOnDisk( s_szMapPathNameOnDisk, GetLoadName(), sizeof( s_szMapPathNameOnDisk ) );
  929. }
  930. //-----------------------------------------------------------------------------
  931. // Purpose:
  932. // Output : byte
  933. //-----------------------------------------------------------------------------
  934. byte *CMapLoadHelper::LumpBase( void )
  935. {
  936. return m_pData;
  937. }
  938. //-----------------------------------------------------------------------------
  939. // Purpose:
  940. // Output : int
  941. //-----------------------------------------------------------------------------
  942. int CMapLoadHelper::LumpSize()
  943. {
  944. return m_nLumpSize;
  945. }
  946. int CMapLoadHelper::UncompressedLumpSize()
  947. {
  948. return m_nUncompressedLumpSize;
  949. }
  950. int CMapLoadHelper::LumpOffset()
  951. {
  952. return m_nLumpOffset;
  953. }
  954. int CMapLoadHelper::LumpVersion() const
  955. {
  956. return m_nLumpVersion;
  957. }
  958. void EnableHDR( bool bEnable )
  959. {
  960. if ( g_pMaterialSystemHardwareConfig->GetHDREnabled() == bEnable )
  961. return;
  962. g_pMaterialSystemHardwareConfig->SetHDREnabled( bEnable );
  963. if ( IsGameConsole() )
  964. {
  965. // cannot do what the pc does and ditch resources, we're loading!
  966. // can safely do the state update only, knowing that the state change won't affect 360 resources
  967. ((MaterialSystem_Config_t *)g_pMaterialSystemConfig)->SetFlag( MATSYS_VIDCFG_FLAGS_ENABLE_HDR, bEnable );
  968. return;
  969. }
  970. // And this is okay here!
  971. materials->ReEnableRenderTargetAllocation_IRealizeIfICallThisAllTexturesWillBeUnloadedAndLoadTimeWillSufferHorribly();
  972. ShutdownWellKnownRenderTargets();
  973. InitWellKnownRenderTargets();
  974. // Grah. This is terrible. changin mat_hdr_enabled at the commandline
  975. // will by definition break because the release/restore methods don't call
  976. // ShutdownWellKnownRenderTargets/InitWellKnownRenderTargets.
  977. // Also, this forces two alt-tabs, one for InitWellKnownRenderTargets, one
  978. // for UpdateMaterialSystemConfig.
  979. UpdateMaterialSystemConfig();
  980. // Worse, since we need to init+shutdown render targets here, we can't
  981. // rely on UpdateMaterialSystemConfig to release + reacquire resources
  982. // because it could be called at any time. We have to precisely control
  983. // when hdr is changed since this is the only time the code can handle it.
  984. materials->ReleaseResources();
  985. materials->ReacquireResources();
  986. materials->FinishRenderTargetAllocation();
  987. }
  988. //-----------------------------------------------------------------------------
  989. // Determine feature flags
  990. //-----------------------------------------------------------------------------
  991. void Map_CheckFeatureFlags()
  992. {
  993. g_bLoadedMapHasBakedPropLighting = false;
  994. g_bBakedPropLightingNoSeparateHDR = false;
  995. g_bHasLightmapAlphaData = false;
  996. g_bBakedPropLightingStreams3 = false;
  997. g_bHasIndirectOnlyInLightingStreams = false;
  998. g_bLightstylesWithCSM = false;
  999. if ( CMapLoadHelper::LumpSize( LUMP_MAP_FLAGS ) > 0 )
  1000. {
  1001. CMapLoadHelper lh( LUMP_MAP_FLAGS );
  1002. dflagslump_t flags_lump;
  1003. flags_lump = *( (dflagslump_t *)( lh.LumpBase() ) );
  1004. // check if loaded map has baked static prop lighting
  1005. g_bLoadedMapHasBakedPropLighting =
  1006. ( flags_lump.m_LevelFlags & LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_NONHDR ) != 0 ||
  1007. ( flags_lump.m_LevelFlags & LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_HDR ) != 0;
  1008. g_bBakedPropLightingNoSeparateHDR =
  1009. ( flags_lump.m_LevelFlags & LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_HDR ) == 0;
  1010. g_bHasLightmapAlphaData = ( flags_lump.m_LevelFlags & LVLFLAGS_LIGHTMAP_ALPHA ) != 0;
  1011. g_bBakedPropLightingStreams3 = ( flags_lump.m_LevelFlags & LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_3 ) != 0;
  1012. g_bHasLightmapAlphaData3 = ( flags_lump.m_LevelFlags & LVLFLAGS_LIGHTMAP_ALPHA_3 ) != 0;
  1013. g_bHasIndirectOnlyInLightingStreams = ( flags_lump.m_LevelFlags & LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_3_NO_SUN ) != 0;
  1014. g_bLightstylesWithCSM = ( flags_lump.m_LevelFlags & LVLFLAGS_LIGHTSTYLES_WITH_CSM ) != 0;
  1015. // set num static light streams value
  1016. ConVarRef r_staticlight_streams( "r_staticlight_streams" );
  1017. r_staticlight_streams.SetValue( g_bBakedPropLightingStreams3 ? 3 : 1 );
  1018. ConVarRef r_staticlight_streams_indirect_only( "r_staticlight_streams_indirect_only" );
  1019. r_staticlight_streams_indirect_only.SetValue( g_bHasIndirectOnlyInLightingStreams );
  1020. g_pMaterialSystemHardwareConfig->SetCSMAccurateBlending( g_bHasLightmapAlphaData3 );
  1021. }
  1022. }
  1023. //-----------------------------------------------------------------------------
  1024. // Parse the map header for HDR ability. Returns the presence of HDR data only,
  1025. // not the HDR enable state.
  1026. //-----------------------------------------------------------------------------
  1027. bool Map_CheckForHDR( model_t *pModel, const char *pMapPathName )
  1028. {
  1029. // parse the map header only
  1030. CMapLoadHelper::Init( pModel, pMapPathName );
  1031. bool bHasHDR = false;
  1032. if ( IsGameConsole() )
  1033. {
  1034. // If this is true, the 360 MUST use HDR, because the LDR data gets stripped out.
  1035. bHasHDR = CMapLoadHelper::LumpSize( LUMP_LIGHTING_HDR ) > 0;
  1036. }
  1037. else
  1038. {
  1039. // might want to also consider the game lumps GAMELUMP_DETAIL_PROP_LIGHTING_HDR
  1040. bHasHDR = CMapLoadHelper::LumpSize( LUMP_LIGHTING_HDR ) > 0 &&
  1041. CMapLoadHelper::LumpSize( LUMP_WORLDLIGHTS_HDR ) > 0;
  1042. // Mod_GameLumpSize( GAMELUMP_DETAIL_PROP_LIGHTING_HDR ) > 0 // fixme
  1043. }
  1044. if ( s_MapHeader.m_nVersion >= 20 && CMapLoadHelper::LumpSize( LUMP_LEAF_AMBIENT_LIGHTING_HDR ) == 0 )
  1045. {
  1046. // This lump only exists in version 20 and greater, so don't bother checking for it on earlier versions.
  1047. bHasHDR = false;
  1048. }
  1049. bool bEnableHDR = ( IsGameConsole() && bHasHDR ) ||
  1050. bHasHDR &&
  1051. ( mat_hdr_level.GetInt() >= 2 ) &&
  1052. ( g_pMaterialSystemHardwareConfig->GetHardwareHDRType() != HDR_TYPE_NONE );
  1053. EnableHDR( bEnableHDR );
  1054. // this data really should have been in the header, but it isn't
  1055. // establish the features now, before the real bsp load commences
  1056. Map_CheckFeatureFlags();
  1057. CMapLoadHelper::Shutdown();
  1058. return bHasHDR;
  1059. }
  1060. //-----------------------------------------------------------------------------
  1061. // Allocates, frees lighting data
  1062. //-----------------------------------------------------------------------------
  1063. static void AllocateLightingData( worldbrushdata_t *pBrushData, int nSize )
  1064. {
  1065. g_bHunkAllocLightmaps = ( !r_keepstyledlightmapsonly.GetBool() && !r_unloadlightmaps.GetBool() && r_hunkalloclightmaps.GetBool() );
  1066. if ( g_bHunkAllocLightmaps )
  1067. {
  1068. pBrushData->lightdata = (ColorRGBExp32 *)Hunk_AllocName( nSize, "Lightmaps", false );
  1069. }
  1070. else if ( r_keepstyledlightmapsonly.GetBool() && !r_unloadlightmaps.GetBool() && pBrushData->m_pLightingDataStack )
  1071. {
  1072. // the lighting data is allocated from a memory stack
  1073. // this is to facilitate the decommit after compaction due to discarding lightmaps
  1074. pBrushData->m_pLightingDataStack->Term();
  1075. pBrushData->m_pLightingDataStack->Init( "LightingData", nSize );
  1076. pBrushData->lightdata = (ColorRGBExp32 *)pBrushData->m_pLightingDataStack->Alloc( nSize );
  1077. }
  1078. else
  1079. {
  1080. // Specifically *not* adding it to the hunk.
  1081. // If this malloc changes, also change the free in CacheAndUnloadLightmapData()
  1082. pBrushData->lightdata = (ColorRGBExp32 *)malloc( nSize );
  1083. }
  1084. pBrushData->m_nLightingDataSize = nSize;
  1085. pBrushData->m_bUnloadedAllLightmaps = false;
  1086. }
  1087. void DeallocateLightingData( worldbrushdata_t *pBrushData )
  1088. {
  1089. if ( pBrushData && pBrushData->lightdata )
  1090. {
  1091. if ( !g_bHunkAllocLightmaps )
  1092. {
  1093. if ( pBrushData->m_pLightingDataStack && pBrushData->m_pLightingDataStack->GetSize() )
  1094. {
  1095. // the lighting data was placed in the memory stack
  1096. pBrushData->m_pLightingDataStack->Term();
  1097. }
  1098. else
  1099. {
  1100. free( pBrushData->lightdata );
  1101. }
  1102. }
  1103. pBrushData->lightdata = NULL;
  1104. pBrushData->m_nLightingDataSize = 0;
  1105. }
  1106. }
  1107. static int ComputeLightmapSize( dface_t *pFace, mtexinfo_t *pTexInfo )
  1108. {
  1109. bool bNeedsBumpmap = false;
  1110. if( pTexInfo[pFace->texinfo].flags & SURF_BUMPLIGHT )
  1111. {
  1112. bNeedsBumpmap = true;
  1113. }
  1114. int lightstyles;
  1115. for (lightstyles=0; lightstyles < MAXLIGHTMAPS; lightstyles++ )
  1116. {
  1117. if ( pFace->styles[lightstyles] == 255 )
  1118. break;
  1119. }
  1120. int nLuxels = (pFace->m_LightmapTextureSizeInLuxels[0]+1) * (pFace->m_LightmapTextureSizeInLuxels[1]+1);
  1121. if( bNeedsBumpmap )
  1122. {
  1123. return nLuxels * 4 * lightstyles * ( NUM_BUMP_VECTS + 1 );
  1124. }
  1125. return nLuxels * 4 * lightstyles;
  1126. }
  1127. //-----------------------------------------------------------------------------
  1128. // Purpose:
  1129. //-----------------------------------------------------------------------------
  1130. void Mod_LoadLighting( bool bLoadHDR )
  1131. {
  1132. // NOTE: we tell CMapLoadHelper to NOT decompress the lighting lump, since we want to allocate the final buffer here:
  1133. bool bDontDecompress = false;
  1134. CMapLoadHelper lh( bLoadHDR ? LUMP_LIGHTING_HDR : LUMP_LIGHTING, bDontDecompress );
  1135. if ( !lh.LumpSize() )
  1136. {
  1137. lh.GetMap()->lightdata = NULL;
  1138. return;
  1139. }
  1140. Assert ( lh.LumpVersion() != 0 );
  1141. AllocateLightingData( lh.GetMap(), lh.UncompressedLumpSize() );
  1142. if ( !lh.GetMap()->lightdata )
  1143. {
  1144. // TERROR: If we fail this huge malloc, don't crash on a memcpy. We'll rely on the fact that lightdata
  1145. // can be NULL if the lump is 0 bytes above, and hope that we won't crash. No worse than the
  1146. // guaranteed crash below if we don't bail.
  1147. return;
  1148. }
  1149. // Now pass in our buffer for decompression to occur (just a memcpy if the data is not compressed):
  1150. lh.UncompressLump( lh.GetMap()->lightdata );
  1151. if ( IsGameConsole() )
  1152. {
  1153. // Free the lighting lump, to increase the amount of memory free during the rest of loading
  1154. CMapLoadHelper::FreeLightingLump();
  1155. }
  1156. }
  1157. void Mod_LoadFaceBrushes()
  1158. {
  1159. {
  1160. CMapLoadHelper lh( LUMP_FACEBRUSHLIST );
  1161. if ( !lh.LumpSize() )
  1162. {
  1163. lh.GetMap()->m_pSurfaceBrushes = NULL;
  1164. lh.GetMap()->m_pSurfaceBrushList = NULL;
  1165. return;
  1166. }
  1167. lh.GetMap()->m_pSurfaceBrushList = (dfacebrushlist_t *)Hunk_AllocName( lh.LumpSize(), "FaceBrushLists", false );
  1168. memcpy( lh.GetMap()->m_pSurfaceBrushList, lh.LumpBase(), lh.LumpSize() );
  1169. }
  1170. {
  1171. CMapLoadHelper lh( LUMP_FACEBRUSHES );
  1172. lh.GetMap()->m_pSurfaceBrushes = (uint16 *)Hunk_AllocName( lh.LumpSize(), "FaceBrushes", false );
  1173. memcpy( lh.GetMap()->m_pSurfaceBrushes, lh.LumpBase(), lh.LumpSize() );
  1174. }
  1175. }
  1176. //-----------------------------------------------------------------------------
  1177. // Purpose:
  1178. //-----------------------------------------------------------------------------
  1179. void Mod_LoadWorldlights( CMapLoadHelper &lh, bool bIsHDR )
  1180. {
  1181. lh.GetMap()->shadowzbuffers = NULL;
  1182. if ( !lh.LumpSize() )
  1183. {
  1184. lh.GetMap()->numworldlights = 0;
  1185. lh.GetMap()->worldlights = NULL;
  1186. return;
  1187. }
  1188. switch ( lh.LumpVersion() )
  1189. {
  1190. case LUMP_WORLDLIGHTS_VERSION:
  1191. {
  1192. lh.GetMap()->numworldlights = lh.LumpSize() / sizeof( dworldlight_t );
  1193. lh.GetMap()->worldlights = (dworldlight_t *)Hunk_AllocName( lh.LumpSize(), va( "%s [%s]", lh.GetLoadName(), "worldlights" ) );
  1194. memcpy( lh.GetMap()->worldlights, lh.LumpBase(), lh.LumpSize() );
  1195. break;
  1196. }
  1197. case 0:
  1198. {
  1199. int nNumWorldLights = lh.LumpSize() / sizeof( dworldlight_version0_t );
  1200. lh.GetMap()->numworldlights = nNumWorldLights;
  1201. lh.GetMap()->worldlights = (dworldlight_t *)Hunk_AllocName( nNumWorldLights * sizeof( dworldlight_t ), va( "%s [%s]", lh.GetLoadName(), "worldlights" ) );
  1202. dworldlight_version0_t* RESTRICT pOldWorldLight = reinterpret_cast<dworldlight_version0_t*>( lh.LumpBase() );
  1203. dworldlight_t* RESTRICT pNewWorldLight = lh.GetMap()->worldlights;
  1204. for ( int i = 0; i < nNumWorldLights; i++ )
  1205. {
  1206. pNewWorldLight->origin = pOldWorldLight->origin;
  1207. pNewWorldLight->intensity = pOldWorldLight->intensity;
  1208. pNewWorldLight->normal = pOldWorldLight->normal;
  1209. pNewWorldLight->shadow_cast_offset.Init( 0.0f, 0.0f, 0.0f );
  1210. pNewWorldLight->cluster = pOldWorldLight->cluster;
  1211. pNewWorldLight->type = pOldWorldLight->type;
  1212. pNewWorldLight->style = pOldWorldLight->style;
  1213. pNewWorldLight->stopdot = pOldWorldLight->stopdot;
  1214. pNewWorldLight->stopdot2 = pOldWorldLight->stopdot2;
  1215. pNewWorldLight->exponent = pOldWorldLight->exponent;
  1216. pNewWorldLight->radius = pOldWorldLight->radius;
  1217. pNewWorldLight->constant_attn = pOldWorldLight->constant_attn;
  1218. pNewWorldLight->linear_attn = pOldWorldLight->linear_attn;
  1219. pNewWorldLight->quadratic_attn = pOldWorldLight->quadratic_attn;
  1220. pNewWorldLight->flags = pOldWorldLight->flags;
  1221. pNewWorldLight->texinfo = pOldWorldLight->texinfo;
  1222. pNewWorldLight->owner = pOldWorldLight->owner;
  1223. pNewWorldLight++;
  1224. pOldWorldLight++;
  1225. }
  1226. break;
  1227. }
  1228. default:
  1229. Host_Error( "Invalid worldlight lump version!\n" );
  1230. break;
  1231. }
  1232. #if !defined( DEDICATED )
  1233. if ( r_lightcache_zbuffercache.GetInt() )
  1234. {
  1235. size_t zbufSize = lh.GetMap()->numworldlights * sizeof( lightzbuffer_t );
  1236. lh.GetMap()->shadowzbuffers = ( lightzbuffer_t *)Hunk_AllocName( zbufSize, va( "%s [%s]", lh.GetLoadName(), "shadowzbuffers" ) );
  1237. memset( lh.GetMap()->shadowzbuffers, 0, zbufSize ); // mark empty
  1238. }
  1239. #endif
  1240. // Fixup for backward compatability
  1241. for ( int i = 0; i < lh.GetMap()->numworldlights; i++ )
  1242. {
  1243. if ( lh.GetMap()->worldlights[i].type == emit_spotlight )
  1244. {
  1245. if ((lh.GetMap()->worldlights[i].constant_attn == 0.0) &&
  1246. (lh.GetMap()->worldlights[i].linear_attn == 0.0) &&
  1247. (lh.GetMap()->worldlights[i].quadratic_attn == 0.0))
  1248. {
  1249. lh.GetMap()->worldlights[i].quadratic_attn = 1.0;
  1250. }
  1251. if ( lh.GetMap()->worldlights[i].exponent == 0.0 )
  1252. lh.GetMap()->worldlights[i].exponent = 1.0;
  1253. }
  1254. else if ( lh.GetMap()->worldlights[i].type == emit_point )
  1255. {
  1256. // To match earlier lighting, use quadratic...
  1257. if ((lh.GetMap()->worldlights[i].constant_attn == 0.0) &&
  1258. (lh.GetMap()->worldlights[i].linear_attn == 0.0) &&
  1259. (lh.GetMap()->worldlights[i].quadratic_attn == 0.0))
  1260. {
  1261. lh.GetMap()->worldlights[i].quadratic_attn = 1.0;
  1262. }
  1263. }
  1264. // I replaced the cuttoff_dot field (which took a value from 0 to 1)
  1265. // with a max light radius. Radius of less than 1 will never happen,
  1266. // so I can get away with this. When I set radius to 0, it'll
  1267. // run the old code which computed a radius
  1268. if ( lh.GetMap()->worldlights[i].radius < 1 )
  1269. {
  1270. lh.GetMap()->worldlights[i].radius = ComputeLightRadius( &lh.GetMap()->worldlights[i], bIsHDR );
  1271. }
  1272. }
  1273. }
  1274. //-----------------------------------------------------------------------------
  1275. // Purpose:
  1276. //-----------------------------------------------------------------------------
  1277. void Mod_LoadVertices( void )
  1278. {
  1279. dvertex_t *in;
  1280. mvertex_t *out;
  1281. int i, count;
  1282. CMapLoadHelper lh( LUMP_VERTEXES );
  1283. in = (dvertex_t *)lh.LumpBase();
  1284. if ( lh.LumpSize() % sizeof(*in) )
  1285. {
  1286. Host_Error( "Mod_LoadVertices: funny lump size in %s", lh.GetMapPathName() );
  1287. }
  1288. count = lh.LumpSize() / sizeof(*in);
  1289. out = (mvertex_t *)Hunk_AllocName( count*sizeof(*out), va( "%s [%s]", lh.GetLoadName(), "vertexes" ) );
  1290. lh.GetMap()->vertexes = out;
  1291. lh.GetMap()->numvertexes = count;
  1292. for ( i=0 ; i<count ; i++, in++, out++)
  1293. {
  1294. out->position[0] = in->point[0];
  1295. out->position[1] = in->point[1];
  1296. out->position[2] = in->point[2];
  1297. }
  1298. }
  1299. //-----------------------------------------------------------------------------
  1300. // Purpose:
  1301. // Input : mins -
  1302. // maxs -
  1303. // Output : float
  1304. //-----------------------------------------------------------------------------
  1305. static float RadiusFromBounds (Vector& mins, Vector& maxs)
  1306. {
  1307. int i;
  1308. Vector corner;
  1309. for (i=0 ; i<3 ; i++)
  1310. {
  1311. corner[i] = fabs(mins[i]) > fabs(maxs[i]) ? fabs(mins[i]) : fabs(maxs[i]);
  1312. }
  1313. return VectorLength( corner );
  1314. }
  1315. //-----------------------------------------------------------------------------
  1316. // Purpose:
  1317. //-----------------------------------------------------------------------------
  1318. void Mod_LoadSubmodels( CUtlVector<mmodel_t> &submodelList )
  1319. {
  1320. dmodel_t *in;
  1321. int i, j, count;
  1322. CMapLoadHelper lh( LUMP_MODELS );
  1323. in = (dmodel_t *)lh.LumpBase();
  1324. if (lh.LumpSize() % sizeof(*in))
  1325. Host_Error("Mod_LoadSubmodels: funny lump size in %s", lh.GetMapPathName());
  1326. count = lh.LumpSize() / sizeof(*in);
  1327. submodelList.SetCount( count );
  1328. lh.GetMap()->numsubmodels = count;
  1329. // first submodel is the world, copy out the face count
  1330. lh.GetMap()->nWorldFaceCount = in->numfaces;
  1331. for ( i=0 ; i<count ; i++, in++)
  1332. {
  1333. for (j=0 ; j<3 ; j++)
  1334. { // spread the mins / maxs by a pixel
  1335. submodelList[i].mins[j] = in->mins[j] - 1;
  1336. submodelList[i].maxs[j] = in->maxs[j] + 1;
  1337. submodelList[i].origin[j] = in->origin[j];
  1338. }
  1339. submodelList[i].radius = RadiusFromBounds (submodelList[i].mins, submodelList[i].maxs);
  1340. submodelList[i].headnode = in->headnode;
  1341. submodelList[i].firstface = in->firstface;
  1342. submodelList[i].numfaces = in->numfaces;
  1343. }
  1344. }
  1345. //-----------------------------------------------------------------------------
  1346. // Purpose:
  1347. // Output : medge_t *Mod_LoadEdges
  1348. //-----------------------------------------------------------------------------
  1349. medge_t *Mod_LoadEdges ( void )
  1350. {
  1351. dedge_t *in;
  1352. medge_t *out;
  1353. int i, count;
  1354. CMapLoadHelper lh( LUMP_EDGES );
  1355. in = (dedge_t *)lh.LumpBase();
  1356. if (lh.LumpSize() % sizeof(*in))
  1357. Host_Error ("Mod_LoadEdges: funny lump size in %s", lh.GetMapPathName());
  1358. count = lh.LumpSize() / sizeof(*in);
  1359. medge_t *pedges = new medge_t[count];
  1360. out = pedges;
  1361. for ( i=0 ; i<count ; i++, in++, out++)
  1362. {
  1363. out->v[0] = in->v[0];
  1364. out->v[1] = in->v[1];
  1365. }
  1366. // delete this in the loader
  1367. return pedges;
  1368. }
  1369. //-----------------------------------------------------------------------------
  1370. // Purpose:
  1371. //-----------------------------------------------------------------------------
  1372. void Mod_LoadOcclusion( void )
  1373. {
  1374. CMapLoadHelper lh( LUMP_OCCLUSION );
  1375. worldbrushdata_t *b = lh.GetMap();
  1376. b->numoccluders = 0;
  1377. b->occluders = NULL;
  1378. b->numoccluderpolys = 0;
  1379. b->occluderpolys = NULL;
  1380. b->numoccludervertindices = 0;
  1381. b->occludervertindices = NULL;
  1382. if ( !lh.LumpSize() )
  1383. {
  1384. return;
  1385. }
  1386. CUtlBuffer buf( lh.LumpBase(), lh.LumpSize(), CUtlBuffer::READ_ONLY );
  1387. switch( lh.LumpVersion() )
  1388. {
  1389. case LUMP_OCCLUSION_VERSION:
  1390. {
  1391. b->numoccluders = buf.GetInt();
  1392. if (b->numoccluders)
  1393. {
  1394. int nSize = b->numoccluders * sizeof(doccluderdata_t);
  1395. b->occluders = (doccluderdata_t*)Hunk_AllocName( nSize, "occluder data" );
  1396. buf.Get( b->occluders, nSize );
  1397. }
  1398. b->numoccluderpolys = buf.GetInt();
  1399. if (b->numoccluderpolys)
  1400. {
  1401. int nSize = b->numoccluderpolys * sizeof(doccluderpolydata_t);
  1402. b->occluderpolys = (doccluderpolydata_t*)Hunk_AllocName( nSize, "occluder poly data" );
  1403. buf.Get( b->occluderpolys, nSize );
  1404. }
  1405. b->numoccludervertindices = buf.GetInt();
  1406. if (b->numoccludervertindices)
  1407. {
  1408. int nSize = b->numoccludervertindices * sizeof(int);
  1409. b->occludervertindices = (int*)Hunk_AllocName( nSize, "occluder vertices" );
  1410. buf.Get( b->occludervertindices, nSize );
  1411. }
  1412. }
  1413. break;
  1414. case 1:
  1415. {
  1416. b->numoccluders = buf.GetInt();
  1417. if (b->numoccluders)
  1418. {
  1419. int nSize = b->numoccluders * sizeof(doccluderdata_t);
  1420. b->occluders = (doccluderdata_t*)Hunk_AllocName( nSize, "occluder data" );
  1421. doccluderdataV1_t temp;
  1422. for ( int i = 0; i < b->numoccluders; ++i )
  1423. {
  1424. buf.Get( &temp, sizeof(doccluderdataV1_t) );
  1425. memcpy( &b->occluders[i], &temp, sizeof(doccluderdataV1_t) );
  1426. b->occluders[i].area = 1;
  1427. }
  1428. }
  1429. b->numoccluderpolys = buf.GetInt();
  1430. if (b->numoccluderpolys)
  1431. {
  1432. int nSize = b->numoccluderpolys * sizeof(doccluderpolydata_t);
  1433. b->occluderpolys = (doccluderpolydata_t*)Hunk_AllocName( nSize, "occluder poly data" );
  1434. buf.Get( b->occluderpolys, nSize );
  1435. }
  1436. b->numoccludervertindices = buf.GetInt();
  1437. if (b->numoccludervertindices)
  1438. {
  1439. int nSize = b->numoccludervertindices * sizeof(int);
  1440. b->occludervertindices = (int*)Hunk_AllocName( nSize, "occluder vertices" );
  1441. buf.Get( b->occludervertindices, nSize );
  1442. }
  1443. }
  1444. break;
  1445. case 0:
  1446. break;
  1447. default:
  1448. Host_Error("Invalid occlusion lump version!\n");
  1449. break;
  1450. }
  1451. }
  1452. // UNDONE: Really, it's stored 2 times because the texture system keeps a
  1453. // copy of the name too. I guess we'll get rid of this when we have a material
  1454. // system that works without a graphics context. At that point, everyone can
  1455. // reference the name in the material, or just the material itself.
  1456. //-----------------------------------------------------------------------------
  1457. // Purpose:
  1458. //-----------------------------------------------------------------------------
  1459. void Mod_LoadTexdata( void )
  1460. {
  1461. // Don't bother loading these again; they're already stored in the collision model
  1462. // which is guaranteed to be loaded at this point
  1463. s_pMap->numtexdata = GetCollisionBSPData()->numtextures;
  1464. s_pMap->texdata = GetCollisionBSPData()->map_surfaces.Base();
  1465. }
  1466. //-----------------------------------------------------------------------------
  1467. // Purpose:
  1468. //-----------------------------------------------------------------------------
  1469. void Mod_LoadTexinfo( CMapLoadHelper &lh )
  1470. {
  1471. texinfo_t *in;
  1472. mtexinfo_t *out;
  1473. int i, j, count;
  1474. // UNDONE: Fix this
  1475. in = (texinfo_t *)lh.LumpBase();
  1476. if (lh.LumpSize() % sizeof(*in))
  1477. Host_Error ("Mod_LoadTexinfo: funny lump size in %s", lh.GetMapPathName());
  1478. count = lh.LumpSize() / sizeof(*in);
  1479. out = (mtexinfo_t *)Hunk_AllocName( count*sizeof(*out), va( "%s [%s]", lh.GetLoadName(), "texinfo" ) );
  1480. s_pMap->texinfo = out;
  1481. s_pMap->numtexinfo = count;
  1482. #if defined( DEVELOPMENT_ONLY ) || defined( ALLOW_TEXT_MODE )
  1483. static bool s_bTextMode = CommandLine()->HasParm( "-textmode" );
  1484. #else
  1485. const bool s_bTextMode = false;
  1486. #endif
  1487. bool loadtextures = mat_loadtextures.GetBool() && !s_bTextMode;
  1488. for ( i=0 ; i<count ; ++i, ++in, ++out )
  1489. {
  1490. for (j=0; j<2; ++j)
  1491. {
  1492. for (int k=0 ; k<4 ; ++k)
  1493. {
  1494. out->textureVecsTexelsPerWorldUnits[j][k] = in->textureVecsTexelsPerWorldUnits[j][k];
  1495. out->lightmapVecsLuxelsPerWorldUnits[j][k] = in->lightmapVecsLuxelsPerWorldUnits[j][k] ;
  1496. }
  1497. }
  1498. // assume that the scale is the same on both s and t.
  1499. out->luxelsPerWorldUnit = VectorLength( out->lightmapVecsLuxelsPerWorldUnits[0].AsVector3D() );
  1500. // Protect against divide-by-zero
  1501. if ( out->luxelsPerWorldUnit != 0 )
  1502. out->worldUnitsPerLuxel = 1.0f / out->luxelsPerWorldUnit;
  1503. out->flags = in->flags;
  1504. out->texinfoFlags = 0;
  1505. if ( loadtextures )
  1506. {
  1507. if ( in->texdata >= 0 )
  1508. {
  1509. out->material = GL_LoadMaterial( lh.GetMap()->texdata[ in->texdata ].name, TEXTURE_GROUP_WORLD );
  1510. if ( out->material->IsErrorMaterial() == true )
  1511. {
  1512. Msg( "Missing map material: %s\n", lh.GetMap()->texdata[ in->texdata ].name );
  1513. }
  1514. }
  1515. else
  1516. {
  1517. DevMsg( "Mod_LoadTexinfo: texdata < 0 (index==%i/%i)\n", i, count );
  1518. out->material = NULL;
  1519. }
  1520. if ( !out->material )
  1521. {
  1522. out->material = g_materialEmpty;
  1523. g_materialEmpty->IncrementReferenceCount();
  1524. }
  1525. }
  1526. else
  1527. {
  1528. out->material = g_materialEmpty;
  1529. g_materialEmpty->IncrementReferenceCount();
  1530. }
  1531. }
  1532. }
  1533. // code to scan the lightmaps for empty lightstyles
  1534. static void LinearToGamma( unsigned char *pDstRGB, const float *pSrcRGB )
  1535. {
  1536. pDstRGB[0] = LinearToScreenGamma( pSrcRGB[0] );
  1537. pDstRGB[1] = LinearToScreenGamma( pSrcRGB[1] );
  1538. pDstRGB[2] = LinearToScreenGamma( pSrcRGB[2] );
  1539. }
  1540. static void CheckSurfaceLighting( SurfaceHandle_t surfID, worldbrushdata_t *pBrushData )
  1541. {
  1542. #if !defined( DEDICATED )
  1543. host_state.worldbrush = pBrushData;
  1544. msurfacelighting_t *pLighting = SurfaceLighting( surfID, pBrushData );
  1545. if ( !pLighting->m_pSamples )
  1546. return;
  1547. int smax = ( pLighting->m_LightmapExtents[0] ) + 1;
  1548. int tmax = ( pLighting->m_LightmapExtents[1] ) + 1;
  1549. int size = smax * tmax;
  1550. int offset = size;
  1551. if ( SurfHasBumpedLightmaps( surfID ) )
  1552. {
  1553. offset *= ( NUM_BUMP_VECTS + 1 );
  1554. }
  1555. // for old maps revert to data layout before lightstyles were fixed to work with CSM's
  1556. // this is so as not to break modders who used lightstyles in way that worked for them (i.e. no env_cascade light)
  1557. if ( g_bLightstylesWithCSM )
  1558. {
  1559. // extra CSM alpha data - for new maps
  1560. offset += size;
  1561. }
  1562. // how many additional lightmaps does this surface have?
  1563. int maxLightmapIndex = 0;
  1564. for (int maps = 1 ; maps < MAXLIGHTMAPS && pLighting->m_nStyles[maps] != 255 ; ++maps)
  1565. {
  1566. maxLightmapIndex = maps;
  1567. }
  1568. if ( maxLightmapIndex < 1 )
  1569. {
  1570. // can't purge the base lightmap
  1571. return;
  1572. }
  1573. // iterate and test each lightmap
  1574. for ( int maps = maxLightmapIndex; maps != 0; maps-- )
  1575. {
  1576. ColorRGBExp32 *pLightmap = pLighting->m_pSamples + (maps * offset);
  1577. float maxLen = -1;
  1578. Vector maxLight;
  1579. maxLight.Init();
  1580. for ( int i = 0; i < offset; i++ )
  1581. {
  1582. Vector c;
  1583. ColorRGBExp32ToVector( pLightmap[i], c );
  1584. if ( c.Length() > maxLen )
  1585. {
  1586. maxLight = c;
  1587. maxLen = c.Length();
  1588. }
  1589. }
  1590. unsigned char color[4];
  1591. LinearToGamma( color, maxLight.Base() );
  1592. const int minLightVal = 1;
  1593. if ( color[0] <= minLightVal && color[1] <= minLightVal && color[2] <= minLightVal )
  1594. {
  1595. // found a lightmap that is too dark, remove it and shift over the subsequent maps/styles
  1596. for ( int i = maps; i < maxLightmapIndex; i++ )
  1597. {
  1598. ColorRGBExp32 *pLightmapOverwrite = pLighting->m_pSamples + ( i * offset );
  1599. memcpy( pLightmapOverwrite, pLightmapOverwrite+offset, offset * sizeof( ColorRGBExp32 ) );
  1600. pLighting->m_nStyles[i] = pLighting->m_nStyles[i+1];
  1601. // shift 'up' avgcolor values
  1602. // the '-' is correct, the avgcolors are stored behind the lightmaps and in reverse order
  1603. pLighting->m_pSamples[-( i + 1 )] = pLighting->m_pSamples[-( i + 2 )];
  1604. }
  1605. // mark end lightstyle as removed, decrement max index
  1606. pLighting->m_nStyles[maxLightmapIndex] = 255;
  1607. maxLightmapIndex--;
  1608. }
  1609. }
  1610. // we removed all of the lightstyle maps so clear the flag
  1611. if ( maxLightmapIndex == 0 )
  1612. {
  1613. surfID->flags &= ~SURFDRAW_HASLIGHTSYTLES;
  1614. }
  1615. #endif
  1616. }
  1617. //-----------------------------------------------------------------------------
  1618. // Purpose:
  1619. // Input : *loadmodel -
  1620. // *s -
  1621. // Output : void CalcSurfaceExtents
  1622. //-----------------------------------------------------------------------------
  1623. static void CalcSurfaceExtents( CMapLoadHelper& lh, SurfaceHandle_t surfID )
  1624. {
  1625. float textureMins[2], textureMaxs[2], val;
  1626. int i,j, e;
  1627. mvertex_t *v;
  1628. mtexinfo_t *tex;
  1629. int bmins[2], bmaxs[2];
  1630. textureMins[0] = textureMins[1] = 999999;
  1631. textureMaxs[0] = textureMaxs[1] = -99999;
  1632. worldbrushdata_t *pBrushData = lh.GetMap();
  1633. tex = MSurf_TexInfo( surfID, pBrushData );
  1634. for (i=0 ; i<MSurf_VertCount( surfID ); i++)
  1635. {
  1636. e = pBrushData->vertindices[MSurf_FirstVertIndex( surfID )+i];
  1637. v = &pBrushData->vertexes[e];
  1638. for (j=0 ; j<2 ; j++)
  1639. {
  1640. val = v->position[0] * tex->textureVecsTexelsPerWorldUnits[j][0] +
  1641. v->position[1] * tex->textureVecsTexelsPerWorldUnits[j][1] +
  1642. v->position[2] * tex->textureVecsTexelsPerWorldUnits[j][2] +
  1643. tex->textureVecsTexelsPerWorldUnits[j][3];
  1644. if (val < textureMins[j])
  1645. textureMins[j] = val;
  1646. if (val > textureMaxs[j])
  1647. textureMaxs[j] = val;
  1648. }
  1649. }
  1650. for (i=0 ; i<2 ; i++)
  1651. {
  1652. if( MSurf_LightmapExtents( surfID, pBrushData )[i] == 0 && !MSurf_Samples( surfID, pBrushData ) )
  1653. {
  1654. MSurf_Flags( surfID ) |= SURFDRAW_NOLIGHT;
  1655. }
  1656. bmins[i] = Float2Int( textureMins[i] );
  1657. bmaxs[i] = Ceil2Int( textureMaxs[i] );
  1658. MSurf_TextureMins( surfID, pBrushData )[i] = bmins[i];
  1659. MSurf_TextureExtents( surfID, pBrushData )[i] = ( bmaxs[i] - bmins[i] );
  1660. if ( !(tex->flags & SURF_NOLIGHT) && MSurf_LightmapExtents( surfID, pBrushData )[i] > MSurf_MaxLightmapSizeWithBorder( surfID ) )
  1661. {
  1662. Sys_Error ("Bad surface extents on texture %s", tex->material->GetName() );
  1663. }
  1664. }
  1665. }
  1666. //-----------------------------------------------------------------------------
  1667. // Input : *pModel -
  1668. // *pLump -
  1669. // *loadname -
  1670. //-----------------------------------------------------------------------------
  1671. void Mod_LoadVertNormals( void )
  1672. {
  1673. CMapLoadHelper lh( LUMP_VERTNORMALS );
  1674. // get a pointer to the vertex normal data.
  1675. Vector *pVertNormals = ( Vector * )lh.LumpBase();
  1676. //
  1677. // verify vertnormals data size
  1678. //
  1679. if( lh.LumpSize() % sizeof( *pVertNormals ) )
  1680. Host_Error( "Mod_LoadVertNormals: funny lump size in %s!\n", lh.GetMapPathName() );
  1681. int count = lh.LumpSize() / sizeof(*pVertNormals);
  1682. Vector *out = (Vector *)Hunk_AllocName( lh.LumpSize(), va( "%s [%s]", lh.GetLoadName(), "vertnormals" ) );
  1683. memcpy( out, pVertNormals, lh.LumpSize() );
  1684. lh.GetMap()->vertnormals = out;
  1685. lh.GetMap()->numvertnormals = count;
  1686. }
  1687. //-----------------------------------------------------------------------------
  1688. // Purpose:
  1689. //-----------------------------------------------------------------------------
  1690. void Mod_LoadVertNormalIndices( void )
  1691. {
  1692. CMapLoadHelper lh( LUMP_VERTNORMALINDICES );
  1693. // get a pointer to the vertex normal data.
  1694. unsigned short *pIndices = ( unsigned short * )lh.LumpBase();
  1695. int count = lh.LumpSize() / sizeof(*pIndices);
  1696. unsigned short *out = (unsigned short *)Hunk_AllocName( lh.LumpSize(), va( "%s [%s]", lh.GetLoadName(), "vertnormalindices" ) );
  1697. memcpy( out, pIndices, lh.LumpSize() );
  1698. lh.GetMap()->vertnormalindices = out;
  1699. lh.GetMap()->numvertnormalindices = count;
  1700. // OPTIMIZE: Water surfaces don't need vertex normals?
  1701. int normalIndex = 0;
  1702. for( int i = 0; i < lh.GetMap()->numsurfaces; i++ )
  1703. {
  1704. SurfaceHandle_t surfID = SurfaceHandleFromIndex( i, lh.GetMap() );
  1705. MSurf_FirstVertNormal( surfID, lh.GetMap() ) = normalIndex;
  1706. normalIndex += MSurf_VertCount( surfID );
  1707. }
  1708. }
  1709. //-----------------------------------------------------------------------------
  1710. // Purpose:
  1711. // Input : *loadmodel -
  1712. // *l -
  1713. // *loadname -
  1714. //-----------------------------------------------------------------------------
  1715. void Mod_LoadPrimitives( void )
  1716. {
  1717. dprimitive_t *in;
  1718. mprimitive_t *out;
  1719. int i, count;
  1720. CMapLoadHelper lh( LUMP_PRIMITIVES );
  1721. in = (dprimitive_t *)lh.LumpBase();
  1722. if (lh.LumpSize() % sizeof(*in))
  1723. Host_Error ("Mod_LoadPrimitives: funny lump size in %s", lh.GetMapPathName());
  1724. count = lh.LumpSize() / sizeof(*in);
  1725. out = (mprimitive_t *)Hunk_AllocName( count*sizeof(*out), va( "%s [%s]", lh.GetLoadName(), "primitives" ) );
  1726. memset( out, 0, count * sizeof( mprimitive_t ) );
  1727. lh.GetMap()->primitives = out;
  1728. lh.GetMap()->numprimitives = count;
  1729. for ( i=0 ; i<count ; i++, in++, out++)
  1730. {
  1731. out->firstIndex = in->firstIndex;
  1732. out->firstVert = in->firstVert;
  1733. out->indexCount = in->indexCount;
  1734. out->type = in->type;
  1735. out->vertCount = in->vertCount;
  1736. }
  1737. }
  1738. //-----------------------------------------------------------------------------
  1739. // Purpose:
  1740. // Input : *loadmodel -
  1741. // *l -
  1742. // *loadname -
  1743. //-----------------------------------------------------------------------------
  1744. void Mod_LoadPrimVerts( void )
  1745. {
  1746. dprimvert_t *in;
  1747. mprimvert_t *out;
  1748. int i, count;
  1749. CMapLoadHelper lh( LUMP_PRIMVERTS );
  1750. in = (dprimvert_t *)lh.LumpBase();
  1751. if (lh.LumpSize() % sizeof(*in))
  1752. Host_Error ("Mod_LoadPrimVerts: funny lump size in %s", lh.GetMapPathName());
  1753. count = lh.LumpSize() / sizeof(*in);
  1754. out = (mprimvert_t *)Hunk_AllocName( count*sizeof(*out), va( "%s [%s]", lh.GetLoadName(), "primverts" ) );
  1755. memset( out, 0, count * sizeof( mprimvert_t ) );
  1756. lh.GetMap()->primverts = out;
  1757. lh.GetMap()->numprimverts = count;
  1758. for ( i=0 ; i<count ; i++, in++, out++)
  1759. {
  1760. out->pos = in->pos;
  1761. }
  1762. }
  1763. //-----------------------------------------------------------------------------
  1764. // Purpose:
  1765. // Input : *loadmodel -
  1766. // *l -
  1767. // *loadname -
  1768. //-----------------------------------------------------------------------------
  1769. void Mod_LoadPrimIndices( void )
  1770. {
  1771. unsigned short *in;
  1772. unsigned short *out;
  1773. int count;
  1774. CMapLoadHelper lh( LUMP_PRIMINDICES );
  1775. in = (unsigned short *)lh.LumpBase();
  1776. if (lh.LumpSize() % sizeof(*in))
  1777. Host_Error ("Mod_LoadPrimIndices: funny lump size in %s", lh.GetMapPathName());
  1778. count = lh.LumpSize() / sizeof(*in);
  1779. out = (unsigned short *)Hunk_AllocName( count*sizeof(*out), va("%s [%s]", lh.GetLoadName(), "primindices" ) );
  1780. memset( out, 0, count * sizeof( unsigned short ) );
  1781. lh.GetMap()->primindices = out;
  1782. lh.GetMap()->numprimindices = count;
  1783. memcpy( out, in, count * sizeof( unsigned short ) );
  1784. }
  1785. // This allocates memory for a lump and copies the lump data in.
  1786. void Mod_LoadLump(
  1787. model_t *loadmodel,
  1788. int iLump,
  1789. char *loadname,
  1790. int elementSize,
  1791. void **ppData,
  1792. int *nElements )
  1793. {
  1794. CMapLoadHelper lh( iLump );
  1795. if ( lh.LumpSize() % elementSize )
  1796. {
  1797. Host_Error( "Mod_LoadLump: funny lump size in %s", loadmodel->szPathName );
  1798. }
  1799. // How many elements?
  1800. *nElements = lh.LumpSize() / elementSize;
  1801. // Make room for the data and copy the data in.
  1802. *ppData = Hunk_AllocName( lh.LumpSize(), loadname );
  1803. memcpy( *ppData, lh.LumpBase(), lh.LumpSize() );
  1804. }
  1805. //-----------------------------------------------------------------------------
  1806. // Sets up the msurfacelighting_t structure
  1807. //-----------------------------------------------------------------------------
  1808. bool Mod_LoadSurfaceLighting( msurfacelighting_t *pLighting, dface_t *in, ColorRGBExp32 *pBaseLightData )
  1809. {
  1810. // Get lightmap extents from the file.
  1811. pLighting->m_LightmapExtents[0] = in->m_LightmapTextureSizeInLuxels[0];
  1812. pLighting->m_LightmapExtents[1] = in->m_LightmapTextureSizeInLuxels[1];
  1813. pLighting->m_LightmapMins[0] = in->m_LightmapTextureMinsInLuxels[0];
  1814. pLighting->m_LightmapMins[1] = in->m_LightmapTextureMinsInLuxels[1];
  1815. int lightOffset = in->lightofs;
  1816. if ( ( lightOffset == -1 ) || !pBaseLightData )
  1817. {
  1818. pLighting->m_pSamples = NULL;
  1819. // Can't have *any* lightstyles if we have no samples
  1820. for ( int i = 0; i < MAXLIGHTMAPS; ++i )
  1821. {
  1822. pLighting->m_nStyles[i] = 255;
  1823. }
  1824. }
  1825. else
  1826. {
  1827. pLighting->m_pSamples = (ColorRGBExp32 *)( ((byte *)pBaseLightData) + lightOffset );
  1828. for ( int i = 0; i<MAXLIGHTMAPS; ++i )
  1829. {
  1830. pLighting->m_nStyles[i] = in->styles[i];
  1831. }
  1832. }
  1833. return ((pLighting->m_nStyles[0] != 0) && (pLighting->m_nStyles[0] != 255)) || (pLighting->m_nStyles[1] != 255);
  1834. }
  1835. void *Hunk_AllocNameAlignedClear_( int size, int alignment, const char *pHunkName )
  1836. {
  1837. Assert(IsPowerOfTwo(alignment));
  1838. void *pMem = Hunk_AllocName( alignment + size, pHunkName );
  1839. memset( pMem, 0, size + alignment );
  1840. pMem = (void *)( ( ( ( unsigned long )pMem ) + (alignment-1) ) & ~(alignment-1) );
  1841. return pMem;
  1842. }
  1843. // Allocates a block of T from the hunk. Aligns as specified and clears the memory
  1844. template< typename T >
  1845. T *Hunk_AllocNameAlignedClear( int count, int alignment, const char *pHunkName )
  1846. {
  1847. return (T *)Hunk_AllocNameAlignedClear_( alignment + count * sizeof(T), alignment, pHunkName );
  1848. }
  1849. //-----------------------------------------------------------------------------
  1850. // Purpose:
  1851. // Input : *loadmodel -
  1852. // *l -
  1853. // *loadname -
  1854. //-----------------------------------------------------------------------------
  1855. void Mod_LoadFaces( void )
  1856. {
  1857. dface_t *in;
  1858. int count, surfnum;
  1859. int planenum;
  1860. int ti, di;
  1861. int face_lump_to_load = LUMP_FACES;
  1862. if ( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE &&
  1863. CMapLoadHelper::LumpSize( LUMP_FACES_HDR ) > 0 )
  1864. {
  1865. face_lump_to_load = LUMP_FACES_HDR;
  1866. }
  1867. CMapLoadHelper lh( face_lump_to_load );
  1868. in = (dface_t *)lh.LumpBase();
  1869. if (lh.LumpSize() % sizeof(*in))
  1870. Host_Error ("Mod_LoadFaces: funny lump size in %s", lh.GetMapPathName());
  1871. count = lh.LumpSize() / sizeof(*in);
  1872. // align these allocations
  1873. // If you trip one of these, you need to rethink the alignment of the struct
  1874. Assert( sizeof(msurface1_t) == 16 );
  1875. Assert( sizeof(msurface2_t) == 32 );
  1876. Assert( sizeof(msurfacelighting_t) == 32 );
  1877. msurface1_t *out1 = Hunk_AllocNameAlignedClear< msurface1_t >( count, 16, va( "%s [%s]", lh.GetLoadName(), "surface1" ) );
  1878. msurface2_t *out2 = Hunk_AllocNameAlignedClear< msurface2_t >( count, 32, va( "%s [%s]", lh.GetLoadName(), "surface2" ) );
  1879. msurfacelighting_t *pLighting = Hunk_AllocNameAlignedClear< msurfacelighting_t >( count, 32, va( "%s [%s]", lh.GetLoadName(), "surfacelighting" ) );
  1880. lh.GetMap()->surfaces1 = out1;
  1881. lh.GetMap()->surfaces2 = out2;
  1882. lh.GetMap()->surfacelighting = pLighting;
  1883. lh.GetMap()->surfacenormals = Hunk_AllocNameAlignedClear< msurfacenormal_t >( count, 2, va( "%s [%s]", lh.GetLoadName(), "surfacenormal" ) );
  1884. lh.GetMap()->numsurfaces = count;
  1885. worldbrushdata_t *pBrushData = lh.GetMap();
  1886. for ( surfnum=0 ; surfnum<count ; ++surfnum, ++in, ++out1, ++out2, ++pLighting )
  1887. {
  1888. SurfaceHandle_t surfID = SurfaceHandleFromIndex( surfnum, pBrushData );
  1889. MSurf_FirstVertIndex( surfID ) = in->firstedge;
  1890. int vertCount = in->numedges;
  1891. MSurf_Flags( surfID ) = 0;
  1892. Assert( vertCount <= 255 );
  1893. MSurf_SetVertCount( surfID, vertCount );
  1894. planenum = in->planenum;
  1895. if ( in->onNode )
  1896. {
  1897. MSurf_Flags( surfID ) |= SURFDRAW_NODE;
  1898. }
  1899. if ( in->side )
  1900. {
  1901. MSurf_Flags( surfID ) |= SURFDRAW_PLANEBACK;
  1902. }
  1903. #ifndef _PS3
  1904. out2->plane = lh.GetMap()->planes + planenum;
  1905. #else
  1906. out2->m_plane = *(lh.GetMap()->planes + planenum);
  1907. #endif
  1908. ti = in->texinfo;
  1909. if (ti < 0 || ti >= lh.GetMap()->numtexinfo)
  1910. {
  1911. Host_Error( "Mod_LoadFaces: bad texinfo number" );
  1912. }
  1913. surfID->texinfo = ti;
  1914. surfID->m_bDynamicShadowsEnabled = in->AreDynamicShadowsEnabled();
  1915. mtexinfo_t *pTex = lh.GetMap()->texinfo + ti;
  1916. // big hack!
  1917. if ( !pTex->material )
  1918. {
  1919. pTex->material = g_materialEmpty;
  1920. g_materialEmpty->IncrementReferenceCount();
  1921. }
  1922. // lighting info
  1923. if ( Mod_LoadSurfaceLighting( pLighting, in, lh.GetMap()->lightdata ) )
  1924. {
  1925. MSurf_Flags( surfID ) |= SURFDRAW_HASLIGHTSYTLES;
  1926. }
  1927. // set the drawing flags flag
  1928. if ( pTex->flags & SURF_NOLIGHT )
  1929. {
  1930. MSurf_Flags( surfID ) |= SURFDRAW_NOLIGHT;
  1931. }
  1932. if ( pTex->flags & SURF_NOSHADOWS )
  1933. {
  1934. MSurf_Flags( surfID ) |= SURFDRAW_NOSHADOWS;
  1935. }
  1936. if ( pTex->flags & SURF_WARP )
  1937. {
  1938. MSurf_Flags( surfID ) |= SURFDRAW_WATERSURFACE;
  1939. }
  1940. if ( pTex->flags & SURF_SKY )
  1941. {
  1942. MSurf_Flags( surfID ) |= SURFDRAW_SKY;
  1943. }
  1944. if ( pTex->flags & SURF_NOPAINT )
  1945. {
  1946. MSurf_Flags( surfID ) |= SURFDRAW_NOPAINT;
  1947. }
  1948. di = in->dispinfo;
  1949. out2->pDispInfo = NULL;
  1950. if( di != -1 )
  1951. {
  1952. // out->origSurfaceID = in->origFace;
  1953. MSurf_Flags( surfID ) |= SURFDRAW_HAS_DISP;
  1954. }
  1955. else
  1956. {
  1957. // non-displacement faces shouldn't come out of VBSP if they have nodraw.
  1958. Assert( !(pTex->flags & SURF_NODRAW) );
  1959. out1->prims.numPrims = in->GetNumPrims();
  1960. out1->prims.firstPrimID = in->firstPrimID;
  1961. if ( in->GetNumPrims() )
  1962. {
  1963. MSurf_Flags( surfID ) |= SURFDRAW_HAS_PRIMS;
  1964. mprimitive_t *pPrim = &pBrushData->primitives[in->firstPrimID];
  1965. if ( pPrim->vertCount > 0 )
  1966. {
  1967. MSurf_Flags( surfID ) |= SURFDRAW_DYNAMIC;
  1968. }
  1969. }
  1970. }
  1971. // No shadows on the surface to start with
  1972. out2->m_ShadowDecals = SHADOW_DECAL_HANDLE_INVALID;
  1973. out2->decals = WORLD_DECAL_HANDLE_INVALID;
  1974. // No overlays on the surface to start with
  1975. out2->m_nFirstOverlayFragment = OVERLAY_FRAGMENT_INVALID;
  1976. CalcSurfaceExtents( lh, surfID );
  1977. // check and purge needless lightmaps
  1978. CheckSurfaceLighting( surfID, pBrushData );
  1979. }
  1980. }
  1981. //-----------------------------------------------------------------------------
  1982. // Purpose:
  1983. // Input : *node -
  1984. // *parent -
  1985. // Output : void Mod_SetParent
  1986. //-----------------------------------------------------------------------------
  1987. void Mod_SetParent (mnode_t *node, mnode_t *parent)
  1988. {
  1989. node->parent = parent;
  1990. if (node->contents >= 0)
  1991. return;
  1992. Mod_SetParent (node->children[0], node);
  1993. Mod_SetParent (node->children[1], node);
  1994. }
  1995. //-----------------------------------------------------------------------------
  1996. // Mark an entire subtree as being too small to bother with
  1997. //-----------------------------------------------------------------------------
  1998. static void MarkSmallNode( mnode_t *node )
  1999. {
  2000. if (node->contents >= 0)
  2001. return;
  2002. node->contents = -2;
  2003. MarkSmallNode (node->children[0]);
  2004. MarkSmallNode (node->children[1]);
  2005. }
  2006. static void CheckSmallVolumeDifferences( mnode_t *pNode, const Vector &parentSize )
  2007. {
  2008. if (pNode->contents >= 0)
  2009. return;
  2010. Vector delta;
  2011. VectorSubtract( parentSize, pNode->m_vecHalfDiagonal, delta );
  2012. if ((delta.x < 5) && (delta.y < 5) && (delta.z < 5))
  2013. {
  2014. pNode->contents = -3;
  2015. CheckSmallVolumeDifferences( pNode->children[0], parentSize );
  2016. CheckSmallVolumeDifferences( pNode->children[1], parentSize );
  2017. }
  2018. }
  2019. //-----------------------------------------------------------------------------
  2020. // Purpose:
  2021. // Input : *loadmodel -
  2022. // *l -
  2023. // *loadname -
  2024. //-----------------------------------------------------------------------------
  2025. void Mod_LoadNodes( void )
  2026. {
  2027. Vector mins( 0, 0, 0 ), maxs( 0, 0, 0 );
  2028. int i, j, count, p;
  2029. dnode_t *in;
  2030. mnode_t *out;
  2031. CMapLoadHelper lh( LUMP_NODES );
  2032. in = (dnode_t *)lh.LumpBase();
  2033. if (lh.LumpSize() % sizeof(*in))
  2034. Host_Error ("Mod_LoadNodes: funny lump size in %s", lh.GetMapPathName());
  2035. count = lh.LumpSize() / sizeof(*in);
  2036. out = (mnode_t *)Hunk_AllocName( count*sizeof(*out), va( "%s [%s]", lh.GetLoadName(), "nodes" ) );
  2037. lh.GetMap()->nodes = out;
  2038. lh.GetMap()->numnodes = count;
  2039. for ( i=0 ; i<count ; i++, in++, out++)
  2040. {
  2041. for (j=0 ; j<3 ; j++)
  2042. {
  2043. mins[j] = in->mins[j];
  2044. maxs[j] = in->maxs[j];
  2045. }
  2046. VectorAdd( mins, maxs, out->m_vecCenter );
  2047. out->m_vecCenter *= 0.5f;
  2048. VectorSubtract( maxs, out->m_vecCenter, out->m_vecHalfDiagonal );
  2049. p = in->planenum;
  2050. out->plane = lh.GetMap()->planes + p;
  2051. out->firstsurface = in->firstface;
  2052. out->numsurfaces = in->numfaces;
  2053. out->area = in->area;
  2054. out->contents = -1; // differentiate from leafs
  2055. for (j=0 ; j<2 ; j++)
  2056. {
  2057. p = in->children[j];
  2058. if (p >= 0)
  2059. out->children[j] = lh.GetMap()->nodes + p;
  2060. else
  2061. out->children[j] = (mnode_t *)(lh.GetMap()->leafs + (-1 - p));
  2062. }
  2063. }
  2064. Mod_SetParent (lh.GetMap()->nodes, NULL); // sets nodes and leafs
  2065. // Check for small-area parents... no culling below them...
  2066. mnode_t *pNode = lh.GetMap()->nodes;
  2067. for ( i=0 ; i<count ; ++i, ++pNode)
  2068. {
  2069. if (pNode->contents == -1)
  2070. {
  2071. if ((pNode->m_vecHalfDiagonal.x <= 50) && (pNode->m_vecHalfDiagonal.y <= 50) &&
  2072. (pNode->m_vecHalfDiagonal.z <= 50))
  2073. {
  2074. // Mark all children as being too small to bother with...
  2075. MarkSmallNode( pNode->children[0] );
  2076. MarkSmallNode( pNode->children[1] );
  2077. }
  2078. else
  2079. {
  2080. CheckSmallVolumeDifferences( pNode->children[0], pNode->m_vecHalfDiagonal );
  2081. CheckSmallVolumeDifferences( pNode->children[1], pNode->m_vecHalfDiagonal );
  2082. }
  2083. }
  2084. }
  2085. }
  2086. //-----------------------------------------------------------------------------
  2087. // Purpose:
  2088. // Input : *loadmodel -
  2089. // *l -
  2090. // *loadname -
  2091. //-----------------------------------------------------------------------------
  2092. void Mod_LoadLeafs_Version_0( CMapLoadHelper &lh )
  2093. {
  2094. Vector mins( 0, 0, 0 ), maxs( 0, 0, 0 );
  2095. dleaf_version_0_t *in;
  2096. mleaf_t *out;
  2097. int i, j, count, p;
  2098. in = (dleaf_version_0_t *)lh.LumpBase();
  2099. if (lh.LumpSize() % sizeof(*in))
  2100. Host_Error ("Mod_LoadLeafs: funny lump size in %s", lh.GetMapPathName());
  2101. count = lh.LumpSize() / sizeof(*in);
  2102. out = (mleaf_t *)Hunk_AllocName( count*sizeof(*out), va( "%s [%s]", lh.GetLoadName(), "leafs" ) );
  2103. lh.GetMap()->leafs = out;
  2104. lh.GetMap()->numleafs = count;
  2105. // one sample per leaf
  2106. lh.GetMap()->m_pLeafAmbient = (mleafambientindex_t *)Hunk_AllocName( count * sizeof(*lh.GetMap()->m_pLeafAmbient), "LeafAmbient" );
  2107. lh.GetMap()->m_pAmbientSamples = (mleafambientlighting_t *)Hunk_AllocName( count * sizeof(*lh.GetMap()->m_pAmbientSamples), "LeafAmbientSamples" );
  2108. mleafambientindex_t *pTable = lh.GetMap()->m_pLeafAmbient;
  2109. mleafambientlighting_t *pSamples = lh.GetMap()->m_pAmbientSamples;
  2110. for ( i=0 ; i<count ; i++, in++, out++)
  2111. {
  2112. for (j=0 ; j<3 ; j++)
  2113. {
  2114. mins[j] = in->mins[j];
  2115. maxs[j] = in->maxs[j];
  2116. }
  2117. VectorAdd( mins, maxs, out->m_vecCenter );
  2118. out->m_vecCenter *= 0.5f;
  2119. VectorSubtract( maxs, out->m_vecCenter, out->m_vecHalfDiagonal );
  2120. pTable[i].ambientSampleCount = 1;
  2121. pTable[i].firstAmbientSample = i;
  2122. pSamples[i].x = pSamples[i].y = pSamples[i].z = 128;
  2123. pSamples[i].pad = 0;
  2124. Q_memcpy( &pSamples[i].cube, &in->m_AmbientLighting, sizeof(pSamples[i].cube) );
  2125. p = in->contents;
  2126. out->contents = p;
  2127. out->cluster = in->cluster;
  2128. out->area = in->area;
  2129. out->flags = in->flags;
  2130. /*
  2131. out->firstmarksurface = lh.GetMap()->marksurfaces + in->firstleafface;
  2132. */
  2133. out->firstmarksurface = in->firstleafface;
  2134. out->nummarksurfaces = in->numleaffaces;
  2135. out->parent = NULL;
  2136. out->dispCount = 0;
  2137. out->leafWaterDataID = in->leafWaterDataID;
  2138. }
  2139. }
  2140. //-----------------------------------------------------------------------------
  2141. // Purpose:
  2142. // Input : *loadmodel -
  2143. // *l -
  2144. // *loadname -
  2145. //-----------------------------------------------------------------------------
  2146. void Mod_LoadLeafs_Version_1( CMapLoadHelper &lh, CMapLoadHelper &ambientLightingLump, CMapLoadHelper &ambientLightingTable )
  2147. {
  2148. Vector mins( 0, 0, 0 ), maxs( 0, 0, 0 );
  2149. dleaf_t *in;
  2150. mleaf_t *out;
  2151. int i, j, count, p;
  2152. in = (dleaf_t *)lh.LumpBase();
  2153. if (lh.LumpSize() % sizeof(*in))
  2154. Host_Error ("Mod_LoadLeafs: funny lump size in %s", lh.GetMapPathName());
  2155. count = lh.LumpSize() / sizeof(*in);
  2156. out = (mleaf_t *)Hunk_AllocName( count*sizeof(*out), va( "%s [%s]", lh.GetLoadName(), "leafs" ) );
  2157. lh.GetMap()->leafs = out;
  2158. lh.GetMap()->numleafs = count;
  2159. if ( ambientLightingLump.LumpVersion() != LUMP_LEAF_AMBIENT_LIGHTING_VERSION || ambientLightingTable.LumpSize() == 0 )
  2160. {
  2161. // convert from previous version
  2162. CompressedLightCube *inLightCubes = NULL;
  2163. if ( ambientLightingLump.LumpSize() )
  2164. {
  2165. inLightCubes = ( CompressedLightCube * )ambientLightingLump.LumpBase();
  2166. Assert( ambientLightingLump.LumpSize() % sizeof( CompressedLightCube ) == 0 );
  2167. Assert( ambientLightingLump.LumpSize() / sizeof( CompressedLightCube ) == lh.LumpSize() / sizeof( dleaf_t ) );
  2168. }
  2169. lh.GetMap()->m_pLeafAmbient = (mleafambientindex_t *)Hunk_AllocName( count * sizeof(*lh.GetMap()->m_pLeafAmbient), "LeafAmbient" );
  2170. lh.GetMap()->m_pAmbientSamples = (mleafambientlighting_t *)Hunk_AllocName( count * sizeof(*lh.GetMap()->m_pAmbientSamples), "LeafAmbientSamples" );
  2171. mleafambientindex_t *pTable = lh.GetMap()->m_pLeafAmbient;
  2172. mleafambientlighting_t *pSamples = lh.GetMap()->m_pAmbientSamples;
  2173. Vector gray(0.5, 0.5, 0.5);
  2174. ColorRGBExp32 grayColor;
  2175. VectorToColorRGBExp32( gray, grayColor );
  2176. for ( i = 0; i < count; i++ )
  2177. {
  2178. pTable[i].ambientSampleCount = 1;
  2179. pTable[i].firstAmbientSample = i;
  2180. pSamples[i].x = pSamples[i].y = pSamples[i].z = 128;
  2181. pSamples[i].pad = 0;
  2182. if ( inLightCubes )
  2183. {
  2184. Q_memcpy( &pSamples[i].cube, &inLightCubes[i], sizeof(pSamples[i].cube) );
  2185. }
  2186. else
  2187. {
  2188. for ( j = 0; j < 6; j++ )
  2189. {
  2190. pSamples[i].cube.m_Color[j] = grayColor;
  2191. }
  2192. }
  2193. }
  2194. }
  2195. else
  2196. {
  2197. Assert( ambientLightingLump.LumpSize() % sizeof( dleafambientlighting_t ) == 0 );
  2198. Assert( ambientLightingTable.LumpSize() % sizeof( dleafambientindex_t ) == 0 );
  2199. Assert((ambientLightingTable.LumpSize() / sizeof(dleafambientindex_t)) == (unsigned)count); // should have one of these per leaf
  2200. lh.GetMap()->m_pLeafAmbient = (mleafambientindex_t *)Hunk_AllocName( ambientLightingTable.LumpSize(), "LeafAmbient" );
  2201. lh.GetMap()->m_pAmbientSamples = (mleafambientlighting_t *)Hunk_AllocName( ambientLightingLump.LumpSize(), "LeafAmbientSamples" );
  2202. Q_memcpy( lh.GetMap()->m_pLeafAmbient, ambientLightingTable.LumpBase(), ambientLightingTable.LumpSize() );
  2203. Q_memcpy( lh.GetMap()->m_pAmbientSamples, ambientLightingLump.LumpBase(), ambientLightingLump.LumpSize() );
  2204. }
  2205. for ( i=0 ; i<count ; i++, in++, out++ )
  2206. {
  2207. for (j=0 ; j<3 ; j++)
  2208. {
  2209. mins[j] = in->mins[j];
  2210. maxs[j] = in->maxs[j];
  2211. }
  2212. VectorAdd( mins, maxs, out->m_vecCenter );
  2213. out->m_vecCenter *= 0.5f;
  2214. VectorSubtract( maxs, out->m_vecCenter, out->m_vecHalfDiagonal );
  2215. p = in->contents;
  2216. out->contents = p;
  2217. out->cluster = in->cluster;
  2218. out->area = in->area;
  2219. out->flags = in->flags;
  2220. /*
  2221. out->firstmarksurface = lh.GetMap()->marksurfaces + in->firstleafface;
  2222. */
  2223. out->firstmarksurface = in->firstleafface;
  2224. out->nummarksurfaces = in->numleaffaces;
  2225. out->parent = NULL;
  2226. out->dispCount = 0;
  2227. out->leafWaterDataID = in->leafWaterDataID;
  2228. }
  2229. }
  2230. void Mod_LoadLeafs( void )
  2231. {
  2232. CMapLoadHelper lh( LUMP_LEAFS );
  2233. switch( lh.LumpVersion() )
  2234. {
  2235. case 0:
  2236. Mod_LoadLeafs_Version_0( lh );
  2237. break;
  2238. case 1:
  2239. if( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE &&
  2240. CMapLoadHelper::LumpSize( LUMP_LEAF_AMBIENT_LIGHTING_HDR ) > 0 )
  2241. {
  2242. CMapLoadHelper mlh( LUMP_LEAF_AMBIENT_LIGHTING_HDR );
  2243. CMapLoadHelper mlhTable( LUMP_LEAF_AMBIENT_INDEX_HDR );
  2244. Mod_LoadLeafs_Version_1( lh, mlh, mlhTable );
  2245. }
  2246. else
  2247. {
  2248. CMapLoadHelper mlh( LUMP_LEAF_AMBIENT_LIGHTING );
  2249. CMapLoadHelper mlhTable( LUMP_LEAF_AMBIENT_INDEX );
  2250. Mod_LoadLeafs_Version_1( lh, mlh, mlhTable );
  2251. }
  2252. break;
  2253. default:
  2254. Assert( 0 );
  2255. Error( "Unknown LUMP_LEAFS version\n" );
  2256. break;
  2257. }
  2258. worldbrushdata_t *pMap = lh.GetMap();
  2259. cleaf_t *pCLeaf = GetCollisionBSPData()->map_leafs.Base();
  2260. for ( int i = 0; i < pMap->numleafs; i++ )
  2261. {
  2262. pMap->leafs[i].dispCount = pCLeaf[i].dispCount;
  2263. pMap->leafs[i].dispListStart = pCLeaf[i].dispListStart;
  2264. }
  2265. // HACKHACK: Copy over the shared global list here. Hunk_Alloc a copy?
  2266. pMap->m_pDispInfoReferences = GetCollisionBSPData()->map_dispList.Base();
  2267. pMap->m_nDispInfoReferences = GetCollisionBSPData()->numdisplist;
  2268. }
  2269. //-----------------------------------------------------------------------------
  2270. // Purpose:
  2271. //-----------------------------------------------------------------------------
  2272. void Mod_LoadLeafWaterData( void )
  2273. {
  2274. dleafwaterdata_t *in;
  2275. mleafwaterdata_t *out;
  2276. int count, i;
  2277. CMapLoadHelper lh( LUMP_LEAFWATERDATA );
  2278. in = (dleafwaterdata_t *)lh.LumpBase();
  2279. if (lh.LumpSize() % sizeof(*in))
  2280. Host_Error ("Mod_LoadLeafs: funny lump size in %s", lh.GetMapPathName());
  2281. count = lh.LumpSize() / sizeof(*in);
  2282. out = (mleafwaterdata_t *)Hunk_AllocName( count*sizeof(*out), va( "%s [%s]", lh.GetLoadName(), "leafwaterdata" ) );
  2283. lh.GetMap()->leafwaterdata = out;
  2284. lh.GetMap()->numleafwaterdata = count;
  2285. for ( i=0 ; i<count ; i++, in++, out++)
  2286. {
  2287. out->minZ = in->minZ;
  2288. out->surfaceTexInfoID = in->surfaceTexInfoID;
  2289. out->surfaceZ = in->surfaceZ;
  2290. out->firstLeafIndex = -1;
  2291. }
  2292. if ( count == 1 )
  2293. {
  2294. worldbrushdata_t *brush = lh.GetMap();
  2295. for ( i = 0; i < brush->numleafs; i++ )
  2296. {
  2297. if ( brush->leafs[i].leafWaterDataID >= 0 )
  2298. {
  2299. brush->leafwaterdata[0].firstLeafIndex = i;
  2300. break;
  2301. }
  2302. }
  2303. }
  2304. }
  2305. //-----------------------------------------------------------------------------
  2306. // Purpose:
  2307. //-----------------------------------------------------------------------------
  2308. void Mod_LoadCubemapSamples( void )
  2309. {
  2310. char textureName[512];
  2311. char loadName[ MAX_PATH ];
  2312. dcubemapsample_t *in;
  2313. mcubemapsample_t *out;
  2314. int count, i;
  2315. CMapLoadHelper lh( LUMP_CUBEMAPS );
  2316. V_StripExtension( lh.GetLoadName(), loadName, sizeof(loadName) );
  2317. in = (dcubemapsample_t *)lh.LumpBase();
  2318. if (lh.LumpSize() % sizeof(*in))
  2319. Host_Error ("Mod_LoadCubemapSamples: funny lump size in %s", lh.GetMapPathName());
  2320. count = lh.LumpSize() / sizeof(*in);
  2321. out = (mcubemapsample_t *)Hunk_AllocName( count*sizeof(*out), va( "%s [%s]", lh.GetLoadName(), "cubemapsample" ) );
  2322. lh.GetMap()->m_pCubemapSamples = out;
  2323. lh.GetMap()->m_nCubemapSamples = count;
  2324. // We have separate HDR versions of the textures. In order to deal with this,
  2325. // we have blahenvmap.hdr.vtf and blahenvmap.vtf.
  2326. char *pHDRExtension = "";
  2327. if( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE )
  2328. {
  2329. pHDRExtension = ".hdr";
  2330. }
  2331. for ( i=0 ; i<count ; i++, in++, out++)
  2332. {
  2333. out->origin.Init( ( float )in->origin[0], ( float )in->origin[1], ( float )in->origin[2] );
  2334. out->size = in->size;
  2335. Q_snprintf( textureName, sizeof( textureName ), "%s/c%d_%d_%d%s", loadName, ( int )in->origin[0],
  2336. ( int )in->origin[1], ( int )in->origin[2], pHDRExtension );
  2337. out->pTexture = materials->FindTexture( textureName, TEXTURE_GROUP_CUBE_MAP, true );
  2338. if ( IsErrorTexture( out->pTexture ) )
  2339. {
  2340. if ( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE )
  2341. {
  2342. Warning( "Couldn't get HDR '%s' -- ", textureName );
  2343. // try non hdr version
  2344. Q_snprintf( textureName, sizeof( textureName ), "%s/c%d_%d_%d", loadName, ( int )in->origin[0],
  2345. ( int )in->origin[1], ( int )in->origin[2]);
  2346. Warning( "Trying non HDR '%s'\n", textureName);
  2347. out->pTexture = materials->FindTexture( textureName, TEXTURE_GROUP_CUBE_MAP, true );
  2348. }
  2349. if ( IsErrorTexture( out->pTexture ) )
  2350. {
  2351. Q_snprintf( textureName, sizeof( textureName ), "%s/cubemapdefault", loadName );
  2352. out->pTexture = materials->FindTexture( textureName, TEXTURE_GROUP_CUBE_MAP, true );
  2353. if ( IsErrorTexture( out->pTexture ) )
  2354. {
  2355. out->pTexture = materials->FindTexture( "engine/defaultcubemap", TEXTURE_GROUP_CUBE_MAP, true );
  2356. }
  2357. Warning( "Failed, using default cubemap '%s'\n", out->pTexture->GetName() );
  2358. }
  2359. }
  2360. out->pTexture->IncrementReferenceCount();
  2361. }
  2362. CMatRenderContextPtr pRenderContext( materials );
  2363. if ( count )
  2364. {
  2365. pRenderContext->BindLocalCubemap( lh.GetMap()->m_pCubemapSamples[0].pTexture );
  2366. }
  2367. else
  2368. {
  2369. if ( CommandLine()->CheckParm( "-requirecubemaps" ) )
  2370. {
  2371. Sys_Error( "Map \"%s\" does not have cubemaps!", lh.GetMapPathName() );
  2372. }
  2373. ITexture *pTexture;
  2374. Q_snprintf( textureName, sizeof( textureName ), "%s/cubemapdefault", loadName );
  2375. pTexture = materials->FindTexture( textureName, TEXTURE_GROUP_CUBE_MAP, true );
  2376. if ( IsErrorTexture( pTexture ) )
  2377. {
  2378. pTexture = materials->FindTexture( "engine/defaultcubemap", TEXTURE_GROUP_CUBE_MAP, true );
  2379. }
  2380. pTexture->IncrementReferenceCount();
  2381. pRenderContext->BindLocalCubemap( pTexture );
  2382. }
  2383. }
  2384. //-----------------------------------------------------------------------------
  2385. // Purpose:
  2386. //-----------------------------------------------------------------------------
  2387. void Mod_LoadSimpleWorldModel( const char *pMapBaseName )
  2388. {
  2389. #if defined( CSTRIKE15 )
  2390. // We only load the world imposter models for specific maps on cstrike15
  2391. if( !( V_stristr( pMapBaseName, "de_lake" ) ||
  2392. V_stristr( pMapBaseName, "de_stmarc" ) ||
  2393. V_stristr( pMapBaseName, "de_aztec" ) ) )
  2394. {
  2395. return;
  2396. }
  2397. #else
  2398. // We only load the world imposter models for multiplayer maps on consoles
  2399. // Note: This seems super-sketchy, but apparently we use the map name to decide if we are co-op or not in portal 2.
  2400. if ( !V_stristr( pMapBaseName, "mp_coop_" ) && IsGameConsole() )
  2401. {
  2402. return;
  2403. }
  2404. #endif
  2405. char modelPath[MAX_PATH];
  2406. V_snprintf( modelPath, MAX_PATH, "models/maps/%s/simpleworldmodel.mdl", pMapBaseName );
  2407. char modelPathWater[MAX_PATH];
  2408. V_snprintf( modelPathWater, MAX_PATH, "models/maps/%s/simpleworldmodel_water.mdl", pMapBaseName );
  2409. IModelLoader::REFERENCETYPE referenceType = IModelLoader::FMODELLOADER_SIMPLEWORLD;
  2410. g_pSimpleWorldModel = g_ModelLoader.GetModelForName( modelPath, referenceType );
  2411. g_pSimpleWorldModelWater = g_ModelLoader.GetModelForName( modelPathWater, referenceType );
  2412. if ( !g_pSimpleWorldModel )
  2413. {
  2414. // This is BAD: it implies an image-building failure, and will cause huge perf regressions!
  2415. Warning("\n\n###########################################\n"
  2416. "## !!FAILED TO LOAD SIMPLE WORLD MODEL!! ##\n"
  2417. "## (perf will be terrible) ##\n"
  2418. "## (image is broken) ##\n"
  2419. "###########################################\n\n\n" );
  2420. }
  2421. }
  2422. //-----------------------------------------------------------------------------
  2423. // Purpose:
  2424. //-----------------------------------------------------------------------------
  2425. void Mod_LoadLeafMinDistToWater( void )
  2426. {
  2427. CMapLoadHelper lh( LUMP_LEAFMINDISTTOWATER );
  2428. unsigned short *pTmp = ( unsigned short * )lh.LumpBase();
  2429. int i;
  2430. bool foundOne = false;
  2431. for( i = 0; i < ( int )( lh.LumpSize() / sizeof( *pTmp ) ); i++ )
  2432. {
  2433. if( pTmp[i] != 65535 ) // FIXME: make a marcro for this.
  2434. {
  2435. foundOne = true;
  2436. break;
  2437. }
  2438. }
  2439. if( !foundOne || lh.LumpSize() == 0 || !g_pMaterialSystemHardwareConfig )
  2440. {
  2441. // We don't bother keeping this if:
  2442. // 1) there is no water in the map
  2443. // 2) we don't have this lump in the bsp file (old bsp file)
  2444. // 3) we aren't going to use it because we are on old hardware.
  2445. lh.GetMap()->m_LeafMinDistToWater = NULL;
  2446. }
  2447. else
  2448. {
  2449. int count;
  2450. unsigned short *in;
  2451. unsigned short *out;
  2452. in = (unsigned short *)lh.LumpBase();
  2453. if (lh.LumpSize() % sizeof(*in))
  2454. Host_Error ("Mod_LoadLeafMinDistToWater: funny lump size in %s", lh.GetMapPathName());
  2455. count = lh.LumpSize() / sizeof(*in);
  2456. out = (unsigned short *)Hunk_AllocName( count*sizeof(*out), va( "%s [%s]", lh.GetLoadName(), "leafmindisttowater" ) );
  2457. memcpy( out, in, sizeof( out[0] ) * count );
  2458. lh.GetMap()->m_LeafMinDistToWater = out;
  2459. }
  2460. }
  2461. //-----------------------------------------------------------------------------
  2462. // Purpose:
  2463. //-----------------------------------------------------------------------------
  2464. void Mod_LoadMarksurfaces( void )
  2465. {
  2466. int i, j, count;
  2467. unsigned short *in;
  2468. CMapLoadHelper lh( LUMP_LEAFFACES );
  2469. in = (unsigned short *)lh.LumpBase();
  2470. if (lh.LumpSize() % sizeof(*in))
  2471. Host_Error ("Mod_LoadMarksurfaces: funny lump size in %s", lh.GetMapPathName());
  2472. count = lh.LumpSize() / sizeof(*in);
  2473. SurfaceHandle_t *tempDiskData = new SurfaceHandle_t[count];
  2474. worldbrushdata_t *pBrushData = lh.GetMap();
  2475. pBrushData->marksurfaces = tempDiskData;
  2476. pBrushData->nummarksurfaces = count;
  2477. // read in the mark surfaces, count out how many we'll actually need to store
  2478. int realCount = 0;
  2479. for ( i=0 ; i<count ; i++)
  2480. {
  2481. j = in[i];
  2482. if (j >= lh.GetMap()->numsurfaces)
  2483. Host_Error ("Mod_LoadMarksurfaces: bad surface number");
  2484. SurfaceHandle_t surfID = SurfaceHandleFromIndex( j, pBrushData );
  2485. tempDiskData[i] = surfID;
  2486. if ( !SurfaceHasDispInfo( surfID ) && !(MSurf_Flags(surfID) & SURFDRAW_NODRAW) )
  2487. {
  2488. realCount++;
  2489. }
  2490. }
  2491. // now allocate the permanent list, and copy the non-terrain, non-nodraw surfs into it
  2492. SurfaceHandle_t *surfList = (SurfaceHandle_t *)Hunk_AllocName( realCount*sizeof(SurfaceHandle_t), va( "%s [%s]", lh.GetLoadName(), "surfacehandle" ) );
  2493. int outCount = 0;
  2494. mleaf_t *pLeaf = pBrushData->leafs;
  2495. for ( i = 0; i < pBrushData->numleafs; i++ )
  2496. {
  2497. int firstMark = outCount;
  2498. int numMark = 0;
  2499. bool foundDetail = false;
  2500. int numMarkNode = 0;
  2501. for ( j = 0; j < pLeaf[i].nummarksurfaces; j++ )
  2502. {
  2503. // write a new copy of the mark surfaces for this leaf, strip out the nodraw & terrain
  2504. SurfaceHandle_t surfID = tempDiskData[pLeaf[i].firstmarksurface+j];
  2505. if ( !SurfaceHasDispInfo( surfID ) && !(MSurf_Flags(surfID) & SURFDRAW_NODRAW) )
  2506. {
  2507. surfList[outCount++] = surfID;
  2508. numMark++;
  2509. Assert(outCount<=realCount);
  2510. if ( MSurf_Flags(surfID) & SURFDRAW_NODE )
  2511. {
  2512. // this assert assures that all SURFDRAW_NODE surfs appear coherently
  2513. Assert( !foundDetail );
  2514. numMarkNode++;
  2515. }
  2516. else
  2517. {
  2518. foundDetail = true;
  2519. }
  2520. }
  2521. }
  2522. // update the leaf count
  2523. pLeaf[i].nummarksurfaces = numMark;
  2524. pLeaf[i].firstmarksurface = firstMark;
  2525. pLeaf[i].nummarknodesurfaces = numMarkNode;
  2526. }
  2527. // write out the compacted array
  2528. pBrushData->marksurfaces = surfList;
  2529. pBrushData->nummarksurfaces = realCount;
  2530. // remove the temp copy of the disk data
  2531. delete[] tempDiskData;
  2532. //Msg("Must check %d / %d faces\n", checkCount, pModel->brush.numsurfaces );
  2533. }
  2534. //-----------------------------------------------------------------------------
  2535. // Purpose:
  2536. // Input : *pedges -
  2537. // *loadmodel -
  2538. // *l -
  2539. // *loadname -
  2540. //-----------------------------------------------------------------------------
  2541. void Mod_LoadSurfedges( medge_t *pedges )
  2542. {
  2543. int i, count;
  2544. int *in;
  2545. unsigned short *out;
  2546. CMapLoadHelper lh( LUMP_SURFEDGES );
  2547. in = (int *)lh.LumpBase();
  2548. if (lh.LumpSize() % sizeof(*in))
  2549. Host_Error ("Mod_LoadSurfedges: funny lump size in %s", lh.GetMapPathName());
  2550. count = lh.LumpSize() / sizeof(*in);
  2551. if (count < 1 || count >= MAX_MAP_SURFEDGES)
  2552. Host_Error ("Mod_LoadSurfedges: bad surfedges count in %s: %i",
  2553. lh.GetMapPathName(), count);
  2554. out = (unsigned short *)Hunk_AllocName( count*sizeof(*out), va( "%s [%s]", lh.GetLoadName(), "surfedges" ) );
  2555. lh.GetMap()->vertindices = out;
  2556. lh.GetMap()->numvertindices = count;
  2557. for ( i=0 ; i<count ; i++)
  2558. {
  2559. int edge = in[i];
  2560. int index = 0;
  2561. if ( edge < 0 )
  2562. {
  2563. edge = -edge;
  2564. index = 1;
  2565. }
  2566. out[i] = pedges[edge].v[index];
  2567. }
  2568. delete[] pedges;
  2569. }
  2570. //-----------------------------------------------------------------------------
  2571. // Purpose:
  2572. // Input : *loadmodel -
  2573. // *l -
  2574. // *loadname -
  2575. //-----------------------------------------------------------------------------
  2576. void Mod_LoadPlanes( void )
  2577. {
  2578. // Don't bother loading them, they're already stored
  2579. s_pMap->planes = GetCollisionBSPData()->map_planes.Base();
  2580. s_pMap->numplanes = GetCollisionBSPData()->numplanes;
  2581. }
  2582. //-----------------------------------------------------------------------------
  2583. // Returns game lump version
  2584. //-----------------------------------------------------------------------------
  2585. int Mod_GameLumpVersion( int lumpId )
  2586. {
  2587. for ( int i = g_GameLumpDict.Count(); --i >= 0; )
  2588. {
  2589. if ( g_GameLumpDict[i].id == lumpId )
  2590. {
  2591. return g_GameLumpDict[i].version;
  2592. }
  2593. }
  2594. return 0;
  2595. }
  2596. //-----------------------------------------------------------------------------
  2597. // Returns game lump size
  2598. //-----------------------------------------------------------------------------
  2599. int Mod_GameLumpSize( int lumpId )
  2600. {
  2601. for ( int i = g_GameLumpDict.Count(); --i >= 0; )
  2602. {
  2603. if ( g_GameLumpDict[i].id == lumpId )
  2604. {
  2605. return g_GameLumpDict[i].uncompressedSize;
  2606. }
  2607. }
  2608. return 0;
  2609. }
  2610. //-----------------------------------------------------------------------------
  2611. // Loads game lumps
  2612. //-----------------------------------------------------------------------------
  2613. bool Mod_LoadGameLump( int lumpId, void *pOutBuffer, int size )
  2614. {
  2615. int i;
  2616. for ( i = g_GameLumpDict.Count(); --i >= 0; )
  2617. {
  2618. if ( g_GameLumpDict[i].id == lumpId )
  2619. {
  2620. break;
  2621. }
  2622. }
  2623. if ( i < 0 )
  2624. {
  2625. // unknown
  2626. return false;
  2627. }
  2628. byte *pData;
  2629. bool bIsCompressed = ( g_GameLumpDict[i].flags & GAMELUMPFLAG_COMPRESSED ) != 0;
  2630. int dataLength;
  2631. int outSize;
  2632. if ( bIsCompressed )
  2633. {
  2634. // lump data length is always original uncompressed size
  2635. // compressed lump data length is determined from next dictionary entry offset
  2636. dataLength = g_GameLumpDict[i].compressedSize;
  2637. outSize = g_GameLumpDict[i].uncompressedSize;
  2638. }
  2639. else
  2640. {
  2641. dataLength = outSize = g_GameLumpDict[i].uncompressedSize;
  2642. }
  2643. if ( size < 0 || size < outSize )
  2644. {
  2645. // caller must supply a buffer that is large enough to hold the data
  2646. return false;
  2647. }
  2648. if ( s_MapBuffer.GetUsed() )
  2649. {
  2650. // data is in memory
  2651. Assert( CMapLoadHelper::GetRefCount() );
  2652. if ( g_GameLumpDict[i].offset + dataLength > (unsigned int)s_MapBuffer.GetUsed() )
  2653. {
  2654. // out of range
  2655. Assert( 0 );
  2656. return false;
  2657. }
  2658. pData = (unsigned char *)s_MapBuffer.GetBase() + g_GameLumpDict[i].offset;
  2659. if ( !bIsCompressed )
  2660. {
  2661. V_memcpy( pOutBuffer, pData, outSize );
  2662. return true;
  2663. }
  2664. }
  2665. else
  2666. {
  2667. // Load file into buffer
  2668. char szNameOnDisk[MAX_PATH];
  2669. GetMapPathNameOnDisk( szNameOnDisk, g_GameLumpFilename, sizeof( szNameOnDisk ) );
  2670. FileHandle_t fileHandle = g_pFileSystem->OpenEx( szNameOnDisk, "rb", IsGameConsole() ? FSOPEN_NEVERINPACK : 0, IsGameConsole() ? "GAME" : NULL );
  2671. if ( fileHandle == FILESYSTEM_INVALID_HANDLE )
  2672. {
  2673. return false;
  2674. }
  2675. g_pFileSystem->Seek( fileHandle, g_GameLumpDict[i].offset, FILESYSTEM_SEEK_HEAD );
  2676. if ( !bIsCompressed )
  2677. {
  2678. // read directly into user's buffer
  2679. bool bOK = ( g_pFileSystem->Read( pOutBuffer, outSize, fileHandle ) > 0 );
  2680. g_pFileSystem->Close( fileHandle );
  2681. return bOK;
  2682. }
  2683. else
  2684. {
  2685. // data is compressed, read into temporary
  2686. pData = (byte *)malloc( dataLength );
  2687. bool bOK = ( g_pFileSystem->Read( pData, dataLength, fileHandle ) > 0 );
  2688. g_pFileSystem->Close( fileHandle );
  2689. if ( !bOK )
  2690. {
  2691. free( pData );
  2692. return false;
  2693. }
  2694. }
  2695. }
  2696. // NOTE: TF2 added support for compressed lumps on PC (see CL#2898466 & CL#2898683 in //Valve mainline), but we'll keep it disabled in CS:GO
  2697. #if COMPRESSED_GAMELUMPS_SUPPORTED_ON_PC
  2698. // We'll fall though to here through here if we're compressed
  2699. bool bResult = false;
  2700. if ( !CLZMA::IsCompressed( pData ) || CLZMA::GetActualSize( (unsigned char *)pData ) != g_GameLumpDict[i].uncompressedSize )
  2701. {
  2702. Warning( "Failed loading game lump %i: lump claims to be compressed but metadata does not match\n", lumpId );
  2703. }
  2704. else
  2705. {
  2706. // uncompress directly into caller's buffer
  2707. int outputLength = CLZMA::Uncompress( pData, ( unsigned char * ) pOutBuffer );
  2708. bResult = ( outputLength > 0 && ( unsigned int ) outputLength == g_GameLumpDict[ i ].uncompressedSize );
  2709. }
  2710. if ( !s_MapBuffer.Base() )
  2711. {
  2712. // done with temporary buffer
  2713. free( pData );
  2714. }
  2715. return bResult;
  2716. #endif
  2717. // only 360 has compressed gamelumps
  2718. Assert( 0 );
  2719. return false;
  2720. }
  2721. //-----------------------------------------------------------------------------
  2722. // Loads game lump dictionary
  2723. //-----------------------------------------------------------------------------
  2724. void Mod_LoadGameLumpDict( void )
  2725. {
  2726. CMapLoadHelper lh( LUMP_GAME_LUMP );
  2727. // FIXME: This is brittle. If we ever try to load two game lumps
  2728. // (say, in multiple BSP files), the dictionary info I store here will get whacked
  2729. g_GameLumpDict.RemoveAll();
  2730. Q_strncpy( g_GameLumpFilename, lh.GetMapPathName(), sizeof( g_GameLumpFilename ) );
  2731. unsigned int lhSize = (unsigned int)Max( lh.LumpSize(), 0 );
  2732. if ( lhSize >= sizeof( dgamelumpheader_t ) )
  2733. {
  2734. dgamelumpheader_t* pGameLumpHeader = (dgamelumpheader_t*)lh.LumpBase();
  2735. // Ensure (lumpsize * numlumps + headersize) doesn't overflow
  2736. const int nMaxGameLumps = ( INT_MAX - sizeof( dgamelumpheader_t ) ) / sizeof( dgamelump_t );
  2737. if ( pGameLumpHeader->lumpCount < 0 ||
  2738. pGameLumpHeader->lumpCount > nMaxGameLumps ||
  2739. sizeof( dgamelumpheader_t ) + sizeof( dgamelump_t ) * pGameLumpHeader->lumpCount > lhSize )
  2740. {
  2741. Warning( "Bogus gamelump header in map, rejecting\n" );
  2742. }
  2743. else
  2744. {
  2745. // Load in lumps
  2746. dgamelump_t* pGameLump = (dgamelump_t*)(pGameLumpHeader + 1);
  2747. for (int i = 0; i < pGameLumpHeader->lumpCount; ++i )
  2748. {
  2749. if ( pGameLump[ i ].fileofs >= 0 &&
  2750. ( unsigned int ) pGameLump[ i ].fileofs >= ( unsigned int ) lh.LumpOffset() &&
  2751. ( unsigned int ) pGameLump[ i ].fileofs < ( unsigned int ) lh.LumpOffset() + lhSize &&
  2752. pGameLump[ i ].filelen > 0 )
  2753. {
  2754. unsigned int compressedSize = 0;
  2755. if ( i + 1 < pGameLumpHeader->lumpCount &&
  2756. pGameLump[ i + 1 ].fileofs > pGameLump[ i ].fileofs &&
  2757. pGameLump[ i + 1 ].fileofs >= 0 &&
  2758. ( unsigned int ) pGameLump[ i + 1 ].fileofs <= ( unsigned int ) lh.LumpOffset() + lhSize )
  2759. {
  2760. compressedSize = ( unsigned int ) pGameLump[ i + 1 ].fileofs - ( unsigned int ) pGameLump[ i ].fileofs;
  2761. }
  2762. else
  2763. {
  2764. compressedSize = ( unsigned int ) lh.LumpOffset() + lhSize - ( unsigned int ) pGameLump[ i ].fileofs;
  2765. }
  2766. g_GameLumpDict.AddToTail( { pGameLump[ i ], compressedSize } );
  2767. }
  2768. }
  2769. }
  2770. }
  2771. }
  2772. //-----------------------------------------------------------------------------
  2773. // Re-Loads all of a model's peer data
  2774. //-----------------------------------------------------------------------------
  2775. void Mod_TouchAllData( model_t *pModel, int nServerCount )
  2776. {
  2777. double t1 = Plat_FloatTime();
  2778. MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
  2779. virtualmodel_t *pVirtualModel = g_pMDLCache->GetVirtualModel( pModel->studio );
  2780. double t2 = Plat_FloatTime();
  2781. g_flAccumulatedModelLoadTimeVirtualModel += ( t2 - t1 );
  2782. if ( pVirtualModel && nServerCount >= 1 )
  2783. {
  2784. // ensure all sub models get current count to avoid purge
  2785. // mark first to prevent re-entrant issues during possible reload
  2786. // skip self, start at children
  2787. for ( int i=1; i<pVirtualModel->m_group.Count(); ++i )
  2788. {
  2789. MDLHandle_t childHandle = VoidPtrToMDLHandle( pVirtualModel->m_group[i].cache );
  2790. model_t *pChildModel = (model_t *)g_pMDLCache->GetUserData( childHandle );
  2791. if ( pChildModel )
  2792. {
  2793. // child inherits parent reference
  2794. pChildModel->nLoadFlags |= ( pModel->nLoadFlags & IModelLoader::FMODELLOADER_REFERENCEMASK );
  2795. pChildModel->nLoadFlags |= IModelLoader::FMODELLOADER_LOADED;
  2796. pChildModel->nLoadFlags &= ~IModelLoader::FMODELLOADER_LOADED_BY_PRELOAD;
  2797. pChildModel->nServerCount = nServerCount;
  2798. }
  2799. }
  2800. }
  2801. // don't touch all the data
  2802. if ( !mod_forcetouchdata.GetBool() )
  2803. return;
  2804. g_pMDLCache->TouchAllData( pModel->studio );
  2805. }
  2806. //-----------------------------------------------------------------------------
  2807. // Callbacks to get called when various data is loaded or unloaded
  2808. //-----------------------------------------------------------------------------
  2809. class CMDLCacheNotify : public IMDLCacheNotify
  2810. {
  2811. public:
  2812. virtual void OnDataLoaded( MDLCacheDataType_t type, MDLHandle_t handle );
  2813. virtual void OnCombinerPreCache( MDLHandle_t OldHandle, MDLHandle_t NewHandle );
  2814. virtual void OnDataUnloaded( MDLCacheDataType_t type, MDLHandle_t handle );
  2815. virtual bool ShouldSupressLoadWarning( MDLHandle_t handle );
  2816. private:
  2817. void ComputeModelFlags( model_t* mod, MDLHandle_t handle );
  2818. // Sets the bounds from the studiohdr
  2819. void SetBoundsFromStudioHdr( model_t *pModel, MDLHandle_t handle );
  2820. };
  2821. static CMDLCacheNotify s_MDLCacheNotify;
  2822. //-----------------------------------------------------------------------------
  2823. // Computes model flags
  2824. //-----------------------------------------------------------------------------
  2825. void CMDLCacheNotify::ComputeModelFlags( model_t* pModel, MDLHandle_t handle )
  2826. {
  2827. studiohdr_t *pStudioHdr = g_pMDLCache->GetStudioHdr( handle );
  2828. // Clear out those flags we set...
  2829. pModel->flags &= ~(MODELFLAG_TRANSLUCENT_TWOPASS | MODELFLAG_VERTEXLIT |
  2830. MODELFLAG_TRANSLUCENT | MODELFLAG_MATERIALPROXY | MODELFLAG_FRAMEBUFFER_TEXTURE |
  2831. MODELFLAG_STUDIOHDR_USES_FB_TEXTURE | MODELFLAG_STUDIOHDR_USES_BUMPMAPPING |
  2832. MODELFLAG_STUDIOHDR_USES_ENV_CUBEMAP | MODELFLAG_STUDIOHDR_IS_STATIC_PROP | MODELFLAG_STUDIOHDR_BAKED_VERTEX_LIGHTING_IS_INDIRECT_ONLY );
  2833. bool bForceOpaque = (pStudioHdr->flags & STUDIOHDR_FLAGS_FORCE_OPAQUE) != 0;
  2834. if ( pStudioHdr->flags & STUDIOHDR_FLAGS_TRANSLUCENT_TWOPASS )
  2835. {
  2836. pModel->flags |= MODELFLAG_TRANSLUCENT_TWOPASS;
  2837. }
  2838. if ( pStudioHdr->flags & STUDIOHDR_FLAGS_USES_FB_TEXTURE )
  2839. {
  2840. pModel->flags |= MODELFLAG_STUDIOHDR_USES_FB_TEXTURE;
  2841. }
  2842. if ( pStudioHdr->flags & STUDIOHDR_FLAGS_STATIC_PROP )
  2843. {
  2844. pModel->flags |= MODELFLAG_STUDIOHDR_IS_STATIC_PROP;
  2845. }
  2846. if ( pStudioHdr->flags & STUDIOHDR_FLAGS_USES_BUMPMAPPING )
  2847. {
  2848. pModel->flags |= MODELFLAG_STUDIOHDR_USES_BUMPMAPPING;
  2849. }
  2850. if ( pStudioHdr->flags & STUDIOHDR_BAKED_VERTEX_LIGHTING_IS_INDIRECT_ONLY )
  2851. {
  2852. pModel->flags |= MODELFLAG_STUDIOHDR_BAKED_VERTEX_LIGHTING_IS_INDIRECT_ONLY;
  2853. }
  2854. if ( pStudioHdr->flags & STUDIOHDR_FLAGS_USES_ENV_CUBEMAP )
  2855. {
  2856. pModel->flags |= MODELFLAG_STUDIOHDR_USES_ENV_CUBEMAP;
  2857. }
  2858. if ( pStudioHdr->flags & STUDIOHDR_FLAGS_AMBIENT_BOOST )
  2859. {
  2860. pModel->flags |= MODELFLAG_STUDIOHDR_AMBIENT_BOOST;
  2861. }
  2862. if ( pStudioHdr->flags & STUDIOHDR_FLAGS_DO_NOT_CAST_SHADOWS )
  2863. {
  2864. pModel->flags |= MODELFLAG_STUDIOHDR_DO_NOT_CAST_SHADOWS;
  2865. }
  2866. IMaterial *materials[ 128 ];
  2867. int materialCount = Mod_GetModelMaterials( pModel, ARRAYSIZE( materials ), materials );
  2868. for ( int i = 0; i < materialCount; ++i )
  2869. {
  2870. IMaterial *pMaterial = materials[ i ];
  2871. if ( !pMaterial )
  2872. continue;
  2873. if ( pMaterial->IsVertexLit() )
  2874. {
  2875. pModel->flags |= MODELFLAG_VERTEXLIT;
  2876. }
  2877. if ( !bForceOpaque && pMaterial->IsTranslucent() )
  2878. {
  2879. //Msg("Translucent material %s for model %s\n", pLODData->ppMaterials[i]->GetName(), pModel->name );
  2880. pModel->flags |= MODELFLAG_TRANSLUCENT;
  2881. }
  2882. if ( pMaterial->HasProxy() )
  2883. {
  2884. pModel->flags |= MODELFLAG_MATERIALPROXY;
  2885. }
  2886. if ( pMaterial->NeedsPowerOfTwoFrameBufferTexture( false ) ) // The false checks if it will ever need the frame buffer, not just this frame
  2887. {
  2888. pModel->flags |= MODELFLAG_FRAMEBUFFER_TEXTURE;
  2889. }
  2890. }
  2891. }
  2892. //-----------------------------------------------------------------------------
  2893. // Sets the bounds from the studiohdr
  2894. //-----------------------------------------------------------------------------
  2895. void CMDLCacheNotify::SetBoundsFromStudioHdr( model_t *pModel, MDLHandle_t handle )
  2896. {
  2897. studiohdr_t *pStudioHdr = g_pMDLCache->GetStudioHdr( handle );
  2898. VectorCopy( pStudioHdr->hull_min, pModel->mins );
  2899. VectorCopy( pStudioHdr->hull_max, pModel->maxs );
  2900. pModel->radius = 0.0f;
  2901. for ( int i = 0; i < 3; i++ )
  2902. {
  2903. if ( fabs(pModel->mins[i]) > pModel->radius )
  2904. {
  2905. pModel->radius = fabs(pModel->mins[i]);
  2906. }
  2907. if ( fabs(pModel->maxs[i]) > pModel->radius )
  2908. {
  2909. pModel->radius = fabs(pModel->maxs[i]);
  2910. }
  2911. }
  2912. }
  2913. //-----------------------------------------------------------------------------
  2914. // Callbacks to get called when various data is loaded or unloaded
  2915. //-----------------------------------------------------------------------------
  2916. void CMDLCacheNotify::OnDataLoaded( MDLCacheDataType_t type, MDLHandle_t handle )
  2917. {
  2918. model_t *pModel = (model_t*)g_pMDLCache->GetUserData( handle );
  2919. // NOTE: A NULL model can occur for dependent MDLHandle_ts (like .ani files)
  2920. if ( !pModel )
  2921. return;
  2922. switch( type )
  2923. {
  2924. case MDLCACHE_STUDIOHDR:
  2925. {
  2926. // FIXME: This code only works because it assumes StudioHdr
  2927. // is loaded before VCollide.
  2928. SetBoundsFromStudioHdr( pModel, handle );
  2929. }
  2930. break;
  2931. case MDLCACHE_VCOLLIDE:
  2932. {
  2933. SetBoundsFromStudioHdr( pModel, handle );
  2934. // Expand the model bounds to enclose the collision model (should be done in studiomdl)
  2935. vcollide_t *pCollide = g_pMDLCache->GetVCollide( handle );
  2936. if ( pCollide )
  2937. {
  2938. Vector mins, maxs;
  2939. physcollision->CollideGetAABB( &mins, &maxs, pCollide->solids[0], vec3_origin, vec3_angle );
  2940. AddPointToBounds( mins, pModel->mins, pModel->maxs );
  2941. AddPointToBounds( maxs, pModel->mins, pModel->maxs );
  2942. }
  2943. }
  2944. break;
  2945. case MDLCACHE_STUDIOHWDATA:
  2946. {
  2947. ComputeModelFlags( pModel, handle );
  2948. #if !defined( DEDICATED )
  2949. if ( g_ModelLoader.m_bAllowWeaponModelCache )
  2950. {
  2951. int nMapIndex = g_ModelLoader.m_WeaponModelCache.Find( pModel );
  2952. if ( nMapIndex != g_ModelLoader.m_WeaponModelCache.InvalidIndex() )
  2953. {
  2954. g_ModelLoader.m_WeaponModelCache[nMapIndex]->m_bStudioHWDataResident = true;
  2955. }
  2956. }
  2957. #endif
  2958. }
  2959. break;
  2960. }
  2961. }
  2962. void CMDLCacheNotify::OnCombinerPreCache( MDLHandle_t OldHandle, MDLHandle_t NewHandle )
  2963. {
  2964. model_t *pModel = ( model_t * )g_pMDLCache->GetUserData( OldHandle );
  2965. if ( !pModel )
  2966. {
  2967. Assert( 0 );
  2968. return;
  2969. }
  2970. pModel->studio = NewHandle;
  2971. g_pMDLCache->SetUserData( OldHandle, NULL );
  2972. g_pMDLCache->SetUserData( NewHandle, pModel );
  2973. }
  2974. void CMDLCacheNotify::OnDataUnloaded( MDLCacheDataType_t type, MDLHandle_t handle )
  2975. {
  2976. #if defined( PLATFORM_WINDOWS_PC ) || defined( DEDICATED )
  2977. // NOTE: This is because CMDLCache::UnloadQueuedHardwareData() FUNDAMENTALLY broke the modelcache due to its
  2978. // need to break the "flush" dependency. I did not investigate the validity of WHY THAT needed to be done.
  2979. // Since CMDLCache::ShutdownStudioData() breaks the dependency, the higher code does the m_MDLDict.RemoveAt( handle );
  2980. // not realizing the flush has been deferred, along comes the flush later and all sorts of code that expected
  2981. // the MDLHandle_t to be valid (now it's invalid) via the removal from underlying dictionary and code crashes.
  2982. return;
  2983. #endif
  2984. #if !defined( DEDICATED )
  2985. if ( g_ModelLoader.m_bAllowWeaponModelCache && type == MDLCACHE_STUDIOHWDATA )
  2986. {
  2987. model_t *pModel = (model_t*)g_pMDLCache->GetUserData( handle );
  2988. if ( pModel )
  2989. {
  2990. int nMapIndex = g_ModelLoader.m_WeaponModelCache.Find( pModel );
  2991. if ( nMapIndex != g_ModelLoader.m_WeaponModelCache.InvalidIndex() )
  2992. {
  2993. g_ModelLoader.m_WeaponModelCache[nMapIndex]->m_bStudioHWDataResident = false;
  2994. }
  2995. }
  2996. }
  2997. #endif
  2998. }
  2999. bool CMDLCacheNotify::ShouldSupressLoadWarning( MDLHandle_t handle )
  3000. {
  3001. #if !defined( DEDICATED )
  3002. if ( g_ModelLoader.m_bAllowWeaponModelCache )
  3003. {
  3004. // the QL wants to warn about loading data outside its awareness
  3005. // weapon models are explicitly prevented from the QL path
  3006. model_t *pModel = (model_t*)g_pMDLCache->GetUserData( handle );
  3007. if ( pModel )
  3008. {
  3009. int nMapIndex = g_ModelLoader.m_WeaponModelCache.Find( pModel );
  3010. if ( nMapIndex != g_ModelLoader.m_WeaponModelCache.InvalidIndex() )
  3011. {
  3012. // model is part of weapon model cache
  3013. // any QL load warnings are benign and should be suppressed
  3014. return true;
  3015. }
  3016. }
  3017. }
  3018. #endif
  3019. return false;
  3020. }
  3021. //-----------------------------------------------------------------------------
  3022. // Hooks the cache notify into the MDL cache system
  3023. //-----------------------------------------------------------------------------
  3024. void ConnectMDLCacheNotify( )
  3025. {
  3026. g_pMDLCache->SetCacheNotify( &s_MDLCacheNotify );
  3027. }
  3028. void DisconnectMDLCacheNotify( )
  3029. {
  3030. g_pMDLCache->SetCacheNotify( NULL );
  3031. }
  3032. //-----------------------------------------------------------------------------
  3033. // Initialize studiomdl state
  3034. //-----------------------------------------------------------------------------
  3035. void InitStudioModelState( model_t *pModel )
  3036. {
  3037. Assert( pModel->type == mod_studio );
  3038. if ( g_pMDLCache->IsDataLoaded( pModel->studio, MDLCACHE_STUDIOHDR ) )
  3039. {
  3040. s_MDLCacheNotify.OnDataLoaded( MDLCACHE_STUDIOHDR, pModel->studio );
  3041. }
  3042. if ( g_pMDLCache->IsDataLoaded( pModel->studio, MDLCACHE_STUDIOHWDATA ) )
  3043. {
  3044. s_MDLCacheNotify.OnDataLoaded( MDLCACHE_STUDIOHWDATA, pModel->studio );
  3045. }
  3046. if ( g_pMDLCache->IsDataLoaded( pModel->studio, MDLCACHE_VCOLLIDE ) )
  3047. {
  3048. s_MDLCacheNotify.OnDataLoaded( MDLCACHE_VCOLLIDE, pModel->studio );
  3049. }
  3050. }
  3051. //-----------------------------------------------------------------------------
  3052. // Resource loading for models
  3053. //-----------------------------------------------------------------------------
  3054. class CResourcePreloadModel : public CResourcePreload
  3055. {
  3056. static void QueuedLoaderMapCallback( void *pContext, void *pContext2, const void *pData, int nSize, LoaderError_t loaderError )
  3057. {
  3058. if ( loaderError == LOADERERROR_NONE )
  3059. {
  3060. // 360 mounts its bsp entirely into memory
  3061. // this data is discarded at the conclusion of the entire load process
  3062. Assert( CMapLoadHelper::GetRefCount() == 0 );
  3063. CMapLoadHelper::InitFromMemory( (model_t *)pContext, pData, nSize );
  3064. }
  3065. }
  3066. virtual bool CreateResource( const char *pName )
  3067. {
  3068. modtype_t modType = g_ModelLoader.GetTypeFromName( pName );
  3069. // each model type resource has entirely differnt schemes for loading/creating
  3070. if ( modType == mod_brush )
  3071. {
  3072. // expect to be the map bsp model
  3073. MEM_ALLOC_CREDIT_( "CResourcePreloadModel(BSP)" );
  3074. model_t *pMapModel = g_ModelLoader.FindModelNoCreate( pName );
  3075. if ( pMapModel )
  3076. {
  3077. Assert( CMapLoadHelper::GetRefCount() == 0 );
  3078. // 360 reads its specialized bsp into memory up to the pack lump, guaranteed last
  3079. // the real size of the i/o operation is up to pack lump
  3080. CMapLoadHelper::Init( pMapModel, pMapModel->szPathName );
  3081. int nBytesToRead = CMapLoadHelper::LumpOffset( LUMP_PAKFILE );
  3082. CMapLoadHelper::Shutdown();
  3083. void *pTargetData = NULL;
  3084. Assert( ( s_MapBuffer.GetUsed() == 0 ) && ( s_MapBuffer.GetMaxSize() >= nBytesToRead ) );
  3085. if ( ( ( s_MapBuffer.GetUsed() == 0 ) && ( s_MapBuffer.GetMaxSize() >= nBytesToRead ) ) )
  3086. pTargetData = s_MapBuffer.Alloc( nBytesToRead );
  3087. // create a loader job to perform i/o operation to mount the .bsp
  3088. char szNameOnDisk[MAX_PATH];
  3089. GetMapPathNameOnDisk( szNameOnDisk, pMapModel->szPathName, sizeof( szNameOnDisk ) );
  3090. LoaderJob_t loaderJobBSP;
  3091. loaderJobBSP.m_pFilename = szNameOnDisk;
  3092. loaderJobBSP.m_pPathID = "GAME";
  3093. loaderJobBSP.m_pCallback = QueuedLoaderMapCallback;
  3094. loaderJobBSP.m_pContext = (void *)pMapModel;
  3095. loaderJobBSP.m_pTargetData = pTargetData;
  3096. loaderJobBSP.m_nBytesToRead = nBytesToRead;
  3097. loaderJobBSP.m_Priority = LOADERPRIORITY_DURINGPRELOAD;
  3098. g_pQueuedLoader->AddJob( &loaderJobBSP );
  3099. bool bPreventAIN = false;
  3100. const char *pGame = V_UnqualifiedFileName( com_gamedir );
  3101. bPreventAIN = StringHasPrefix( pGame, "csgo" ) ||
  3102. StringHasPrefix( pGame, "cstrike" ) ||
  3103. StringHasPrefix( pGame, "portal2" ) ||
  3104. StringHasPrefix( pGame, "left4dead" );
  3105. if ( !bPreventAIN )
  3106. {
  3107. // create an anonymous job to perform i/o operation to mount the .ain
  3108. // the .ain gets claimed later
  3109. char szLoadName[MAX_PATH];
  3110. V_FileBase( pMapModel->szPathName, szLoadName, sizeof( szLoadName ) );
  3111. V_snprintf( szNameOnDisk, sizeof( szNameOnDisk ), "maps/graphs/%s" PLATFORM_EXT ".ain", szLoadName );
  3112. LoaderJob_t loaderJobAIN;
  3113. loaderJobAIN.m_pFilename = szNameOnDisk;
  3114. loaderJobAIN.m_pPathID = "GAME";
  3115. loaderJobAIN.m_Priority = LOADERPRIORITY_DURINGPRELOAD;
  3116. g_pQueuedLoader->AddJob( &loaderJobAIN );
  3117. }
  3118. return true;
  3119. }
  3120. }
  3121. else if ( modType == mod_studio )
  3122. {
  3123. MEM_ALLOC_CREDIT_( "CResourcePreloadModel(MDL)" );
  3124. char szFilename[MAX_PATH];
  3125. V_ComposeFileName( "models", pName, szFilename, sizeof( szFilename ) );
  3126. // find model or create empty entry
  3127. model_t *pModel = g_ModelLoader.FindModel( szFilename );
  3128. if ( g_ModelLoader.IsModelInWeaponCache( pModel ) )
  3129. {
  3130. // ignore it, these cannot be loaded now
  3131. return true;
  3132. }
  3133. // mark as touched
  3134. pModel->nLoadFlags |= IModelLoader::FMODELLOADER_TOUCHED_BY_PRELOAD;
  3135. if ( pModel->nLoadFlags & ( IModelLoader::FMODELLOADER_LOADED|IModelLoader::FMODELLOADER_LOADED_BY_PRELOAD ) )
  3136. {
  3137. // already loaded or preloaded
  3138. return true;
  3139. }
  3140. // the model in not supposed to be in memory
  3141. Assert( pModel->type == mod_bad );
  3142. // set its type
  3143. pModel->type = mod_studio;
  3144. // mark the model so that the normal studio load path can perform a final fixup
  3145. pModel->nLoadFlags |= IModelLoader::FMODELLOADER_LOADED_BY_PRELOAD;
  3146. // setup the new entry for preload to operate
  3147. pModel->studio = g_pMDLCache->FindMDL( pModel->szPathName );
  3148. // the model is not supposed to be in memory
  3149. // if this hits, the mdlcache is out of sync with the modelloder
  3150. // if this hits, the mdlcache has the model, but the modelloader doesn't think so
  3151. // if the refcounts go haywire, bad evil bugs will occur
  3152. Assert( g_pMDLCache->GetRef( pModel->studio ) == 1 );
  3153. g_pMDLCache->SetUserData( pModel->studio, pModel );
  3154. // get it into the cache
  3155. g_pMDLCache->PreloadModel( pModel->studio );
  3156. return true;
  3157. }
  3158. // unknown
  3159. return false;
  3160. }
  3161. void PurgeModels( bool bPurgeAll )
  3162. {
  3163. bool bSpew = ( g_pQueuedLoader->GetSpewDetail() & LOADER_DETAIL_PURGES ) != 0;
  3164. // purge any model that was not touched by the preload process
  3165. int iIndex = -1;
  3166. CUtlVector< model_t* > firstList;
  3167. CUtlVector< model_t* > otherList;
  3168. for ( ;; )
  3169. {
  3170. model_t *pModel;
  3171. iIndex = g_ModelLoader.FindNext( iIndex, &pModel );
  3172. if ( iIndex == -1 || !pModel )
  3173. {
  3174. // end of list
  3175. break;
  3176. }
  3177. if ( pModel->type == mod_studio )
  3178. {
  3179. // models that were touched during the preload stay, otherwise purged
  3180. bool bDoPurge = bPurgeAll || !( pModel->nLoadFlags & IModelLoader::FMODELLOADER_TOUCHED_BY_PRELOAD );
  3181. pModel->nLoadFlags &= ~IModelLoader::FMODELLOADER_TOUCHED_BY_PRELOAD;
  3182. if ( bDoPurge )
  3183. {
  3184. if ( bSpew )
  3185. {
  3186. Msg( "CResourcePreloadModel: Purging: %s\n", pModel->szPathName );
  3187. }
  3188. // Models that have virtual models have to unload first to
  3189. // ensure they properly unreference their virtual models.
  3190. if ( g_pMDLCache->IsDataLoaded( pModel->studio, MDLCACHE_VIRTUALMODEL ) )
  3191. {
  3192. firstList.AddToTail( pModel );
  3193. }
  3194. else
  3195. {
  3196. otherList.AddToTail( pModel );
  3197. }
  3198. }
  3199. }
  3200. }
  3201. for ( int i=0; i<firstList.Count(); i++ )
  3202. {
  3203. g_ModelLoader.UnloadModel( firstList[i] );
  3204. }
  3205. for ( int i=0; i<otherList.Count(); i++ )
  3206. {
  3207. g_ModelLoader.UnloadModel( otherList[i] );
  3208. }
  3209. if ( bPurgeAll || !g_pQueuedLoader->IsSameMapLoading() )
  3210. {
  3211. g_pMDLCache->Flush( MDLCACHE_FLUSH_ANIMBLOCK );
  3212. }
  3213. }
  3214. //-----------------------------------------------------------------------------
  3215. // Called before queued loader i/o jobs are actually performed. Must free up memory
  3216. // to ensure i/o requests have enough memory to succeed. The models that were
  3217. // touched by the CreateResource() are the ones to keep, all others get purged.
  3218. //-----------------------------------------------------------------------------
  3219. virtual void PurgeUnreferencedResources()
  3220. {
  3221. PurgeModels( false );
  3222. }
  3223. virtual void PurgeAll()
  3224. {
  3225. PurgeModels( true );
  3226. }
  3227. virtual void OnEndMapLoading( bool bAbort )
  3228. {
  3229. // discard the memory mounted bsp
  3230. CMapLoadHelper::Shutdown();
  3231. Assert( CMapLoadHelper::GetRefCount() == 0 );
  3232. }
  3233. #if defined( _PS3 )
  3234. virtual bool RequiresRendererLock()
  3235. {
  3236. return true;
  3237. }
  3238. #endif // _PS3
  3239. };
  3240. static CResourcePreloadModel s_ResourcePreloadModel;
  3241. bool ProcessWeaponModelCacheOperations()
  3242. {
  3243. return g_ModelLoader.ProcessWeaponModelCacheOperations();
  3244. }
  3245. //-----------------------------------------------------------------------------
  3246. // Purpose:
  3247. //-----------------------------------------------------------------------------
  3248. void CModelLoader::Init( void )
  3249. {
  3250. m_Models.RemoveAll();
  3251. m_InlineModels.Purge();
  3252. m_pWorldModel = NULL;
  3253. m_bMapRenderInfoLoaded = false;
  3254. m_bMapHasHDRLighting = false;
  3255. g_bLoadedMapHasBakedPropLighting = false;
  3256. g_bBakedPropLightingStreams3 = false;
  3257. g_bHasIndirectOnlyInLightingStreams = false;
  3258. m_worldBrushData.m_pLightingDataStack = &m_WorldLightingDataStack;
  3259. // Make sure we have physcollision and physprop interfaces
  3260. CollisionBSPData_LinkPhysics();
  3261. if ( IsGameConsole() && g_pQueuedLoader )
  3262. {
  3263. g_pQueuedLoader->InstallLoader( RESOURCEPRELOAD_MODEL, &s_ResourcePreloadModel );
  3264. }
  3265. if ( IsGameConsole() )
  3266. {
  3267. s_MapBuffer.Init( "s_MapBuffer", 32*1024*1024, 64*1024 );
  3268. }
  3269. #if defined( PLATFORM_WINDOWS_PC ) || defined( DEDICATED )
  3270. // not compatible for any platform but the game consoles due to at least CMDLCache::UnloadQueuedHardwareData() concepts
  3271. m_bAllowWeaponModelCache = false;
  3272. m_bAllowWeaponVertexEviction = false;
  3273. m_bAllowWorldWeaponEviction = false;
  3274. #else
  3275. // on for 360 by default
  3276. m_bAllowWeaponModelCache = IsX360() || IsPS3() || CommandLine()->FindParm( "-weaponmodelcache" ) != 0;
  3277. if ( CommandLine()->FindParm( "-noweaponmodelcache" ) != 0 )
  3278. {
  3279. // explicit opt-out
  3280. m_bAllowWeaponModelCache = false;
  3281. }
  3282. m_bAllowWeaponVertexEviction = true;
  3283. if ( CommandLine()->FindParm( "-keepweaponverts" ) != 0 )
  3284. {
  3285. // explicit opt-out
  3286. m_bAllowWeaponVertexEviction = false;
  3287. }
  3288. m_bAllowWorldWeaponEviction = true;
  3289. #endif
  3290. // now invalid due to m_Models purge
  3291. m_nNumWeaponsPartialResident = 0;
  3292. m_WeaponModelCache.PurgeAndDeleteElements();
  3293. #if !defined( DEDICATED )
  3294. if ( IsGameConsole() && m_bAllowWorldWeaponEviction && g_pMaterialSystem )
  3295. {
  3296. g_pMaterialSystem->AddEndFramePriorToNextContextFunc( ::ProcessWeaponModelCacheOperations );
  3297. }
  3298. #endif
  3299. }
  3300. //-----------------------------------------------------------------------------
  3301. // Purpose:
  3302. //-----------------------------------------------------------------------------
  3303. void CModelLoader::Shutdown( void )
  3304. {
  3305. m_pWorldModel = NULL;
  3306. UnloadAllModels( false );
  3307. g_pMDLCache->UnloadQueuedHardwareData();
  3308. m_ModelPool.Clear();
  3309. if ( IsGameConsole() )
  3310. {
  3311. s_MapBuffer.Term();
  3312. }
  3313. if ( IsGameConsole() && m_bAllowWorldWeaponEviction && g_pMaterialSystem )
  3314. {
  3315. g_pMaterialSystem->RemoveEndFramePriorToNextContextFunc( ::ProcessWeaponModelCacheOperations );
  3316. }
  3317. }
  3318. int CModelLoader::GetCount( void )
  3319. {
  3320. return m_Models.Count();
  3321. }
  3322. model_t *CModelLoader::GetModelForIndex( int i )
  3323. {
  3324. if ( i < 0 || (unsigned)i >= m_Models.Count() )
  3325. {
  3326. return NULL;
  3327. }
  3328. return m_Models[i].modelpointer;
  3329. }
  3330. //-----------------------------------------------------------------------------
  3331. // Purpose: Look up name for model
  3332. // Input : *model -
  3333. // Output : const char
  3334. //-----------------------------------------------------------------------------
  3335. const char *CModelLoader::GetName( const model_t *pModel )
  3336. {
  3337. if ( pModel )
  3338. {
  3339. return pModel->szPathName;
  3340. }
  3341. return NULL;
  3342. }
  3343. //-----------------------------------------------------------------------------
  3344. // Purpose: Finds the model, builds entry if not present, always returns a model
  3345. // Input : *name -
  3346. // referencetype -
  3347. // Output : model_t
  3348. //-----------------------------------------------------------------------------
  3349. model_t *CModelLoader::FindModel( const char *pName )
  3350. {
  3351. if ( !pName || !pName[0] )
  3352. {
  3353. Sys_Error( "CModelLoader::FindModel: NULL name" );
  3354. }
  3355. // inline models are grabbed only from worldmodel
  3356. if ( pName[0] == '*' )
  3357. {
  3358. int modelNum = atoi( pName + 1 );
  3359. if ( !IsWorldModelSet() )
  3360. {
  3361. Warning( "bad inline model number %i, worldmodel not yet setup\n", modelNum );
  3362. return NULL;
  3363. }
  3364. if ( modelNum < 1 || modelNum >= GetNumWorldSubmodels() )
  3365. {
  3366. Warning( "bad inline model number %i\n", modelNum );
  3367. return NULL;
  3368. }
  3369. return &m_InlineModels[modelNum];
  3370. }
  3371. model_t *pModel = NULL;
  3372. // get a handle suitable to use as the model key
  3373. // handles are insensitive to case and slashes
  3374. FileNameHandle_t fnHandle = g_pFileSystem->FindOrAddFileName( pName );
  3375. int i = m_Models.Find( fnHandle );
  3376. if ( i == m_Models.InvalidIndex() )
  3377. {
  3378. pModel = (model_t *)m_ModelPool.Alloc();
  3379. Assert( pModel );
  3380. memset( pModel, 0, sizeof( model_t ) );
  3381. pModel->fnHandle = fnHandle;
  3382. // Mark that we should load from disk
  3383. pModel->nLoadFlags = FMODELLOADER_NOTLOADEDORREFERENCED;
  3384. // Copy in name and normalize!
  3385. // Various other subsystems fetch this 'object' name to do dictionary lookups,
  3386. // which are usually case insensitive, but not to slashes or dotslashes.
  3387. Q_strncpy( pModel->szPathName, pName, sizeof( pModel->szPathName ) );
  3388. V_RemoveDotSlashes( pModel->szPathName, '/' );
  3389. //
  3390. // Model censoring for perfect world, banlist loads here
  3391. //
  3392. static struct BannedMDLs_t
  3393. {
  3394. BannedMDLs_t()
  3395. {
  3396. bool bLoadBannedWords = !!CommandLine()->FindParm( "-perfectworld" );
  3397. bLoadBannedWords |= !!CommandLine()->FindParm( "-usebanlist" );
  3398. if ( bLoadBannedWords )
  3399. {
  3400. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  3401. if ( g_pFullFileSystem->ReadFile( "banmdls.res", "MOD", buf ) )
  3402. {
  3403. while ( buf.IsValid() )
  3404. {
  3405. char chModel[ 256 ] = {};
  3406. buf.GetString( chModel, sizeof( chModel ) - 1 );
  3407. if ( chModel[0] )
  3408. {
  3409. m_map.AddString( chModel );
  3410. #ifdef _DEBUG
  3411. DevMsg( "Banned MDL: %s\n", chModel );
  3412. #endif
  3413. }
  3414. }
  3415. }
  3416. }
  3417. }
  3418. CUtlSymbolTable m_map;
  3419. } s_BannedMDLs;
  3420. //
  3421. // Check the banlist and flag models as render disabled
  3422. //
  3423. if ( s_BannedMDLs.m_map.GetNumStrings() > 0 )
  3424. {
  3425. if ( s_BannedMDLs.m_map.Find( pModel->szPathName ).IsValid() )
  3426. {
  3427. COMPILE_TIME_ASSERT( MODELFLAG_RENDER_DISABLED == ENGINE_MODEL_CLIENT_MODELFLAG_RENDER_DISABLED );
  3428. COMPILE_TIME_ASSERT( offsetof( model_t, flags ) == ENGINE_MODEL_CLIENT_MODELT_OFFSET_FLAGS );
  3429. pModel->flags |= MODELFLAG_RENDER_DISABLED;
  3430. #ifdef _DEBUG
  3431. DevMsg( "Render disabled for banned MDL: %s\n", pModel->szPathName );
  3432. #endif
  3433. }
  3434. }
  3435. //
  3436. // Proceed with inserting this model entry
  3437. //
  3438. ModelEntry_t entry;
  3439. entry.modelpointer = pModel;
  3440. m_Models.Insert( fnHandle, entry );
  3441. #if !defined( DEDICATED )
  3442. if ( m_bAllowWeaponModelCache )
  3443. {
  3444. // setup for tracking weapon models BEFORE anything else happens
  3445. // need the entries established before any data starts to arrive
  3446. if ( V_stristr( pModel->szPathName, "weapons/v_" ) != NULL )
  3447. {
  3448. // track weapon view models
  3449. int nMapIndex = m_WeaponModelCache.Find( pModel );
  3450. if ( nMapIndex == m_WeaponModelCache.InvalidIndex() )
  3451. {
  3452. nMapIndex = m_WeaponModelCache.Insert( pModel );
  3453. m_WeaponModelCache[nMapIndex] = new ViewWeaponEntry_t( true );
  3454. pModel->flags |= MODELFLAG_VIEW_WEAPON_MODEL;
  3455. }
  3456. }
  3457. else if ( V_stristr( pModel->szPathName, "weapons/w_" ) != NULL )
  3458. {
  3459. // track weapon world models
  3460. int nMapIndex = m_WeaponModelCache.Find( pModel );
  3461. if ( nMapIndex == m_WeaponModelCache.InvalidIndex() )
  3462. {
  3463. nMapIndex = m_WeaponModelCache.Insert( pModel );
  3464. m_WeaponModelCache[nMapIndex] = new ViewWeaponEntry_t( false );
  3465. }
  3466. }
  3467. }
  3468. #endif
  3469. }
  3470. else
  3471. {
  3472. pModel = m_Models[i].modelpointer;
  3473. }
  3474. // notify the reslist generator that this model may be referenced later in the level
  3475. // (does nothing if reslist generation is not enabled)
  3476. MapReslistGenerator().OnModelPrecached( pName );
  3477. Assert( pModel );
  3478. return pModel;
  3479. }
  3480. //-----------------------------------------------------------------------------
  3481. // Purpose: Finds the model, and loads it if it isn't already present. Updates reference flags
  3482. // Input : *name -
  3483. // referencetype -
  3484. // Output : model_t
  3485. //-----------------------------------------------------------------------------
  3486. model_t *CModelLoader::GetModelForName( const char *name, REFERENCETYPE referencetype )
  3487. {
  3488. // find or build new entry
  3489. model_t *model = FindModel( name );
  3490. if ( !model )
  3491. return NULL;
  3492. // touch and load if not present
  3493. model_t *retval = LoadModel( model, &referencetype );
  3494. return retval;
  3495. }
  3496. //-----------------------------------------------------------------------------
  3497. // Purpose: Add a reference to the model in question
  3498. // Input : *name -
  3499. // referencetype -
  3500. //-----------------------------------------------------------------------------
  3501. model_t *CModelLoader::ReferenceModel( const char *name, REFERENCETYPE referencetype )
  3502. {
  3503. model_t *model = FindModel( name );
  3504. model->nLoadFlags |= referencetype;
  3505. return model;
  3506. }
  3507. static void QueuedLoaderBeginMapLoadingCallback( int nStage )
  3508. {
  3509. if ( IsGameConsole() )
  3510. {
  3511. // unload lightmap textures before loading the next map (PC does this in CMatLightmaps::BeginLightmapAllocation)
  3512. g_pMaterialSystem->CleanupLightmaps();
  3513. }
  3514. #ifdef _PS3
  3515. // Reclaim the space from unloaded lightmaps and (if the queued loader ran) pre-purged assets not used by the next map
  3516. g_pMaterialSystem->CompactRsxLocalMemory( "BEGIN MAP LOADING" );
  3517. #endif
  3518. }
  3519. //-----------------------------------------------------------------------------
  3520. // Purpose:
  3521. // Input : *entry -
  3522. // referencetype -
  3523. //-----------------------------------------------------------------------------
  3524. model_t *CModelLoader::LoadModel( model_t *mod, REFERENCETYPE *pReferencetype )
  3525. {
  3526. if ( pReferencetype )
  3527. {
  3528. mod->nLoadFlags |= *pReferencetype;
  3529. }
  3530. // during initial load mark the model with an unique session ticket
  3531. // at load end, models that have a mismatch count are considered candidates for purge
  3532. // models that get marked, touch *all* their sub data to ensure the cache is pre-populated
  3533. // and hitches less during gameplay
  3534. bool bTouchAllData = false;
  3535. int nServerCount = Host_GetServerCount();
  3536. if ( mod->nServerCount != nServerCount )
  3537. {
  3538. // server has changed
  3539. mod->nServerCount = nServerCount;
  3540. bTouchAllData = true;
  3541. }
  3542. // Check if the studio model is in cache.
  3543. // The model type will not be set for first time models that need to fall through to the load path.
  3544. // A model that needs a post precache fixup will fall through to the load path.
  3545. if ( mod->type == mod_studio && !( mod->nLoadFlags & FMODELLOADER_LOADED_BY_PRELOAD ) )
  3546. {
  3547. // in cache
  3548. Verify( g_pMDLCache->GetStudioHdr( mod->studio ) != 0 );
  3549. Assert( FMODELLOADER_LOADED & mod->nLoadFlags );
  3550. if ( bTouchAllData )
  3551. {
  3552. // Touch all related .ani files and sub/dependent models
  3553. // only touches once, when server changes
  3554. Mod_TouchAllData( mod, nServerCount );
  3555. }
  3556. return mod;
  3557. }
  3558. // Check if brushes or sprites are loaded
  3559. if ( FMODELLOADER_LOADED & mod->nLoadFlags )
  3560. {
  3561. return mod;
  3562. }
  3563. // model needs to be loaded
  3564. double st = Plat_FloatTime();
  3565. // Set the name of the current model we are loading
  3566. V_FileBase( mod->szPathName, m_szBaseName, sizeof( m_szBaseName ) );
  3567. // load the file
  3568. if ( developer.GetInt() > 1 )
  3569. {
  3570. DevMsg( "Loading: %s\n", mod->szPathName );
  3571. }
  3572. mod->type = GetTypeFromName( mod->szPathName );
  3573. if ( developer.GetInt() > 1 )
  3574. {
  3575. DevMsg( "Loading type: %d\n", mod->type );
  3576. }
  3577. if ( mod->type == mod_bad )
  3578. {
  3579. mod->type = mod_studio;
  3580. }
  3581. // finalize the model data
  3582. switch ( mod->type )
  3583. {
  3584. case mod_sprite:
  3585. {
  3586. MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
  3587. double t1 = Plat_FloatTime();
  3588. Sprite_LoadModel( mod );
  3589. double t2 = Plat_FloatTime();
  3590. g_flAccumulatedModelLoadTimeSprite += ( t2 - t1 );
  3591. }
  3592. break;
  3593. case mod_studio:
  3594. {
  3595. MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
  3596. double t1 = Plat_FloatTime();
  3597. Studio_LoadModel( mod, bTouchAllData );
  3598. double t2 = Plat_FloatTime();
  3599. g_flAccumulatedModelLoadTimeStudio += ( t2 - t1 );
  3600. }
  3601. break;
  3602. case mod_brush:
  3603. {
  3604. double t1 = Plat_FloatTime();
  3605. if ( developer.GetInt() > 1 )
  3606. {
  3607. DevMsg( "Loading brush, compacting heap...\n" );
  3608. }
  3609. g_pMemAlloc->CompactHeap();
  3610. // the training map needs ALL the world weapons at high-res on a display wall
  3611. m_bAllowWorldWeaponEviction = ( V_stristr( m_szBaseName, "training1" ) == NULL );
  3612. // This is necessary on dedicated clients. On listen + dedicated servers, it's called twice.
  3613. // The second invocation is harmless.
  3614. // Add to file system before loading so referenced objects in map can use the filename.
  3615. char szNameOnDisk[MAX_PATH];
  3616. GetMapPathNameOnDisk( szNameOnDisk, mod->szPathName, sizeof( szNameOnDisk ) );
  3617. if ( developer.GetInt() > 1 )
  3618. {
  3619. DevMsg( "Loading map: %s\n", szNameOnDisk );
  3620. }
  3621. g_pFileSystem->AddSearchPath( szNameOnDisk, "GAME", PATH_ADD_TO_HEAD );
  3622. // the map may have explicit texture exclusion
  3623. // the texture state needs to be established before any loading work
  3624. if ( IsGameConsole() || mat_excludetextures.GetBool() )
  3625. {
  3626. #if defined( PORTAL2 )
  3627. char szExcludePath[MAX_PATH] = "";
  3628. // PORTAL2: we aren't using per-map excludes, we just need a few textures excluded in SP
  3629. if ( V_stristr( m_szBaseName, "sp_" ) )
  3630. {
  3631. v_snprintf( szExcludePath, sizeof( szExcludePath ), "//MOD/maps/sp_exclude.lst" );
  3632. }
  3633. #else
  3634. char szExcludePath[MAX_PATH];
  3635. V_snprintf( szExcludePath, sizeof( szExcludePath ), "//MOD/maps/%s_exclude.lst", m_szBaseName );
  3636. #endif
  3637. if ( developer.GetInt() > 1 )
  3638. {
  3639. DevMsg( "Setting excluded textures: %s\n", szExcludePath );
  3640. }
  3641. g_pMaterialSystem->SetExcludedTextures( szExcludePath, m_bAllowWeaponModelCache );
  3642. }
  3643. NotifyHunkBeginMapLoad( m_szBaseName );
  3644. bool bQueuedLoader = false;
  3645. if ( IsGameConsole() )
  3646. {
  3647. // must establish the bsp feature set first to ensure proper state during queued loading
  3648. Map_CheckForHDR( mod, mod->szPathName );
  3649. // Do not optimize map-to-same-map loading in TF
  3650. // FIXME/HACK: this fixes a bug (when shipping Orange Box) where static props would sometimes
  3651. // disappear when a client disconnects and reconnects to the same map+server
  3652. // (static prop lighting data persists when loading map A after map A)
  3653. bool bIsTF = !V_stricmp( COM_GetModDirectory(), "tf" );
  3654. bool bIsCSGO = !V_stricmp( COM_GetModDirectory(), "csgo" );
  3655. bool bOptimizeMapReload = !bIsTF && !bIsCSGO;
  3656. // start the queued loading process
  3657. if ( developer.GetInt() > 1 )
  3658. {
  3659. DevMsg( "Loading map: BeginMapLoading...\n" );
  3660. }
  3661. bQueuedLoader = g_pQueuedLoader && g_pQueuedLoader->BeginMapLoading( mod->szPathName, g_pMaterialSystemHardwareConfig->GetHDREnabled(), bOptimizeMapReload, QueuedLoaderBeginMapLoadingCallback );
  3662. }
  3663. if ( !bQueuedLoader )
  3664. {
  3665. if ( IsGameConsole() || mat_excludetextures.GetBool() )
  3666. {
  3667. // the queued loader process needs to own the actual texture update
  3668. g_pMaterialSystem->UpdateExcludedTextures();
  3669. }
  3670. // This needs to get run if the queued loader did not call it:
  3671. QueuedLoaderBeginMapLoadingCallback( 1 );
  3672. }
  3673. if ( developer.GetInt() > 1 )
  3674. {
  3675. DevMsg( "Loading map: BeginLoadingUpdates...\n" );
  3676. }
  3677. BeginLoadingUpdates( MATERIAL_NON_INTERACTIVE_MODE_LEVEL_LOAD );
  3678. g_pFileSystem->BeginMapAccess();
  3679. if ( developer.GetInt() > 1 )
  3680. {
  3681. DevMsg( "Loading map: Map_LoadModel...\n" );
  3682. }
  3683. Map_LoadModel( mod );
  3684. g_pFileSystem->EndMapAccess();
  3685. double t2 = Plat_FloatTime();
  3686. g_flAccumulatedModelLoadTimeBrush += (t2 - t1);
  3687. }
  3688. break;
  3689. default:
  3690. Assert( 0 );
  3691. break;
  3692. };
  3693. float dt = ( Plat_FloatTime() - st );
  3694. COM_TimestampedLog( "Load of %s took %.3f msec", mod->szPathName, 1000.0f * dt );
  3695. g_flAccumulatedModelLoadTime += dt;
  3696. return mod;
  3697. }
  3698. //-----------------------------------------------------------------------------
  3699. // Purpose: Creates the name of the sprite
  3700. //-----------------------------------------------------------------------------
  3701. static void BuildSpriteLoadName( const char *pName, char *pOut, int outLen, bool &bIsAVI, bool &bIsBIK )
  3702. {
  3703. // If it's a .vmt and they put a path in there, then use the path.
  3704. // Otherwise, use the old method of prepending the sprites directory.
  3705. const char *pExt = V_GetFileExtension( pName );
  3706. bIsAVI = !Q_stricmp( pExt, "avi" );
  3707. bIsBIK = !Q_stricmp( pExt, "bik" );
  3708. bool bIsVMT = !Q_stricmp( pExt, "vmt" );
  3709. if ( ( bIsAVI || bIsBIK || bIsVMT ) && ( strchr( pName, '/' ) || strchr( pName, '\\' ) ) )
  3710. {
  3711. // The material system cannot handle a prepended "materials" dir
  3712. // Keep .avi extensions on the material to load avi-based materials
  3713. if ( bIsVMT )
  3714. {
  3715. const char *pNameStart = pName;
  3716. if ( Q_stristr( pName, "materials/" ) == pName ||
  3717. Q_stristr( pName, "materials\\" ) == pName )
  3718. {
  3719. // skip past materials/
  3720. pNameStart = &pName[10];
  3721. }
  3722. Q_StripExtension( pNameStart, pOut, outLen );
  3723. }
  3724. else
  3725. {
  3726. // name is good as is
  3727. Q_strncpy( pOut, pName, outLen );
  3728. }
  3729. }
  3730. else
  3731. {
  3732. char szBase[MAX_PATH];
  3733. Q_FileBase( pName, szBase, sizeof( szBase ) );
  3734. Q_snprintf( pOut, outLen, "sprites/%s", szBase );
  3735. }
  3736. return;
  3737. }
  3738. //-----------------------------------------------------------------------------
  3739. // Purpose:
  3740. // Input : *name -
  3741. // Output : int
  3742. //-----------------------------------------------------------------------------
  3743. int CModelLoader::GetModelFileSize( char const *name )
  3744. {
  3745. if ( !name || !name[ 0 ] )
  3746. return -1;
  3747. model_t *model = FindModel( name );
  3748. int size = -1;
  3749. if ( Q_stristr( model->szPathName, ".spr" ) || Q_stristr( model->szPathName, ".vmt" ) )
  3750. {
  3751. char spritename[ MAX_PATH ];
  3752. Q_StripExtension( va( "materials/%s", model->szPathName ), spritename, MAX_PATH );
  3753. Q_DefaultExtension( spritename, ".vmt", sizeof( spritename ) );
  3754. size = COM_FileSize( spritename );
  3755. }
  3756. else
  3757. {
  3758. size = COM_FileSize( name );
  3759. }
  3760. return size;
  3761. }
  3762. //-----------------------------------------------------------------------------
  3763. // Purpose: Unmasks the referencetype field for the model
  3764. // Input : *model -
  3765. // referencetype -
  3766. //-----------------------------------------------------------------------------
  3767. void CModelLoader::UnreferenceModel( model_t *model, REFERENCETYPE referencetype )
  3768. {
  3769. model->nLoadFlags &= ~referencetype;
  3770. }
  3771. //-----------------------------------------------------------------------------
  3772. // Purpose: Unmasks the specified reference type across all models
  3773. // Input : referencetype -
  3774. //-----------------------------------------------------------------------------
  3775. void CModelLoader::UnreferenceAllModels( REFERENCETYPE referencetype )
  3776. {
  3777. int i;
  3778. model_t *mod;
  3779. // UNDONE: If we ever free a studio model, write code to free the collision data
  3780. // UNDONE: Reference count collision data?
  3781. int c = m_Models.Count();
  3782. for ( i=0 ; i < c ; i++ )
  3783. {
  3784. mod = m_Models[ i ].modelpointer;
  3785. UnreferenceModel( mod, referencetype );
  3786. }
  3787. }
  3788. void CModelLoader::ReloadFilesInList( IFileList *pFilesToReload )
  3789. {
  3790. int c = m_Models.Count();
  3791. for ( int i=0 ; i < c ; i++ )
  3792. {
  3793. model_t *pModel = m_Models[i].modelpointer;
  3794. if ( pModel->type != mod_studio )
  3795. continue;
  3796. if ( !IsLoaded( pModel ) )
  3797. continue;
  3798. if ( pModel->type != mod_studio )
  3799. continue;
  3800. if ( pFilesToReload->IsFileInList( pModel->szPathName ) )
  3801. {
  3802. // Flush out the model cache
  3803. // Don't flush vcollides since the vphysics system currently
  3804. // has no way of indicating they refer to vcollides
  3805. g_pMDLCache->Flush( pModel->studio, (int)(MDLCACHE_FLUSH_ALL & ~(MDLCACHE_FLUSH_IGNORELOCK|MDLCACHE_FLUSH_VCOLLIDE)) );
  3806. MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
  3807. // Get the studiohdr into the cache
  3808. g_pMDLCache->GetStudioHdr( pModel->studio );
  3809. // force the collision to load
  3810. g_pMDLCache->GetVCollide( pModel->studio );
  3811. }
  3812. else
  3813. {
  3814. if ( g_pMDLCache->IsDataLoaded( pModel->studio, MDLCACHE_STUDIOHWDATA ) )
  3815. {
  3816. studiohdr_t *pStudioHdr = g_pMDLCache->GetStudioHdr( pModel->studio );
  3817. if ( pStudioHdr )
  3818. {
  3819. // Ok, we didn't have to do a full reload, but if any of our materials changed, flush out the studiohwdata because the
  3820. // vertex format may have changed.
  3821. IMaterial *pMaterials[128];
  3822. int nMaterials = g_pStudioRender->GetMaterialList( pStudioHdr, ARRAYSIZE( pMaterials ), &pMaterials[0] );
  3823. for ( int j=0; j < nMaterials; j++ )
  3824. {
  3825. if ( pMaterials[j] && pMaterials[j]->WasReloadedFromWhitelist() )
  3826. {
  3827. g_pMDLCache->Flush( pModel->studio, MDLCACHE_FLUSH_STUDIOHWDATA );
  3828. break;
  3829. }
  3830. }
  3831. }
  3832. }
  3833. }
  3834. }
  3835. }
  3836. //-----------------------------------------------------------------------------
  3837. // Purpose: For any models with referencetype blank (if checking), frees all memory associated with the model
  3838. // and frees up the models slot
  3839. //-----------------------------------------------------------------------------
  3840. void CModelLoader::UnloadAllModels( bool bCheckReference )
  3841. {
  3842. int i;
  3843. model_t *model;
  3844. int c = m_Models.Count();
  3845. for ( i=0 ; i < c ; i++ )
  3846. {
  3847. model = m_Models[ i ].modelpointer;
  3848. if ( bCheckReference )
  3849. {
  3850. if ( model->nLoadFlags & FMODELLOADER_REFERENCEMASK )
  3851. {
  3852. if ( model->type == mod_studio && !IsModelInWeaponCache( model ) )
  3853. {
  3854. g_pMDLCache->MarkAsLoaded( model->studio );
  3855. }
  3856. continue;
  3857. }
  3858. }
  3859. else
  3860. {
  3861. // Wipe current flags
  3862. model->nLoadFlags &= ~FMODELLOADER_REFERENCEMASK;
  3863. }
  3864. if ( IsGameConsole() &&
  3865. g_pQueuedLoader && g_pQueuedLoader->IsMapLoading() &&
  3866. ( model->nLoadFlags & FMODELLOADER_LOADED_BY_PRELOAD ) )
  3867. {
  3868. // models preloaded by the queued loader are not initially claimed and MUST remain until the end of the load process
  3869. // unclaimed models get unloaded during the post load purge
  3870. continue;
  3871. }
  3872. if ( model->nLoadFlags & ( FMODELLOADER_LOADED | FMODELLOADER_LOADED_BY_PRELOAD ) )
  3873. {
  3874. UnloadModel( model );
  3875. }
  3876. }
  3877. }
  3878. //-----------------------------------------------------------------------------
  3879. // Purpose: For any models with referencetype blank (if checking), frees all memory associated with the model
  3880. // and frees up the models slot
  3881. //-----------------------------------------------------------------------------
  3882. void CModelLoader::UnloadUnreferencedModels( void )
  3883. {
  3884. // unload all unreferenced models
  3885. UnloadAllModels( true );
  3886. }
  3887. void CModelLoader::AddCompatibilityPath( const char* szNewCompatibilityPath )
  3888. {
  3889. g_pFullFileSystem->AddSearchPath( szNewCompatibilityPath, "COMPAT:GAME", PATH_ADD_TO_HEAD );
  3890. compatibility_path_t tNewCompatibilityPath;
  3891. tNewCompatibilityPath.mPath = szNewCompatibilityPath;
  3892. tNewCompatibilityPath.mPathId = "COMPAT:GAME";
  3893. m_vecSzCompatibilityPaths.AddToTail( tNewCompatibilityPath );
  3894. }
  3895. void CModelLoader::UnMountCompatibilityPaths( void )
  3896. {
  3897. FOR_EACH_VEC_BACK( m_vecSzCompatibilityPaths, i )
  3898. {
  3899. DevWarning( "UnMounting content compatibility path [%s] %s.\n", m_vecSzCompatibilityPaths[i].mPathId.Get(), m_vecSzCompatibilityPaths[i].mPath.Get() );
  3900. g_pFullFileSystem->RemoveSearchPath( m_vecSzCompatibilityPaths[i].mPath.Get(), m_vecSzCompatibilityPaths[i].mPathId.Get() );
  3901. m_vecSzCompatibilityPaths.Remove(i);
  3902. }
  3903. }
  3904. //-----------------------------------------------------------------------------
  3905. // Called at the conclusion of loading.
  3906. // Frees all memory associated with models (and their materials) that are not
  3907. // marked with the current session.
  3908. //-----------------------------------------------------------------------------
  3909. void CModelLoader::PurgeUnusedModels( void )
  3910. {
  3911. int nServerCount = Host_GetServerCount();
  3912. int count = m_Models.Count();
  3913. for ( int i = 0; i < count; i++ )
  3914. {
  3915. model_t *pModel = m_Models[i].modelpointer;
  3916. // Don't purge the simple world model here; it's managed separately
  3917. if ( ( pModel->nLoadFlags & FMODELLOADER_LOADED ) && ( pModel->nServerCount != nServerCount ) && !( pModel->nLoadFlags & FMODELLOADER_SIMPLEWORLD ) )
  3918. {
  3919. // mark as unreferenced
  3920. pModel->nLoadFlags &= ~FMODELLOADER_REFERENCEMASK;
  3921. }
  3922. }
  3923. // unload unreferenced models only
  3924. UnloadAllModels( true );
  3925. // now purge unreferenced materials
  3926. materials->UncacheUnusedMaterials( true );
  3927. }
  3928. byte *CModelLoader::GetLightstyles( model_t *pModel )
  3929. {
  3930. if ( pModel->brush.nLightstyleCount != 0 )
  3931. {
  3932. byte *pLightstyles = &m_LightStyleList[pModel->brush.nLightstyleIndex];
  3933. return pLightstyles;
  3934. }
  3935. return NULL;
  3936. }
  3937. void CModelLoader::AllocateLightstyles( model_t *pModel, byte *pStyles, int nStyleCount )
  3938. {
  3939. unsigned short nLast = m_LightStyleList.Count();
  3940. for ( int i = 0; i < nStyleCount; i++ )
  3941. {
  3942. m_LightStyleList.AddToTail( pStyles[i] );
  3943. }
  3944. pModel->brush.nLightstyleCount = nStyleCount;
  3945. pModel->brush.nLightstyleIndex = nLast;
  3946. }
  3947. bool Mod_NeedsLightstyleUpdate( model_t *pModel )
  3948. {
  3949. #ifndef SWDS
  3950. if ( pModel->brush.nLightstyleCount != 0 )
  3951. {
  3952. byte *pLightstyles = g_ModelLoader.GetLightstyles( pModel );
  3953. int nCount = pModel->brush.nLightstyleCount;
  3954. int nLastComputed = pModel->brush.nLightstyleLastComputedFrame;
  3955. for( int i = 0; i < nCount; i++ )
  3956. {
  3957. if( d_lightstyleframe[pLightstyles[i]] > nLastComputed )
  3958. return true;
  3959. }
  3960. }
  3961. #endif
  3962. return false;
  3963. }
  3964. //-----------------------------------------------------------------------------
  3965. // Compute whether this submodel uses material proxies or not
  3966. //-----------------------------------------------------------------------------
  3967. static void Mod_ComputeBrushModelFlags( model_t *mod )
  3968. {
  3969. Assert( mod );
  3970. worldbrushdata_t *pBrushData = mod->brush.pShared;
  3971. // Clear out flags we're going to set
  3972. mod->flags &= ~(MODELFLAG_MATERIALPROXY | MODELFLAG_TRANSLUCENT | MODELFLAG_FRAMEBUFFER_TEXTURE | MODELFLAG_TRANSLUCENT_TWOPASS );
  3973. mod->flags = MODELFLAG_HAS_DLIGHT; // force this check the first time
  3974. int i;
  3975. int scount = mod->brush.nummodelsurfaces;
  3976. bool bHasOpaqueSurfaces = false;
  3977. bool bHasTranslucentSurfaces = false;
  3978. mod->brush.nLightstyleIndex = 0;
  3979. mod->brush.nLightstyleCount = 0;
  3980. CUtlVector<byte> lightStyles;
  3981. for ( i = 0; i < scount; ++i )
  3982. {
  3983. SurfaceHandle_t surfID = SurfaceHandleFromIndex( mod->brush.firstmodelsurface + i, pBrushData );
  3984. // Clear out flags we're going to set
  3985. MSurf_Flags( surfID ) &= ~(SURFDRAW_NOCULL | SURFDRAW_TRANS | SURFDRAW_ALPHATEST | SURFDRAW_NODECALS);
  3986. mtexinfo_t *pTex = MSurf_TexInfo( surfID, pBrushData );
  3987. IMaterial* pMaterial = pTex->material;
  3988. if ( pMaterial->HasProxy() )
  3989. {
  3990. mod->flags |= MODELFLAG_MATERIALPROXY;
  3991. }
  3992. if ( pMaterial->NeedsPowerOfTwoFrameBufferTexture( false ) ) // The false checks if it will ever need the frame buffer, not just this frame
  3993. {
  3994. mod->flags |= MODELFLAG_FRAMEBUFFER_TEXTURE;
  3995. }
  3996. // Deactivate culling if the material is two sided
  3997. if ( pMaterial->IsTwoSided() )
  3998. {
  3999. MSurf_Flags( surfID ) |= SURFDRAW_NOCULL;
  4000. }
  4001. if ( (pTex->flags & SURF_TRANS) || pMaterial->IsTranslucent() )
  4002. {
  4003. mod->flags |= MODELFLAG_TRANSLUCENT;
  4004. MSurf_Flags( surfID ) |= SURFDRAW_TRANS;
  4005. bHasTranslucentSurfaces = true;
  4006. }
  4007. else
  4008. {
  4009. bHasOpaqueSurfaces = true;
  4010. }
  4011. // Certain surfaces don't want decals at all
  4012. if ( (pTex->flags & SURF_NODECALS) || pMaterial->GetMaterialVarFlag( MATERIAL_VAR_SUPPRESS_DECALS ) || pMaterial->IsAlphaTested() )
  4013. {
  4014. MSurf_Flags( surfID ) |= SURFDRAW_NODECALS;
  4015. }
  4016. if ( pMaterial->IsAlphaTested() )
  4017. {
  4018. MSurf_Flags( surfID ) |= SURFDRAW_ALPHATEST;
  4019. }
  4020. msurfacelighting_t *pLighting = SurfaceLighting( surfID, pBrushData );
  4021. for (int maps = 0; maps < MAXLIGHTMAPS && pLighting->m_nStyles[maps] != 255 ; ++maps )
  4022. {
  4023. byte nStyle = pLighting->m_nStyles[maps];
  4024. if ( lightStyles.Find(nStyle) == -1 )
  4025. {
  4026. lightStyles.AddToTail( nStyle );
  4027. }
  4028. }
  4029. }
  4030. if ( bHasOpaqueSurfaces && bHasTranslucentSurfaces )
  4031. {
  4032. mod->flags |= MODELFLAG_TRANSLUCENT_TWOPASS;
  4033. }
  4034. if ( lightStyles.Count() )
  4035. {
  4036. mod->brush.nLightstyleLastComputedFrame = 0;
  4037. g_ModelLoader.AllocateLightstyles( mod, lightStyles.Base(), lightStyles.Count() );
  4038. }
  4039. }
  4040. //-----------------------------------------------------------------------------
  4041. // Recomputes translucency for the model...
  4042. //-----------------------------------------------------------------------------
  4043. RenderableTranslucencyType_t Mod_ComputeTranslucencyType( model_t* mod, int nSkin, int nBody )
  4044. {
  4045. switch( mod->type )
  4046. {
  4047. case mod_brush:
  4048. if ( ( mod->flags & MODELFLAG_TRANSLUCENT ) == 0 )
  4049. return RENDERABLE_IS_OPAQUE;
  4050. return ( mod->flags & MODELFLAG_TRANSLUCENT_TWOPASS ) ? RENDERABLE_IS_TWO_PASS : RENDERABLE_IS_TRANSLUCENT;
  4051. case mod_studio:
  4052. {
  4053. studiohdr_t *pStudioHdr = g_pMDLCache->GetStudioHdr( mod->studio );
  4054. if ( pStudioHdr->flags & STUDIOHDR_FLAGS_FORCE_OPAQUE )
  4055. return RENDERABLE_IS_OPAQUE;
  4056. if ( IsGameConsole() && !g_ModelLoader.IsViewWeaponModelResident( mod ) )
  4057. {
  4058. // best guess
  4059. // purposely preventing any request that would cause the hwmesh to load
  4060. return RENDERABLE_IS_OPAQUE;
  4061. }
  4062. IMaterial *pMaterials[ 128 ];
  4063. int materialCount = g_pStudioRender->GetMaterialListFromBodyAndSkin( mod->studio, nSkin, nBody, ARRAYSIZE( pMaterials ), pMaterials );
  4064. for ( int i = 0; i < materialCount; i++ )
  4065. {
  4066. if ( pMaterials[i] == NULL )
  4067. continue;
  4068. bool bIsTranslucent = pMaterials[i]->IsTranslucent();
  4069. if ( bIsTranslucent )
  4070. return ( pStudioHdr->flags & STUDIOHDR_FLAGS_TRANSLUCENT_TWOPASS ) ? RENDERABLE_IS_TWO_PASS : RENDERABLE_IS_TRANSLUCENT;
  4071. }
  4072. return RENDERABLE_IS_OPAQUE;
  4073. }
  4074. break;
  4075. case mod_sprite:
  4076. return RENDERABLE_IS_TRANSLUCENT;
  4077. default:
  4078. return RENDERABLE_IS_OPAQUE;
  4079. }
  4080. }
  4081. //-----------------------------------------------------------------------------
  4082. // returns the material count...
  4083. //-----------------------------------------------------------------------------
  4084. int Mod_GetMaterialCount( model_t* mod )
  4085. {
  4086. switch( mod->type )
  4087. {
  4088. case mod_brush:
  4089. {
  4090. CUtlVector<IMaterial*> uniqueMaterials( 0, 32 );
  4091. for (int i = 0; i < mod->brush.nummodelsurfaces; ++i)
  4092. {
  4093. SurfaceHandle_t surfID = SurfaceHandleFromIndex( mod->brush.firstmodelsurface + i, mod->brush.pShared );
  4094. if ( MSurf_Flags( surfID ) & SURFDRAW_NODRAW )
  4095. continue;
  4096. IMaterial* pMaterial = MSurf_TexInfo( surfID, mod->brush.pShared )->material;
  4097. // Try to find the material in the unique list of materials
  4098. // if it's not there, then add it
  4099. if (uniqueMaterials.Find(pMaterial) < 0)
  4100. uniqueMaterials.AddToTail(pMaterial);
  4101. }
  4102. return uniqueMaterials.Count();
  4103. }
  4104. break;
  4105. case mod_studio:
  4106. {
  4107. // FIXME: This should return the list of all materials
  4108. // across all LODs if we every decide to implement this
  4109. Assert(0);
  4110. }
  4111. break;
  4112. default:
  4113. // unimplemented
  4114. Assert(0);
  4115. break;
  4116. }
  4117. return 0;
  4118. }
  4119. //-----------------------------------------------------------------------------
  4120. // returns the first n materials.
  4121. //-----------------------------------------------------------------------------
  4122. int Mod_GetModelMaterials( model_t* pModel, int count, IMaterial** ppMaterials )
  4123. {
  4124. studiohdr_t *pStudioHdr;
  4125. int found = 0;
  4126. int i;
  4127. switch( pModel->type )
  4128. {
  4129. case mod_brush:
  4130. {
  4131. for ( i = 0; i < pModel->brush.nummodelsurfaces; ++i)
  4132. {
  4133. SurfaceHandle_t surfID = SurfaceHandleFromIndex( pModel->brush.firstmodelsurface + i, pModel->brush.pShared );
  4134. if ( MSurf_Flags( surfID ) & SURFDRAW_NODRAW )
  4135. continue;
  4136. IMaterial* pMaterial = MSurf_TexInfo( surfID, pModel->brush.pShared )->material;
  4137. // Try to find the material in the unique list of materials
  4138. // if it's not there, then add it
  4139. int j = found;
  4140. while ( --j >= 0 )
  4141. {
  4142. if ( ppMaterials[j] == pMaterial )
  4143. break;
  4144. }
  4145. if (j < 0)
  4146. ppMaterials[found++] = pMaterial;
  4147. // Stop when we've gotten count materials
  4148. if ( found >= count )
  4149. return found;
  4150. }
  4151. }
  4152. break;
  4153. case mod_studio:
  4154. // Get the studiohdr into the cache
  4155. pStudioHdr = g_pMDLCache->GetStudioHdr( pModel->studio );
  4156. // Get the list of materials
  4157. found = g_pStudioRender->GetMaterialList( pStudioHdr, count, ppMaterials );
  4158. break;
  4159. default:
  4160. // unimplemented
  4161. Assert( 0 );
  4162. break;
  4163. }
  4164. return found;
  4165. }
  4166. //-----------------------------------------------------------------------------
  4167. // Used to compute which surfaces are in water or not
  4168. //-----------------------------------------------------------------------------
  4169. static void MarkWaterSurfaces_ProcessLeafNode( mleaf_t *pLeaf )
  4170. {
  4171. int i;
  4172. int flags = ( pLeaf->leafWaterDataID == -1 ) ? SURFDRAW_ABOVEWATER : SURFDRAW_UNDERWATER;
  4173. SurfaceHandle_t *pHandle = &host_state.worldbrush->marksurfaces[pLeaf->firstmarksurface];
  4174. for( i = 0; i < pLeaf->nummarksurfaces; i++ )
  4175. {
  4176. SurfaceHandle_t surfID = pHandle[i];
  4177. ASSERT_SURF_VALID( surfID );
  4178. if( MSurf_Flags( surfID ) & SURFDRAW_WATERSURFACE )
  4179. continue;
  4180. if (SurfaceHasDispInfo( surfID ))
  4181. continue;
  4182. MSurf_Flags( surfID ) |= flags;
  4183. }
  4184. // FIXME: This is somewhat bogus, but I can do it quickly, and it's
  4185. // not clear I need to solve the harder problem.
  4186. // If any portion of a displacement surface hits a water surface,
  4187. // I'm going to mark it as being in water, and vice versa.
  4188. for ( i = 0; i < pLeaf->dispCount; i++ )
  4189. {
  4190. IDispInfo *pDispInfo = MLeaf_Disaplcement( pLeaf, i );
  4191. SurfaceHandle_t parentSurfID = pDispInfo->GetParent();
  4192. MSurf_Flags( parentSurfID ) |= flags;
  4193. }
  4194. }
  4195. void MarkWaterSurfaces_r( mnode_t *node )
  4196. {
  4197. // no polygons in solid nodes
  4198. if (node->contents == CONTENTS_SOLID)
  4199. return; // solid
  4200. // if a leaf node, . .mark all the polys as to whether or not they are in water.
  4201. if (node->contents >= 0)
  4202. {
  4203. MarkWaterSurfaces_ProcessLeafNode( (mleaf_t *)node );
  4204. return;
  4205. }
  4206. MarkWaterSurfaces_r( node->children[0] );
  4207. MarkWaterSurfaces_r( node->children[1] );
  4208. }
  4209. //-----------------------------------------------------------------------------
  4210. // Computes the sort group for a particular face
  4211. //-----------------------------------------------------------------------------
  4212. static int SurfFlagsToSortGroup( SurfaceHandle_t surfID, int flags )
  4213. {
  4214. if( flags & SURFDRAW_WATERSURFACE )
  4215. return MAT_SORT_GROUP_WATERSURFACE;
  4216. if( ( flags & ( SURFDRAW_UNDERWATER | SURFDRAW_ABOVEWATER ) ) == ( SURFDRAW_UNDERWATER | SURFDRAW_ABOVEWATER ) )
  4217. return MAT_SORT_GROUP_INTERSECTS_WATER_SURFACE;
  4218. if( flags & SURFDRAW_UNDERWATER )
  4219. return MAT_SORT_GROUP_STRICTLY_UNDERWATER;
  4220. if( flags & SURFDRAW_ABOVEWATER )
  4221. return MAT_SORT_GROUP_STRICTLY_ABOVEWATER;
  4222. static int warningcount = 0;
  4223. if ( ++warningcount < 10 )
  4224. {
  4225. Vector vecCenter;
  4226. Surf_ComputeCentroid( surfID, &vecCenter );
  4227. DevWarning( "SurfFlagsToSortGroup: unhandled flags (%X) (%s)!\n", flags, MSurf_TexInfo(surfID)->material->GetName() );
  4228. DevWarning( "- This implies you have a surface (usually a displacement) embedded in solid.\n" );
  4229. DevWarning( "- Look near (%.1f, %.1f, %.1f)\n", vecCenter.x, vecCenter.y, vecCenter.z );
  4230. }
  4231. //Assert( 0 );
  4232. return MAT_SORT_GROUP_STRICTLY_ABOVEWATER;
  4233. }
  4234. //-----------------------------------------------------------------------------
  4235. // Computes sort group
  4236. //-----------------------------------------------------------------------------
  4237. bool Mod_MarkWaterSurfaces( model_t *pModel )
  4238. {
  4239. bool bHasWaterSurfaces = false;
  4240. model_t *pSaveModel = host_state.worldmodel;
  4241. // garymcthack!!!!!!!!
  4242. // host_state.worldmodel isn't set at this point, so. . . .
  4243. host_state.SetWorldModel( pModel );
  4244. MarkWaterSurfaces_r( pModel->brush.pShared->nodes );
  4245. for ( int i = 0; i < pModel->brush.pShared->numsurfaces; i++ )
  4246. {
  4247. SurfaceHandle_t surfID = SurfaceHandleFromIndex( i, pModel->brush.pShared );
  4248. int sortGroup = SurfFlagsToSortGroup( surfID, MSurf_Flags( surfID ) );
  4249. if ( sortGroup == MAT_SORT_GROUP_WATERSURFACE )
  4250. {
  4251. bHasWaterSurfaces = true;
  4252. }
  4253. MSurf_SetSortGroup( surfID, sortGroup );
  4254. }
  4255. host_state.SetWorldModel( pSaveModel );
  4256. return bHasWaterSurfaces;
  4257. }
  4258. //-----------------------------------------------------------------------------
  4259. // Marks identity brushes as being in fog volumes or not
  4260. //-----------------------------------------------------------------------------
  4261. class CBrushBSPIterator : public ISpatialLeafEnumerator
  4262. {
  4263. public:
  4264. CBrushBSPIterator( model_t *pWorld, model_t *pBrush )
  4265. {
  4266. m_pWorld = pWorld;
  4267. m_pBrush = pBrush;
  4268. m_pShared = pBrush->brush.pShared;
  4269. m_count = 0;
  4270. }
  4271. bool EnumerateLeaf( int leaf, intp )
  4272. {
  4273. // garymcthack - need to test identity brush models
  4274. int flags = ( m_pShared->leafs[leaf].leafWaterDataID == -1 ) ? SURFDRAW_ABOVEWATER : SURFDRAW_UNDERWATER;
  4275. MarkModelSurfaces( flags );
  4276. m_count++;
  4277. return true;
  4278. }
  4279. void MarkModelSurfaces( int flags )
  4280. {
  4281. // Iterate over all this models surfaces
  4282. int surfaceCount = m_pBrush->brush.nummodelsurfaces;
  4283. for (int i = 0; i < surfaceCount; ++i)
  4284. {
  4285. SurfaceHandle_t surfID = SurfaceHandleFromIndex( m_pBrush->brush.firstmodelsurface + i, m_pShared );
  4286. MSurf_Flags( surfID ) &= ~(SURFDRAW_ABOVEWATER | SURFDRAW_UNDERWATER);
  4287. MSurf_Flags( surfID ) |= flags;
  4288. }
  4289. }
  4290. void CheckSurfaces()
  4291. {
  4292. if ( !m_count )
  4293. {
  4294. MarkModelSurfaces( SURFDRAW_ABOVEWATER );
  4295. }
  4296. }
  4297. model_t* m_pWorld;
  4298. model_t* m_pBrush;
  4299. worldbrushdata_t *m_pShared;
  4300. int m_count;
  4301. };
  4302. static void MarkBrushModelWaterSurfaces( model_t* world,
  4303. Vector const& mins, Vector const& maxs, model_t* brush )
  4304. {
  4305. // HACK: This is a totally brutal hack dealing with initialization order issues.
  4306. // I want to use the same box enumeration code so I don't have multiple
  4307. // copies, but I want to use it from modelloader. host_state.worldmodel isn't
  4308. // set up at that time however, so I have to fly through these crazy hoops.
  4309. // Massive suckage.
  4310. model_t* pTemp = host_state.worldmodel;
  4311. CBrushBSPIterator brushIterator( world, brush );
  4312. host_state.SetWorldModel( world );
  4313. g_pToolBSPTree->EnumerateLeavesInBox( mins, maxs, &brushIterator, (intp)brush );
  4314. brushIterator.CheckSurfaces();
  4315. host_state.SetWorldModel( pTemp );
  4316. }
  4317. //-----------------------------------------------------------------------------
  4318. // Purpose:
  4319. // Input : *mod -
  4320. // *buffer -
  4321. //-----------------------------------------------------------------------------
  4322. void CModelLoader::Map_LoadModel( model_t *mod )
  4323. {
  4324. if ( sv.IsDedicated() )
  4325. BeginWatchdogTimer( 4 * 60 ); // reset watchdog timer to allow 4 minutes for map load
  4326. #ifndef MEM_DETAILED_ACCOUNTING_MAP_LOADMODEL
  4327. MEM_ALLOC_CREDIT();
  4328. #endif
  4329. #ifndef DEDICATED
  4330. EngineVGui()->UpdateProgressBar(PROGRESS_LOADWORLDMODEL);
  4331. #endif
  4332. Assert( !( mod->nLoadFlags & FMODELLOADER_LOADED ) );
  4333. COM_TimestampedLog( "Map_LoadModel: Start" );
  4334. double startTime = Plat_FloatTime();
  4335. SetWorldModel( mod );
  4336. // point at the shared world/brush data
  4337. mod->brush.pShared = &m_worldBrushData;
  4338. mod->brush.renderHandle = 0;
  4339. mod->type = mod_brush;
  4340. mod->nLoadFlags |= FMODELLOADER_LOADED;
  4341. CMapLoadHelper::Init( mod, mod->szPathName );
  4342. Map_LoadModelGuts( mod );
  4343. // Close map file, etc.
  4344. CMapLoadHelper::Shutdown();
  4345. double elapsed = Plat_FloatTime() - startTime;
  4346. COM_TimestampedLog( "Map_LoadModel: Finish - loading took %.4f seconds", elapsed );
  4347. if ( sv.IsDedicated() )
  4348. EndWatchdogTimer();
  4349. }
  4350. int g_nMapLoadCount = 0;
  4351. // do all of the I/O. This is broken out to bracket the MapLoadHelper::Init/Shutdown
  4352. void CModelLoader::Map_LoadModelGuts( model_t *mod )
  4353. {
  4354. ++g_nMapLoadCount;
  4355. // HDR and features must be established first
  4356. COM_TimestampedLog( " Map_CheckForHDR" );
  4357. m_bMapHasHDRLighting = Map_CheckForHDR( mod, mod->szPathName );
  4358. if ( IsGameConsole() && !m_bMapHasHDRLighting )
  4359. {
  4360. Warning( "Map '%s' lacks exepected HDR data! 360 does not support accurate LDR visuals.\n", mod->szPathName );
  4361. }
  4362. // load the texinfo lump (used by many subsequent lumps in raw form)
  4363. CMapLoadHelper lhTexinfo( LUMP_TEXINFO );
  4364. texinfo_t *pTexinfo = (texinfo_t *)lhTexinfo.LumpBase();
  4365. if ( lhTexinfo.LumpSize() % sizeof( *pTexinfo ) )
  4366. Host_Error( "Map_LoadModelGuts: bad LUMP_TEXINFO size in %s", mod->szPathName );
  4367. int texinfoCount = lhTexinfo.LumpSize() / sizeof( *pTexinfo );
  4368. if ( texinfoCount < 1 )
  4369. {
  4370. if ( !g_bClearingClientState )
  4371. {
  4372. Sys_Error( "Map_LoadModelGuts: Map with no texinfo, %s", mod->szPathName );
  4373. }
  4374. return;
  4375. }
  4376. if ( texinfoCount > MAX_MAP_TEXINFO )
  4377. Sys_Error( "Map_LoadModelGuts: Map has too many surfaces, %s", mod->szPathName );
  4378. // Load the collision model
  4379. COM_TimestampedLog( " CM_LoadMap" );
  4380. unsigned int checksum;
  4381. CM_LoadMap( mod->szPathName, false, pTexinfo, texinfoCount, &checksum );
  4382. // The MEM_ALLOC_CREDITs here will be overridden and credited to Map_LoadModel()
  4383. // unless you #define MEM_DETAILED_ACCOUNTING_MAP_LOADMODEL at the top of this file.
  4384. {
  4385. MEM_ALLOC_CREDIT_("Mod_LoadVertices");
  4386. COM_TimestampedLog( " Mod_LoadVertices" );
  4387. Mod_LoadVertices();
  4388. }
  4389. {
  4390. MEM_ALLOC_CREDIT_("Mod_LoadEdges");
  4391. COM_TimestampedLog( " Mod_LoadEdges" );
  4392. medge_t *pedges = Mod_LoadEdges();
  4393. COM_TimestampedLog( " Mod_LoadSurfedges" );
  4394. Mod_LoadSurfedges( pedges );
  4395. }
  4396. {
  4397. MEM_ALLOC_CREDIT_("Mod_LoadPlanes");
  4398. COM_TimestampedLog( " Mod_LoadPlanes" );
  4399. Mod_LoadPlanes();
  4400. }
  4401. {
  4402. MEM_ALLOC_CREDIT_("Mod_LoadOcclusion");
  4403. COM_TimestampedLog( " Mod_LoadOcclusion" );
  4404. Mod_LoadOcclusion();
  4405. }
  4406. {
  4407. // texdata needs to load before texinfo
  4408. MEM_ALLOC_CREDIT_("Mod_LoadTexdata");
  4409. COM_TimestampedLog( " Mod_LoadTexdata" );
  4410. Mod_LoadTexdata();
  4411. }
  4412. {
  4413. MEM_ALLOC_CREDIT_("Mod_LoadTexinfo");
  4414. COM_TimestampedLog( " Mod_LoadTexinfo" );
  4415. Mod_LoadTexinfo( lhTexinfo );
  4416. }
  4417. #ifndef DEDICATED
  4418. EngineVGui()->UpdateProgressBar(PROGRESS_LOADWORLDMODEL);
  4419. #endif
  4420. // Until BSP version 19, this must occur after loading texinfo
  4421. {
  4422. MEM_ALLOC_CREDIT_("Mod_LoadLighting");
  4423. COM_TimestampedLog( " Mod_LoadLighting" );
  4424. bool bLoadHDR = ( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE ) &&
  4425. ( CMapLoadHelper::LumpSize( LUMP_LIGHTING_HDR ) > 0 );
  4426. Mod_LoadLighting( bLoadHDR );
  4427. }
  4428. {
  4429. MEM_ALLOC_CREDIT_("Mod_LoadPrimitives");
  4430. COM_TimestampedLog( " Mod_LoadPrimitives" );
  4431. Mod_LoadPrimitives();
  4432. }
  4433. {
  4434. MEM_ALLOC_CREDIT_("Mod_LoadPrimVerts");
  4435. COM_TimestampedLog( " Mod_LoadPrimVerts" );
  4436. Mod_LoadPrimVerts();
  4437. }
  4438. {
  4439. MEM_ALLOC_CREDIT_("Mod_LoadPrimIndices");
  4440. COM_TimestampedLog( " Mod_LoadPrimIndices" );
  4441. Mod_LoadPrimIndices();
  4442. }
  4443. #ifndef DEDICATED
  4444. EngineVGui()->UpdateProgressBar(PROGRESS_LOADWORLDMODEL);
  4445. #endif
  4446. {
  4447. // faces need to be loaded before vertnormals
  4448. MEM_ALLOC_CREDIT_("Mod_LoadFaces");
  4449. COM_TimestampedLog( " Mod_LoadFaces" );
  4450. Mod_LoadFaces();
  4451. Mod_LoadFaceBrushes();
  4452. }
  4453. {
  4454. MEM_ALLOC_CREDIT_("Mod_LoadVertNormals");
  4455. COM_TimestampedLog( " Mod_LoadVertNormals" );
  4456. Mod_LoadVertNormals();
  4457. }
  4458. {
  4459. MEM_ALLOC_CREDIT_("Mod_LoadVertNormalIndices");
  4460. COM_TimestampedLog( " Mod_LoadVertNormalIndices" );
  4461. Mod_LoadVertNormalIndices();
  4462. }
  4463. #ifndef DEDICATED
  4464. EngineVGui()->UpdateProgressBar(PROGRESS_LOADWORLDMODEL);
  4465. #endif
  4466. {
  4467. // note leafs must load befor marksurfaces
  4468. MEM_ALLOC_CREDIT_("Mod_LoadLeafs");
  4469. COM_TimestampedLog( " Mod_LoadLeafs" );
  4470. Mod_LoadLeafs();
  4471. }
  4472. {
  4473. MEM_ALLOC_CREDIT_("Mod_LoadMarksurfaces");
  4474. COM_TimestampedLog( " Mod_LoadMarksurfaces" );
  4475. Mod_LoadMarksurfaces();
  4476. }
  4477. {
  4478. MEM_ALLOC_CREDIT_("Mod_LoadNodes");
  4479. COM_TimestampedLog( " Mod_LoadNodes" );
  4480. Mod_LoadNodes();
  4481. }
  4482. {
  4483. MEM_ALLOC_CREDIT_("Mod_LoadLeafWaterData");
  4484. COM_TimestampedLog( " Mod_LoadLeafWaterData" );
  4485. Mod_LoadLeafWaterData();
  4486. }
  4487. #ifndef DEDICATED
  4488. {
  4489. // UNDONE: Does the cmodel need worldlights?
  4490. MEM_ALLOC_CREDIT_("OverlayMgr()->LoadOverlays");
  4491. COM_TimestampedLog( " OverlayMgr()->LoadOverlays" );
  4492. OverlayMgr()->LoadOverlays();
  4493. }
  4494. #endif
  4495. {
  4496. MEM_ALLOC_CREDIT_("Mod_LoadLeafMinDistToWater");
  4497. COM_TimestampedLog( " Mod_LoadLeafMinDistToWater" );
  4498. Mod_LoadLeafMinDistToWater();
  4499. }
  4500. #ifndef DEDICATED
  4501. EngineVGui()->UpdateProgressBar(PROGRESS_LOADWORLDMODEL);
  4502. #endif
  4503. {
  4504. MEM_ALLOC_CREDIT_("LUMP_CLIPPORTALVERTS");
  4505. COM_TimestampedLog( " LUMP_CLIPPORTALVERTS" );
  4506. Mod_LoadLump( mod,
  4507. LUMP_CLIPPORTALVERTS,
  4508. va( "%s [%s]", m_szBaseName, "clipportalverts" ),
  4509. sizeof(m_worldBrushData.m_pClipPortalVerts[0]),
  4510. (void**)&m_worldBrushData.m_pClipPortalVerts,
  4511. &m_worldBrushData.m_nClipPortalVerts );
  4512. }
  4513. {
  4514. MEM_ALLOC_CREDIT_("LUMP_AREAPORTALS");
  4515. COM_TimestampedLog( " LUMP_AREAPORTALS" );
  4516. Mod_LoadLump( mod,
  4517. LUMP_AREAPORTALS,
  4518. va( "%s [%s]", m_szBaseName, "areaportals" ),
  4519. sizeof(m_worldBrushData.m_pAreaPortals[0]),
  4520. (void**)&m_worldBrushData.m_pAreaPortals,
  4521. &m_worldBrushData.m_nAreaPortals );
  4522. }
  4523. {
  4524. MEM_ALLOC_CREDIT_("LUMP_AREAS");
  4525. COM_TimestampedLog( " LUMP_AREAS" );
  4526. Mod_LoadLump( mod,
  4527. LUMP_AREAS,
  4528. va( "%s [%s]", m_szBaseName, "areas" ),
  4529. sizeof(m_worldBrushData.m_pAreas[0]),
  4530. (void**)&m_worldBrushData.m_pAreas,
  4531. &m_worldBrushData.m_nAreas );
  4532. }
  4533. {
  4534. MEM_ALLOC_CREDIT_("Mod_LoadWorldlights");
  4535. COM_TimestampedLog( " Mod_LoadWorldlights" );
  4536. if ( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE &&
  4537. CMapLoadHelper::LumpSize( LUMP_WORLDLIGHTS_HDR ) > 0 )
  4538. {
  4539. CMapLoadHelper mlh( LUMP_WORLDLIGHTS_HDR );
  4540. Mod_LoadWorldlights( mlh, true );
  4541. }
  4542. else
  4543. {
  4544. CMapLoadHelper mlh( LUMP_WORLDLIGHTS );
  4545. Mod_LoadWorldlights( mlh, false );
  4546. }
  4547. }
  4548. {
  4549. MEM_ALLOC_CREDIT_("Mod_LoadCubemapSamples");
  4550. COM_TimestampedLog( " Mod_LoadCubemapSamples" );
  4551. Mod_LoadCubemapSamples();
  4552. }
  4553. #if defined( PORTAL2 ) || defined( CSTRIKE15 )
  4554. {
  4555. MEM_ALLOC_CREDIT_("Mod_LoadSimpleWorldModel");
  4556. COM_TimestampedLog( " Mod_LoadSimpleWorldModel" );
  4557. Mod_LoadSimpleWorldModel( m_szBaseName );
  4558. }
  4559. #endif
  4560. {
  4561. MEM_ALLOC_CREDIT_("Mod_LoadGameLumpDict");
  4562. COM_TimestampedLog( " Mod_LoadGameLumpDict" );
  4563. Mod_LoadGameLumpDict();
  4564. }
  4565. #ifndef DEDICATED
  4566. EngineVGui()->UpdateProgressBar(PROGRESS_LOADWORLDMODEL);
  4567. #endif
  4568. {
  4569. MEM_ALLOC_CREDIT_("Mod_LoadSubmodels");
  4570. COM_TimestampedLog( " Mod_LoadSubmodels" );
  4571. CUtlVector<mmodel_t> submodelList;
  4572. Mod_LoadSubmodels( submodelList );
  4573. #ifndef DEDICATED
  4574. EngineVGui()->UpdateProgressBar(PROGRESS_LOADWORLDMODEL);
  4575. #endif
  4576. COM_TimestampedLog( " SetupSubModels" );
  4577. SetupSubModels( mod, submodelList );
  4578. }
  4579. {
  4580. MEM_ALLOC_CREDIT_("RecomputeSurfaceFlags");
  4581. COM_TimestampedLog( " RecomputeSurfaceFlags" );
  4582. RecomputeSurfaceFlags( mod );
  4583. }
  4584. #ifndef DEDICATED
  4585. EngineVGui()->UpdateProgressBar(PROGRESS_LOADWORLDMODEL);
  4586. #endif
  4587. {
  4588. MEM_ALLOC_CREDIT_("Map_VisClear");
  4589. COM_TimestampedLog( " Map_VisClear" );
  4590. Map_VisClear();
  4591. }
  4592. {
  4593. MEM_ALLOC_CREDIT_("Map_SetRenderInfoAllocated");
  4594. COM_TimestampedLog( " Map_SetRenderInfoAllocated" );
  4595. Map_SetRenderInfoAllocated( false );
  4596. }
  4597. }
  4598. void CModelLoader::Map_UnloadCubemapSamples( model_t *mod )
  4599. {
  4600. int i;
  4601. for ( i=0 ; i < mod->brush.pShared->m_nCubemapSamples ; i++ )
  4602. {
  4603. mcubemapsample_t *pSample = &mod->brush.pShared->m_pCubemapSamples[i];
  4604. pSample->pTexture->DecrementReferenceCount();
  4605. }
  4606. }
  4607. void CModelLoader::Map_UnloadSimpleWorldModel( model_t *mod )
  4608. {
  4609. if ( g_pSimpleWorldModel )
  4610. {
  4611. UnloadModel( g_pSimpleWorldModel );
  4612. }
  4613. if ( g_pSimpleWorldModelWater )
  4614. {
  4615. UnloadModel( g_pSimpleWorldModelWater );
  4616. }
  4617. g_pSimpleWorldModel = NULL;
  4618. g_pSimpleWorldModelWater = NULL;
  4619. }
  4620. //-----------------------------------------------------------------------------
  4621. // Recomputes surface flags
  4622. //-----------------------------------------------------------------------------
  4623. void CModelLoader::RecomputeSurfaceFlags( model_t *mod )
  4624. {
  4625. for (int i=0 ; i<mod->brush.pShared->numsubmodels ; i++)
  4626. {
  4627. model_t *pSubModel = &m_InlineModels[i];
  4628. // Compute whether this submodel uses material proxies or not
  4629. Mod_ComputeBrushModelFlags( pSubModel );
  4630. // Mark if brush models are in water or not; we'll use this
  4631. // for identity brushes. If the brush is not an identity brush,
  4632. // then we'll not have to worry.
  4633. if ( i != 0 )
  4634. {
  4635. MarkBrushModelWaterSurfaces( mod, pSubModel->mins, pSubModel->maxs, pSubModel );
  4636. }
  4637. }
  4638. }
  4639. //-----------------------------------------------------------------------------
  4640. // Setup sub models
  4641. //-----------------------------------------------------------------------------
  4642. void CModelLoader::SetupSubModels( model_t *mod, CUtlVector<mmodel_t> &list )
  4643. {
  4644. int i;
  4645. m_InlineModels.SetCount( m_worldBrushData.numsubmodels );
  4646. for (i=0 ; i<m_worldBrushData.numsubmodels ; i++)
  4647. {
  4648. model_t *starmod;
  4649. mmodel_t *bm;
  4650. bm = &list[i];
  4651. starmod = &m_InlineModels[i];
  4652. *starmod = *mod;
  4653. starmod->brush.firstmodelsurface = bm->firstface;
  4654. starmod->brush.nummodelsurfaces = bm->numfaces;
  4655. starmod->brush.firstnode = bm->headnode;
  4656. if ( starmod->brush.firstnode >= m_worldBrushData.numnodes )
  4657. {
  4658. Sys_Error( "Inline model %i has bad firstnode", i );
  4659. }
  4660. VectorCopy(bm->maxs, starmod->maxs);
  4661. VectorCopy(bm->mins, starmod->mins);
  4662. starmod->radius = bm->radius;
  4663. if (i == 0)
  4664. {
  4665. *mod = *starmod;
  4666. }
  4667. else
  4668. {
  4669. Q_snprintf( starmod->szPathName, sizeof( starmod->szPathName ), "*%d", i );
  4670. starmod->fnHandle = g_pFileSystem->FindOrAddFileName( starmod->szPathName );
  4671. }
  4672. }
  4673. }
  4674. //-----------------------------------------------------------------------------
  4675. // Purpose:
  4676. // Input : *mod -
  4677. //-----------------------------------------------------------------------------
  4678. void CModelLoader::Map_UnloadModel( model_t *mod )
  4679. {
  4680. Assert( !( mod->nLoadFlags & FMODELLOADER_REFERENCEMASK ) );
  4681. mod->nLoadFlags &= ~FMODELLOADER_LOADED;
  4682. #ifndef DEDICATED
  4683. OverlayMgr()->UnloadOverlays();
  4684. #endif
  4685. DeallocateLightingData( &m_worldBrushData );
  4686. #ifndef DEDICATED
  4687. DispInfo_ReleaseMaterialSystemObjects( mod );
  4688. #endif
  4689. Map_UnloadSimpleWorldModel( mod );
  4690. Map_UnloadCubemapSamples( mod );
  4691. #ifndef DEDICATED
  4692. // Free decals in displacements.
  4693. R_DecalTerm( &m_worldBrushData, true, true );
  4694. #endif
  4695. if ( m_worldBrushData.hDispInfos )
  4696. {
  4697. DispInfo_DeleteArray( m_worldBrushData.hDispInfos );
  4698. m_worldBrushData.hDispInfos = NULL;
  4699. }
  4700. // Model loader loads world model materials, unload them here
  4701. for( int texinfoID = 0; texinfoID < m_worldBrushData.numtexinfo; texinfoID++ )
  4702. {
  4703. mtexinfo_t *pTexinfo = &m_worldBrushData.texinfo[texinfoID];
  4704. if ( pTexinfo )
  4705. {
  4706. GL_UnloadMaterial( pTexinfo->material );
  4707. }
  4708. }
  4709. MaterialSystem_DestroySortinfo();
  4710. // Don't store any reference to it here
  4711. ClearWorldModel();
  4712. Map_SetRenderInfoAllocated( false );
  4713. }
  4714. //-----------------------------------------------------------------------------
  4715. // Computes dimensions + frame count of a material
  4716. //-----------------------------------------------------------------------------
  4717. static void GetSpriteInfo( const char *pName, bool bIsAVI, bool bIsBIK, int &nWidth, int &nHeight, int &nFrameCount )
  4718. {
  4719. nFrameCount = 1;
  4720. nWidth = nHeight = 1;
  4721. // FIXME: The reason we are putting logic related to AVIs here,
  4722. // logic which is duplicated in the client DLL related to loading sprites,
  4723. // is that this code gets run on dedicated servers also.
  4724. IMaterial *pMaterial = NULL;
  4725. AVIMaterial_t hAVIMaterial = AVIMATERIAL_INVALID;
  4726. #if !defined( _GAMECONSOLE ) || defined( BINK_ENABLED_FOR_CONSOLE )
  4727. BIKMaterial_t hBIKMaterial = BIKMATERIAL_INVALID;
  4728. #endif
  4729. if ( bIsAVI )
  4730. {
  4731. hAVIMaterial = avi->CreateAVIMaterial( pName, pName, "GAME" );
  4732. avi->GetFrameSize( hAVIMaterial, &nWidth, &nHeight );
  4733. nFrameCount = avi->GetFrameCount( hAVIMaterial );
  4734. if ( hAVIMaterial != AVIMATERIAL_INVALID )
  4735. {
  4736. pMaterial = avi->GetMaterial( hAVIMaterial );
  4737. }
  4738. }
  4739. #if !defined( _GAMECONSOLE ) || defined( BINK_ENABLED_FOR_CONSOLE )
  4740. else if ( bIsBIK )
  4741. {
  4742. hBIKMaterial = bik->CreateMaterial( pName, pName, "GAME" );
  4743. if (hBIKMaterial != BIKMATERIAL_INVALID )
  4744. {
  4745. bik->GetFrameSize( hBIKMaterial, &nWidth, &nHeight );
  4746. nFrameCount = bik->GetFrameCount( hBIKMaterial );
  4747. pMaterial = bik->GetMaterial( hBIKMaterial );
  4748. }
  4749. }
  4750. #endif
  4751. else
  4752. {
  4753. pMaterial = GL_LoadMaterial( pName, TEXTURE_GROUP_OTHER );
  4754. if ( pMaterial )
  4755. {
  4756. // Store off our source height, width, frame count
  4757. nWidth = pMaterial->GetMappingWidth();
  4758. nHeight = pMaterial->GetMappingHeight();
  4759. nFrameCount = pMaterial->GetNumAnimationFrames();
  4760. }
  4761. }
  4762. if ( pMaterial == g_materialEmpty )
  4763. {
  4764. DevMsg( "Missing sprite material %s\n", pName );
  4765. }
  4766. if ( hAVIMaterial != AVIMATERIAL_INVALID )
  4767. {
  4768. avi->DestroyAVIMaterial( hAVIMaterial );
  4769. }
  4770. #if !defined( _GAMECONSOLE ) || defined( BINK_ENABLED_FOR_CONSOLE )
  4771. if ( hBIKMaterial != BIKMATERIAL_INVALID )
  4772. {
  4773. bik->DestroyMaterial( hBIKMaterial );
  4774. }
  4775. #endif
  4776. }
  4777. //-----------------------------------------------------------------------------
  4778. // Purpose:
  4779. //-----------------------------------------------------------------------------
  4780. void CModelLoader::Sprite_LoadModel( model_t *mod )
  4781. {
  4782. Assert( !( mod->nLoadFlags & FMODELLOADER_LOADED ) );
  4783. mod->nLoadFlags |= FMODELLOADER_LOADED;
  4784. // The hunk data is not used on the server
  4785. byte* pSprite = NULL;
  4786. #ifndef DEDICATED
  4787. if ( g_ClientDLL )
  4788. {
  4789. int nSize = g_ClientDLL->GetSpriteSize();
  4790. if ( nSize )
  4791. {
  4792. pSprite = ( byte * )new byte[ nSize ];
  4793. }
  4794. }
  4795. #endif
  4796. mod->type = mod_sprite;
  4797. mod->sprite.sprite = (CEngineSprite *)pSprite;
  4798. // Fake the bounding box. We need it for PVS culling, and we don't
  4799. // know the scale at which the sprite is going to be rendered at
  4800. // when we load it
  4801. mod->mins = mod->maxs = Vector(0,0,0);
  4802. // Figure out the real load name..
  4803. char loadName[MAX_PATH];
  4804. bool bIsAVI, bIsBIK;
  4805. BuildSpriteLoadName( mod->szPathName, loadName, MAX_PATH, bIsAVI, bIsBIK );
  4806. GetSpriteInfo( loadName, bIsAVI, bIsBIK, mod->sprite.width, mod->sprite.height, mod->sprite.numframes );
  4807. #ifndef DEDICATED
  4808. if ( g_ClientDLL && mod->sprite.sprite )
  4809. {
  4810. g_ClientDLL->InitSprite( mod->sprite.sprite, loadName );
  4811. }
  4812. #endif
  4813. }
  4814. //-----------------------------------------------------------------------------
  4815. // Purpose:
  4816. //-----------------------------------------------------------------------------
  4817. void CModelLoader::Sprite_UnloadModel( model_t *mod )
  4818. {
  4819. Assert( !( mod->nLoadFlags & FMODELLOADER_REFERENCEMASK ) );
  4820. mod->nLoadFlags &= ~FMODELLOADER_LOADED;
  4821. char loadName[MAX_PATH];
  4822. bool bIsAVI, bIsBIK;
  4823. BuildSpriteLoadName( mod->szPathName, loadName, sizeof( loadName ), bIsAVI, bIsBIK );
  4824. IMaterial *mat = materials->FindMaterial( loadName, TEXTURE_GROUP_OTHER );
  4825. if ( !IsErrorMaterial( mat ) )
  4826. {
  4827. GL_UnloadMaterial( mat );
  4828. }
  4829. #ifndef DEDICATED
  4830. if ( g_ClientDLL && mod->sprite.sprite )
  4831. {
  4832. g_ClientDLL->ShutdownSprite( mod->sprite.sprite );
  4833. }
  4834. #endif
  4835. delete[] (byte *)mod->sprite.sprite;
  4836. mod->sprite.sprite = 0;
  4837. mod->sprite.numframes = 0;
  4838. }
  4839. //-----------------------------------------------------------------------------
  4840. // Purpose: Flush and reload models. Intended for use when lod changes.
  4841. //-----------------------------------------------------------------------------
  4842. void CModelLoader::Studio_ReloadModels( CModelLoader::ReloadType_t reloadType )
  4843. {
  4844. #if !defined( DEDICATED )
  4845. if ( g_ClientDLL )
  4846. g_ClientDLL->InvalidateMdlCache();
  4847. #endif // DEDICATED
  4848. if ( serverGameDLL )
  4849. serverGameDLL->InvalidateMdlCache();
  4850. // ensure decals have no stale references to invalid lods
  4851. modelrender->RemoveAllDecalsFromAllModels( false );
  4852. // ensure static props have no stale references to invalid lods
  4853. modelrender->ReleaseAllStaticPropColorData();
  4854. // Flush out the model cache
  4855. // Don't flush vcollides since the vphysics system currently
  4856. // has no way of indicating they refer to vcollides
  4857. g_pMDLCache->Flush( (MDLCacheFlush_t) (MDLCACHE_FLUSH_ALL & ~(MDLCACHE_FLUSH_IGNORELOCK|MDLCACHE_FLUSH_VCOLLIDE)) );
  4858. // Load the critical pieces now
  4859. // The model cache will re-populate as models render
  4860. int c = m_Models.Count();
  4861. for ( int i=0 ; i < c ; i++ )
  4862. {
  4863. model_t *pModel = m_Models[ i ].modelpointer;
  4864. if ( !IsLoaded( pModel ) )
  4865. continue;
  4866. if ( pModel->type != mod_studio )
  4867. continue;
  4868. MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
  4869. // Get the studiohdr into the cache
  4870. g_pMDLCache->GetStudioHdr( pModel->studio );
  4871. // force the collision to load
  4872. g_pMDLCache->GetVCollide( pModel->studio );
  4873. }
  4874. }
  4875. struct modelsize_t
  4876. {
  4877. const char *pName;
  4878. int size;
  4879. };
  4880. class CModelsize_Less
  4881. {
  4882. public:
  4883. bool Less( const modelsize_t& src1, const modelsize_t& src2, void *pCtx )
  4884. {
  4885. return ( src1.size < src2.size );
  4886. }
  4887. };
  4888. void CModelLoader::DumpVCollideStats()
  4889. {
  4890. int i;
  4891. CUtlSortVector< modelsize_t, CModelsize_Less > list;
  4892. for ( i = m_Models.Count(); --i >= 0; )
  4893. {
  4894. model_t *pModel = m_Models[ i ].modelpointer;
  4895. if ( pModel && pModel->type == mod_studio )
  4896. {
  4897. int size = 0;
  4898. bool loaded = g_pMDLCache->GetVCollideSize( pModel->studio, &size );
  4899. if ( loaded && size )
  4900. {
  4901. modelsize_t elem;
  4902. elem.pName = pModel->szPathName;
  4903. elem.size = size;
  4904. list.Insert( elem );
  4905. }
  4906. }
  4907. }
  4908. for ( i = m_InlineModels.Count(); --i >= 0; )
  4909. {
  4910. vcollide_t *pCollide = CM_VCollideForModel( i+1, &m_InlineModels[i] );
  4911. if ( pCollide )
  4912. {
  4913. int size = 0;
  4914. for ( int j = 0; j < pCollide->solidCount; j++ )
  4915. {
  4916. size += physcollision->CollideSize( pCollide->solids[j] );
  4917. }
  4918. size += pCollide->descSize;
  4919. if ( size )
  4920. {
  4921. modelsize_t elem;
  4922. elem.pName = m_InlineModels[i].szPathName;
  4923. elem.size = size;
  4924. list.Insert( elem );
  4925. }
  4926. }
  4927. }
  4928. Msg("VCollides loaded: %d\n", list.Count() );
  4929. int totalVCollideMemory = 0;
  4930. for ( i = 0; i < list.Count(); i++ )
  4931. {
  4932. Msg("%8d bytes:%s\n", list[i].size, list[i].pName);
  4933. totalVCollideMemory += list[i].size;
  4934. }
  4935. int bboxCount, bboxSize;
  4936. physcollision->GetBBoxCacheSize( &bboxSize, &bboxCount );
  4937. Msg( "%8d bytes BBox physics: %d boxes\n", bboxSize, bboxCount );
  4938. totalVCollideMemory += bboxSize;
  4939. Msg( "--------------\n%8d bytes total VCollide Memory\n", totalVCollideMemory );
  4940. }
  4941. //-----------------------------------------------------------------------------
  4942. // Is the model loaded?
  4943. //-----------------------------------------------------------------------------
  4944. bool CModelLoader::IsLoaded( const model_t *mod )
  4945. {
  4946. return (mod->nLoadFlags & FMODELLOADER_LOADED) != 0;
  4947. }
  4948. bool CModelLoader::LastLoadedMapHasHDRLighting(void)
  4949. {
  4950. return m_bMapHasHDRLighting;
  4951. }
  4952. bool CModelLoader::LastLoadedMapHasLightmapAlphaData( void )
  4953. {
  4954. return g_bHasLightmapAlphaData;
  4955. }
  4956. //-----------------------------------------------------------------------------
  4957. // Loads a studio model
  4958. //-----------------------------------------------------------------------------
  4959. void CModelLoader::Studio_LoadModel( model_t *pModel, bool bTouchAllData )
  4960. {
  4961. if ( !mod_touchalldata.GetBool() )
  4962. {
  4963. bTouchAllData = false;
  4964. }
  4965. // a preloaded model requires specific fixup behavior
  4966. bool bPreLoaded = ( pModel->nLoadFlags & FMODELLOADER_LOADED_BY_PRELOAD ) != 0;
  4967. bool bLoadPhysics = true;
  4968. if ( pModel->nLoadFlags == FMODELLOADER_STATICPROP )
  4969. {
  4970. // this is the first call in loading as a static prop (load bit not set), don't load physics yet
  4971. // the next call in causes the physics to load
  4972. bLoadPhysics = false;
  4973. }
  4974. // mark as loaded and fixed up
  4975. pModel->nLoadFlags |= FMODELLOADER_LOADED;
  4976. pModel->nLoadFlags &= ~FMODELLOADER_LOADED_BY_PRELOAD;
  4977. if ( !bPreLoaded )
  4978. {
  4979. pModel->studio = g_pMDLCache->FindMDL( pModel->szPathName );
  4980. g_pMDLCache->SetUserData( pModel->studio, pModel );
  4981. InitStudioModelState( pModel );
  4982. }
  4983. // Get the studiohdr into the cache
  4984. studiohdr_t *pStudioHdr = g_pMDLCache->GetStudioHdr( pModel->studio );
  4985. // a preloaded model already has its physics data resident
  4986. if ( bLoadPhysics && !bPreLoaded )
  4987. {
  4988. // load the collision data now
  4989. bool bSynchronous = bTouchAllData;
  4990. double t1 = Plat_FloatTime();
  4991. g_pMDLCache->GetVCollideEx( pModel->studio, bSynchronous );
  4992. double t2 = Plat_FloatTime();
  4993. if ( bSynchronous )
  4994. {
  4995. g_flAccumulatedModelLoadTimeVCollideSync += ( t2 - t1 );
  4996. }
  4997. else
  4998. {
  4999. g_flAccumulatedModelLoadTimeVCollideAsync += ( t2 - t1 );
  5000. }
  5001. }
  5002. // querying materials forces sync setup operations (materials/shaders) to build out now during load and not at runtime
  5003. double t1 = Plat_FloatTime();
  5004. IMaterial *pMaterials[128];
  5005. int nMaterials = g_pStudioRender->GetMaterialList( pStudioHdr, ARRAYSIZE( pMaterials ), &pMaterials[0] );
  5006. if ( nMaterials > 0 )
  5007. {
  5008. for ( int i = 0; i < nMaterials; i++ )
  5009. {
  5010. // Up the reference to all of this model's materials (decremented during UnloadModel)
  5011. // otherwise the post-load purge will discard materials whose meshes are not yet in the cache.
  5012. pMaterials[i]->IncrementReferenceCount();
  5013. }
  5014. // track the refcount bump
  5015. pModel->nLoadFlags |= FMODELLOADER_TOUCHED_MATERIALS;
  5016. }
  5017. double t2 = Plat_FloatTime();
  5018. g_flAccumulatedModelLoadTimeMaterialNamesOnly += ( t2 - t1 );
  5019. // a preloaded model must touch its children
  5020. if ( bTouchAllData || bPreLoaded )
  5021. {
  5022. Mod_TouchAllData( pModel, Host_GetServerCount() );
  5023. }
  5024. // track weapon models and their materials
  5025. int nMapIndex = m_WeaponModelCache.Find( pModel );
  5026. if ( nMapIndex != m_WeaponModelCache.InvalidIndex() )
  5027. {
  5028. if ( nMaterials > 0 && !m_WeaponModelCache[nMapIndex]->m_Materials.Count() )
  5029. {
  5030. m_WeaponModelCache[nMapIndex]->m_Materials.AddMultipleToTail( nMaterials );
  5031. for ( int i = 0; i < nMaterials; i++ )
  5032. {
  5033. m_WeaponModelCache[nMapIndex]->m_Materials[i] = pMaterials[i]->GetName();
  5034. }
  5035. }
  5036. // this is truly unfortunate, but the act of getting the material dependencies, built out all the model's resources
  5037. // evict now because the aggregate memory for the view models has been allocated elsewhere
  5038. EvictWeaponModel( nMapIndex, true );
  5039. }
  5040. }
  5041. //-----------------------------------------------------------------------------
  5042. // Purpose:
  5043. // Input : *mod -
  5044. //-----------------------------------------------------------------------------
  5045. void CModelLoader::Studio_UnloadModel( model_t *pModel )
  5046. {
  5047. if ( pModel->nLoadFlags & FMODELLOADER_TOUCHED_MATERIALS )
  5048. {
  5049. // remove the added reference to all of this model's materials
  5050. IMaterial *pMaterials[128];
  5051. int nMaterials = Mod_GetModelMaterials( pModel, ARRAYSIZE( pMaterials ), &pMaterials[0] );
  5052. for ( int j=0; j<nMaterials; j++ )
  5053. {
  5054. pMaterials[j]->DecrementReferenceCount();
  5055. }
  5056. pModel->nLoadFlags &= ~FMODELLOADER_TOUCHED_MATERIALS;
  5057. }
  5058. // leave these flags alone since we are going to return from alt-tab at some point.
  5059. // Assert( !( mod->needload & FMODELLOADER_REFERENCEMASK ) );
  5060. pModel->nLoadFlags &= ~( FMODELLOADER_LOADED | FMODELLOADER_LOADED_BY_PRELOAD );
  5061. if ( IsGameConsole() )
  5062. {
  5063. // 360 doesn't need to keep the reference flags, but the PC does
  5064. pModel->nLoadFlags &= ~FMODELLOADER_REFERENCEMASK;
  5065. }
  5066. #ifdef DBGFLAG_ASSERT
  5067. int nRef =
  5068. #endif
  5069. g_pMDLCache->Release( pModel->studio );
  5070. // the refcounts must be as expected, or evil latent bugs will occur
  5071. Assert( InEditMode() || ( nRef == 0 ) );
  5072. pModel->studio = MDLHANDLE_INVALID;
  5073. pModel->type = mod_bad;
  5074. }
  5075. //-----------------------------------------------------------------------------
  5076. // Purpose:
  5077. // Input : *mod -
  5078. //-----------------------------------------------------------------------------
  5079. void CModelLoader::SetWorldModel( model_t *mod )
  5080. {
  5081. Assert( mod );
  5082. m_pWorldModel = mod;
  5083. // host_state.SetWorldModel( mod ); // garymcthack
  5084. }
  5085. //-----------------------------------------------------------------------------
  5086. // Purpose:
  5087. //-----------------------------------------------------------------------------
  5088. void CModelLoader::ClearWorldModel( void )
  5089. {
  5090. m_pWorldModel = NULL;
  5091. m_InlineModels.Purge();
  5092. // zero out the world brush data and restore any embedded pointers
  5093. memset( &m_worldBrushData, 0, sizeof( m_worldBrushData ) );
  5094. m_worldBrushData.m_pLightingDataStack = &m_WorldLightingDataStack;
  5095. }
  5096. //-----------------------------------------------------------------------------
  5097. // Purpose:
  5098. // Output : Returns true on success, false on failure.
  5099. //-----------------------------------------------------------------------------
  5100. bool CModelLoader::IsWorldModelSet( void )
  5101. {
  5102. return m_pWorldModel ? true : false;
  5103. }
  5104. //-----------------------------------------------------------------------------
  5105. // Purpose:
  5106. // Output : int
  5107. //-----------------------------------------------------------------------------
  5108. int CModelLoader::GetNumWorldSubmodels( void )
  5109. {
  5110. if ( !IsWorldModelSet() )
  5111. return 0;
  5112. return m_worldBrushData.numsubmodels;
  5113. }
  5114. //-----------------------------------------------------------------------------
  5115. // Purpose: Check cache or union data for info, reload studio model if needed
  5116. // Input : *model -
  5117. //-----------------------------------------------------------------------------
  5118. void *CModelLoader::GetExtraData( model_t *model )
  5119. {
  5120. if ( !model )
  5121. {
  5122. return NULL;
  5123. }
  5124. switch ( model->type )
  5125. {
  5126. case mod_sprite:
  5127. {
  5128. // sprites don't use the real cache yet
  5129. if ( model->type == mod_sprite )
  5130. {
  5131. // The sprite got unloaded.
  5132. if ( !( FMODELLOADER_LOADED & model->nLoadFlags ) )
  5133. {
  5134. return NULL;
  5135. }
  5136. return model->sprite.sprite;
  5137. }
  5138. }
  5139. break;
  5140. case mod_studio:
  5141. return g_pMDLCache->GetStudioHdr( model->studio );
  5142. default:
  5143. case mod_brush:
  5144. // Should never happen
  5145. Assert( 0 );
  5146. break;
  5147. };
  5148. return NULL;
  5149. }
  5150. //-----------------------------------------------------------------------------
  5151. // Purpose:
  5152. // Output : Returns true on success, false on failure.
  5153. //-----------------------------------------------------------------------------
  5154. bool CModelLoader::Map_GetRenderInfoAllocated( void )
  5155. {
  5156. return m_bMapRenderInfoLoaded;
  5157. }
  5158. //-----------------------------------------------------------------------------
  5159. // Purpose:
  5160. //-----------------------------------------------------------------------------
  5161. void CModelLoader::Map_SetRenderInfoAllocated( bool allocated )
  5162. {
  5163. m_bMapRenderInfoLoaded = allocated;
  5164. }
  5165. //-----------------------------------------------------------------------------
  5166. // Purpose:
  5167. // Input : *mod -
  5168. //-----------------------------------------------------------------------------
  5169. void CModelLoader::Map_LoadDisplacements( model_t *pModel, bool bRestoring )
  5170. {
  5171. if ( !pModel )
  5172. {
  5173. Assert( false );
  5174. return;
  5175. }
  5176. V_FileBase( pModel->szPathName, m_szBaseName, sizeof( m_szBaseName ) );
  5177. CMapLoadHelper::Init( pModel, pModel->szPathName );
  5178. DispInfo_LoadDisplacements( pModel, bRestoring );
  5179. CMapLoadHelper::Shutdown();
  5180. }
  5181. //-----------------------------------------------------------------------------
  5182. // Purpose: List the model dictionary
  5183. //-----------------------------------------------------------------------------
  5184. void CModelLoader::Print( void )
  5185. {
  5186. ConMsg( "Models:\n" );
  5187. int c = m_Models.Count();
  5188. for ( int i = 0; i < c; i++ )
  5189. {
  5190. model_t *pModel = m_Models[i].modelpointer;
  5191. if ( pModel->type == mod_studio || pModel->type == mod_bad )
  5192. {
  5193. // studio models have ref counts
  5194. // bad models are unloaded models which need to be listed
  5195. int refCount = ( pModel->type == mod_studio ) ? g_pMDLCache->GetRef( pModel->studio ) : 0;
  5196. ConMsg( "%4d: Flags:0x%8.8x RefCount:%2d %s\n", i, pModel->nLoadFlags, refCount, pModel->szPathName );
  5197. }
  5198. else
  5199. {
  5200. ConMsg( "%4d: Flags:0x%8.8x %s\n", i, pModel->nLoadFlags, pModel->szPathName );
  5201. }
  5202. }
  5203. }
  5204. //-----------------------------------------------------------------------------
  5205. // Calls utility function to create .360 version of a file.
  5206. //-----------------------------------------------------------------------------
  5207. int CModelLoader::UpdateOrCreate( const char *pSourceName, char *pTargetName, int targetLen, bool bForce )
  5208. {
  5209. #if defined( _GAMECONSOLE )
  5210. return ::UpdateOrCreate( pSourceName, pTargetName, targetLen, NULL, NULL, bForce );
  5211. #else
  5212. return UOC_NOT_CREATED;
  5213. #endif
  5214. }
  5215. //-----------------------------------------------------------------------------
  5216. // Purpose: Determine if specified .bsp is valid
  5217. // Input : *mapname -
  5218. // Output : Returns true on success, false on failure.
  5219. //-----------------------------------------------------------------------------
  5220. bool CModelLoader::Map_IsValid( char const *pBaseMapName, bool bQuiet /* = false */ )
  5221. {
  5222. static char s_szBaseMapName[MAX_PATH];
  5223. if ( !pBaseMapName || !pBaseMapName[0] )
  5224. {
  5225. if ( !bQuiet )
  5226. {
  5227. ConMsg( "CModelLoader::Map_IsValid: Empty mapname!!!\n" );
  5228. }
  5229. return false;
  5230. }
  5231. if ( ( IsGameConsole() || sv.IsDedicated() ) && !V_stricmp( pBaseMapName, s_szBaseMapName ) )
  5232. {
  5233. // already been checked, no reason to do multiple i/o validations
  5234. return true;
  5235. }
  5236. FileHandle_t mapfile;
  5237. char mapname[MAX_PATH];
  5238. V_snprintf( mapname, sizeof( mapname ), "maps/%s.bsp", pBaseMapName );
  5239. V_FixSlashes( mapname );
  5240. if ( IsGameConsole() )
  5241. {
  5242. char szMapName360[MAX_PATH];
  5243. UpdateOrCreate( mapname, szMapName360, sizeof( szMapName360 ), false );
  5244. Q_strncpy( mapname, szMapName360, sizeof( mapname ) );
  5245. }
  5246. mapfile = g_pFileSystem->OpenEx( mapname, "rb", IsGameConsole() ? FSOPEN_NEVERINPACK : 0, "GAME" );
  5247. if ( mapfile != FILESYSTEM_INVALID_HANDLE )
  5248. {
  5249. BSPHeader_t header;
  5250. memset( &header, 0, sizeof( header ) );
  5251. g_pFileSystem->Read( &header, sizeof( BSPHeader_t ), mapfile );
  5252. g_pFileSystem->Close( mapfile );
  5253. if ( header.ident == IDBSPHEADER )
  5254. {
  5255. if ( header.m_nVersion >= MINBSPVERSION && header.m_nVersion <= BSPVERSION )
  5256. {
  5257. V_strncpy( s_szBaseMapName, pBaseMapName, sizeof( s_szBaseMapName ) );
  5258. return true;
  5259. }
  5260. else
  5261. {
  5262. if ( !bQuiet )
  5263. {
  5264. Warning( "CModelLoader::Map_IsValid: Map '%s' bsp version %i, expecting %i\n", mapname, header.m_nVersion, BSPVERSION );
  5265. }
  5266. }
  5267. }
  5268. else
  5269. {
  5270. if ( !bQuiet )
  5271. {
  5272. Warning( "CModelLoader::Map_IsValid: '%s' is not a valid BSP file\n", mapname );
  5273. }
  5274. }
  5275. }
  5276. else
  5277. {
  5278. if ( !bQuiet )
  5279. {
  5280. Warning( "CModelLoader::Map_IsValid: No such map '%s'\n", mapname );
  5281. }
  5282. }
  5283. // Get outta here if we are checking vidmemstats.
  5284. if ( CommandLine()->CheckParm( "-dumpvidmemstats" ) )
  5285. {
  5286. Cbuf_AddText( Cbuf_GetCurrentPlayer(), "quit\n" );
  5287. }
  5288. return false;
  5289. }
  5290. model_t *CModelLoader::FindModelNoCreate( const char *pModelName )
  5291. {
  5292. FileNameHandle_t fnHandle = g_pFileSystem->FindOrAddFileName( pModelName );
  5293. int i = m_Models.Find( fnHandle );
  5294. if ( i != m_Models.InvalidIndex() )
  5295. {
  5296. return m_Models[i].modelpointer;
  5297. }
  5298. // not found
  5299. return NULL;
  5300. }
  5301. modtype_t CModelLoader::GetTypeFromName( const char *pModelName )
  5302. {
  5303. // HACK HACK, force sprites to correctly
  5304. const char *pExt = V_GetFileExtension( pModelName );
  5305. if ( pExt )
  5306. {
  5307. if ( !V_stricmp( pExt, "spr" ) || !V_stricmp( pExt, "vmt" ) || !V_stricmp( pExt, "avi" ) || !V_stricmp( pExt, "bik" ) )
  5308. {
  5309. return mod_sprite;
  5310. }
  5311. else if ( !V_stricmp( pExt, "bsp" ) )
  5312. {
  5313. return mod_brush;
  5314. }
  5315. else if ( !V_stricmp( pExt, "mdl" ) )
  5316. {
  5317. return mod_studio;
  5318. }
  5319. }
  5320. return mod_bad;
  5321. }
  5322. int CModelLoader::FindNext( int iIndex, model_t **ppModel )
  5323. {
  5324. if ( iIndex == -1 && m_Models.Count() )
  5325. {
  5326. iIndex = m_Models.FirstInorder();
  5327. }
  5328. else if ( !m_Models.Count() || !m_Models.IsValidIndex( iIndex ) )
  5329. {
  5330. *ppModel = NULL;
  5331. return -1;
  5332. }
  5333. *ppModel = m_Models[iIndex].modelpointer;
  5334. iIndex = m_Models.NextInorder( iIndex );
  5335. if ( iIndex == m_Models.InvalidIndex() )
  5336. {
  5337. // end of list
  5338. iIndex = -1;
  5339. }
  5340. return iIndex;
  5341. }
  5342. void CModelLoader::UnloadModel( model_t *pModel )
  5343. {
  5344. switch ( pModel->type )
  5345. {
  5346. case mod_brush:
  5347. // Let it free data and call destructors.
  5348. Map_UnloadModel( pModel );
  5349. // Re-write .bsp if networkstringtable dictionary was updated.
  5350. g_pStringTableDictionary->OnBSPFullyUnloaded();
  5351. // Remove from file system
  5352. char szNameOnDisk[MAX_PATH];
  5353. GetMapPathNameOnDisk( szNameOnDisk, pModel->szPathName, sizeof( szNameOnDisk ) );
  5354. g_pFileSystem->RemoveSearchPath( szNameOnDisk, "GAME" );
  5355. break;
  5356. case mod_studio:
  5357. Studio_UnloadModel( pModel );
  5358. break;
  5359. case mod_sprite:
  5360. Sprite_UnloadModel( pModel );
  5361. break;
  5362. }
  5363. if ( pModel->m_pKeyValues )
  5364. {
  5365. pModel->m_pKeyValues->deleteThis();
  5366. pModel->m_pKeyValues = NULL;
  5367. }
  5368. }
  5369. bool CModelLoader::IsModelInWeaponCache( const model_t *pModel )
  5370. {
  5371. #if !defined( DEDICATED )
  5372. if ( m_bAllowWeaponModelCache )
  5373. {
  5374. int nMapIndex = m_WeaponModelCache.Find( (model_t*)pModel );
  5375. return ( nMapIndex != g_ModelLoader.m_WeaponModelCache.InvalidIndex() );
  5376. }
  5377. #endif
  5378. return false;
  5379. }
  5380. bool CModelLoader::IsViewWeaponModelResident( const model_t *pModel )
  5381. {
  5382. #if !defined( DEDICATED )
  5383. if ( m_bAllowWeaponModelCache && ( pModel->flags & MODELFLAG_VIEW_WEAPON_MODEL ) )
  5384. {
  5385. // only view weapon models might have their verts evicted
  5386. int nMapIndex = m_WeaponModelCache.Find( (model_t*)pModel );
  5387. if ( nMapIndex != g_ModelLoader.m_WeaponModelCache.InvalidIndex() )
  5388. {
  5389. return g_ModelLoader.m_WeaponModelCache[nMapIndex]->m_bStudioHWDataResident;
  5390. }
  5391. }
  5392. #endif
  5393. return true;
  5394. }
  5395. void CModelLoader::EvictAllWeaponsFromModelCache( bool bLoadingComplete )
  5396. {
  5397. #if !defined( DEDICATED )
  5398. if ( !m_bAllowWeaponModelCache )
  5399. return;
  5400. // for reliability, ensure the studiohwdata presence is accurate
  5401. // only need to do this once as the baseline eviction state is established
  5402. for ( int i = m_WeaponModelCache.FirstInorder(); i != m_WeaponModelCache.InvalidIndex(); i = m_WeaponModelCache.NextInorder( i ) )
  5403. {
  5404. if ( !m_WeaponModelCache[i]->m_bViewModel )
  5405. continue;
  5406. m_WeaponModelCache[i]->m_bStudioHWDataResident = g_pMDLCache->IsDataLoaded( m_WeaponModelCache.Key( i )->studio, MDLCACHE_STUDIOHWDATA );
  5407. }
  5408. // forcefully evict all the known weapon models
  5409. for ( int i = m_WeaponModelCache.FirstInorder(); i != m_WeaponModelCache.InvalidIndex(); i = m_WeaponModelCache.NextInorder( i ) )
  5410. {
  5411. EvictWeaponModel( i, true );
  5412. }
  5413. g_pMaterialSystem->ClearForceExcludes();
  5414. if ( bLoadingComplete && !m_bAllowWorldWeaponEviction )
  5415. {
  5416. // loading is complete, put all the world weapons back
  5417. for ( int i = m_WeaponModelCache.FirstInorder(); i != m_WeaponModelCache.InvalidIndex(); i = m_WeaponModelCache.NextInorder( i ) )
  5418. {
  5419. if ( m_WeaponModelCache[i]->m_bViewModel )
  5420. {
  5421. // ignore non-desired model type
  5422. continue;
  5423. }
  5424. // ensure world model is restored
  5425. RestoreWeaponModel( i );
  5426. }
  5427. }
  5428. #endif
  5429. }
  5430. void CModelLoader::UpdateViewWeaponModelCache( const char **ppWeaponModels, int nWeaponModels )
  5431. {
  5432. #if !defined( DEDICATED )
  5433. if ( !m_bAllowWeaponModelCache )
  5434. return;
  5435. // age any orphaned view models
  5436. // owned view models will have their age reset below
  5437. int nNumResident = 0;
  5438. m_nNumWeaponsPartialResident = 0;
  5439. for ( int i = m_WeaponModelCache.FirstInorder(); i != m_WeaponModelCache.InvalidIndex(); i = m_WeaponModelCache.NextInorder( i ) )
  5440. {
  5441. if ( !m_WeaponModelCache[i]->m_bViewModel )
  5442. continue;
  5443. if ( m_WeaponModelCache[i]->m_nAgeTime == UINT_MAX )
  5444. {
  5445. // one time age tag
  5446. // the oldest will be the smallest and identify a LRU (i.e. thrown out from the player's inventory)
  5447. m_WeaponModelCache[i]->m_nAgeTime = Plat_MSTime();
  5448. }
  5449. if ( m_WeaponModelCache[i]->m_nAgeTime )
  5450. {
  5451. nNumResident++;
  5452. }
  5453. else if ( m_WeaponModelCache[i]->m_bStudioHWDataResident )
  5454. {
  5455. // not resident, but hw mesh is and it should not be
  5456. // retry eviction after any weapons have been restored
  5457. m_nNumWeaponsPartialResident++;
  5458. }
  5459. }
  5460. for ( int i = 0; i < nWeaponModels; i++ )
  5461. {
  5462. // resolve from name to model
  5463. model_t* pModel = FindModelNoCreate( ppWeaponModels[i] );
  5464. if ( !pModel )
  5465. continue;
  5466. // find view model
  5467. int nIndex = m_WeaponModelCache.Find( pModel );
  5468. if ( nIndex != m_WeaponModelCache.InvalidIndex() )
  5469. {
  5470. if ( !m_WeaponModelCache[nIndex]->m_bViewModel )
  5471. continue;
  5472. RestoreWeaponModel( nIndex );
  5473. // LRU bump any known player inventory view models
  5474. // all view model's currently within primary player's inventory do not age AT ALL
  5475. // marked uniquely to not be subject to any LRU eviction
  5476. // this ensures that weapon switching avoids any restore latency
  5477. m_WeaponModelCache[nIndex]->m_nAgeTime = UINT_MAX;
  5478. }
  5479. }
  5480. // drive the view model cache down to its desired size
  5481. while ( mod_weaponviewmodelcache.GetInt() && nNumResident > mod_weaponviewmodelcache.GetInt() )
  5482. {
  5483. // find suitable candidates for discard
  5484. // will NOT consider view model's in the primary inventory (i.e at UINT_MAX)
  5485. unsigned int nOldestTime = UINT_MAX;
  5486. int nOldestIndex = m_WeaponModelCache.InvalidIndex();
  5487. for ( int i = m_WeaponModelCache.FirstInorder(); i != m_WeaponModelCache.InvalidIndex(); i = m_WeaponModelCache.NextInorder( i ) )
  5488. {
  5489. if ( m_WeaponModelCache[i]->m_bViewModel && m_WeaponModelCache[i]->m_nAgeTime && nOldestTime > m_WeaponModelCache[i]->m_nAgeTime )
  5490. {
  5491. nOldestIndex = i;
  5492. nOldestTime = m_WeaponModelCache[i]->m_nAgeTime;
  5493. }
  5494. }
  5495. if ( nOldestIndex == m_WeaponModelCache.InvalidIndex() )
  5496. {
  5497. // there was no suitable oldest candidate this frame
  5498. break;
  5499. }
  5500. EvictWeaponModel( nOldestIndex, false );
  5501. nNumResident--;
  5502. }
  5503. #endif
  5504. }
  5505. void CModelLoader::TouchWorldWeaponModelCache( const char **ppWeaponModels, int nWeaponModels )
  5506. {
  5507. #if !defined( DEDICATED )
  5508. if ( !m_bAllowWeaponModelCache || !m_bAllowWorldWeaponEviction )
  5509. return;
  5510. // touch the world weapons
  5511. unsigned int nCurrentTime = Plat_MSTime();
  5512. for ( int i = 0; i < nWeaponModels; i++ )
  5513. {
  5514. // resolve from name to model
  5515. model_t* pModel = FindModelNoCreate( ppWeaponModels[i] );
  5516. if ( !pModel )
  5517. continue;
  5518. // touch world model
  5519. int nIndex = m_WeaponModelCache.Find( pModel );
  5520. if ( nIndex != m_WeaponModelCache.InvalidIndex() )
  5521. {
  5522. if ( m_WeaponModelCache[nIndex]->m_bViewModel )
  5523. continue;
  5524. RestoreWeaponModel( nIndex );
  5525. m_WeaponModelCache[nIndex]->m_nAgeTime = nCurrentTime;
  5526. }
  5527. }
  5528. int nNumResident = 0;
  5529. for ( int i = m_WeaponModelCache.FirstInorder(); i != m_WeaponModelCache.InvalidIndex(); i = m_WeaponModelCache.NextInorder( i ) )
  5530. {
  5531. if ( m_WeaponModelCache[i]->m_bViewModel )
  5532. continue;
  5533. if ( m_WeaponModelCache[i]->m_nAgeTime )
  5534. {
  5535. nNumResident++;
  5536. }
  5537. }
  5538. // drive the world model cache down to its desired size
  5539. while ( mod_weaponworldmodelcache.GetInt() && nNumResident > mod_weaponworldmodelcache.GetInt() )
  5540. {
  5541. // find suitable candidates for discard
  5542. unsigned int nOldestTime = UINT_MAX;
  5543. int nOldestIndex = m_WeaponModelCache.InvalidIndex();
  5544. for ( int i = m_WeaponModelCache.FirstInorder(); i != m_WeaponModelCache.InvalidIndex(); i = m_WeaponModelCache.NextInorder( i ) )
  5545. {
  5546. if ( m_WeaponModelCache[i]->m_bViewModel )
  5547. continue;
  5548. if ( m_WeaponModelCache[i]->m_nAgeTime && nOldestTime > m_WeaponModelCache[i]->m_nAgeTime )
  5549. {
  5550. // prevent world model thrashing by only considering for purging aged world models beyond a minimum age
  5551. if ( nCurrentTime - m_WeaponModelCache[i]->m_nAgeTime >= (unsigned int)mod_weaponworldmodelminage.GetInt() )
  5552. {
  5553. nOldestIndex = i;
  5554. nOldestTime = m_WeaponModelCache[i]->m_nAgeTime;
  5555. }
  5556. }
  5557. }
  5558. if ( nOldestIndex == m_WeaponModelCache.InvalidIndex() )
  5559. {
  5560. // there was no suitable oldest candidate this frame
  5561. break;
  5562. }
  5563. EvictWeaponModel( nOldestIndex, false );
  5564. nNumResident--;
  5565. }
  5566. #endif
  5567. }
  5568. void CModelLoader::EvictWeaponModel( int nCacheIndex, bool bForce )
  5569. {
  5570. #if !defined( DEDICATED )
  5571. if ( !m_bAllowWeaponModelCache || !m_WeaponModelCache.IsValidIndex( nCacheIndex ) )
  5572. return;
  5573. if ( !bForce && !m_WeaponModelCache[nCacheIndex]->m_nAgeTime )
  5574. {
  5575. // already evicted
  5576. return;
  5577. }
  5578. if ( m_WeaponModelCache.Key( nCacheIndex )->studio == MDLHANDLE_INVALID )
  5579. {
  5580. // this model was explicitly unloaded
  5581. // ignore this model until the model loader restores it
  5582. return;
  5583. }
  5584. DevMsg( "Evicting: %s\n", m_WeaponModelCache.Key( nCacheIndex )->szPathName );
  5585. for ( int i = 0; i < m_WeaponModelCache[nCacheIndex]->m_Materials.Count(); i++ )
  5586. {
  5587. IMaterial *pMaterial = materials->FindMaterial( m_WeaponModelCache[nCacheIndex]->m_Materials[i].Get(), TEXTURE_GROUP_OTHER, false );
  5588. if ( !pMaterial || pMaterial->IsErrorMaterial() )
  5589. {
  5590. // cannot evict the material that should have been there
  5591. AssertMsg( false, CFmtStr( "EvictWeaponModel( %s ): Could not find expected valid material %s\n", m_WeaponModelCache.Key( nCacheIndex )->szPathName, m_WeaponModelCache[nCacheIndex]->m_Materials[i].Get() ).Access() );
  5592. continue;
  5593. }
  5594. // force material to temporarily consume less memory
  5595. // force worldmodels and viewmodels to be very small versions
  5596. // as viewmodels are restored they will render temporarily with their small versions, but the pop is not noticeable
  5597. pMaterial->SetTempExcluded( true, 16 );
  5598. }
  5599. // mark as evicted, the actual mesh eviction is deferred until frame boundary in ProcessWeaponModelCacheOperations()
  5600. m_WeaponModelCache[nCacheIndex]->m_nAgeTime = 0;
  5601. if ( bForce )
  5602. {
  5603. // THIS IS NOT FOR GENERAL-PURPOSE ANYTIME USE - IT WILL DESTABILIZE THE CONSOLES
  5604. // Force the model processing operations to do the evict (which can only be done when rendering is stopped (i.e. at least no queued model draws) ).
  5605. m_nNumWeaponsPartialResident = 1;
  5606. ProcessWeaponModelCacheOperations();
  5607. }
  5608. #endif
  5609. }
  5610. void CModelLoader::RestoreWeaponModel( int nCacheIndex )
  5611. {
  5612. #if !defined( DEDICATED )
  5613. if ( !m_bAllowWeaponModelCache || !m_WeaponModelCache.IsValidIndex( nCacheIndex ) )
  5614. return;
  5615. if ( m_WeaponModelCache[nCacheIndex]->m_nAgeTime )
  5616. {
  5617. // already restored
  5618. return;
  5619. }
  5620. if ( m_WeaponModelCache.Key( nCacheIndex )->studio == MDLHANDLE_INVALID )
  5621. {
  5622. // this model was explicitly unloaded
  5623. // ignore this model until the model loader restores it
  5624. return;
  5625. }
  5626. DevMsg( "Restoring: %s\n", m_WeaponModelCache.Key( nCacheIndex )->szPathName );
  5627. for ( int i = 0; i < m_WeaponModelCache[nCacheIndex]->m_Materials.Count(); i++ )
  5628. {
  5629. IMaterial *pMaterial = materials->FindMaterial( m_WeaponModelCache[nCacheIndex]->m_Materials[i].Get(), TEXTURE_GROUP_OTHER, false );
  5630. if ( !pMaterial || pMaterial->IsErrorMaterial() )
  5631. {
  5632. // cannot restore the material that should have been there
  5633. AssertMsg( false, CFmtStr( "RestoreWeaponModel( %s ): Could not find expected valid material %s\n", m_WeaponModelCache.Key( nCacheIndex )->szPathName, m_WeaponModelCache[nCacheIndex]->m_Materials[i].Get() ).Access() );
  5634. continue;
  5635. }
  5636. // force material back to its original memory state
  5637. pMaterial->SetTempExcluded( false );
  5638. }
  5639. // only view model weapons could have had their vertexes evicted
  5640. if ( m_bAllowWeaponVertexEviction && m_WeaponModelCache[nCacheIndex]->m_bViewModel && !m_WeaponModelCache[nCacheIndex]->m_bStudioHWDataResident )
  5641. {
  5642. g_pMDLCache->RestoreHardwareData( m_WeaponModelCache.Key( nCacheIndex )->studio, &m_WeaponModelCache[nCacheIndex]->m_hAsyncVTXControl, &m_WeaponModelCache[nCacheIndex]->m_hAsyncVVDControl );
  5643. }
  5644. // mark as restored
  5645. m_WeaponModelCache[nCacheIndex]->m_nAgeTime = Plat_MSTime();
  5646. #endif
  5647. }
  5648. bool CModelLoader::ProcessWeaponModelCacheOperations()
  5649. {
  5650. bool bMeshedRestored = false;
  5651. #if !defined( DEDICATED )
  5652. if ( !ThreadInMainThread() )
  5653. {
  5654. // must be on main thread to do any of the mesh eviction/restore
  5655. return false;
  5656. }
  5657. if ( m_bAllowWeaponVertexEviction )
  5658. {
  5659. // restore all the pending meshes
  5660. bMeshedRestored |= g_pMDLCache->ProcessPendingHardwareRestore();
  5661. // avoid doing any work unless pre-qualified by UpdateViewModelCache() that evictions need to occur
  5662. if ( m_nNumWeaponsPartialResident )
  5663. {
  5664. // It's possible that the hw mesh eviction could not occur due to frame lock or
  5665. // it was accessed by code that was unaware of this violation and restored it.
  5666. for ( int i = m_WeaponModelCache.FirstInorder(); i != m_WeaponModelCache.InvalidIndex(); i = m_WeaponModelCache.NextInorder( i ) )
  5667. {
  5668. if ( !m_WeaponModelCache[i]->m_bViewModel )
  5669. continue;
  5670. if ( !m_WeaponModelCache[i]->m_nAgeTime && m_WeaponModelCache[i]->m_bStudioHWDataResident )
  5671. {
  5672. // expect model to be evicted but hw mesh is still resident and it should not be
  5673. DevMsg( "*** Evicting: %s\n", m_WeaponModelCache.Key( i )->szPathName );
  5674. // ensure any outstanding prior async hwdata restore is complete
  5675. // cannot have an outstanding async restore operation in-flight while trying to evict
  5676. if ( m_WeaponModelCache[i]->m_hAsyncVTXControl || m_WeaponModelCache[i]->m_hAsyncVVDControl )
  5677. {
  5678. // do the sync finish in sequence to ensure the io callback completes
  5679. // if the async operation have already completed, these will do nothing as expected
  5680. g_pFullFileSystem->AsyncFinish( m_WeaponModelCache[i]->m_hAsyncVTXControl, true );
  5681. g_pFullFileSystem->AsyncFinish( m_WeaponModelCache[i]->m_hAsyncVVDControl, true );
  5682. if ( m_WeaponModelCache[i]->m_hAsyncVTXControl )
  5683. {
  5684. // further safety to ensure the async i/o operation completed as requested
  5685. FSAsyncStatus_t status = g_pFullFileSystem->AsyncStatus( m_WeaponModelCache[i]->m_hAsyncVTXControl );
  5686. if ( status != FSASYNC_STATUS_PENDING && status != FSASYNC_STATUS_INPROGRESS && status != FSASYNC_STATUS_UNSERVICED )
  5687. {
  5688. // operation was completed
  5689. // release the handle to avoid leak
  5690. g_pFullFileSystem->AsyncRelease( m_WeaponModelCache[i]->m_hAsyncVTXControl );
  5691. m_WeaponModelCache[i]->m_hAsyncVTXControl = NULL;
  5692. }
  5693. }
  5694. if ( m_WeaponModelCache[i]->m_hAsyncVVDControl )
  5695. {
  5696. // further safety to ensure the async i/o operation completed as requested
  5697. FSAsyncStatus_t status = g_pFullFileSystem->AsyncStatus( m_WeaponModelCache[i]->m_hAsyncVVDControl );
  5698. if ( status != FSASYNC_STATUS_PENDING && status != FSASYNC_STATUS_INPROGRESS && status != FSASYNC_STATUS_UNSERVICED )
  5699. {
  5700. // operation was completed
  5701. // release the handle to avoid leak
  5702. g_pFullFileSystem->AsyncRelease( m_WeaponModelCache[i]->m_hAsyncVVDControl );
  5703. m_WeaponModelCache[i]->m_hAsyncVVDControl = NULL;
  5704. }
  5705. }
  5706. }
  5707. if ( !m_WeaponModelCache[i]->m_hAsyncVTXControl && !m_WeaponModelCache[i]->m_hAsyncVVDControl )
  5708. {
  5709. // This is unfortunate and ideally is a no-op. The AsyncFinish() only finished the i/o and not the actual mesh restore.
  5710. // The mesh must be restored for the evict logic to work properly. ALL the pending restores must be drained to ensure this
  5711. // possible mesh (whose i/o was just forced to complete) performs its build out. In practice this list should be nominally
  5712. // empty or one, and not really more than that.
  5713. bMeshedRestored |= g_pMDLCache->ProcessPendingHardwareRestore();
  5714. // only an evicted view model can flush all their vertexes because we don't expect it to render or be queried
  5715. // the expected contract is that an evicted viewmodel won't be rendering anytime soon
  5716. // it's possible that the eviction does not occur due to frame locks or some other preventing condition
  5717. // the next UpdateViewWeaponModelCache() will catch these for another retry
  5718. g_pMDLCache->Flush( m_WeaponModelCache.Key( i )->studio, MDLCACHE_FLUSH_STUDIOHWDATA|MDLCACHE_FLUSH_ANIMBLOCK|MDLCACHE_FLUSH_VIRTUALMODEL|MDLCACHE_FLUSH_AUTOPLAY|MDLCACHE_FLUSH_VERTEXES );
  5719. }
  5720. }
  5721. }
  5722. // assume all evicted, the UpdateViewModelCache() will update
  5723. m_nNumWeaponsPartialResident = 0;
  5724. }
  5725. }
  5726. #endif
  5727. return bMeshedRestored;
  5728. }
  5729. void CModelLoader::DumpWeaponModelCache( bool bViewModelsOnly )
  5730. {
  5731. #if !defined( DEDICATED )
  5732. if ( !m_bAllowWeaponModelCache )
  5733. return;
  5734. Color defaultColor( 0, 0, 0, 255 );
  5735. Color anomalyColor( 255, 0, 0, 255 );
  5736. Msg( "Weapon %s Model Cache:\n", bViewModelsOnly ? "View" : "World" );
  5737. Msg( "------------------------\n" );
  5738. for ( int nPass = 0; nPass < 2; nPass++ )
  5739. {
  5740. if ( !nPass )
  5741. {
  5742. Con_ColorPrintf( Color( 0, 255, 255, 255 ), "\nEvicted:--------------------------------\n" );
  5743. }
  5744. else
  5745. {
  5746. Con_ColorPrintf( Color( 0, 255, 255, 255 ), "\nResident:-------------------------------\n" );
  5747. }
  5748. for ( int i = m_WeaponModelCache.FirstInorder(); i != m_WeaponModelCache.InvalidIndex(); i = m_WeaponModelCache.NextInorder( i ) )
  5749. {
  5750. if ( bViewModelsOnly != m_WeaponModelCache[i]->m_bViewModel )
  5751. {
  5752. // ignore non-desired model type
  5753. continue;
  5754. }
  5755. if ( !nPass && m_WeaponModelCache[i]->m_nAgeTime )
  5756. {
  5757. // ignore resident
  5758. continue;
  5759. }
  5760. else if ( nPass && !m_WeaponModelCache[i]->m_nAgeTime )
  5761. {
  5762. // ignore evicted
  5763. continue;
  5764. }
  5765. Color color = defaultColor;
  5766. if ( bViewModelsOnly && !m_WeaponModelCache[i]->m_nAgeTime && m_WeaponModelCache[i]->m_bStudioHWDataResident )
  5767. {
  5768. color = anomalyColor;
  5769. }
  5770. Con_ColorPrintf( color, "\nModel: %s\n", m_WeaponModelCache.Key( i )->szPathName );
  5771. Con_ColorPrintf( defaultColor, " Age: %u\n", m_WeaponModelCache[i]->m_nAgeTime );
  5772. if ( bViewModelsOnly )
  5773. {
  5774. Con_ColorPrintf( defaultColor, " %sStudioHWData\n", m_WeaponModelCache[i]->m_bStudioHWDataResident ? "+" : "-");
  5775. }
  5776. for ( int j = 0; j < m_WeaponModelCache[i]->m_Materials.Count(); j++ )
  5777. {
  5778. IMaterial *pMaterial = materials->FindMaterial( m_WeaponModelCache[i]->m_Materials[j].Get(), TEXTURE_GROUP_OTHER, false );
  5779. Con_ColorPrintf( defaultColor, " Material: %s (Ref: %d)\n", m_WeaponModelCache[i]->m_Materials[j].Get(), pMaterial->GetReferenceCount() );
  5780. int nMatParamCount = pMaterial->ShaderParamCount();
  5781. IMaterialVar **ppMatVars = pMaterial->GetShaderParams();
  5782. for ( int nParam = 0; nParam < nMatParamCount; nParam++ )
  5783. {
  5784. IMaterialVar *pVar = ppMatVars[nParam];
  5785. if ( !pVar ||
  5786. pVar->GetType() != MATERIAL_VAR_TYPE_TEXTURE ||
  5787. pVar->IsTextureValueInternalEnvCubemap() )
  5788. {
  5789. // not possible to temp exclude these, so not interested
  5790. continue;
  5791. }
  5792. ITexture *pTex = pVar->GetTextureValue();
  5793. if ( !pTex )
  5794. {
  5795. // not possible to temp exclude these, so not interested
  5796. continue;
  5797. }
  5798. Con_ColorPrintf(
  5799. pTex->CanBeTempExcluded() ? Color( 0, 0, 0, 255 ) : Color( 255, 255, 0, 255 ),
  5800. " %sTexture: %s (Ref: %d)\n",
  5801. pTex->IsTempExcluded() ? "-" : "+",
  5802. pTex->GetName(),
  5803. pTex->GetReferenceCount() );
  5804. }
  5805. }
  5806. }
  5807. }
  5808. #endif
  5809. }
  5810. model_t *CModelLoader::GetDynamicModel( const char *name, bool bClientOnly )
  5811. {
  5812. model_t *pModel = FindModel( name );
  5813. Assert( pModel );
  5814. CDynamicModelInfo &dyn = m_DynamicModels[ m_DynamicModels.Insert( pModel ) ]; // Insert returns existing if key is already set
  5815. if ( dyn.m_nLoadFlags == CDynamicModelInfo::INVALIDFLAG )
  5816. {
  5817. dyn.m_nLoadFlags = 0;
  5818. DynamicModelDebugMsg( "model %p [%s] registered\n", pModel, pModel->szPathName );
  5819. }
  5820. dyn.m_uLastTouchedMS_Div256 = Plat_MSTime() >> 8;
  5821. return pModel;
  5822. }
  5823. model_t *CModelLoader::GetDynamicCombinedModel( const char *name, bool bClientOnly )
  5824. {
  5825. model_t *pModel = FindModel( name );
  5826. Assert( pModel );
  5827. CDynamicModelInfo &dyn = m_DynamicModels[ m_DynamicModels.Insert( pModel ) ]; // Insert returns existing if key is already set
  5828. if ( dyn.m_nLoadFlags == CDynamicModelInfo::INVALIDFLAG )
  5829. {
  5830. dyn.m_nLoadFlags = CDynamicModelInfo::COMBINED | CDynamicModelInfo::READY;
  5831. pModel->type = mod_studio;
  5832. pModel->nLoadFlags = FMODELLOADER_COMBINED | ( bClientOnly ? FMODELLOADER_DYNCLIENT : FMODELLOADER_DYNSERVER );
  5833. dyn.m_iRefCount = 1;
  5834. dyn.m_iClientRefCount = ( bClientOnly ? 1 : 0 );
  5835. DynamicModelDebugMsg( "model %p [%s] registered\n", pModel, pModel->szPathName );
  5836. }
  5837. else
  5838. {
  5839. pModel->nLoadFlags |= ( bClientOnly ? FMODELLOADER_DYNCLIENT : FMODELLOADER_DYNSERVER );
  5840. }
  5841. dyn.m_uLastTouchedMS_Div256 = Plat_MSTime() >> 8;
  5842. return pModel;
  5843. }
  5844. void CModelLoader::UpdateDynamicCombinedModel( model_t *pModel, MDLHandle_t Handle, bool bClientSide )
  5845. {
  5846. if ( pModel->studio != MDLHANDLE_INVALID && pModel->studio != Handle )
  5847. {
  5848. UnloadModel( pModel );
  5849. }
  5850. pModel->studio = Handle;
  5851. pModel->type = mod_studio;
  5852. pModel->nLoadFlags = IModelLoader::FMODELLOADER_LOADED | FMODELLOADER_COMBINED | ( bClientSide ? FMODELLOADER_DYNCLIENT : FMODELLOADER_DYNSERVER );
  5853. pModel->nServerCount = Host_GetServerCount();
  5854. g_pMDLCache->SetUserData( pModel->studio, pModel );
  5855. }
  5856. bool CModelLoader::SetCombineModels( model_t *pModel, const CUtlVector< SCombinerModelInput_t > &vecModelsToCombine )
  5857. {
  5858. if ( !pModel )
  5859. return false;
  5860. return g_pMDLCache->SetCombineModels( pModel->studio, vecModelsToCombine );
  5861. }
  5862. bool CModelLoader::FinishCombinedModel( model_t *pModel, CombinedModelLoadedCallback pFunc, void *pUserData )
  5863. {
  5864. return g_pMDLCache->FinishCombinedModel( pModel->studio, pFunc, pUserData );
  5865. }
  5866. void CModelLoader::UpdateDynamicModelLoadQueue()
  5867. {
  5868. if ( mod_dynamicloadpause.GetBool() )
  5869. return;
  5870. static double s_LastDynamicLoadTime = 0.0;
  5871. if ( mod_dynamicloadthrottle.GetFloat() > 0 && Plat_FloatTime() < s_LastDynamicLoadTime + mod_dynamicloadthrottle.GetFloat() )
  5872. return;
  5873. if ( m_bDynamicLoadQueueHeadActive )
  5874. {
  5875. Assert( m_DynamicModelLoadQueue.Count() >= 1 );
  5876. MaterialLock_t matLock = g_pMaterialSystem->Lock(); // ASDFADFASFASEGAafliejsfjaslaslgsaigas
  5877. bool bComplete = true;
  5878. //bool bComplete = g_pQueuedLoader->CompleteDynamicLoad();
  5879. g_pMaterialSystem->Unlock(matLock);
  5880. if ( bComplete )
  5881. {
  5882. model_t *pModel = m_DynamicModelLoadQueue[0];
  5883. m_DynamicModelLoadQueue.Remove(0);
  5884. m_bDynamicLoadQueueHeadActive = false;
  5885. Assert( pModel->nLoadFlags & FMODELLOADER_DYNAMIC );
  5886. Assert( pModel->type == mod_bad || ( pModel->nLoadFlags & (FMODELLOADER_LOADED | FMODELLOADER_LOADED_BY_PRELOAD) ) );
  5887. (void) LoadModel( pModel, NULL );
  5888. Assert( pModel->type == mod_studio );
  5889. UtlHashHandle_t hDyn = m_DynamicModels.Find( pModel );
  5890. Assert( hDyn != m_DynamicModels.InvalidHandle() );
  5891. if ( hDyn != m_DynamicModels.InvalidHandle() )
  5892. {
  5893. CDynamicModelInfo &dyn = m_DynamicModels[hDyn];
  5894. Assert( dyn.m_nLoadFlags & CDynamicModelInfo::QUEUED );
  5895. Assert( dyn.m_nLoadFlags & CDynamicModelInfo::LOADING );
  5896. dyn.m_nLoadFlags &= ~( CDynamicModelInfo::QUEUED | CDynamicModelInfo::LOADING );
  5897. g_pMDLCache->LockStudioHdr( pModel->studio );
  5898. dyn.m_nLoadFlags |= CDynamicModelInfo::LOCKED;
  5899. dyn.m_uLastTouchedMS_Div256 = Plat_MSTime() >> 8;
  5900. FinishDynamicModelLoadIfReady( &dyn, pModel );
  5901. }
  5902. s_LastDynamicLoadTime = Plat_FloatTime();
  5903. }
  5904. }
  5905. // If we're not working, and we have work to do, and the queued loader is open for business...
  5906. if ( !m_bDynamicLoadQueueHeadActive && m_DynamicModelLoadQueue.Count() > 0 && g_pQueuedLoader->IsFinished() )
  5907. {
  5908. model_t *pModel = m_DynamicModelLoadQueue[0];
  5909. UtlHashHandle_t hDyn = m_DynamicModels.Find( pModel );
  5910. Assert( hDyn != m_DynamicModels.InvalidHandle() );
  5911. if ( hDyn != m_DynamicModels.InvalidHandle() )
  5912. {
  5913. m_bDynamicLoadQueueHeadActive = true;
  5914. CDynamicModelInfo &dyn = m_DynamicModels[hDyn];
  5915. Assert( dyn.m_nLoadFlags & CDynamicModelInfo::QUEUED );
  5916. Assert( !(dyn.m_nLoadFlags & CDynamicModelInfo::LOADING) );
  5917. Assert( !(dyn.m_nLoadFlags & CDynamicModelInfo::LOCKED) );
  5918. dyn.m_nLoadFlags |= CDynamicModelInfo::LOADING;
  5919. // the queued loader is very ... particular about path names. it doesn't like leading "models/"
  5920. const char* pName = pModel->szPathName;
  5921. int nLen = V_strlen( "models" );
  5922. if ( StringHasPrefix( pName, "models" ) && ( pName[nLen] == '/' || pName[nLen] == '\\' ) )
  5923. {
  5924. pName += ( nLen + 1 );
  5925. }
  5926. MaterialLock_t matLock = g_pMaterialSystem->Lock();
  5927. //g_pQueuedLoader->DynamicLoadMapResource( pName );
  5928. g_pMaterialSystem->Unlock(matLock);
  5929. }
  5930. else
  5931. {
  5932. m_DynamicModelLoadQueue.Remove(0);
  5933. }
  5934. }
  5935. }
  5936. void CModelLoader::FinishDynamicModelLoadIfReady( CDynamicModelInfo *pDyn, model_t *pModel )
  5937. {
  5938. CDynamicModelInfo &dyn = *pDyn;
  5939. if ( ( dyn.m_nLoadFlags & CDynamicModelInfo::LOCKED ) && !( dyn.m_nLoadFlags & CDynamicModelInfo::SERVERLOADING ) )
  5940. {
  5941. // There ought to be a better way to plumb this through, but this should be ok...
  5942. if ( sv.m_pDynamicModelTable )
  5943. {
  5944. int netidx = sv.m_pDynamicModelTable->FindStringIndex( pModel->szPathName );
  5945. if ( netidx != INVALID_STRING_INDEX )
  5946. {
  5947. char nIsLoaded = 1;
  5948. sv.m_pDynamicModelTable->SetStringUserData( netidx, 1, &nIsLoaded );
  5949. }
  5950. }
  5951. DynamicModelDebugMsg( "model %p [%s] loaded\n", pModel, pModel->szPathName );
  5952. dyn.m_nLoadFlags |= CDynamicModelInfo::READY;
  5953. while ( dyn.m_Callbacks.Count() > 0 )
  5954. {
  5955. IModelLoadCallback* pCallback = dyn.m_Callbacks.Tail();
  5956. UnregisterModelLoadCallback( pModel, pCallback );
  5957. pCallback->OnModelLoadComplete( pModel );
  5958. }
  5959. }
  5960. }
  5961. bool CModelLoader::RegisterModelLoadCallback( model_t *pModel, IModelLoadCallback *pCallback, bool bCallImmediatelyIfLoaded )
  5962. {
  5963. UtlHashHandle_t hDyn = m_DynamicModels.Find( pModel );
  5964. Assert( hDyn != m_DynamicModels.InvalidHandle() );
  5965. if ( hDyn == m_DynamicModels.InvalidHandle() )
  5966. return false;
  5967. CDynamicModelInfo &dyn = m_DynamicModels[ hDyn ];
  5968. AssertMsg( dyn.m_iRefCount > 0, "RegisterModelLoadCallback requires non-zero model refcount" );
  5969. if ( dyn.m_nLoadFlags & CDynamicModelInfo::READY )
  5970. {
  5971. if ( !bCallImmediatelyIfLoaded )
  5972. return false;
  5973. pCallback->OnModelLoadComplete( pModel );
  5974. }
  5975. else
  5976. {
  5977. if ( !dyn.m_Callbacks.HasElement( pCallback ) )
  5978. {
  5979. dyn.m_Callbacks.AddToTail( pCallback );
  5980. // Set registration count for callback pointer
  5981. m_RegisteredDynamicCallbacks[ m_RegisteredDynamicCallbacks.Insert( pCallback, 0 ) ]++;
  5982. }
  5983. }
  5984. return true;
  5985. }
  5986. bool CModelLoader::IsDynamicModelLoading( model_t *pModel )
  5987. {
  5988. Assert( pModel->nLoadFlags & FMODELLOADER_DYNAMIC );
  5989. UtlHashHandle_t hDyn = m_DynamicModels.Find( pModel );
  5990. Assert( hDyn != m_DynamicModels.InvalidHandle() );
  5991. if ( hDyn != m_DynamicModels.InvalidHandle() )
  5992. {
  5993. CDynamicModelInfo &dyn = m_DynamicModels[ hDyn ];
  5994. AssertMsg( dyn.m_iRefCount > 0, "dynamic model state cannot be queried with zero refcount" );
  5995. if ( dyn.m_iRefCount > 0 )
  5996. {
  5997. return !( dyn.m_nLoadFlags & CDynamicModelInfo::READY );
  5998. }
  5999. }
  6000. return false;
  6001. }
  6002. void CModelLoader::AddRefDynamicModel( model_t *pModel, bool bClientSideRef )
  6003. {
  6004. extern IVModelInfo* modelinfo;
  6005. UtlHashHandle_t hDyn = m_DynamicModels.Insert( pModel );
  6006. CDynamicModelInfo& dyn = m_DynamicModels[ hDyn ];
  6007. dyn.m_iRefCount++;
  6008. dyn.m_iClientRefCount += ( bClientSideRef ? 1 : 0 );
  6009. Assert( dyn.m_iRefCount > 0 );
  6010. DynamicModelDebugMsg( "model %p [%s] addref %d (%d)\n", pModel, pModel->szPathName, dyn.m_iRefCount, dyn.m_iClientRefCount );
  6011. if ( !( dyn.m_nLoadFlags & ( CDynamicModelInfo::QUEUED | CDynamicModelInfo::LOCKED ) ) )
  6012. {
  6013. QueueDynamicModelLoad( &dyn, pModel );
  6014. // Try to kick it off asap if we aren't already busy.
  6015. if ( !m_bDynamicLoadQueueHeadActive )
  6016. {
  6017. UpdateDynamicModelLoadQueue();
  6018. }
  6019. }
  6020. }
  6021. void CModelLoader::ReleaseDynamicModel( model_t *pModel, bool bClientSideRef )
  6022. {
  6023. Assert( pModel->nLoadFlags & FMODELLOADER_DYNAMIC );
  6024. UtlHashHandle_t hDyn = m_DynamicModels.Find( pModel );
  6025. if ( hDyn != m_DynamicModels.InvalidHandle() )
  6026. {
  6027. CDynamicModelInfo &dyn = m_DynamicModels[ hDyn ];
  6028. Assert( dyn.m_iRefCount > 0 );
  6029. if ( dyn.m_iRefCount > 0 )
  6030. {
  6031. DynamicModelDebugMsg( "model %p [%s] release %d (%dc)\n", pModel, pModel->szPathName, dyn.m_iRefCount, dyn.m_iClientRefCount );
  6032. dyn.m_iRefCount--;
  6033. dyn.m_iClientRefCount -= ( bClientSideRef ? 1 : 0 );
  6034. Assert( dyn.m_iClientRefCount >= 0 );
  6035. if ( dyn.m_iClientRefCount < 0 )
  6036. dyn.m_iClientRefCount = 0;
  6037. dyn.m_uLastTouchedMS_Div256 = Plat_MSTime() >> 8;
  6038. }
  6039. }
  6040. }
  6041. void CModelLoader::UnregisterModelLoadCallback( model_t *pModel, IModelLoadCallback *pCallback )
  6042. {
  6043. if ( int *pCallbackRegistrationCount = m_RegisteredDynamicCallbacks.GetPtr( pCallback ) )
  6044. {
  6045. if ( pModel )
  6046. {
  6047. UtlHashHandle_t i = m_DynamicModels.Find( pModel );
  6048. if ( i != m_DynamicModels.InvalidHandle() )
  6049. {
  6050. CDynamicModelInfo &dyn = m_DynamicModels[ i ];
  6051. if ( dyn.m_Callbacks.FindAndFastRemove( pCallback ) )
  6052. {
  6053. if ( dyn.m_Callbacks.Count() == 0 )
  6054. {
  6055. dyn.m_Callbacks.Purge();
  6056. }
  6057. if ( --(*pCallbackRegistrationCount) == 0 )
  6058. {
  6059. m_RegisteredDynamicCallbacks.Remove( pCallback );
  6060. return;
  6061. }
  6062. }
  6063. }
  6064. }
  6065. else
  6066. {
  6067. for ( UtlHashHandle_t i = m_DynamicModels.FirstHandle(); i != m_DynamicModels.InvalidHandle(); i = m_DynamicModels.NextHandle(i) )
  6068. {
  6069. CDynamicModelInfo &dyn = m_DynamicModels[ i ];
  6070. if ( dyn.m_Callbacks.FindAndFastRemove( pCallback ) )
  6071. {
  6072. if ( dyn.m_Callbacks.Count() == 0 )
  6073. {
  6074. dyn.m_Callbacks.Purge();
  6075. }
  6076. if ( --(*pCallbackRegistrationCount) == 0 )
  6077. {
  6078. m_RegisteredDynamicCallbacks.Remove( pCallback );
  6079. return;
  6080. }
  6081. }
  6082. }
  6083. }
  6084. }
  6085. }
  6086. void CModelLoader::QueueDynamicModelLoad( CDynamicModelInfo *dyn, model_t *mod )
  6087. {
  6088. Assert( !(dyn->m_nLoadFlags & CDynamicModelInfo::QUEUED) );
  6089. // Client-side entities have priority over server-side entities
  6090. // because they are more likely to be used in UI elements. --henryg
  6091. if ( dyn->m_iClientRefCount > 0 && m_DynamicModelLoadQueue.Count() > 1 )
  6092. {
  6093. m_DynamicModelLoadQueue.InsertAfter( 0, mod );
  6094. }
  6095. else
  6096. {
  6097. m_DynamicModelLoadQueue.AddToTail( mod );
  6098. }
  6099. dyn->m_nLoadFlags |= CDynamicModelInfo::QUEUED;
  6100. mod->nLoadFlags |= ( dyn->m_iClientRefCount > 0 ? FMODELLOADER_DYNCLIENT : FMODELLOADER_DYNSERVER );
  6101. }
  6102. bool CModelLoader::CancelDynamicModelLoad( CDynamicModelInfo *dyn, model_t *mod )
  6103. {
  6104. int i = m_DynamicModelLoadQueue.Find( mod );
  6105. Assert( (i < 0) == !(dyn->m_nLoadFlags & CDynamicModelInfo::QUEUED) );
  6106. if ( i >= 0 )
  6107. {
  6108. if ( i == 0 && m_bDynamicLoadQueueHeadActive )
  6109. {
  6110. // can't remove head of queue
  6111. return false;
  6112. }
  6113. else
  6114. {
  6115. Assert( dyn->m_nLoadFlags & CDynamicModelInfo::QUEUED );
  6116. m_DynamicModelLoadQueue.Remove( i );
  6117. dyn->m_nLoadFlags &= ~CDynamicModelInfo::QUEUED;
  6118. mod->nLoadFlags &= ~FMODELLOADER_DYNAMIC;
  6119. return true;
  6120. }
  6121. }
  6122. return false;
  6123. }
  6124. void CModelLoader::InternalUpdateDynamicModels( bool bForceFlushUnreferenced )
  6125. {
  6126. const uint now = Plat_MSTime();
  6127. const uint delay = bForceFlushUnreferenced ? 0 : (int)( clamp( mod_dynamicunloadtime.GetFloat(), 1.f, 600.f ) * 1000 );
  6128. UpdateDynamicModelLoadQueue();
  6129. #ifdef _DEBUG
  6130. extern CNetworkStringTableContainer *networkStringTableContainerServer;
  6131. bool bPrevStringTableLockState = networkStringTableContainerServer->Lock( false );
  6132. #endif
  6133. // Scan for models to unload. TODO: accelerate with a "models to potentially unload" list?
  6134. UtlHashHandle_t i = m_DynamicModels.FirstHandle();
  6135. while ( i != m_DynamicModels.InvalidHandle() )
  6136. {
  6137. model_t *pModel = m_DynamicModels.Key( i );
  6138. CDynamicModelInfo& dyn = m_DynamicModels[ i ];
  6139. // UNLOAD THIS MODEL if zero refcount and not currently loading, and either timed out or never loaded
  6140. if ( dyn.m_iRefCount <= 0 && !(dyn.m_nLoadFlags & CDynamicModelInfo::LOADING) &&
  6141. ( ( now - (dyn.m_uLastTouchedMS_Div256 << 8) ) >= delay || !( dyn.m_nLoadFlags & CDynamicModelInfo::LOCKED ) ) )
  6142. {
  6143. // Remove from load queue
  6144. if ( dyn.m_nLoadFlags & CDynamicModelInfo::QUEUED )
  6145. {
  6146. if ( !CancelDynamicModelLoad( &dyn, pModel ) )
  6147. {
  6148. // Couldn't remove from queue, advance to next entry and do not remove
  6149. i = m_DynamicModels.NextHandle(i);
  6150. continue;
  6151. }
  6152. }
  6153. // Unlock studiohdr_t
  6154. if ( dyn.m_nLoadFlags & CDynamicModelInfo::LOCKED )
  6155. {
  6156. g_pMDLCache->UnlockStudioHdr( pModel->studio );
  6157. }
  6158. // There ought to be a better way to plumb this through, but this should be ok...
  6159. if ( sv.m_pDynamicModelTable )
  6160. {
  6161. int netidx = sv.m_pDynamicModelTable->FindStringIndex( pModel->szPathName );
  6162. if ( netidx != INVALID_STRING_INDEX )
  6163. {
  6164. char nIsLoaded = 0;
  6165. sv.m_pDynamicModelTable->SetStringUserData( netidx, 1, &nIsLoaded );
  6166. }
  6167. }
  6168. if ( pModel->nLoadFlags & FMODELLOADER_DYNAMIC )
  6169. {
  6170. pModel->nLoadFlags &= ~FMODELLOADER_DYNAMIC;
  6171. // Actually unload the model if all system references are gone
  6172. if ( pModel->nLoadFlags & FMODELLOADER_REFERENCEMASK )
  6173. {
  6174. DynamicModelDebugMsg( "model %p [%s] unload - deferred: non-dynamic reference\n", pModel, pModel->szPathName );
  6175. }
  6176. else
  6177. {
  6178. DynamicModelDebugMsg( "model %p [%s] unload\n", pModel, pModel->szPathName );
  6179. Studio_UnloadModel( pModel );
  6180. if ( mod_dynamicunloadtextures.GetBool() )
  6181. {
  6182. if ( ICallQueue* pCallQueue = materials->GetRenderContext()->GetCallQueue() )
  6183. {
  6184. pCallQueue->QueueCall( materials, &IMaterialSystem::UncacheUnusedMaterials, false );
  6185. }
  6186. else
  6187. {
  6188. materials->UncacheUnusedMaterials();
  6189. }
  6190. }
  6191. }
  6192. }
  6193. // Remove from table, advance to next entry
  6194. i = m_DynamicModels.RemoveAndAdvance(i);
  6195. continue;
  6196. }
  6197. // Advance to next entry in table
  6198. i = m_DynamicModels.NextHandle(i);
  6199. }
  6200. #ifdef _DEBUG
  6201. networkStringTableContainerServer->Lock( bPrevStringTableLockState );
  6202. #endif
  6203. }
  6204. void CModelLoader::Client_OnServerModelStateChanged( model_t *pModel, bool bServerLoaded )
  6205. {
  6206. #ifndef DEDICATED
  6207. // Listen server don't distinguish between server and client ready, never use SERVERLOADING flag
  6208. if ( sv.IsActive() )
  6209. return;
  6210. UtlHashHandle_t i = m_DynamicModels.Find( pModel );
  6211. if ( i != m_DynamicModels.InvalidHandle() )
  6212. {
  6213. CDynamicModelInfo &dyn = m_DynamicModels[i];
  6214. if ( !bServerLoaded )
  6215. {
  6216. if ( dyn.m_nLoadFlags & CDynamicModelInfo::READY )
  6217. DynamicModelDebugMsg( "dynamic model [%s] loaded on client but not server! is this bad? unknown...", pModel->szPathName );
  6218. // XXX DESIGN WART - WHAT IF A CLIENT-SIDE MODEL IS SHARED WITH A SERVER-SIDE MODEL?
  6219. // The client side model may still be in use while the server side model is unloaded.
  6220. // We don't clear the READY flag for this reason. This means that new dynamic uses of
  6221. // the model on the client will trigger READY before the server is ready to show the
  6222. // model, and the client may show wrong animation state or body groups temporarily.
  6223. // Is this a real problem? We would require the ability for a model to be marked both
  6224. // as client-side AND networked in order to fix it, with separate refcounts...
  6225. //dyn.m_nLoadFlags &= ~CDynamicModelInfo::READY;
  6226. dyn.m_nLoadFlags |= CDynamicModelInfo::SERVERLOADING;
  6227. }
  6228. else
  6229. {
  6230. dyn.m_nLoadFlags &= ~CDynamicModelInfo::SERVERLOADING;
  6231. FinishDynamicModelLoadIfReady( &dyn, pModel );
  6232. }
  6233. }
  6234. #endif
  6235. }
  6236. void CModelLoader::ForceUnloadNonClientDynamicModels()
  6237. {
  6238. UtlHashHandle_t i = m_DynamicModels.FirstHandle();
  6239. while ( i != m_DynamicModels.InvalidHandle() )
  6240. {
  6241. CDynamicModelInfo &dyn = m_DynamicModels[i];
  6242. dyn.m_iRefCount = dyn.m_iClientRefCount;
  6243. i = m_DynamicModels.NextHandle( i );
  6244. }
  6245. // Flush everything
  6246. InternalUpdateDynamicModels( true );
  6247. }
  6248. // reconstruct the ambient lighting for a leaf at the given position in worldspace
  6249. void Mod_LeafAmbientColorAtPos( Vector *pOut, const Vector &pos, int leafIndex )
  6250. {
  6251. for ( int i = 0; i < 6; i++ )
  6252. {
  6253. pOut[i].Init();
  6254. }
  6255. mleafambientindex_t *pAmbient = &host_state.worldbrush->m_pLeafAmbient[leafIndex];
  6256. if ( !pAmbient->ambientSampleCount && pAmbient->firstAmbientSample )
  6257. {
  6258. // this leaf references another leaf, move there (this leaf is a solid leaf so it borrows samples from a neighbor)
  6259. leafIndex = pAmbient->firstAmbientSample;
  6260. pAmbient = &host_state.worldbrush->m_pLeafAmbient[leafIndex];
  6261. }
  6262. int count = pAmbient->ambientSampleCount;
  6263. if ( count > 0 )
  6264. {
  6265. int start = host_state.worldbrush->m_pLeafAmbient[leafIndex].firstAmbientSample;
  6266. mleafambientlighting_t *pSamples = host_state.worldbrush->m_pAmbientSamples + start;
  6267. mleaf_t *pLeaf = &host_state.worldbrush->leafs[leafIndex];
  6268. float totalFactor = 0;
  6269. for ( int i = 0; i < count; i++ )
  6270. {
  6271. // do an inverse squared distance weighted average of the samples to reconstruct
  6272. // the original function
  6273. // the sample positions are packed as leaf bounds fractions, compute
  6274. Vector samplePos = pLeaf->m_vecCenter - pLeaf->m_vecHalfDiagonal;
  6275. samplePos.x += float(pSamples[i].x) * pLeaf->m_vecHalfDiagonal.x * (2.0f / 255.0f);
  6276. samplePos.y += float(pSamples[i].y) * pLeaf->m_vecHalfDiagonal.y * (2.0f / 255.0f);
  6277. samplePos.z += float(pSamples[i].z) * pLeaf->m_vecHalfDiagonal.z * (2.0f / 255.0f);
  6278. float dist = (samplePos - pos).LengthSqr();
  6279. float factor = 1.0f / (dist + 1.0f);
  6280. totalFactor += factor;
  6281. for ( int j = 0; j < 6; j++ )
  6282. {
  6283. Vector v;
  6284. ColorRGBExp32ToVector( pSamples[i].cube.m_Color[j], v );
  6285. pOut[j] += v * factor;
  6286. }
  6287. }
  6288. for ( int i = 0; i < 6; i++ )
  6289. {
  6290. pOut[i] *= (1.0f / totalFactor);
  6291. }
  6292. }
  6293. }
  6294. #if defined( _X360 ) || defined( _PS3 ) || defined( PLATFORM_WINDOWS_PC )
  6295. #if defined( PLATFORM_WINDOWS_PC )
  6296. struct xModelList_t
  6297. {
  6298. char name[MAX_PATH];
  6299. int dataSize;
  6300. int numVertices;
  6301. int triCount;
  6302. int dataSizeLod0;
  6303. int numVerticesLod0;
  6304. int triCountLod0;
  6305. int numBones;
  6306. int numParts;
  6307. int numLODs;
  6308. int numMeshes;
  6309. };
  6310. #endif // PLATFORM_WINDOWS_PC
  6311. int ComputeSize( studiohwdata_t *hwData, int *numVerts, int *pTriCount, bool onlyTopLod = false )
  6312. {
  6313. unsigned size = 0;
  6314. Assert(hwData && numVerts);
  6315. int max_lod = (onlyTopLod ? 1 : hwData->m_NumLODs);
  6316. *pTriCount = 0;
  6317. for ( int i=0; i < max_lod; i++ )
  6318. {
  6319. studioloddata_t *pLOD = &hwData->m_pLODs[i];
  6320. for ( int j = 0; j < hwData->m_NumStudioMeshes; j++ )
  6321. {
  6322. studiomeshdata_t *pMeshData = &pLOD->m_pMeshData[j];
  6323. for ( int k = 0; k < pMeshData->m_NumGroup; k++ )
  6324. {
  6325. studiomeshgroup_t *pMeshGroup = &pMeshData->m_pMeshGroup[k];
  6326. IMesh* mesh = pMeshGroup->m_pMesh;
  6327. size += mesh->ComputeMemoryUsed();
  6328. // This doesn't seem relevant since it has no bearing on GPU memory, but keeping it here
  6329. // on the PC, since the reason it's being aded back in is to look at differences between
  6330. // main and portal2.
  6331. #if defined( PLATFORM_WINDOWS_PC )
  6332. size += 2 * pMeshGroup->m_NumVertices; // Size of m_pGroupIndexToMeshIndex[] array
  6333. #endif
  6334. *numVerts += mesh->VertexCount();
  6335. Assert( mesh->VertexCount() == pMeshGroup->m_NumVertices );
  6336. for ( int l = 0; l < pMeshGroup->m_NumStrips; ++l )
  6337. {
  6338. OptimizedModel::StripHeader_t *pStripData = &pMeshGroup->m_pStripData[l];
  6339. *pTriCount += pStripData->numIndices / 3;
  6340. }
  6341. }
  6342. }
  6343. }
  6344. return size;
  6345. }
  6346. // APSFIXME: needs to only do models that are resident, sizes might be wrong, i.e lacking compressed vert state?
  6347. CON_COMMAND( vx_model_list, "Dump models to VXConsole" )
  6348. {
  6349. CUtlVector< xModelList_t > modelList;
  6350. modelList.SetCount( modelloader->GetCount() );
  6351. int numActualModels = 0;
  6352. for ( int i = 0; i < modelList.Count(); i++ )
  6353. {
  6354. const char* name = "Unknown";
  6355. int dataSizeLod0 = 0;
  6356. int dataSize = 0;
  6357. int numParts = 0;
  6358. int numBones = 0;
  6359. int numVertsLod0 = 0;
  6360. int numVerts = 0;
  6361. int numLODs = 0;
  6362. int numMeshes = 0;
  6363. int nTriCount = 0;
  6364. int nTriCountLod0 = 0;
  6365. model_t* model = modelloader->GetModelForIndex( i );
  6366. if ( model )
  6367. {
  6368. // other model types are not interesting
  6369. if ( model->type != mod_studio )
  6370. continue;
  6371. name = model->szPathName;
  6372. studiohwdata_t *hwData = g_pMDLCache->GetHardwareData( model->studio );
  6373. if ( hwData )
  6374. {
  6375. numMeshes = hwData->m_NumStudioMeshes;
  6376. numLODs = hwData->m_NumLODs;
  6377. dataSize = ComputeSize( hwData, &numVerts, &nTriCount, false );
  6378. dataSizeLod0 = ComputeSize( hwData, &numVertsLod0, &nTriCountLod0, true );
  6379. }
  6380. studiohdr_t *pStudioHdr = (studiohdr_t *)modelloader->GetExtraData( model );
  6381. numBones = pStudioHdr->numbones;
  6382. numParts = pStudioHdr->numbodyparts;
  6383. }
  6384. xModelList_t &modelInfo = modelList[numActualModels];
  6385. ++numActualModels;
  6386. strcpy( modelInfo.name, name );
  6387. modelInfo.dataSize = dataSize;
  6388. modelInfo.numVertices = numVerts;
  6389. modelInfo.triCount = nTriCount;
  6390. modelInfo.dataSizeLod0 = dataSizeLod0;
  6391. modelInfo.numVerticesLod0 = numVertsLod0;
  6392. modelInfo.triCountLod0 = nTriCountLod0;
  6393. modelInfo.numParts = numParts;
  6394. modelInfo.numBones = numBones;
  6395. modelInfo.numLODs = numLODs;
  6396. modelInfo.numMeshes = numMeshes;
  6397. }
  6398. #if defined( _X360 )
  6399. XBX_rModelList( numActualModels, modelList.Base() );
  6400. #elif defined( _PS3 )
  6401. g_pValvePS3Console->ModelList( numActualModels, modelList.Base() ); // super stupid, it just gets copied into yet another cutlvec on the other side, but that's the way the 360 ver does it.
  6402. #elif defined( PLATFORM_WINDOWS_PC )
  6403. extern IVEngineClient *engineClient;
  6404. char csvFileName[ MAX_PATH ];
  6405. Q_snprintf( csvFileName, MAX_PATH, "modellist_%s.csv", engineClient->GetLevelNameShort() );
  6406. Msg( "Writing model list to ""%s""...\n", csvFileName );
  6407. FileHandle_t fileHandle = g_pFullFileSystem->Open( csvFileName, "w" );
  6408. g_pFullFileSystem->FPrintf( fileHandle, "Model,DataSize,Tris,Verts,DataSize (LOD0),Tris (LOD0),Verts (LOD0),Parts,Bones,LODs,Meshes\n" );
  6409. for ( int i = 0; i < numActualModels; ++ i )
  6410. {
  6411. g_pFullFileSystem->FPrintf( fileHandle, "%s,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
  6412. modelList[ i ].name, modelList[ i ].dataSize, modelList[ i ].triCount, modelList[ i ].numVertices, modelList[ i ].dataSizeLod0, modelList[ i ].triCountLod0, modelList[ i ].numVerticesLod0,
  6413. modelList[ i ].numParts, modelList[ i ].numBones, modelList[ i ].numLODs, modelList[ i ].numMeshes );
  6414. }
  6415. g_pFullFileSystem->Close( fileHandle );
  6416. #endif // PLATFORM_WINDOWS_PC
  6417. }
  6418. #endif // _X360 || PLATFORM_WINDOWS_PC
  6419. CON_COMMAND_F( mod_dynamicmodeldebug, "debug spew for dynamic model loading", FCVAR_HIDDEN | FCVAR_DONTRECORD )
  6420. {
  6421. ((CModelLoader*)modelloader)->DebugPrintDynamicModels();
  6422. }
  6423. #include "server.h"
  6424. #ifndef DEDICATED
  6425. #include "client.h"
  6426. #endif
  6427. void CModelLoader::DebugPrintDynamicModels()
  6428. {
  6429. Msg( "network table (server):\n" );
  6430. if ( sv.m_pDynamicModelTable )
  6431. {
  6432. for ( int i = 0; i < sv.m_pDynamicModelTable->GetNumStrings(); ++i )
  6433. {
  6434. int dummy = 0;
  6435. char* data = (char*) sv.m_pDynamicModelTable->GetStringUserData( i, &dummy );
  6436. bool bLoadedOnServer = !(data && dummy && data[0] == 0);
  6437. Msg( "%3i: %c %s\n", i, bLoadedOnServer ? '*' : ' ', sv.m_pDynamicModelTable->GetString(i) );
  6438. }
  6439. }
  6440. #ifndef DEDICATED
  6441. Msg( "\nnetwork table (client):\n" );
  6442. if ( GetBaseLocalClient().m_pDynamicModelTable )
  6443. {
  6444. for ( int i = 0; i < GetBaseLocalClient().m_pDynamicModelTable->GetNumStrings(); ++i )
  6445. {
  6446. int dummy = 0;
  6447. char* data = (char*) GetBaseLocalClient().m_pDynamicModelTable->GetStringUserData( i, &dummy );
  6448. bool bLoadedOnServer = !(data && dummy && data[0] == 0);
  6449. Msg( "%3i: %c %s\n", i, bLoadedOnServer ? '*' : ' ', GetBaseLocalClient().m_pDynamicModelTable->GetString(i) );
  6450. }
  6451. }
  6452. #endif
  6453. extern IVModelInfo *modelinfo;
  6454. extern IVModelInfoClient *modelinfoclient;
  6455. Msg( "\ndynamic models:\n" );
  6456. for ( UtlHashHandle_t h = m_DynamicModels.FirstHandle(); h != m_DynamicModels.InvalidHandle(); h = m_DynamicModels.NextHandle(h) )
  6457. {
  6458. CDynamicModelInfo &dyn = m_DynamicModels[h];
  6459. int idx = modelinfo->GetModelIndex( m_DynamicModels.Key(h)->szPathName );
  6460. #ifndef DEDICATED
  6461. if ( idx == -1 ) idx = modelinfoclient->GetModelIndex( m_DynamicModels.Key(h)->szPathName );
  6462. #endif
  6463. Msg( "%d (%d%c): %s [ref: %d (%dc)] %s%s%s%s%s\n", idx, ((-2 - idx) >> 1), (idx & 1) ? 'c' : 's',
  6464. m_DynamicModels.Key(h)->szPathName, dyn.m_iRefCount, dyn.m_iClientRefCount,
  6465. (dyn.m_nLoadFlags & CDynamicModelInfo::QUEUED) ? " QUEUED" : "",
  6466. (dyn.m_nLoadFlags & CDynamicModelInfo::LOADING) ? " LOADING" : "",
  6467. (dyn.m_nLoadFlags & CDynamicModelInfo::LOCKED) ? " LOCKED" : "",
  6468. (dyn.m_nLoadFlags & CDynamicModelInfo::READY) ? " READY" : "" ,
  6469. (dyn.m_nLoadFlags & CDynamicModelInfo::COMBINED) ? " COMBINED" : "" );
  6470. }
  6471. }
  6472. #if !defined ( _CERT )
  6473. CON_COMMAND( mod_combiner_info, "debug spew for Combiner Info" )
  6474. {
  6475. ((CModelLoader*)modelloader)->DebugCombinerInfo();
  6476. }
  6477. #endif
  6478. void CModelLoader::DebugCombinerInfo()
  6479. {
  6480. extern IVModelInfo *modelinfo;
  6481. extern IVModelInfoClient *modelinfoclient;
  6482. Msg( "Dynamic Combined Models:\n" );
  6483. for ( UtlHashHandle_t h = m_DynamicModels.FirstHandle(); h != m_DynamicModels.InvalidHandle(); h = m_DynamicModels.NextHandle(h) )
  6484. {
  6485. CDynamicModelInfo &dyn = m_DynamicModels[h];
  6486. if ( ( dyn.m_nLoadFlags & CDynamicModelInfo::COMBINED ) == 0 )
  6487. {
  6488. continue;
  6489. }
  6490. int idx = modelinfo->GetModelIndex( m_DynamicModels.Key( h )->szPathName );
  6491. #ifndef DEDICATED
  6492. if ( idx == -1 ) idx = modelinfoclient->GetModelIndex( m_DynamicModels.Key( h )->szPathName );
  6493. #endif
  6494. Msg( "%d ( %d : %s ): %s [ reference count: %d / %d client ] %s%s%s%s%s\n", idx, ((-2 - idx) >> 1), (idx & 1) ? "client" : "server",
  6495. m_DynamicModels.Key(h)->szPathName, dyn.m_iRefCount, dyn.m_iClientRefCount,
  6496. (dyn.m_nLoadFlags & CDynamicModelInfo::QUEUED) ? " QUEUED" : "",
  6497. (dyn.m_nLoadFlags & CDynamicModelInfo::LOADING) ? " LOADING" : "",
  6498. (dyn.m_nLoadFlags & CDynamicModelInfo::LOCKED) ? " LOCKED" : "",
  6499. (dyn.m_nLoadFlags & CDynamicModelInfo::READY) ? " READY" : "" ,
  6500. (dyn.m_nLoadFlags & CDynamicModelInfo::COMBINED) ? " COMBINED" : "" );
  6501. }
  6502. Msg( "\n" );
  6503. g_pMDLCache->DebugCombinerInfo();
  6504. }