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.

1140 lines
36 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=====================================================================================//
  6. #include "imaterialinternal.h"
  7. #include "bitmap/tgaloader.h"
  8. #include "colorspace.h"
  9. #include "materialsystem/imaterialvar.h"
  10. #include "materialsystem/itexture.h"
  11. #include <string.h>
  12. #include "materialsystem_global.h"
  13. #include "shaderapi/ishaderapi.h"
  14. #include "materialsystem/imaterialproxy.h"
  15. #include "shadersystem.h"
  16. #include "materialsystem/imaterialproxyfactory.h"
  17. #include "IHardwareConfigInternal.h"
  18. #include "utlsymbol.h"
  19. #ifndef _PS3
  20. #include <malloc.h>
  21. #endif
  22. #include "filesystem.h"
  23. #include <keyvalues.h>
  24. #include "mempool.h"
  25. #include "shaderapi/ishaderutil.h"
  26. #include "vtf/vtf.h"
  27. #include "tier1/strtools.h"
  28. #include <ctype.h>
  29. #include "utlbuffer.h"
  30. #include "mathlib/vmatrix.h"
  31. #include "texturemanager.h"
  32. #include "itextureinternal.h"
  33. #include "cmaterial_queuefriendly.h"
  34. #include "mempool.h"
  35. static IMaterialVar *CreateMaterialVarFromKeyValue( IMaterial* pMaterial, KeyValues* pKeyValue );
  36. //-----------------------------------------------------------------------------
  37. // Material SubRect implementation
  38. //-----------------------------------------------------------------------------
  39. class CMaterialSubRect : public IMaterialInternal
  40. {
  41. public:
  42. // pVMTKeyValues and pPatchKeyValues should come from LoadVMTFile()
  43. CMaterialSubRect( char const *pMaterialName, const char *pTextureGroupName, KeyValues *pVMTKeyValues, KeyValues *pPatchKeyValues, bool bAssumeCreateFromFile );
  44. virtual ~CMaterialSubRect();
  45. // IMaterial Interface
  46. const char *GetName() const;
  47. const char *GetTextureGroupName() const;
  48. int GetMappingWidth();
  49. int GetMappingHeight();
  50. bool InMaterialPage( void ) { return true; }
  51. void GetMaterialOffset( float *pOffset );
  52. void GetMaterialScale( float *pScale );
  53. IMaterial *GetMaterialPage( void ) { return m_pMaterialPage; }
  54. void IncrementReferenceCount( void );
  55. void DecrementReferenceCount( void );
  56. IMaterialVar *FindVar( char const *varName, bool *found, bool complain = true );
  57. IMaterialVar *FindVarFast( char const *pVarName, unsigned int *pToken );
  58. // Sets new VMT shader parameters for the material
  59. virtual void SetShaderAndParams( KeyValues *pKeyValues );
  60. int GetEnumerationID() const;
  61. // Maybe!
  62. void GetReflectivity( Vector& reflect ) { m_pMaterialPage->GetReflectivity( reflect ); }
  63. // IMaterialInternal Interface
  64. int GetReferenceCount( void ) const;
  65. void Precache();
  66. void Uncache( bool bPreserveVars = false );
  67. // If provided, pKeyValues and pPatchKeyValues should come from LoadVMTFile()
  68. bool PrecacheVars( KeyValues *pKeyValues = NULL, KeyValues *pPatchKeyValues = NULL, CUtlVector<FileNameHandle_t> *pIncludes = NULL );
  69. bool IsPrecached() const;
  70. bool IsPrecachedVars( ) const;
  71. bool IsManuallyCreated() const;
  72. void SetEnumerationID( int id );
  73. void AddMaterialVar( IMaterialVar *pMaterialVar );
  74. void MarkAsPreloaded( bool bSet );
  75. bool IsPreloaded() const;
  76. void ArtificialAddRef();
  77. void ArtificialRelease();
  78. //=============================
  79. // Chained to the material page.
  80. //=============================
  81. // IMaterial Interface.
  82. PreviewImageRetVal_t GetPreviewImageProperties( int *width, int *height, ImageFormat *imageFormat, bool* isTranslucent ) const
  83. { return m_pMaterialPage->GetPreviewImageProperties( width, height, imageFormat, isTranslucent ); }
  84. PreviewImageRetVal_t GetPreviewImage( unsigned char *data, int width, int height, ImageFormat imageFormat ) const
  85. { return m_pMaterialPage->GetPreviewImage( data, width, height, imageFormat ); }
  86. ShaderRenderState_t *GetRenderState() { return m_pMaterialPage->GetRenderState(); }
  87. int GetNumAnimationFrames() { return m_pMaterialPage->GetNumAnimationFrames(); }
  88. void GetLowResColorSample( float s, float t, float *color ) const { m_pMaterialPage->GetLowResColorSample( s, t, color ); }
  89. bool UsesEnvCubemap( void ) { return m_pMaterialPage->UsesEnvCubemap(); }
  90. bool NeedsSoftwareSkinning( void ) { return m_pMaterialPage->NeedsSoftwareSkinning(); }
  91. bool NeedsSoftwareLighting( void ) { return m_pMaterialPage->NeedsSoftwareLighting(); }
  92. bool NeedsTangentSpace( void ) { return m_pMaterialPage->NeedsTangentSpace(); }
  93. bool NeedsPowerOfTwoFrameBufferTexture( bool bCheckSpecificToThisFrame = true ) { return m_pMaterialPage->NeedsPowerOfTwoFrameBufferTexture( bCheckSpecificToThisFrame ); }
  94. bool NeedsFullFrameBufferTexture( bool bCheckSpecificToThisFrame = true ) { return m_pMaterialPage->NeedsFullFrameBufferTexture( bCheckSpecificToThisFrame ); }
  95. bool NeedsLightmapBlendAlpha( void ) { return m_pMaterialPage->NeedsLightmapBlendAlpha(); }
  96. void AlphaModulate( float alpha ) { m_pMaterialPage->AlphaModulate( alpha ); }
  97. void ColorModulate( float r, float g, float b ) { m_pMaterialPage->ColorModulate( r, g, b ); }
  98. float GetAlphaModulation( ) { return m_pMaterialPage->GetAlphaModulation( ); }
  99. void GetColorModulation( float *r, float *g, float *b ) { m_pMaterialPage->GetColorModulation( r, g, b ); }
  100. void SetMaterialVarFlag( MaterialVarFlags_t flag, bool on ) { m_pMaterialPage->SetMaterialVarFlag( flag, on ); }
  101. bool GetMaterialVarFlag( MaterialVarFlags_t flag ) const { return m_pMaterialPage->GetMaterialVarFlag( flag ); }
  102. bool IsTranslucent() { return m_pMaterialPage->IsTranslucent(); }
  103. bool IsTranslucentInternal( float fAlphaModulation ) const { return m_pMaterialPage->IsTranslucentInternal( fAlphaModulation ); }
  104. virtual bool IsTranslucentUnderModulation( float fAlphaModulation ) const { return m_pMaterialPage->IsTranslucentUnderModulation( fAlphaModulation ); }
  105. bool IsAlphaTested() { return m_pMaterialPage->IsAlphaTested(); }
  106. bool IsVertexLit() { return m_pMaterialPage->IsVertexLit(); }
  107. bool GetPropertyFlag( MaterialPropertyTypes_t type ) { return m_pMaterialPage->GetPropertyFlag( type ); }
  108. bool IsTwoSided() { return m_pMaterialPage->IsTwoSided(); }
  109. int GetNumPasses( void ) { return m_pMaterialPage->GetNumPasses(); }
  110. int GetTextureMemoryBytes( void ) { return m_pMaterialPage->GetTextureMemoryBytes(); }
  111. // IMaterialInternal Interface.
  112. void DrawMesh( VertexCompressionType_t vertexCompression, bool bIsAlphaModulating, bool bRenderingPreTessPatchMesh ) { m_pMaterialPage->DrawMesh( vertexCompression, bIsAlphaModulating, bRenderingPreTessPatchMesh ); }
  113. void ReloadTextures( void ) { m_pMaterialPage->ReloadTextures(); }
  114. void SetMinLightmapPageID( int pageID )
  115. {
  116. m_pMaterialPage->SetMinLightmapPageID( pageID );
  117. }
  118. void SetMaxLightmapPageID( int pageID )
  119. {
  120. m_pMaterialPage->SetMaxLightmapPageID( pageID );
  121. }
  122. int GetMinLightmapPageID( ) const { return m_pMaterialPage->GetMinLightmapPageID(); }
  123. int GetMaxLightmapPageID( ) const { return m_pMaterialPage->GetMaxLightmapPageID(); }
  124. void SetNeedsWhiteLightmap( bool val )
  125. {
  126. m_pMaterialPage->SetNeedsWhiteLightmap( val );
  127. }
  128. bool GetNeedsWhiteLightmap( ) const { return m_pMaterialPage->GetNeedsWhiteLightmap(); }
  129. IShader * GetShader() const { return m_pMaterialPage->GetShader(); }
  130. void CallBindProxy( void *proxyData, ICallQueue *pCallQueue ) { m_pMaterialPage->CallBindProxy( proxyData, pCallQueue ); }
  131. bool HasProxy( void ) const { return m_pMaterialPage->HasProxy(); }
  132. // Sets the shader associated with the material
  133. void SetShader( const char *pShaderName ) { m_pMaterialPage->SetShader( pShaderName ); }
  134. const char * GetShaderName() const { return m_pMaterialPage->GetShaderName(); }
  135. virtual void DeleteIfUnreferenced();
  136. virtual bool IsSpriteCard() { return m_pMaterialPage->IsSpriteCard(); }
  137. // Can we override this material in debug?
  138. bool NoDebugOverride() const { return m_pMaterialPage->NoDebugOverride(); }
  139. // Gets the vertex format
  140. VertexFormat_t GetVertexFormat() const { return m_pMaterialPage->GetVertexFormat(); }
  141. // diffuse bump lightmap?
  142. // bool IsUsingDiffuseBumpedLighting() const { return m_pChainMaterial->IsUsingDiffuseBumpedLighting(); }
  143. // lightmap?
  144. // bool IsUsingLightmap() const { return m_pChainMaterial->IsUsingLightmap(); }
  145. // Gets the vertex usage flags
  146. VertexFormat_t GetVertexUsage() const { return m_pMaterialPage->GetVertexUsage(); }
  147. // Debugs this material
  148. bool PerformDebugTrace() const { return m_pMaterialPage->PerformDebugTrace(); }
  149. // Are we suppressed?
  150. bool IsSuppressed() const { return m_pMaterialPage->IsSuppressed(); }
  151. // Do we use fog?
  152. bool UseFog( void ) const { return m_pMaterialPage->UseFog(); }
  153. // Should we draw?
  154. void ToggleSuppression() { m_pMaterialPage->ToggleSuppression(); }
  155. void ToggleDebugTrace() { m_pMaterialPage->ToggleDebugTrace(); }
  156. // Refresh material based on current var values
  157. void Refresh() { m_pMaterialPage->Refresh(); }
  158. void RefreshPreservingMaterialVars() { m_pMaterialPage->RefreshPreservingMaterialVars(); }
  159. // This computes the state snapshots for this material
  160. void RecomputeStateSnapshots() { m_pMaterialPage->RecomputeStateSnapshots(); }
  161. // Gets at the shader parameters
  162. int ShaderParamCount() const { return m_pMaterialPage->ShaderParamCount(); }
  163. IMaterialVar **GetShaderParams( void ) { return m_pMaterialPage->GetShaderParams(); }
  164. bool IsErrorMaterial() const { return false; }
  165. bool NeedsFixedFunctionFlashlight() const { return m_pMaterialPage->NeedsFixedFunctionFlashlight(); }
  166. virtual void DecideShouldReloadFromWhitelist( IFileList *pFileList ) { m_pMaterialPage->DecideShouldReloadFromWhitelist( pFileList ); }
  167. virtual void ReloadFromWhitelistIfMarked() { return m_pMaterialPage->ReloadFromWhitelistIfMarked(); }
  168. virtual bool WasReloadedFromWhitelist() { return m_pMaterialPage->WasReloadedFromWhitelist(); }
  169. bool IsUsingVertexID( ) const { return m_pMaterialPage->IsUsingVertexID(); }
  170. virtual void ReportVarChanged( IMaterialVar *pVar ) { m_pMaterialPage->ReportVarChanged(pVar); }
  171. virtual uint32 GetChangeID() const { return m_pMaterialPage->GetChangeID(); }
  172. virtual bool IsRealTimeVersion( void ) const { return true; }
  173. virtual IMaterialInternal *GetRealTimeVersion( void ) { return this; }
  174. virtual IMaterialInternal *GetQueueFriendlyVersion( void ) { return &m_QueueFriendlyVersion; };
  175. virtual void PrecacheMappingDimensions( void ) { m_pMaterialPage->PrecacheMappingDimensions(); }
  176. virtual void FindRepresentativeTexture( void ) { m_pMaterialPage->FindRepresentativeTexture(); }
  177. virtual void CompactMaterialVars() { ::CompactMaterialVars( m_aMaterialVars.Base(), m_aMaterialVars.Count() ); }
  178. virtual bool HasQueueFriendlyProxies() const OVERRIDE { return m_pMaterialPage->HasQueueFriendlyProxies(); }
  179. virtual bool SetTempExcluded( bool bSet, int nExcludedDimensionLimit ) { return false; }
  180. private:
  181. void ParseMaterialVars( KeyValues &keyValues );
  182. void SetupMaterialVars( void );
  183. // Do we use a UNC-specified materal name?
  184. bool UsesUNCFileName() const;
  185. IMaterialVar *GetDummyMaterialVar();
  186. private:
  187. enum
  188. {
  189. MATERIALSUBRECT_IS_PRECACHED = 0x1,
  190. MATERIALSUBRECT_VARS_IS_PRECACHED = 0x2,
  191. MATERIALSUBRECT_IS_MANUALLY_CREATED = 0x4,
  192. MATERIALSUBRECT_USES_UNC_FILENAME = 0x20,
  193. MATERIALSUBRECT_IS_PRELOADED = 0x40,
  194. MATERIALSUBRECT_ARTIFICIAL_REFCOUNT = 0x80,
  195. };
  196. // Fixed-size allocator
  197. DECLARE_FIXEDSIZE_ALLOCATOR( CMaterialSubRect );
  198. IMaterialInternal *m_pMaterialPage;
  199. IMaterialInternal *m_pModelMaterialPage;
  200. int m_iEnumID;
  201. CUtlSymbol m_symName;
  202. CUtlSymbol m_symTextureGroupName;
  203. Vector2D m_vecOffset;
  204. Vector2D m_vecScale;
  205. Vector2D m_vecSize;
  206. short m_nRefCount;
  207. unsigned int m_fLocal; // Local flags - precached etc...
  208. CUtlVector<IMaterialVar*> m_aMaterialVars;
  209. // Used only by procedural materials; it essentially is an in-memory .VMT file
  210. KeyValues *m_pVMTKeyValues;
  211. #ifdef _DEBUG
  212. // Makes it easier to see what's going on
  213. char* m_pDebugName;
  214. #endif
  215. CMaterial_QueueFriendly m_QueueFriendlyVersion;
  216. };
  217. // NOTE: This must be the last file included
  218. // Has to exist *after* fixed size allocator declaration
  219. #include "tier0/memdbgon.h"
  220. DEFINE_FIXEDSIZE_ALLOCATOR( CMaterialSubRect, 256, true );
  221. //-----------------------------------------------------------------------------
  222. // Purpose: Static create method for material subrect.
  223. //-----------------------------------------------------------------------------
  224. IMaterialInternal* IMaterialInternal::CreateMaterialSubRect( char const* pMaterialName, const char *pTextureGroupName,
  225. KeyValues *pVMTKeyValues, KeyValues *pPatchKeyValues, bool bAssumeCreateFromFile )
  226. {
  227. return new CMaterialSubRect( pMaterialName, pTextureGroupName, pVMTKeyValues, pPatchKeyValues, bAssumeCreateFromFile );
  228. }
  229. //-----------------------------------------------------------------------------
  230. // Purpose: Static destroy method for material subrect.
  231. //-----------------------------------------------------------------------------
  232. void IMaterialInternal::DestroyMaterialSubRect( IMaterialInternal* pMaterial )
  233. {
  234. if ( pMaterial )
  235. {
  236. CMaterialSubRect* pMat = static_cast<CMaterialSubRect*>( pMaterial );
  237. delete pMat;
  238. }
  239. }
  240. //-----------------------------------------------------------------------------
  241. // Purpose: Constructor
  242. //-----------------------------------------------------------------------------
  243. CMaterialSubRect::CMaterialSubRect( const char *pMaterialName, const char *pTextureGroupName,
  244. KeyValues *pVMTKeyValues, KeyValues *pPatchKeyValues, bool bAssumeCreateFromFile )
  245. {
  246. m_QueueFriendlyVersion.SetRealTimeVersion( this );
  247. // Name with extension stripped off.
  248. int len = Q_strlen( pMaterialName );
  249. char* pTemp = ( char* ) stackalloc( len + 1 );
  250. Q_strncpy( pTemp, pMaterialName, len + 1 );
  251. Q_strlower( pTemp );
  252. pTemp[ len - 4 ] = '\0';
  253. m_symName = pTemp;
  254. #ifdef _DEBUG
  255. m_pDebugName = new char[Q_strlen( pTemp ) + 1];
  256. Q_strncpy( m_pDebugName, pTemp, Q_strlen( pTemp ) + 1 );
  257. #endif
  258. m_pMaterialPage = NULL;
  259. m_pModelMaterialPage = NULL;
  260. m_iEnumID = 0;
  261. m_symTextureGroupName = pTextureGroupName;
  262. m_vecOffset.Init();
  263. m_vecScale.Init();
  264. m_vecSize.Init();
  265. m_nRefCount = 0;
  266. m_fLocal = 0;
  267. m_aMaterialVars.Purge();
  268. if ( pTemp[0] == '/' && pTemp[1] == '/' && pTemp[2] != '/' )
  269. {
  270. m_fLocal |= MATERIALSUBRECT_USES_UNC_FILENAME;
  271. }
  272. if ( !bAssumeCreateFromFile )
  273. {
  274. m_pVMTKeyValues = pVMTKeyValues;
  275. if (m_pVMTKeyValues)
  276. {
  277. m_fLocal |= MATERIALSUBRECT_IS_MANUALLY_CREATED;
  278. }
  279. // Precache immediately. We need the material page immediately.
  280. Precache();
  281. }
  282. else
  283. {
  284. m_pVMTKeyValues = NULL;
  285. PrecacheVars( pVMTKeyValues, pPatchKeyValues );
  286. Precache();
  287. }
  288. Assert( m_pMaterialPage );
  289. // Increment the material page usage counter.
  290. m_pMaterialPage->IncrementReferenceCount();
  291. if ( m_pModelMaterialPage )
  292. {
  293. m_pModelMaterialPage->IncrementReferenceCount();
  294. }
  295. }
  296. //-----------------------------------------------------------------------------
  297. // Purpose: Deconstructor
  298. //-----------------------------------------------------------------------------
  299. CMaterialSubRect::~CMaterialSubRect()
  300. {
  301. Uncache( );
  302. #if defined( DEVELOPMENT_ONLY ) || defined( ALLOW_TEXT_MODE )
  303. static bool s_bTextMode = CommandLine()->HasParm( "-textmode" );
  304. #else
  305. const bool s_bTextMode = false;
  306. #endif
  307. if( m_nRefCount != 0 && !s_bTextMode )
  308. {
  309. DevWarning( 1, "Reference Count for Material %s (%d) != 0\n", GetName(), m_nRefCount );
  310. }
  311. if ( m_pMaterialPage )
  312. {
  313. m_pMaterialPage->DecrementReferenceCount();
  314. m_pMaterialPage = NULL;
  315. }
  316. if ( m_pModelMaterialPage )
  317. {
  318. m_pModelMaterialPage->DecrementReferenceCount();
  319. m_pModelMaterialPage = NULL;
  320. }
  321. if ( m_pVMTKeyValues )
  322. {
  323. m_pVMTKeyValues->deleteThis();
  324. m_pVMTKeyValues = NULL;
  325. }
  326. #ifdef _DEBUG
  327. if ( m_pDebugName )
  328. {
  329. delete[] m_pDebugName;
  330. m_pDebugName = NULL;
  331. }
  332. #endif
  333. }
  334. //-----------------------------------------------------------------------------
  335. // Sets new VMT shader parameters for the material
  336. //-----------------------------------------------------------------------------
  337. void CMaterialSubRect::SetShaderAndParams( KeyValues *pKeyValues )
  338. {
  339. Uncache();
  340. if ( m_pVMTKeyValues )
  341. {
  342. m_pVMTKeyValues->deleteThis();
  343. m_pVMTKeyValues = NULL;
  344. }
  345. m_pVMTKeyValues = pKeyValues ? pKeyValues->MakeCopy() : NULL;
  346. if (m_pVMTKeyValues)
  347. {
  348. m_fLocal |= MATERIALSUBRECT_IS_MANUALLY_CREATED;
  349. }
  350. if ( g_pShaderDevice->IsUsingGraphics() )
  351. {
  352. Precache();
  353. }
  354. }
  355. //-----------------------------------------------------------------------------
  356. // Purpose:
  357. //-----------------------------------------------------------------------------
  358. const char *CMaterialSubRect::GetName() const
  359. {
  360. return m_symName.String();
  361. }
  362. //-----------------------------------------------------------------------------
  363. // Purpose:
  364. //-----------------------------------------------------------------------------
  365. const char *CMaterialSubRect::GetTextureGroupName() const
  366. {
  367. return m_symTextureGroupName.String();
  368. }
  369. //-----------------------------------------------------------------------------
  370. // Purpose: Return the size of the subrect not the texture page size (width).
  371. //-----------------------------------------------------------------------------
  372. int CMaterialSubRect::GetMappingWidth()
  373. {
  374. return int( m_vecSize.x );
  375. }
  376. //-----------------------------------------------------------------------------
  377. // Purpose: Return the size of the subrect not the texture page size (height).
  378. //-----------------------------------------------------------------------------
  379. int CMaterialSubRect::GetMappingHeight()
  380. {
  381. return int( m_vecSize.y );
  382. }
  383. //-----------------------------------------------------------------------------
  384. // Purpose: Return the texture offset into the texture page.
  385. //-----------------------------------------------------------------------------
  386. void CMaterialSubRect::GetMaterialOffset( float *pOffset )
  387. {
  388. pOffset[0] = m_vecOffset.x;
  389. pOffset[1] = m_vecOffset.y;
  390. }
  391. //-----------------------------------------------------------------------------
  392. // Purpose: Return the texture scale (size) within the texture page.
  393. //-----------------------------------------------------------------------------
  394. void CMaterialSubRect::GetMaterialScale( float *pScale )
  395. {
  396. pScale[0] = m_vecScale.x;
  397. pScale[1] = m_vecScale.y;
  398. }
  399. //-----------------------------------------------------------------------------
  400. // Purpose:
  401. //-----------------------------------------------------------------------------
  402. void CMaterialSubRect::IncrementReferenceCount( void )
  403. {
  404. ++m_nRefCount;
  405. }
  406. //-----------------------------------------------------------------------------
  407. // Purpose:
  408. //-----------------------------------------------------------------------------
  409. void CMaterialSubRect::DecrementReferenceCount( void )
  410. {
  411. --m_nRefCount;
  412. }
  413. //-----------------------------------------------------------------------------
  414. // Purpose:
  415. //-----------------------------------------------------------------------------
  416. int CMaterialSubRect::GetReferenceCount( void ) const
  417. {
  418. return m_nRefCount;
  419. }
  420. //-----------------------------------------------------------------------------
  421. // Purpose:
  422. //-----------------------------------------------------------------------------
  423. bool CMaterialSubRect::IsPrecached() const
  424. {
  425. return ( m_fLocal & MATERIALSUBRECT_IS_PRECACHED ) != 0;
  426. }
  427. //-----------------------------------------------------------------------------
  428. // Purpose:
  429. //-----------------------------------------------------------------------------
  430. bool CMaterialSubRect::IsPrecachedVars( ) const
  431. {
  432. return ( m_fLocal & MATERIALSUBRECT_VARS_IS_PRECACHED ) != 0;
  433. }
  434. //-----------------------------------------------------------------------------
  435. // Purpose:
  436. //-----------------------------------------------------------------------------
  437. bool CMaterialSubRect::IsManuallyCreated() const
  438. {
  439. return ( m_fLocal & MATERIALSUBRECT_IS_MANUALLY_CREATED ) != 0;
  440. }
  441. //-----------------------------------------------------------------------------
  442. // Do we use a UNC-specified materal name?
  443. //-----------------------------------------------------------------------------
  444. bool CMaterialSubRect::UsesUNCFileName() const
  445. {
  446. return ( m_fLocal & MATERIALSUBRECT_USES_UNC_FILENAME ) != 0;
  447. }
  448. //-----------------------------------------------------------------------------
  449. // Purpose:
  450. //-----------------------------------------------------------------------------
  451. void CMaterialSubRect::Precache()
  452. {
  453. // Are we already precached?
  454. if ( IsPrecached() )
  455. return;
  456. // Load data from the .vmt file.
  457. if ( !PrecacheVars() )
  458. return;
  459. m_QueueFriendlyVersion.UpdateToRealTime();
  460. // Precached.
  461. m_fLocal |= MATERIALSUBRECT_IS_PRECACHED;
  462. }
  463. //-----------------------------------------------------------------------------
  464. // Purpose:
  465. //-----------------------------------------------------------------------------
  466. bool CMaterialSubRect::PrecacheVars( KeyValues *pVMTKeyValues, KeyValues *pPatchKeyValues, CUtlVector<FileNameHandle_t> *pIncludes )
  467. {
  468. // FIXME: Should call through to the parent material for all of this???
  469. // We should get both parameters or neither
  470. Assert( ( pVMTKeyValues == NULL ) ? ( pPatchKeyValues == NULL ) : ( pPatchKeyValues != NULL ) );
  471. // Are we already precached?
  472. if( IsPrecachedVars() )
  473. return true;
  474. // load data from the vmt file
  475. bool bOk = false;
  476. KeyValues *vmtKeyValues = NULL;
  477. KeyValues *patchKeyValues = NULL;
  478. if ( m_pVMTKeyValues )
  479. {
  480. // Use the procedural KeyValues
  481. vmtKeyValues = m_pVMTKeyValues;
  482. patchKeyValues = new KeyValues( "vmt_patches" );
  483. // The caller should not be passing in KeyValues if we have procedural ones
  484. Assert( ( pVMTKeyValues == NULL ) && ( pPatchKeyValues == NULL ) );
  485. }
  486. else if ( pVMTKeyValues )
  487. {
  488. // Use the passed-in (already-loaded) KeyValues
  489. vmtKeyValues = pVMTKeyValues;
  490. patchKeyValues = pPatchKeyValues;
  491. }
  492. else
  493. {
  494. // load data from the vmt file
  495. vmtKeyValues = new KeyValues( "vmt" );
  496. patchKeyValues = new KeyValues( "vmt_patches" );
  497. if( !LoadVMTFile( *vmtKeyValues, *patchKeyValues, GetName(), UsesUNCFileName(), NULL ) )
  498. {
  499. Warning( "CMaterialSubRect::PrecacheVars: error loading vmt file for %s\n", GetName() );
  500. goto precacheVarsDone;
  501. }
  502. }
  503. // Get the "Subrect" material vars.
  504. ParseMaterialVars( *vmtKeyValues );
  505. // Setup the "Subrect" material vars.
  506. SetupMaterialVars();
  507. // Vars are precached.
  508. m_fLocal |= MATERIALSUBRECT_VARS_IS_PRECACHED;
  509. bOk = true;
  510. precacheVarsDone:
  511. // Clean up
  512. if ( ( vmtKeyValues != m_pVMTKeyValues ) && ( vmtKeyValues != pVMTKeyValues ) )
  513. {
  514. vmtKeyValues->deleteThis();
  515. }
  516. if ( patchKeyValues != pPatchKeyValues )
  517. {
  518. patchKeyValues->deleteThis();
  519. }
  520. return bOk;
  521. }
  522. //-----------------------------------------------------------------------------
  523. // Purpose:
  524. //-----------------------------------------------------------------------------
  525. void CMaterialSubRect::ParseMaterialVars( KeyValues &keyValues )
  526. {
  527. KeyValues *pKeyValues = &keyValues;
  528. // I'm not quite sure how this can happen, but we'll see...
  529. const char *pShaderName = pKeyValues->GetName();
  530. if ( !pShaderName )
  531. {
  532. DevWarning( 1, "CMaterialSubRect::InitializeShader: Shader not specified in material %s.\n", GetName() );
  533. Assert( 0 );
  534. pShaderName = IsPC() ? "Wireframe_DX6" : "Wireframe_DX9";
  535. }
  536. // Verify we have the correct "shader." There is only one type.
  537. // Needs to be case insensitive because we can't guarantee case specified in VMTs
  538. if ( !Q_stricmp( pShaderName, "subrect" ) )
  539. {
  540. KeyValues *pVar = pKeyValues->GetFirstSubKey();
  541. while ( pVar )
  542. {
  543. if ( !Q_stricmp( pVar->GetName(), "$Pos" ) )
  544. {
  545. sscanf( pVar->GetString(), "%f %f", &m_vecOffset.x, &m_vecOffset.y );
  546. }
  547. else if ( !Q_stricmp( pVar->GetName(), "$Size" ) )
  548. {
  549. sscanf( pVar->GetString(), "%f %f", &m_vecSize.x, &m_vecSize.y );
  550. }
  551. else if ( !Q_stricmp( pVar->GetName(), "$Material" ) )
  552. {
  553. m_pMaterialPage = static_cast<IMaterialInternal*>( MaterialSystem()->FindMaterial( pVar->GetString(), TEXTURE_GROUP_DECAL ) );
  554. m_pMaterialPage = m_pMaterialPage->GetRealTimeVersion(); //always work with the realtime material internally
  555. }
  556. else if ( !Q_stricmp( pVar->GetName(), "$ModelMaterial" ) )
  557. {
  558. IMaterialInternal *pMaterial = static_cast<IMaterialInternal*>( MaterialSystem()->FindMaterial( pVar->GetString(), TEXTURE_GROUP_DECAL ) );
  559. pMaterial = pMaterial->GetRealTimeVersion(); //always work with the realtime material internally
  560. if ( !pMaterial->IsErrorMaterial() )
  561. {
  562. m_pModelMaterialPage = pMaterial;
  563. }
  564. }
  565. // else if ( !Q_stricmp( pVar->GetName(), "$decalscale" ) )
  566. // {
  567. // m_flDecalScale = pVar->GetFloat();
  568. // }
  569. // Add var to list.
  570. IMaterialVar *pNewVar = CreateMaterialVarFromKeyValue( this, pVar );
  571. if ( pNewVar )
  572. {
  573. m_aMaterialVars.AddToTail( pNewVar );
  574. }
  575. // Continue getting the keys until they are all found.
  576. pVar = pVar->GetNextKey();
  577. }
  578. }
  579. }
  580. //-----------------------------------------------------------------------------
  581. // Purpose:
  582. //-----------------------------------------------------------------------------
  583. void CMaterialSubRect::SetupMaterialVars( void )
  584. {
  585. if ( !m_pMaterialPage )
  586. {
  587. DevWarning( 1, "CMaterialSubRect::SetupMaterialVars: Invalid Material Page!\n" );
  588. return;
  589. }
  590. // Ask the material page for its size, causes precache to occur
  591. int nMaterialPageWidth = m_pMaterialPage->GetMappingWidth();
  592. int nMaterialPageHeight = m_pMaterialPage->GetMappingHeight();
  593. if ( m_pModelMaterialPage )
  594. {
  595. // a subrect optionally supports a redirection for a model material
  596. // precache this now, same as the subrect's material page
  597. // otherwise, runtime load hitch when model rendering accesses
  598. m_pModelMaterialPage->GetMappingWidth();
  599. }
  600. // Normalize the offset and scale.
  601. float flOOWidth = 1.0f / static_cast<float>( nMaterialPageWidth );
  602. float flOOHeight = 1.0f / static_cast<float>( nMaterialPageHeight );
  603. // Add 0.5f to push the image "in" by 1/2 a texel, and subtract 1.0f to push it
  604. // "in" by 1/2 a texel on the other side.
  605. m_vecOffset.x += 1.0f;
  606. m_vecOffset.y += 1.0f;
  607. m_vecOffset.x *= flOOWidth;
  608. m_vecOffset.y *= flOOHeight;
  609. m_vecScale.x = ( m_vecSize.x - 2.0f ) * flOOWidth;
  610. m_vecScale.y = ( m_vecSize.y - 2.0f ) * flOOHeight;
  611. }
  612. //-----------------------------------------------------------------------------
  613. // Purpose: Look through
  614. //-----------------------------------------------------------------------------
  615. IMaterialVar *CMaterialSubRect::FindVar( char const *varName, bool *found, bool complain )
  616. {
  617. // Look for the var in the material page - it has precedence.
  618. IMaterialVar *pVar = m_pMaterialPage->FindVar( varName, found, false );
  619. if ( *found )
  620. return pVar;
  621. // Look for the var in the local list of vars.
  622. MaterialVarSym_t symVar = IMaterialVar::FindSymbol( varName );
  623. if ( symVar != UTL_INVAL_SYMBOL )
  624. {
  625. int nVarCount = m_aMaterialVars.Count();
  626. for ( int iVar = 0; iVar < nVarCount; ++iVar )
  627. {
  628. if ( m_aMaterialVars[iVar]->GetNameAsSymbol() == symVar )
  629. {
  630. *found = true;
  631. return m_aMaterialVars[iVar];
  632. }
  633. }
  634. }
  635. // Not found!
  636. if( complain )
  637. {
  638. static int complainCount = 0;
  639. if( complainCount < 100 )
  640. {
  641. DevWarning( 1, "No such variable \"%s\" for material \"%s\"\n", varName, GetName() );
  642. complainCount++;
  643. }
  644. }
  645. return GetDummyMaterialVar();
  646. }
  647. IMaterialVar *CMaterialSubRect::FindVarFast( char const *pVarName, unsigned int *pCacheData )
  648. {
  649. tokencache_t *pToken = reinterpret_cast<tokencache_t *>(pCacheData);
  650. PrecacheVars();
  651. int nVarCount = m_aMaterialVars.Count();
  652. if ( pToken->cached && pToken->subrect )
  653. {
  654. if ( pToken->varIndex < nVarCount && m_aMaterialVars[pToken->varIndex]->GetNameAsSymbol() == pToken->symbol )
  655. return m_aMaterialVars[pToken->varIndex];
  656. }
  657. // Look for the var in the material page - it has precedence.
  658. IMaterialVar *pVar = m_pMaterialPage->FindVarFast( pVarName, pCacheData );
  659. if ( pVar )
  660. return pVar;
  661. Assert( pToken->cached );
  662. if ( pToken->symbol != UTL_INVAL_SYMBOL )
  663. {
  664. for ( int iVar = 0; iVar < nVarCount; ++iVar )
  665. {
  666. if ( m_aMaterialVars[iVar]->GetNameAsSymbol() == pToken->symbol )
  667. {
  668. pToken->varIndex = iVar;
  669. pToken->subrect = true;
  670. return m_aMaterialVars[iVar];
  671. }
  672. }
  673. }
  674. return NULL;
  675. }
  676. //-----------------------------------------------------------------------------
  677. // Purpose:
  678. //-----------------------------------------------------------------------------
  679. IMaterialVar *CMaterialSubRect::GetDummyMaterialVar()
  680. {
  681. static IMaterialVar* pDummyVar = 0;
  682. if ( !pDummyVar )
  683. pDummyVar = IMaterialVar::Create( 0, "$dummyVar", 0 );
  684. return pDummyVar;
  685. }
  686. //-----------------------------------------------------------------------------
  687. // Purpose:
  688. //-----------------------------------------------------------------------------
  689. int CMaterialSubRect::GetEnumerationID() const
  690. {
  691. return m_iEnumID;
  692. }
  693. //-----------------------------------------------------------------------------
  694. // Purpose:
  695. //-----------------------------------------------------------------------------
  696. void CMaterialSubRect::SetEnumerationID( int id )
  697. {
  698. m_iEnumID = id;
  699. }
  700. //-----------------------------------------------------------------------------
  701. // Purpose:
  702. //-----------------------------------------------------------------------------
  703. void CMaterialSubRect::Uncache( bool bPreserveVars )
  704. {
  705. MaterialLock_t hMaterialLock = MaterialSystem()->Lock();
  706. // Don't bother if we're not cached
  707. if ( IsPrecached() )
  708. {
  709. m_fLocal &= ~MATERIALSUBRECT_IS_PRECACHED;
  710. }
  711. if ( !bPreserveVars )
  712. {
  713. if ( IsPrecachedVars() )
  714. {
  715. for ( int i = 0; i < m_aMaterialVars.Count(); ++i )
  716. {
  717. IMaterialVar::Destroy( m_aMaterialVars[i] );
  718. }
  719. m_aMaterialVars.Purge();
  720. m_fLocal &= ~MATERIALSUBRECT_VARS_IS_PRECACHED;
  721. }
  722. }
  723. MaterialSystem()->Unlock( hMaterialLock );
  724. }
  725. //-----------------------------------------------------------------------------
  726. // Purpose:
  727. //-----------------------------------------------------------------------------
  728. void CMaterialSubRect::AddMaterialVar( IMaterialVar *pMaterialVar )
  729. {
  730. m_aMaterialVars.AddToTail( pMaterialVar );
  731. }
  732. void CMaterialSubRect::MarkAsPreloaded( bool bSet )
  733. {
  734. if ( bSet )
  735. {
  736. m_fLocal |= MATERIALSUBRECT_IS_PRELOADED;
  737. }
  738. else
  739. {
  740. m_fLocal &= ~MATERIALSUBRECT_IS_PRELOADED;
  741. }
  742. }
  743. bool CMaterialSubRect::IsPreloaded() const
  744. {
  745. return ( m_fLocal & MATERIALSUBRECT_IS_PRELOADED ) != 0;
  746. }
  747. void CMaterialSubRect::ArtificialAddRef( void )
  748. {
  749. if ( m_fLocal & MATERIALSUBRECT_ARTIFICIAL_REFCOUNT )
  750. {
  751. // already done
  752. return;
  753. }
  754. m_fLocal |= MATERIALSUBRECT_ARTIFICIAL_REFCOUNT;
  755. m_nRefCount++;
  756. }
  757. void CMaterialSubRect::ArtificialRelease( void )
  758. {
  759. if ( !( m_fLocal & MATERIALSUBRECT_ARTIFICIAL_REFCOUNT ) )
  760. {
  761. return;
  762. }
  763. m_fLocal &= ~MATERIALSUBRECT_ARTIFICIAL_REFCOUNT;
  764. m_nRefCount--;
  765. }
  766. //-----------------------------------------------------------------------------
  767. // Parser utilities
  768. //-----------------------------------------------------------------------------
  769. static inline bool IsWhitespace( char c )
  770. {
  771. return c == ' ' || c == '\t';
  772. }
  773. static inline bool IsEndline( char c )
  774. {
  775. return c == '\n' || c == '\0';
  776. }
  777. static inline bool IsVector( char const* v )
  778. {
  779. while (IsWhitespace(*v))
  780. {
  781. ++v;
  782. if (IsEndline(*v))
  783. return false;
  784. }
  785. return *v == '[' || *v == '{';
  786. }
  787. //-----------------------------------------------------------------------------
  788. // Creates a vector material var
  789. //-----------------------------------------------------------------------------
  790. static IMaterialVar* CreateVectorMaterialVarFromKeyValue( IMaterial* pMaterial, KeyValues* pKeyValue )
  791. {
  792. float vecVal[4];
  793. char const* pScan = pKeyValue->GetString();
  794. bool divideBy255 = false;
  795. // skip whitespace
  796. while( IsWhitespace(*pScan) )
  797. {
  798. ++pScan;
  799. }
  800. if( *pScan == '{' )
  801. {
  802. divideBy255 = true;
  803. }
  804. else
  805. {
  806. Assert( *pScan == '[' );
  807. }
  808. // skip the '['
  809. ++pScan;
  810. int i;
  811. for( i = 0; i < 4; i++ )
  812. {
  813. // skip whitespace
  814. while( IsWhitespace(*pScan) )
  815. {
  816. ++pScan;
  817. }
  818. if( IsEndline(*pScan) || *pScan == ']' || *pScan == '}' )
  819. {
  820. if (*pScan != ']' && *pScan != '}')
  821. {
  822. Warning( "Warning in .VMT file (%s): no ']' or '}' found in vector key \"%s\".\n"
  823. "Did you forget to surround the vector with \"s?\n", pMaterial->GetName(), pKeyValue->GetName() );
  824. }
  825. // allow for vec2's, etc.
  826. vecVal[i] = 0.0f;
  827. break;
  828. }
  829. char* pEnd;
  830. vecVal[i] = strtod( pScan, &pEnd );
  831. if (pScan == pEnd)
  832. {
  833. Warning( "Error in .VMT file: error parsing vector element \"%s\" in \"%s\"\n", pKeyValue->GetName(), pMaterial->GetName() );
  834. return 0;
  835. }
  836. pScan = pEnd;
  837. }
  838. if( divideBy255 )
  839. {
  840. vecVal[0] *= ( 1.0f / 255.0f );
  841. vecVal[1] *= ( 1.0f / 255.0f );
  842. vecVal[2] *= ( 1.0f / 255.0f );
  843. vecVal[3] *= ( 1.0f / 255.0f );
  844. }
  845. // Create the variable!
  846. return IMaterialVar::Create( pMaterial, pKeyValue->GetName(), vecVal, i );
  847. }
  848. //-----------------------------------------------------------------------------
  849. // Creates a vector material var
  850. //-----------------------------------------------------------------------------
  851. static IMaterialVar* CreateMatrixMaterialVarFromKeyValue( IMaterial* pMaterial, KeyValues* pKeyValue )
  852. {
  853. char const* pScan = pKeyValue->GetString();
  854. // Matrices can be specified one of two ways:
  855. // [ # # # # # # # # # # # # # # # # ]
  856. // or
  857. // center # # scale # # rotate # translate # #
  858. VMatrix mat;
  859. int count = sscanf( pScan, " [ %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f ]",
  860. &mat.m[0][0], &mat.m[0][1], &mat.m[0][2], &mat.m[0][3],
  861. &mat.m[1][0], &mat.m[1][1], &mat.m[1][2], &mat.m[1][3],
  862. &mat.m[2][0], &mat.m[2][1], &mat.m[2][2], &mat.m[2][3],
  863. &mat.m[3][0], &mat.m[3][1], &mat.m[3][2], &mat.m[3][3] );
  864. if (count == 16)
  865. {
  866. return IMaterialVar::Create( pMaterial, pKeyValue->GetName(), mat );
  867. }
  868. Vector2D scale, center;
  869. float angle;
  870. Vector2D translation;
  871. //scan for pre-rotation scale and translation with assumed center syntax
  872. count = sscanf( pScan, " scale %f %f translate %f %f rotate %f",
  873. &scale.x, &scale.y, &translation.x, &translation.y, &angle );
  874. if (count == 5)
  875. {
  876. VMatrix temp;
  877. MatrixBuildTranslation( mat, translation.x - 0.5, translation.y - 0.5, 0.0f );
  878. MatrixBuildScale( temp, scale.x, scale.y, 1.0f );
  879. MatrixMultiply( mat, temp, mat );
  880. MatrixBuildRotateZ( temp, angle );
  881. MatrixMultiply( mat, temp, mat );
  882. Vector2D vOffset;
  883. vOffset.Init( 0.5f / ( scale.x != 0 ? scale.x : 1.0 ), 0.5f / ( scale.y != 0 ? scale.y : 1.0 ) );
  884. Vector2DRotate( vOffset, -angle, vOffset );
  885. MatrixBuildTranslation( temp, vOffset.x, vOffset.y, 0.0f );
  886. MatrixMultiply( mat, temp, mat );
  887. return IMaterialVar::Create( pMaterial, pKeyValue->GetName(), mat );
  888. }
  889. count = sscanf( pScan, " center %f %f scale %f %f rotate %f translate %f %f",
  890. &center.x, &center.y, &scale.x, &scale.y, &angle, &translation.x, &translation.y );
  891. if (count != 7)
  892. return NULL;
  893. VMatrix temp;
  894. MatrixBuildTranslation( mat, -center.x, -center.y, 0.0f );
  895. MatrixBuildScale( temp, scale.x, scale.y, 1.0f );
  896. MatrixMultiply( temp, mat, mat );
  897. MatrixBuildRotateZ( temp, angle );
  898. MatrixMultiply( temp, mat, mat );
  899. MatrixBuildTranslation( temp, center.x + translation.x, center.y + translation.y, 0.0f );
  900. MatrixMultiply( temp, mat, mat );
  901. // Create the variable!
  902. return IMaterialVar::Create( pMaterial, pKeyValue->GetName(), mat );
  903. }
  904. //-----------------------------------------------------------------------------
  905. // Purpose:
  906. //-----------------------------------------------------------------------------
  907. static IMaterialVar *CreateMaterialVarFromKeyValue( IMaterial* pMaterial, KeyValues* pKeyValue )
  908. {
  909. switch( pKeyValue->GetDataType() )
  910. {
  911. case KeyValues::TYPE_INT:
  912. {
  913. return IMaterialVar::Create( pMaterial, pKeyValue->GetName(), pKeyValue->GetInt() );
  914. }
  915. case KeyValues::TYPE_FLOAT:
  916. {
  917. return IMaterialVar::Create( pMaterial, pKeyValue->GetName(), pKeyValue->GetFloat() );
  918. }
  919. case KeyValues::TYPE_STRING:
  920. {
  921. char const* pString = pKeyValue->GetString();
  922. if (!pString || !pString[0])
  923. return 0;
  924. // Look for matrices
  925. IMaterialVar *pMatrixVar = CreateMatrixMaterialVarFromKeyValue( pMaterial, pKeyValue );
  926. if ( pMatrixVar )
  927. return pMatrixVar;
  928. // Look for vectors
  929. if ( !IsVector( pString ) )
  930. return IMaterialVar::Create( pMaterial, pKeyValue->GetName(), pString );
  931. // Parse the string as a vector...
  932. return CreateVectorMaterialVarFromKeyValue( pMaterial, pKeyValue );
  933. }
  934. }
  935. return 0;
  936. }
  937. void CMaterialSubRect::DeleteIfUnreferenced()
  938. {
  939. if ( m_nRefCount > 0 )
  940. return;
  941. MaterialSystem()->RemoveMaterialSubRect( this );
  942. }