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.

446 lines
11 KiB

  1. //========= Copyright � 1996-2013, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. //#include "cbase.h"
  8. #include "custom_material.h"
  9. #include "composite_texture.h"
  10. #include "materialsystem/base_visuals_data_processor.h"
  11. #include "materialsystem_global.h"
  12. #include "keyvalues.h"
  13. #include "tier0/vprof.h"
  14. #ifndef DEDICATED
  15. #include "filesystem.h"
  16. #endif
  17. //#define DEBUG_CUSTOMMATERIALS
  18. int CCustomMaterial::m_nMaterialCount = 0;
  19. //
  20. // Material applied to the item to give it a custom look
  21. //
  22. CCustomMaterial::CCustomMaterial( KeyValues *pKeyValues )
  23. : m_bValid( false )
  24. , m_nModelMaterialIndex( -1 )
  25. , m_szBaseMaterialName( NULL)
  26. {
  27. // we need to copy this, because the passed in one was allocated outside materialsystem.dll
  28. m_pVMTKeyValues = ( pKeyValues != NULL ) ? pKeyValues->MakeCopy() : NULL;
  29. }
  30. CCustomMaterial::~CCustomMaterial()
  31. {
  32. Shutdown();
  33. }
  34. void CCustomMaterial::AddTexture( ICompositeTexture * pTextureInterface )
  35. {
  36. CCompositeTexture *pTexture = dynamic_cast< CCompositeTexture * >( pTextureInterface );
  37. if ( pTexture )
  38. {
  39. m_pTextures.AddToTail( pTexture );
  40. pTexture->AddRef();
  41. }
  42. }
  43. ICompositeTexture *CCustomMaterial::GetTexture( int nIndex )
  44. {
  45. if ( nIndex >= 0 && nIndex < m_pTextures.Count() )
  46. {
  47. return m_pTextures[ nIndex ];
  48. }
  49. return NULL;
  50. }
  51. bool CCustomMaterial::CheckRegenerate( int nSize )
  52. {
  53. if ( m_pTextures.Count() > 0 )
  54. {
  55. return nSize != ( 2048 >> m_pTextures[ 0 ]->Size() );
  56. }
  57. return false;
  58. }
  59. void CCustomMaterial::SetBaseMaterialName( const char* szName )
  60. {
  61. Assert( !m_szBaseMaterialName );
  62. m_szBaseMaterialName = strdup( szName );
  63. }
  64. void CCustomMaterial::Shutdown()
  65. {
  66. DestroyProceduralMaterial();
  67. if ( m_pVMTKeyValues != NULL )
  68. {
  69. m_pVMTKeyValues->deleteThis();
  70. m_pVMTKeyValues = NULL;
  71. }
  72. for ( int i = 0; i < m_pTextures.Count(); i++ )
  73. {
  74. if ( m_pTextures[ i ] )
  75. {
  76. m_pTextures[ i ]->Release();
  77. m_pTextures[ i ] = NULL;
  78. }
  79. }
  80. if ( m_szBaseMaterialName )
  81. free( ( void * ) m_szBaseMaterialName );
  82. m_pTextures.RemoveAll();
  83. }
  84. void CCustomMaterial::Usage( int& nTextures, int& nBackingTextures )
  85. {
  86. for ( int i = 0; i < m_pTextures.Count(); i++ )
  87. {
  88. m_pTextures[ i ]->Usage( nTextures, nBackingTextures );
  89. }
  90. }
  91. bool CCustomMaterial::TexturesReady() const
  92. {
  93. if ( m_pTextures.Count() == 0 )
  94. {
  95. return false;
  96. }
  97. for ( int i = 0; i < m_pTextures.Count(); i++ )
  98. {
  99. if ( !m_pTextures[ i ]->IsReady() )
  100. {
  101. return false;
  102. }
  103. }
  104. return true;
  105. }
  106. void CCustomMaterial::RegenerateTextures()
  107. {
  108. for ( int i = 0; i < m_pTextures.Count(); i++ )
  109. {
  110. m_pTextures[ i ]->ForceRegenerate();
  111. }
  112. }
  113. bool CCustomMaterial::ShouldRelease()
  114. {
  115. return ( ( GetRefCount() == 1 ) && IsValid() );
  116. }
  117. bool CCustomMaterial::Compare( const CUtlVector< SCompositeTextureInfo > &vecTextures )
  118. {
  119. if ( m_pTextures.Count() == vecTextures.Count() )
  120. {
  121. FOR_EACH_VEC( m_pTextures, i )
  122. {
  123. if ( !m_pTextures[ i ]->Compare( vecTextures[ i ] ) )
  124. {
  125. return false;
  126. }
  127. }
  128. return true;
  129. }
  130. return false;
  131. }
  132. void CCustomMaterial::Finalize()
  133. {
  134. char szUniqueMaterialName[ 256 ];
  135. V_snprintf( szUniqueMaterialName, sizeof( szUniqueMaterialName ), "cs_custom_material_%i", m_nMaterialCount++ );
  136. DestroyProceduralMaterial();
  137. if ( m_pVMTKeyValues != NULL )
  138. {
  139. CreateProceduralMaterial( szUniqueMaterialName, m_pVMTKeyValues->MakeCopy() );
  140. }
  141. else
  142. {
  143. // Create default material key values
  144. m_pVMTKeyValues = new KeyValues( "VertexLitGeneric" );
  145. m_pVMTKeyValues->SetInt( "$phongalbedoboost", 45 );
  146. m_pVMTKeyValues->SetInt( "$phong", 1 );
  147. m_pVMTKeyValues->SetFloat( "$phongboost", 0.4 );
  148. m_pVMTKeyValues->SetString( "$phongfresnelranges", "[.8 .8 1]" );
  149. m_pVMTKeyValues->SetInt( "$basemapalphaphongmask", 1 );
  150. m_pVMTKeyValues->SetString( "$envmap", "env_cubemap" );
  151. m_pVMTKeyValues->SetInt( "$envmapfresnel", 1 );
  152. m_pVMTKeyValues->SetString( "$envmapFresnelMinMaxExp", "[0 5 .4]" );
  153. m_pVMTKeyValues->SetString( "$envmaptint", "[.02 .02 .02]" );
  154. m_pVMTKeyValues->SetInt( "$phongalbedotint", 1 );
  155. CreateProceduralMaterial( szUniqueMaterialName, m_pVMTKeyValues->MakeCopy() );
  156. }
  157. }
  158. void CCustomMaterial::CreateProceduralMaterial( const char *pMaterialName, KeyValues *pVMTKeyValues )
  159. {
  160. // Replace parts of existing material key values
  161. // loop over m_pTextures and set the material params
  162. for ( int i = 0; i < m_pTextures.Count(); i++ )
  163. {
  164. pVMTKeyValues->SetString( g_szMaterialParamNames[ m_pTextures[ i ]->GetMaterialParamNameId() ], m_pTextures[ i ]->GetName() );
  165. }
  166. m_Material.Init( pMaterialName, pVMTKeyValues );
  167. m_Material->Refresh();
  168. }
  169. void CCustomMaterial::DestroyProceduralMaterial()
  170. {
  171. m_Material.Shutdown( true );
  172. }
  173. //
  174. // global custom material manager
  175. // the game uses this to make/get a custom material
  176. //
  177. CCustomMaterialManager::CCustomMaterialManager()
  178. {
  179. #ifndef DEDICATED
  180. m_pCustomMaterials.EnsureCapacity( 128 );
  181. m_mapVMTKeyValues.SetLessFunc( StringLessThan );
  182. #endif
  183. }
  184. CCustomMaterialManager::~CCustomMaterialManager()
  185. {
  186. }
  187. // this is called at the end of each frame
  188. bool ProcessDynamicCustomMaterialGenerator()
  189. {
  190. return MaterialSystem()->GetCustomMaterialManager()->Process();
  191. }
  192. bool CCustomMaterialManager::Init()
  193. {
  194. #ifndef DEDICATED
  195. MaterialSystem()->AddEndFramePriorToNextContextFunc( ::ProcessDynamicCustomMaterialGenerator );
  196. KeyValues *pVMTCache = new KeyValues( "VMTCache" );
  197. if ( pVMTCache->LoadFromFile( g_pFullFileSystem, "resource/vmtcache.txt", "MOD" ) )
  198. {
  199. KeyValues *pValue = pVMTCache->GetFirstValue();
  200. while ( pValue )
  201. {
  202. const char *pszVMTToCache = pValue->GetString();
  203. if ( pszVMTToCache && pszVMTToCache[0] != 0 )
  204. {
  205. KeyValues *pVMTKeyValues = new KeyValues( "VertexLitGeneric" );
  206. bool bVMTExists = pVMTKeyValues->LoadFromFile( g_pFullFileSystem , pszVMTToCache, "MOD" );
  207. if ( bVMTExists )
  208. {
  209. m_mapVMTKeyValues.Insert( pszVMTToCache, pVMTKeyValues );
  210. DevMsg( "CustomMaterialManager: Cached KeyValues %s.\n", pszVMTToCache );
  211. }
  212. else
  213. {
  214. DevMsg( "Failed to load VMT: %s\n", pszVMTToCache );
  215. }
  216. }
  217. pValue = pValue->GetNextValue();
  218. }
  219. }
  220. #endif
  221. return true;
  222. }
  223. void CCustomMaterialManager::Shutdown()
  224. {
  225. #ifndef DEDICATED
  226. MaterialSystem()->RemoveEndFramePriorToNextContextFunc( ::ProcessDynamicCustomMaterialGenerator );
  227. FOR_EACH_MAP_FAST( m_mapVMTKeyValues, i )
  228. {
  229. m_mapVMTKeyValues[ i ]->deleteThis();
  230. }
  231. m_mapVMTKeyValues.Purge();
  232. DestroyMaterials();
  233. #endif
  234. }
  235. bool CCustomMaterialManager::GetVMTKeyValues( const char *pszVMTName, KeyValues **ppVMTKeyValues )
  236. {
  237. // lookup VMT KeyValues in container
  238. int nIndex = m_mapVMTKeyValues.Find( pszVMTName );
  239. if ( nIndex != m_mapVMTKeyValues.InvalidIndex() )
  240. {
  241. // need to return a copy here, since we don't want the caller to change our copy.
  242. *ppVMTKeyValues = m_mapVMTKeyValues[ nIndex ]->MakeCopy();
  243. return true;
  244. }
  245. return false;
  246. }
  247. // handles finalizing materials once all the textures are ready, swapping materials that are pending swap and ready, and cleans up materials that are no longer used
  248. bool CCustomMaterialManager::Process()
  249. {
  250. //TM_ZONE( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  251. for ( int i = 0; i < m_pCustomMaterials.Count(); ++i )
  252. {
  253. if ( m_pCustomMaterials[ i ] &&
  254. m_pCustomMaterials[ i ]->TexturesReady() &&
  255. !m_pCustomMaterials[ i ]->IsValid() )
  256. {
  257. m_pCustomMaterials[ i ]->Finalize();
  258. m_pCustomMaterials[ i ]->SetValid( true );
  259. #ifdef DEBUG_CUSTOMMATERIALS
  260. DevMsg( "Finalized custom material: %s \n", m_pCustomMaterials[ i ]->GetMaterial() ? m_pCustomMaterials[ i ]->GetMaterial()->GetName() : "*unknown*" );
  261. #endif
  262. }
  263. else if ( !m_pCustomMaterials[ i ]->TexturesReady() &&
  264. m_pCustomMaterials[ i ]->IsValid() )
  265. {
  266. // this happens when textures regenerate because of mat_picmip changes
  267. m_pCustomMaterials[ i ]->SetValid( false );
  268. }
  269. }
  270. for ( int i = m_pCustomMaterials.Count() - 1; i >= 0; i-- )
  271. {
  272. if ( m_pCustomMaterials[ i ] )
  273. {
  274. // clean up materials that are no longer used (we are the only reference)
  275. if ( m_pCustomMaterials[ i ]->ShouldRelease() )
  276. {
  277. #ifdef DEBUG_CUSTOMMATERIALS
  278. DevMsg( "Releasing custom material: %s \n", m_pCustomMaterials[ i ]->GetMaterial()->GetName() );
  279. #endif
  280. m_pCustomMaterials[ i ]->Release();
  281. m_pCustomMaterials[ i ] = NULL;
  282. m_pCustomMaterials.Remove( i );
  283. }
  284. }
  285. }
  286. return false;
  287. }
  288. ICustomMaterial * CCustomMaterialManager::GetOrCreateCustomMaterial( KeyValues *pKeyValues, const CUtlVector< SCompositeTextureInfo > &vecTextureInfos, bool bIgnorePicMip /*= false */ )
  289. {
  290. #if defined( DEDICATED ) || defined( DISABLE_CUSTOM_MATERIAL_GENERATION )
  291. return NULL;
  292. #endif
  293. TM_MESSAGE( TELEMETRY_LEVEL0, TMMF_ICON_NOTE | TMMF_SEVERITY_WARNING, "%s %d", __FUNCTION__, vecTextureInfos[0].m_size );
  294. for ( int i = 0; i < m_pCustomMaterials.Count(); ++i )
  295. {
  296. CCustomMaterial *pMaterial = m_pCustomMaterials[ i ];
  297. if ( pMaterial && pMaterial->Compare( vecTextureInfos ) )
  298. {
  299. return pMaterial;
  300. }
  301. }
  302. CCustomMaterial *pMaterial = new CCustomMaterial( pKeyValues );
  303. pMaterial->SetValid( false );
  304. pMaterial->SetBaseMaterialName( vecTextureInfos[ 0 ].m_pVisualsDataProcessor->GetOriginalMaterialBaseName() );
  305. FOR_EACH_VEC( vecTextureInfos, i )
  306. {
  307. ICompositeTexture *pTexture = g_pMaterialSystem->GetCompositeTextureGenerator()->GetCompositeTexture( vecTextureInfos[ i ] );
  308. if ( pTexture )
  309. {
  310. pMaterial->AddTexture( pTexture );
  311. }
  312. else
  313. {
  314. AssertMsg( pTexture != NULL, "Unable to get/create composite texture for custom material!" );
  315. pMaterial->Release();
  316. return NULL;
  317. }
  318. }
  319. m_pCustomMaterials.AddToTail( pMaterial );
  320. #ifdef DEBUG_CUSTOMMATERIALS
  321. DevMsg( "Created custom material for: %s \n", pVisualsDataProcessor->GetOriginalMaterialName() );
  322. #endif
  323. // The material may not be complete yet, but it will be completed over the next few frames via Process()
  324. return pMaterial;
  325. }
  326. void CCustomMaterialManager::ReloadAllMaterials( const CCommand &args )
  327. {
  328. for ( int i = 0; i < m_pCustomMaterials.Count(); ++i )
  329. {
  330. CCustomMaterial *pMaterial = m_pCustomMaterials[ i ];
  331. if ( pMaterial )
  332. {
  333. pMaterial->RegenerateTextures();
  334. }
  335. }
  336. }
  337. void CCustomMaterialManager::ReloadVmtCache( const CCommand &args )
  338. {
  339. }
  340. int CCustomMaterialManager::DebugGetNumActiveCustomMaterials( )
  341. {
  342. int nActive = 0;
  343. for ( int i = 0; i < m_pCustomMaterials.Count(); ++i )
  344. {
  345. CCustomMaterial *pMaterial = m_pCustomMaterials[ i ];
  346. if ( pMaterial && pMaterial->IsValid() )
  347. {
  348. nActive++;
  349. }
  350. }
  351. return nActive;
  352. }
  353. void CCustomMaterialManager::Usage( const CCommand &args )
  354. {
  355. int nTextures = 0;
  356. int nBackingTextures = 0;
  357. int nActive = 0;
  358. for ( int i = 0; i < m_pCustomMaterials.Count(); ++i )
  359. {
  360. CCustomMaterial *pMaterial = m_pCustomMaterials[ i ];
  361. if ( pMaterial && pMaterial->IsValid() )
  362. {
  363. int nBackingTexture = 0;
  364. pMaterial->Usage( nTextures, nBackingTexture );
  365. Msg( "%2d. %s, %d \n", i, pMaterial->GetMaterial() ? pMaterial->GetMaterial()->GetName() : "*pending*", nBackingTexture );
  366. nBackingTextures += nBackingTexture;
  367. nActive++;
  368. }
  369. }
  370. Msg( "Custom Weapon Material Usage: Total: %d Active: %d Textures: %d BackingTextures: %d \n", m_pCustomMaterials.Count(), nActive, nTextures, nBackingTextures );
  371. }
  372. void CCustomMaterialManager::DestroyMaterials( void )
  373. {
  374. for ( int i = 0; i < m_pCustomMaterials.Count(); ++i )
  375. {
  376. if ( m_pCustomMaterials[ i ] )
  377. {
  378. m_pCustomMaterials[ i ]->Release();
  379. m_pCustomMaterials[ i ] = NULL;
  380. }
  381. }
  382. m_pCustomMaterials.RemoveAll();
  383. }