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

1069 lines
34 KiB

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