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.

3599 lines
98 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Implementation of a material
  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 "mempool.h"
  32. #include "tier1/callqueue.h"
  33. #include "cmaterial_queuefriendly.h"
  34. #include "ifilelist.h"
  35. #include "tier0/icommandline.h"
  36. #include "tier0/minidump.h"
  37. // #define PROXY_TRACK_NAMES
  38. //-----------------------------------------------------------------------------
  39. // Material implementation
  40. //-----------------------------------------------------------------------------
  41. class CMaterial : public IMaterialInternal
  42. {
  43. public:
  44. // Members of the IMaterial interface
  45. const char *GetName() const;
  46. const char *GetTextureGroupName() const;
  47. PreviewImageRetVal_t GetPreviewImageProperties( int *width, int *height,
  48. ImageFormat *imageFormat, bool* isTranslucent ) const;
  49. PreviewImageRetVal_t GetPreviewImage( unsigned char *data, int width, int height,
  50. ImageFormat imageFormat ) const;
  51. int GetMappingWidth( );
  52. int GetMappingHeight( );
  53. int GetNumAnimationFrames( );
  54. bool InMaterialPage( void ) { return false; }
  55. void GetMaterialOffset( float *pOffset );
  56. void GetMaterialScale( float *pOffset );
  57. IMaterial *GetMaterialPage( void ) { return NULL; }
  58. void IncrementReferenceCount( );
  59. void DecrementReferenceCount( );
  60. int GetEnumerationID( ) const;
  61. void GetLowResColorSample( float s, float t, float *color ) const;
  62. IMaterialVar * FindVar( char const *varName, bool *found, bool complain = true );
  63. IMaterialVar * FindVarFast( char const *pVarName, unsigned int *pToken );
  64. // Sets new VMT shader parameters for the material
  65. virtual void SetShaderAndParams( KeyValues *pKeyValues );
  66. bool UsesEnvCubemap( void );
  67. bool NeedsSoftwareSkinning( void );
  68. virtual bool NeedsSoftwareLighting( void );
  69. bool NeedsTangentSpace( void );
  70. bool NeedsPowerOfTwoFrameBufferTexture( bool bCheckSpecificToThisFrame = true );
  71. bool NeedsFullFrameBufferTexture( bool bCheckSpecificToThisFrame = true );
  72. virtual bool IsUsingVertexID( ) const;
  73. // GR - Is lightmap alpha needed?
  74. bool NeedsLightmapBlendAlpha( void );
  75. virtual void AlphaModulate( float alpha );
  76. virtual void ColorModulate( float r, float g, float b );
  77. virtual float GetAlphaModulation();
  78. virtual void GetColorModulation( float *r, float *g, float *b );
  79. // Gets the morph format
  80. virtual MorphFormat_t GetMorphFormat() const;
  81. void SetMaterialVarFlag( MaterialVarFlags_t flag, bool on );
  82. bool GetMaterialVarFlag( MaterialVarFlags_t flag ) const;
  83. bool IsTranslucent();
  84. bool IsTranslucentInternal( float fAlphaModulation ) const; //need to centralize the logic without relying on the *current* alpha modulation being that which is stored in m_pShaderParams[ALPHA].
  85. bool IsAlphaTested();
  86. bool IsVertexLit();
  87. virtual bool IsSpriteCard();
  88. void GetReflectivity( Vector& reflect );
  89. bool GetPropertyFlag( MaterialPropertyTypes_t type );
  90. // Is the material visible from both sides?
  91. bool IsTwoSided();
  92. int GetNumPasses( void );
  93. int GetTextureMemoryBytes( void );
  94. void SetUseFixedFunctionBakedLighting( bool bEnable );
  95. virtual bool IsPrecached( ) const;
  96. public:
  97. // stuff that is visible only from within the material system
  98. // constructor, destructor
  99. CMaterial( char const* materialName, const char *pTextureGroupName, KeyValues *pVMTKeyValues );
  100. virtual ~CMaterial();
  101. void DrawMesh( VertexCompressionType_t vertexCompression );
  102. int GetReferenceCount( ) const;
  103. void Uncache( bool bPreserveVars = false );
  104. void Precache();
  105. void ReloadTextures( void );
  106. // If provided, pKeyValues and pPatchKeyValues should come from LoadVMTFile()
  107. bool PrecacheVars( KeyValues *pKeyValues = NULL, KeyValues *pPatchKeyValues = NULL, CUtlVector<FileNameHandle_t> *pIncludes = NULL, int nFindContext = MATERIAL_FINDCONTEXT_NONE );
  108. void SetMinLightmapPageID( int pageID );
  109. void SetMaxLightmapPageID( int pageID );
  110. int GetMinLightmapPageID( ) const;
  111. int GetMaxLightmapPageID( ) const;
  112. void SetNeedsWhiteLightmap( bool val );
  113. bool GetNeedsWhiteLightmap( ) const;
  114. bool IsPrecachedVars( ) const;
  115. IShader * GetShader() const;
  116. const char *GetShaderName() const;
  117. virtual void DeleteIfUnreferenced();
  118. void SetEnumerationID( int id );
  119. void CallBindProxy( void *proxyData );
  120. virtual IMaterial *CheckProxyReplacement( void *proxyData );
  121. bool HasProxy( void ) const;
  122. // Sets the shader associated with the material
  123. void SetShader( const char *pShaderName );
  124. // Can we override this material in debug?
  125. bool NoDebugOverride() const;
  126. // Gets the vertex format
  127. VertexFormat_t GetVertexFormat() const;
  128. // diffuse bump lightmap?
  129. bool IsUsingDiffuseBumpedLighting() const;
  130. // lightmap?
  131. bool IsUsingLightmap() const;
  132. // Gets the vertex usage flags
  133. VertexFormat_t GetVertexUsage() const;
  134. // Debugs this material
  135. bool PerformDebugTrace() const;
  136. // Are we suppressed?
  137. bool IsSuppressed() const;
  138. // Do we use fog?
  139. bool UseFog( void ) const;
  140. // Should we draw?
  141. void ToggleSuppression();
  142. void ToggleDebugTrace();
  143. // Refresh material based on current var values
  144. void Refresh();
  145. void RefreshPreservingMaterialVars();
  146. // This computes the state snapshots for this material
  147. void RecomputeStateSnapshots();
  148. // Gets at the shader parameters
  149. virtual int ShaderParamCount() const;
  150. virtual IMaterialVar **GetShaderParams( void );
  151. virtual void AddMaterialVar( IMaterialVar *pMaterialVar );
  152. virtual bool IsErrorMaterial() const;
  153. // Was this manually created (not read from a file?)
  154. virtual bool IsManuallyCreated() const;
  155. virtual bool NeedsFixedFunctionFlashlight() const;
  156. virtual void MarkAsPreloaded( bool bSet );
  157. virtual bool IsPreloaded() const;
  158. virtual void ArtificialAddRef( void );
  159. virtual void ArtificialRelease( void );
  160. virtual void ReportVarChanged( IMaterialVar *pVar )
  161. {
  162. m_ChangeID++;
  163. }
  164. virtual void ClearContextData( void );
  165. virtual uint32 GetChangeID() const { return m_ChangeID; }
  166. virtual bool IsRealTimeVersion( void ) const { return true; }
  167. virtual IMaterialInternal *GetRealTimeVersion( void ) { return this; }
  168. virtual IMaterialInternal *GetQueueFriendlyVersion( void ) { return &m_QueueFriendlyVersion; }
  169. void DecideShouldReloadFromWhitelist( IFileList *pFilesToReload );
  170. void ReloadFromWhitelistIfMarked();
  171. bool WasReloadedFromWhitelist();
  172. private:
  173. // Initializes, cleans up the shader params
  174. void CleanUpShaderParams();
  175. // Sets up an error shader when we run into problems.
  176. void SetupErrorShader();
  177. // Does this material have a UNC-file name?
  178. bool UsesUNCFileName() const;
  179. // Prints material flags.
  180. void PrintMaterialFlags( int flags, int flagsDefined );
  181. // Parses material flags
  182. bool ParseMaterialFlag( KeyValues* pParseValue, IMaterialVar* pFlagVar,
  183. IMaterialVar* pFlagDefinedVar, bool parsingOverrides, int& flagMask, int& overrideMask );
  184. // Computes the material vars for the shader
  185. int ParseMaterialVars( IShader* pShader, KeyValues& keyValues,
  186. KeyValues* pOverride, bool modelDefault, IMaterialVar** ppVars, int nFindContext = MATERIAL_FINDCONTEXT_NONE );
  187. // Figures out the preview image for worldcraft
  188. char const* GetPreviewImageName( );
  189. char const* GetPreviewImageFileName( void ) const;
  190. // Hooks up the shader, returns keyvalues of fallback that was used
  191. KeyValues* InitializeShader( KeyValues &keyValues, KeyValues &patchKeyValues, int nFindContext = MATERIAL_FINDCONTEXT_NONE );
  192. // Finds the flag associated with a particular flag name
  193. int FindMaterialVarFlag( char const* pFlagName ) const;
  194. // Initializes, cleans up the state snapshots
  195. bool InitializeStateSnapshots();
  196. void CleanUpStateSnapshots();
  197. // Initializes, cleans up the material proxy
  198. void InitializeMaterialProxy( KeyValues* pFallbackKeyValues );
  199. void CleanUpMaterialProxy();
  200. void DetermineProxyReplacements( KeyValues *pFallbackKeyValues );
  201. // Creates, destroys snapshots
  202. RenderPassList_t *CreateRenderPassList();
  203. void DestroyRenderPassList( RenderPassList_t *pPassList );
  204. // Grabs the texture width and height from the var list for faster access
  205. void PrecacheMappingDimensions( );
  206. // Gets the renderstate
  207. virtual ShaderRenderState_t *GetRenderState();
  208. // Do we have a valid renderstate?
  209. bool IsValidRenderState() const;
  210. // Get the material var flags
  211. int GetMaterialVarFlags() const;
  212. void SetMaterialVarFlags( int flags, bool on );
  213. int GetMaterialVarFlags2() const;
  214. void SetMaterialVarFlags2( int flags, bool on );
  215. // Returns a dummy material variable
  216. IMaterialVar* GetDummyVariable();
  217. IMaterialVar* GetShaderParam( int id );
  218. void FindRepresentativeTexture( void );
  219. bool ShouldSkipVar( KeyValues *pMaterialVar, bool * pWasConditional );
  220. // Fixed-size allocator
  221. DECLARE_FIXEDSIZE_ALLOCATOR( CMaterial );
  222. private:
  223. enum
  224. {
  225. MATERIAL_NEEDS_WHITE_LIGHTMAP = 0x1,
  226. MATERIAL_IS_PRECACHED = 0x2,
  227. MATERIAL_VARS_IS_PRECACHED = 0x4,
  228. MATERIAL_VALID_RENDERSTATE = 0x8,
  229. MATERIAL_IS_MANUALLY_CREATED = 0x10,
  230. MATERIAL_USES_UNC_FILENAME = 0x20,
  231. MATERIAL_IS_PRELOADED = 0x40,
  232. MATERIAL_ARTIFICIAL_REFCOUNT = 0x80,
  233. };
  234. int m_iEnumerationID;
  235. int m_minLightmapPageID;
  236. int m_maxLightmapPageID;
  237. unsigned short m_MappingWidth;
  238. unsigned short m_MappingHeight;
  239. IShader *m_pShader;
  240. CUtlSymbol m_Name;
  241. // Any textures created for this material go under this texture group.
  242. CUtlSymbol m_TextureGroupName;
  243. CInterlockedInt m_RefCount;
  244. unsigned short m_Flags;
  245. unsigned char m_VarCount;
  246. CUtlVector< IMaterialProxy * > m_ProxyInfo;
  247. #ifdef PROXY_TRACK_NAMES
  248. // Array to track names of above material proxies. Useful for tracking down issues with proxies.
  249. CUtlVector< CUtlString > m_ProxyInfoNames;
  250. #endif
  251. IMaterialVar** m_pShaderParams;
  252. IMaterialProxy *m_pReplacementProxy;
  253. ShaderRenderState_t m_ShaderRenderState;
  254. // This remembers filenames of VMTs that we included so we can sv_pure/flush ourselves if any of them need to be reloaded.
  255. CUtlVector<FileNameHandle_t> m_VMTIncludes;
  256. bool m_bShouldReloadFromWhitelist; // Tells us if the material decided it should be reloaded due to sv_pure whitelist changes.
  257. ITextureInternal *m_representativeTexture;
  258. Vector m_Reflectivity;
  259. uint32 m_ChangeID;
  260. // Used only by procedural materials; it essentially is an in-memory .VMT file
  261. KeyValues *m_pVMTKeyValues;
  262. #if defined( _DEBUG )
  263. // Makes it easier to see what's going on
  264. char *m_pDebugName;
  265. #endif
  266. protected:
  267. CMaterial_QueueFriendly m_QueueFriendlyVersion;
  268. };
  269. // NOTE: This must be the last file included
  270. // Has to exist *after* fixed size allocator declaration
  271. #include "tier0/memdbgon.h"
  272. // Forward decls of helper functions for dealing with patch vmts.
  273. static void ApplyPatchKeyValues( KeyValues &keyValues, KeyValues &patchKeyValues );
  274. static bool AccumulateRecursiveVmtPatches( KeyValues &patchKeyValuesOut, KeyValues **ppBaseKeyValuesOut,
  275. const KeyValues& keyValues, const char *pPathID, CUtlVector<FileNameHandle_t> *pIncludes );
  276. //-----------------------------------------------------------------------------
  277. // Parser utilities
  278. //-----------------------------------------------------------------------------
  279. static inline bool IsWhitespace( char c )
  280. {
  281. return c == ' ' || c == '\t';
  282. }
  283. static inline bool IsEndline( char c )
  284. {
  285. return c == '\n' || c == '\0';
  286. }
  287. static inline bool IsVector( char const* v )
  288. {
  289. while (IsWhitespace(*v))
  290. {
  291. ++v;
  292. if (IsEndline(*v))
  293. return false;
  294. }
  295. return *v == '[' || *v == '{';
  296. }
  297. //-----------------------------------------------------------------------------
  298. // Methods to create state snapshots
  299. //-----------------------------------------------------------------------------
  300. #include "tier0/memdbgoff.h"
  301. #ifndef _CONSOLE
  302. struct EditorRenderStateList_t
  303. {
  304. // Store combo of alpha, color, fixed-function baked lighting, flashlight, editor mode
  305. RenderPassList_t m_Snapshots[SNAPSHOT_COUNT_EDITOR];
  306. DECLARE_FIXEDSIZE_ALLOCATOR( EditorRenderStateList_t );
  307. };
  308. #endif
  309. struct StandardRenderStateList_t
  310. {
  311. // Store combo of alpha, color, fixed-function baked lighting, flashlight
  312. RenderPassList_t m_Snapshots[SNAPSHOT_COUNT_NORMAL];
  313. DECLARE_FIXEDSIZE_ALLOCATOR( StandardRenderStateList_t );
  314. };
  315. #include "tier0/memdbgon.h"
  316. #ifndef _CONSOLE
  317. DEFINE_FIXEDSIZE_ALLOCATOR( EditorRenderStateList_t, 256, true );
  318. #endif
  319. DEFINE_FIXEDSIZE_ALLOCATOR( StandardRenderStateList_t, 256, true );
  320. //-----------------------------------------------------------------------------
  321. // class factory methods
  322. //-----------------------------------------------------------------------------
  323. DEFINE_FIXEDSIZE_ALLOCATOR( CMaterial, 256, true );
  324. IMaterialInternal* IMaterialInternal::CreateMaterial( char const* pMaterialName, const char *pTextureGroupName, KeyValues *pVMTKeyValues )
  325. {
  326. MaterialLock_t hMaterialLock = MaterialSystem()->Lock();
  327. IMaterialInternal *pResult = new CMaterial( pMaterialName, pTextureGroupName, pVMTKeyValues );
  328. MaterialSystem()->Unlock( hMaterialLock );
  329. return pResult;
  330. }
  331. void IMaterialInternal::DestroyMaterial( IMaterialInternal* pMaterial )
  332. {
  333. MaterialLock_t hMaterialLock = MaterialSystem()->Lock();
  334. if (pMaterial)
  335. {
  336. Assert( pMaterial->IsRealTimeVersion() );
  337. CMaterial* pMatImp = static_cast<CMaterial*>(pMaterial);
  338. // Deletion of the error material is deferred until after all other materials have been deleted.
  339. // See CMaterialSystem::CleanUpErrorMaterial() in cmaterialsystem.cpp.
  340. if ( !pMatImp->IsErrorMaterial() )
  341. {
  342. delete pMatImp;
  343. }
  344. }
  345. MaterialSystem()->Unlock( hMaterialLock );
  346. }
  347. //-----------------------------------------------------------------------------
  348. // constructor, destructor
  349. //-----------------------------------------------------------------------------
  350. CMaterial::CMaterial( char const* materialName, const char *pTextureGroupName, KeyValues *pKeyValues )
  351. {
  352. m_Reflectivity.Init( 0.2f, 0.2f, 0.2f );
  353. int len = Q_strlen(materialName);
  354. char* pTemp = (char*)_alloca( len + 1 );
  355. // Strip off the extension
  356. Q_StripExtension( materialName, pTemp, len+1 );
  357. Q_strlower( pTemp );
  358. #if defined( _X360 )
  359. // material names are expected to be forward slashed for correct sort and find behavior!
  360. // assert now to track alternate or regressed path that is source of inconsistency
  361. Assert( strchr( pTemp, '\\' ) == NULL );
  362. #endif
  363. // Convert it to a symbol
  364. m_Name = pTemp;
  365. #if defined( _DEBUG )
  366. m_pDebugName = new char[strlen(pTemp) + 1];
  367. Q_strncpy( m_pDebugName, pTemp, strlen(pTemp) + 1 );
  368. #endif
  369. m_bShouldReloadFromWhitelist = false;
  370. m_Flags = 0;
  371. m_pShader = NULL;
  372. m_pShaderParams = NULL;
  373. m_RefCount = 0;
  374. m_representativeTexture = NULL;
  375. m_pReplacementProxy = NULL;
  376. m_VarCount = 0;
  377. m_MappingWidth = m_MappingHeight = 0;
  378. m_iEnumerationID = 0;
  379. m_minLightmapPageID = m_maxLightmapPageID = 0;
  380. m_TextureGroupName = pTextureGroupName;
  381. m_pVMTKeyValues = pKeyValues;
  382. if (m_pVMTKeyValues)
  383. {
  384. m_Flags |= MATERIAL_IS_MANUALLY_CREATED;
  385. }
  386. if ( pTemp[0] == '/' && pTemp[1] == '/' && pTemp[2] != '/' )
  387. {
  388. m_Flags |= MATERIAL_USES_UNC_FILENAME;
  389. }
  390. // Initialize the renderstate to something indicating nothing should be drawn
  391. m_ShaderRenderState.m_Flags = 0;
  392. m_ShaderRenderState.m_VertexFormat = m_ShaderRenderState.m_VertexUsage = 0;
  393. m_ShaderRenderState.m_MorphFormat = 0;
  394. m_ShaderRenderState.m_pSnapshots = CreateRenderPassList();
  395. m_ChangeID = 0;
  396. m_QueueFriendlyVersion.SetRealTimeVersion( this );
  397. }
  398. CMaterial::~CMaterial()
  399. {
  400. MaterialSystem()->UnbindMaterial( this );
  401. Uncache();
  402. if ( m_RefCount != 0 )
  403. {
  404. DevWarning( 2, "Reference Count for Material %s (%d) != 0\n", GetName(), (int) m_RefCount );
  405. }
  406. if ( m_pVMTKeyValues )
  407. {
  408. m_pVMTKeyValues->deleteThis();
  409. m_pVMTKeyValues = NULL;
  410. }
  411. DestroyRenderPassList( m_ShaderRenderState.m_pSnapshots );
  412. m_representativeTexture = NULL;
  413. #if defined( _DEBUG )
  414. delete [] m_pDebugName;
  415. #endif
  416. // Deliberately stomp our VTable so that we can detect cases where code tries to access freed materials.
  417. int *p = (int *)this;
  418. *p = 0xc0dedbad;
  419. }
  420. void CMaterial::ClearContextData( void )
  421. {
  422. int nSnapshotCount = SnapshotTypeCount();
  423. for( int i = 0 ; i < nSnapshotCount ; i++ )
  424. for( int j = 0 ; j < m_ShaderRenderState.m_pSnapshots[i].m_nPassCount; j++ )
  425. {
  426. if ( m_ShaderRenderState.m_pSnapshots[i].m_pContextData[j] )
  427. {
  428. delete m_ShaderRenderState.m_pSnapshots[i].m_pContextData[j];
  429. m_ShaderRenderState.m_pSnapshots[i].m_pContextData[j] = NULL;
  430. }
  431. }
  432. }
  433. //-----------------------------------------------------------------------------
  434. // Sets new VMT shader parameters for the material
  435. //-----------------------------------------------------------------------------
  436. void CMaterial::SetShaderAndParams( KeyValues *pKeyValues )
  437. {
  438. Uncache();
  439. if ( m_pVMTKeyValues )
  440. {
  441. m_pVMTKeyValues->deleteThis();
  442. m_pVMTKeyValues = NULL;
  443. }
  444. m_pVMTKeyValues = pKeyValues ? pKeyValues->MakeCopy() : NULL;
  445. if (m_pVMTKeyValues)
  446. {
  447. m_Flags |= MATERIAL_IS_MANUALLY_CREATED;
  448. }
  449. // Apply patches
  450. const char *pMaterialName = GetName();
  451. char pFileName[MAX_PATH];
  452. const char *pPathID = "GAME";
  453. if ( !UsesUNCFileName() )
  454. {
  455. Q_snprintf( pFileName, sizeof( pFileName ), "materials/%s.vmt", pMaterialName );
  456. }
  457. else
  458. {
  459. Q_snprintf( pFileName, sizeof( pFileName ), "%s.vmt", pMaterialName );
  460. if ( pMaterialName[0] == '/' && pMaterialName[1] == '/' && pMaterialName[2] != '/' )
  461. {
  462. // UNC, do full search
  463. pPathID = NULL;
  464. }
  465. }
  466. KeyValues *pLoadedKeyValues = new KeyValues( "vmt" );
  467. if ( pLoadedKeyValues->LoadFromFile( g_pFullFileSystem, pFileName, pPathID ) )
  468. {
  469. // Load succeeded, check if it's a patch file
  470. if ( V_stricmp( pLoadedKeyValues->GetName(), "patch" ) == 0 )
  471. {
  472. // it's a patch file, recursively build up patch keyvalues
  473. KeyValues *pPatchKeyValues = new KeyValues( "vmt_patch" );
  474. bool bSuccess = AccumulateRecursiveVmtPatches( *pPatchKeyValues, NULL, *pLoadedKeyValues, pPathID, NULL );
  475. if ( bSuccess )
  476. {
  477. // Apply accumulated patches to final vmt
  478. ApplyPatchKeyValues( *m_pVMTKeyValues, *pPatchKeyValues );
  479. }
  480. pPatchKeyValues->deleteThis();
  481. }
  482. }
  483. pLoadedKeyValues->deleteThis();
  484. if ( g_pShaderDevice->IsUsingGraphics() )
  485. {
  486. Precache();
  487. }
  488. }
  489. //-----------------------------------------------------------------------------
  490. // Creates, destroys snapshots
  491. //-----------------------------------------------------------------------------
  492. RenderPassList_t *CMaterial::CreateRenderPassList()
  493. {
  494. RenderPassList_t *pRenderPassList;
  495. if ( IsConsole() || !MaterialSystem()->CanUseEditorMaterials() )
  496. {
  497. StandardRenderStateList_t *pList = new StandardRenderStateList_t;
  498. pRenderPassList = (RenderPassList_t*)pList->m_Snapshots;
  499. }
  500. #ifndef _CONSOLE
  501. else
  502. {
  503. EditorRenderStateList_t *pList = new EditorRenderStateList_t;
  504. pRenderPassList = (RenderPassList_t*)pList->m_Snapshots;
  505. }
  506. #endif
  507. int nSnapshotCount = SnapshotTypeCount();
  508. memset( pRenderPassList, 0, nSnapshotCount * sizeof(RenderPassList_t) );
  509. return pRenderPassList;
  510. }
  511. void CMaterial::DestroyRenderPassList( RenderPassList_t *pPassList )
  512. {
  513. if ( !pPassList )
  514. return;
  515. int nSnapshotCount = SnapshotTypeCount();
  516. for( int i = 0 ; i < nSnapshotCount ; i++ )
  517. for( int j = 0 ; j < pPassList[i].m_nPassCount; j++ )
  518. {
  519. if ( pPassList[i].m_pContextData[j] )
  520. {
  521. delete pPassList[i].m_pContextData[j];
  522. pPassList[i].m_pContextData[j] = NULL;
  523. }
  524. }
  525. if ( IsConsole() || !MaterialSystem()->CanUseEditorMaterials() )
  526. {
  527. StandardRenderStateList_t *pList = (StandardRenderStateList_t*)pPassList;
  528. delete pList;
  529. }
  530. #ifndef _CONSOLE
  531. else
  532. {
  533. EditorRenderStateList_t *pList = (EditorRenderStateList_t*)pPassList;
  534. delete pList;
  535. }
  536. #endif
  537. }
  538. //-----------------------------------------------------------------------------
  539. // Gets the renderstate
  540. //-----------------------------------------------------------------------------
  541. ShaderRenderState_t *CMaterial::GetRenderState()
  542. {
  543. Precache();
  544. return &m_ShaderRenderState;
  545. }
  546. //-----------------------------------------------------------------------------
  547. // Returns a dummy material variable
  548. //-----------------------------------------------------------------------------
  549. IMaterialVar* CMaterial::GetDummyVariable()
  550. {
  551. static IMaterialVar* pDummyVar = 0;
  552. if (!pDummyVar)
  553. pDummyVar = IMaterialVar::Create( 0, "$dummyVar", 0 );
  554. return pDummyVar;
  555. }
  556. //-----------------------------------------------------------------------------
  557. // Are vars precached?
  558. //-----------------------------------------------------------------------------
  559. bool CMaterial::IsPrecachedVars( ) const
  560. {
  561. return (m_Flags & MATERIAL_VARS_IS_PRECACHED) != 0;
  562. }
  563. //-----------------------------------------------------------------------------
  564. // Are we precached?
  565. //-----------------------------------------------------------------------------
  566. bool CMaterial::IsPrecached( ) const
  567. {
  568. return (m_Flags & MATERIAL_IS_PRECACHED) != 0;
  569. }
  570. //-----------------------------------------------------------------------------
  571. // Cleans up shader parameters
  572. //-----------------------------------------------------------------------------
  573. void CMaterial::CleanUpShaderParams()
  574. {
  575. if( m_pShaderParams )
  576. {
  577. for (int i = 0; i < m_VarCount; ++i)
  578. {
  579. IMaterialVar::Destroy( m_pShaderParams[i] );
  580. }
  581. free( m_pShaderParams );
  582. m_pShaderParams = 0;
  583. }
  584. m_VarCount = 0;
  585. }
  586. //-----------------------------------------------------------------------------
  587. // Initializes the material proxy
  588. //-----------------------------------------------------------------------------
  589. void CMaterial::InitializeMaterialProxy( KeyValues* pFallbackKeyValues )
  590. {
  591. IMaterialProxyFactory *pMaterialProxyFactory;
  592. pMaterialProxyFactory = MaterialSystem()->GetMaterialProxyFactory();
  593. if( !pMaterialProxyFactory )
  594. return;
  595. DetermineProxyReplacements( pFallbackKeyValues );
  596. if ( m_pReplacementProxy )
  597. {
  598. m_ProxyInfo.AddToTail( m_pReplacementProxy );
  599. #ifdef PROXY_TRACK_NAMES
  600. m_ProxyInfoNames.AddToTail( "__replacementproxy" );
  601. #endif
  602. }
  603. // See if we've got a proxy section; obey fallbacks
  604. KeyValues* pProxySection = pFallbackKeyValues->FindKey("Proxies");
  605. if ( pProxySection )
  606. {
  607. // Iterate through the section + create all of the proxies
  608. KeyValues* pProxyKey = pProxySection->GetFirstSubKey();
  609. for ( ; pProxyKey; pProxyKey = pProxyKey->GetNextKey() )
  610. {
  611. // Each of the proxies should themselves be databases
  612. IMaterialProxy* pProxy = pMaterialProxyFactory->CreateProxy( pProxyKey->GetName() );
  613. if (!pProxy)
  614. {
  615. Warning( "Error: Material \"%s\" : proxy \"%s\" not found!\n", GetName(), pProxyKey->GetName() );
  616. continue;
  617. }
  618. if (!pProxy->Init( this->GetQueueFriendlyVersion(), pProxyKey ))
  619. {
  620. pMaterialProxyFactory->DeleteProxy( pProxy );
  621. Warning( "Error: Material \"%s\" : proxy \"%s\" unable to initialize!\n", GetName(), pProxyKey->GetName() );
  622. }
  623. else
  624. {
  625. m_ProxyInfo.AddToTail( pProxy );
  626. #ifdef PROXY_TRACK_NAMES
  627. m_ProxyInfoNames.AddToTail( pProxyKey->GetName() );
  628. #endif
  629. }
  630. }
  631. }
  632. }
  633. //-----------------------------------------------------------------------------
  634. // Cleans up the material proxy
  635. //-----------------------------------------------------------------------------
  636. void CMaterial::CleanUpMaterialProxy()
  637. {
  638. if ( !m_ProxyInfo.Count() )
  639. return;
  640. IMaterialProxyFactory *pMaterialProxyFactory;
  641. pMaterialProxyFactory = MaterialSystem()->GetMaterialProxyFactory();
  642. if ( !pMaterialProxyFactory )
  643. return;
  644. // Clean up material proxies
  645. for ( int i = m_ProxyInfo.Count() - 1; i >= 0; i-- )
  646. {
  647. IMaterialProxy *pProxy = m_ProxyInfo[ i ];
  648. pMaterialProxyFactory->DeleteProxy( pProxy );
  649. }
  650. m_ProxyInfo.RemoveAll();
  651. #ifdef PROXY_TRACK_NAMES
  652. m_ProxyInfoNames.RemoveAll();
  653. #endif
  654. }
  655. void CMaterial::DetermineProxyReplacements( KeyValues *pFallbackKeyValues )
  656. {
  657. m_pReplacementProxy = MaterialSystem()->DetermineProxyReplacements( this, pFallbackKeyValues );
  658. }
  659. static char const *GetVarName( KeyValues *pVar )
  660. {
  661. char const *pVarName = pVar->GetName();
  662. char const *pQuestion = strchr( pVarName, '?' );
  663. if (! pQuestion )
  664. return pVarName;
  665. else
  666. return pQuestion + 1;
  667. }
  668. //-----------------------------------------------------------------------------
  669. // Finds the index of the material var associated with a var
  670. //-----------------------------------------------------------------------------
  671. static int FindMaterialVar( IShader* pShader, char const* pVarName )
  672. {
  673. if ( !pShader )
  674. return -1;
  675. // Strip preceeding spaces
  676. pVarName += strspn( pVarName, " \t" );
  677. for (int i = pShader->GetNumParams(); --i >= 0; )
  678. {
  679. // Makes the parser a little more lenient.. strips off bogus spaces in the var name.
  680. const char *pParamName = pShader->GetParamName(i);
  681. const char *pFound = Q_stristr( pVarName, pParamName );
  682. // The found string had better start with the first non-whitespace character
  683. if ( pFound != pVarName )
  684. continue;
  685. // Strip spaces at the end
  686. int nLen = Q_strlen( pParamName );
  687. pFound += nLen;
  688. while ( true )
  689. {
  690. if ( !pFound[0] )
  691. return i;
  692. if ( !IsWhitespace( pFound[0] ) )
  693. break;
  694. ++pFound;
  695. }
  696. }
  697. return -1;
  698. }
  699. //-----------------------------------------------------------------------------
  700. // Creates a vector material var
  701. //-----------------------------------------------------------------------------
  702. int ParseVectorFromKeyValueString( KeyValues *pKeyValue, const char *pMaterialName, float vecVal[4] )
  703. {
  704. char const* pScan = pKeyValue->GetString();
  705. bool divideBy255 = false;
  706. // skip whitespace
  707. while( IsWhitespace(*pScan) )
  708. {
  709. ++pScan;
  710. }
  711. if( *pScan == '{' )
  712. {
  713. divideBy255 = true;
  714. }
  715. else
  716. {
  717. Assert( *pScan == '[' );
  718. }
  719. // skip the '['
  720. ++pScan;
  721. int i;
  722. for( i = 0; i < 4; i++ )
  723. {
  724. // skip whitespace
  725. while( IsWhitespace(*pScan) )
  726. {
  727. ++pScan;
  728. }
  729. if( IsEndline(*pScan) || *pScan == ']' || *pScan == '}' )
  730. {
  731. if (*pScan != ']' && *pScan != '}')
  732. {
  733. Warning( "Warning in .VMT file (%s): no ']' or '}' found in vector key \"%s\".\n"
  734. "Did you forget to surround the vector with \"s?\n", pMaterialName, pKeyValue->GetName() );
  735. }
  736. // allow for vec2's, etc.
  737. vecVal[i] = 0.0f;
  738. break;
  739. }
  740. char* pEnd;
  741. vecVal[i] = strtod( pScan, &pEnd );
  742. if (pScan == pEnd)
  743. {
  744. Warning( "Error in .VMT file: error parsing vector element \"%s\" in \"%s\"\n", pKeyValue->GetName(), pMaterialName );
  745. return 0;
  746. }
  747. pScan = pEnd;
  748. }
  749. if( divideBy255 )
  750. {
  751. vecVal[0] *= ( 1.0f / 255.0f );
  752. vecVal[1] *= ( 1.0f / 255.0f );
  753. vecVal[2] *= ( 1.0f / 255.0f );
  754. vecVal[3] *= ( 1.0f / 255.0f );
  755. }
  756. return i;
  757. }
  758. static IMaterialVar* CreateVectorMaterialVarFromKeyValue( IMaterial* pMaterial, KeyValues* pKeyValue )
  759. {
  760. char const *pszName = GetVarName( pKeyValue );
  761. float vecVal[4];
  762. int nDim = ParseVectorFromKeyValueString( pKeyValue, pszName, vecVal );
  763. if ( nDim == 0 )
  764. return NULL;
  765. // Create the variable!
  766. return IMaterialVar::Create( pMaterial, pszName, vecVal, nDim );
  767. }
  768. //-----------------------------------------------------------------------------
  769. // Creates a vector material var
  770. //-----------------------------------------------------------------------------
  771. static IMaterialVar* CreateMatrixMaterialVarFromKeyValue( IMaterial* pMaterial, KeyValues* pKeyValue )
  772. {
  773. char const* pScan = pKeyValue->GetString();
  774. char const *pszName = GetVarName( pKeyValue );
  775. // Matrices can be specified one of two ways:
  776. // [ # # # # # # # # # # # # # # # # ]
  777. // or
  778. // center # # scale # # rotate # translate # #
  779. VMatrix mat;
  780. int count = sscanf( pScan, " [ %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f ]",
  781. &mat.m[0][0], &mat.m[0][1], &mat.m[0][2], &mat.m[0][3],
  782. &mat.m[1][0], &mat.m[1][1], &mat.m[1][2], &mat.m[1][3],
  783. &mat.m[2][0], &mat.m[2][1], &mat.m[2][2], &mat.m[2][3],
  784. &mat.m[3][0], &mat.m[3][1], &mat.m[3][2], &mat.m[3][3] );
  785. if (count == 16)
  786. {
  787. return IMaterialVar::Create( pMaterial, pszName, mat );
  788. }
  789. Vector2D scale, center;
  790. float angle;
  791. Vector2D translation;
  792. count = sscanf( pScan, " center %f %f scale %f %f rotate %f translate %f %f",
  793. &center.x, &center.y, &scale.x, &scale.y, &angle, &translation.x, &translation.y );
  794. if (count != 7)
  795. return NULL;
  796. VMatrix temp;
  797. MatrixBuildTranslation( mat, -center.x, -center.y, 0.0f );
  798. MatrixBuildScale( temp, scale.x, scale.y, 1.0f );
  799. MatrixMultiply( temp, mat, mat );
  800. MatrixBuildRotateZ( temp, angle );
  801. MatrixMultiply( temp, mat, mat );
  802. MatrixBuildTranslation( temp, center.x + translation.x, center.y + translation.y, 0.0f );
  803. MatrixMultiply( temp, mat, mat );
  804. // Create the variable!
  805. return IMaterialVar::Create( pMaterial, pszName, mat );
  806. }
  807. //-----------------------------------------------------------------------------
  808. // Creates a material var from a key value
  809. //-----------------------------------------------------------------------------
  810. static IMaterialVar* CreateMaterialVarFromKeyValue( IMaterial* pMaterial, KeyValues* pKeyValue )
  811. {
  812. char const *pszName = GetVarName( pKeyValue );
  813. switch( pKeyValue->GetDataType() )
  814. {
  815. case KeyValues::TYPE_INT:
  816. return IMaterialVar::Create( pMaterial, pszName, pKeyValue->GetInt() );
  817. case KeyValues::TYPE_FLOAT:
  818. return IMaterialVar::Create( pMaterial, pszName, pKeyValue->GetFloat() );
  819. case KeyValues::TYPE_STRING:
  820. {
  821. char const* pString = pKeyValue->GetString();
  822. if (!pString || !pString[0])
  823. return 0;
  824. // Look for matrices
  825. IMaterialVar *pMatrixVar = CreateMatrixMaterialVarFromKeyValue( pMaterial, pKeyValue );
  826. if (pMatrixVar)
  827. return pMatrixVar;
  828. // Look for vectors
  829. if (!IsVector(pString))
  830. return IMaterialVar::Create( pMaterial, pszName, pString );
  831. // Parse the string as a vector...
  832. return CreateVectorMaterialVarFromKeyValue( pMaterial, pKeyValue );
  833. }
  834. }
  835. return 0;
  836. }
  837. //-----------------------------------------------------------------------------
  838. // Reads out common flags, prevents them from becoming material vars
  839. //-----------------------------------------------------------------------------
  840. int CMaterial::FindMaterialVarFlag( char const* pFlagName ) const
  841. {
  842. // Strip preceeding spaces
  843. while ( pFlagName[0] )
  844. {
  845. if ( !IsWhitespace( pFlagName[0] ) )
  846. break;
  847. ++pFlagName;
  848. }
  849. for( int i = 0; *ShaderSystem()->ShaderStateString(i); ++i )
  850. {
  851. const char *pStateString = ShaderSystem()->ShaderStateString(i);
  852. const char *pFound = Q_stristr( pFlagName, pStateString );
  853. // The found string had better start with the first non-whitespace character
  854. if ( pFound != pFlagName )
  855. continue;
  856. // Strip spaces at the end
  857. int nLen = Q_strlen( pStateString );
  858. pFound += nLen;
  859. while ( true )
  860. {
  861. if ( !pFound[0] )
  862. return (1 << i);
  863. if ( !IsWhitespace( pFound[0] ) )
  864. break;
  865. ++pFound;
  866. }
  867. }
  868. return 0;
  869. }
  870. //-----------------------------------------------------------------------------
  871. // Print material flags
  872. //-----------------------------------------------------------------------------
  873. void CMaterial::PrintMaterialFlags( int flags, int flagsDefined )
  874. {
  875. int i;
  876. for( i = 0; *ShaderSystem()->ShaderStateString(i); i++ )
  877. {
  878. if( flags & ( 1<<i ) )
  879. {
  880. Warning( "%s|", ShaderSystem()->ShaderStateString(i) );
  881. }
  882. }
  883. Warning( "\n" );
  884. }
  885. //-----------------------------------------------------------------------------
  886. // Parses material flags
  887. //-----------------------------------------------------------------------------
  888. bool CMaterial::ParseMaterialFlag( KeyValues* pParseValue, IMaterialVar* pFlagVar,
  889. IMaterialVar* pFlagDefinedVar, bool parsingOverrides, int& flagMask, int& overrideMask )
  890. {
  891. // See if the var is a flag...
  892. int flagbit = FindMaterialVarFlag( GetVarName( pParseValue ) );
  893. if (!flagbit)
  894. return false;
  895. // Allow for flag override
  896. int testMask = parsingOverrides ? overrideMask : flagMask;
  897. if (testMask & flagbit)
  898. {
  899. Warning("Error! Flag \"%s\" is multiply defined in material \"%s\"!\n", pParseValue->GetName(), GetName() );
  900. return true;
  901. }
  902. // Make sure overrides win
  903. if (overrideMask & flagbit)
  904. return true;
  905. if (parsingOverrides)
  906. overrideMask |= flagbit;
  907. else
  908. flagMask |= flagbit;
  909. // If so, then set the flag bit
  910. if (pParseValue->GetInt())
  911. pFlagVar->SetIntValue( pFlagVar->GetIntValue() | flagbit );
  912. else
  913. pFlagVar->SetIntValue( pFlagVar->GetIntValue() & (~flagbit) );
  914. // Mark the flag as being defined
  915. pFlagDefinedVar->SetIntValue( pFlagDefinedVar->GetIntValue() | flagbit );
  916. /*
  917. if( stristr( m_pDebugName, "glasswindow064a" ) )
  918. {
  919. Warning( "flags\n" );
  920. PrintMaterialFlags( pFlagVar->GetIntValue(), pFlagDefinedVar->GetIntValue() );
  921. }
  922. */
  923. return true;
  924. }
  925. ConVar mat_reduceparticles( "mat_reduceparticles", "0", FCVAR_ALLOWED_IN_COMPETITIVE );
  926. bool CMaterial::ShouldSkipVar( KeyValues *pVar, bool *pWasConditional )
  927. {
  928. char const *pVarName = pVar->GetName();
  929. char const *pQuestion = strchr( pVarName, '?' );
  930. if ( ( ! pQuestion ) || (pQuestion == pVarName ) )
  931. {
  932. *pWasConditional = false; // unconditional var
  933. return false;
  934. }
  935. else
  936. {
  937. bool bShouldSkip = true;
  938. *pWasConditional = true;
  939. // parse the conditional part
  940. char pszConditionName[256];
  941. V_strncpy( pszConditionName, pVarName, 1+pQuestion-pVarName );
  942. char const *pCond = pszConditionName;
  943. bool bToggle = false;
  944. if ( pCond[0] == '!' )
  945. {
  946. pCond++;
  947. bToggle = true;
  948. }
  949. if ( ! stricmp( pCond, "lowfill" ) )
  950. {
  951. bShouldSkip = !mat_reduceparticles.GetBool();
  952. }
  953. else if ( ! stricmp( pCond, "hdr" ) )
  954. {
  955. bShouldSkip = ( HardwareConfig()->GetHDRType() == HDR_TYPE_NONE );
  956. }
  957. else if ( ! stricmp( pCond, "srgb" ) )
  958. {
  959. bShouldSkip = ( !HardwareConfig()->UsesSRGBCorrectBlending() );
  960. }
  961. else if ( ! stricmp( pCond, "ldr" ) )
  962. {
  963. bShouldSkip = ( HardwareConfig()->GetHDRType() != HDR_TYPE_NONE );
  964. }
  965. else if ( ! stricmp( pCond, "360" ) )
  966. {
  967. bShouldSkip = !IsX360();
  968. }
  969. else
  970. {
  971. Warning( "unrecognized conditional test %s in %s\n", pVarName, GetName() );
  972. }
  973. return bShouldSkip ^ bToggle;
  974. }
  975. }
  976. //-----------------------------------------------------------------------------
  977. // Computes the material vars for the shader
  978. //-----------------------------------------------------------------------------
  979. int CMaterial::ParseMaterialVars( IShader* pShader, KeyValues& keyValues,
  980. KeyValues* pOverrideKeyValues, bool modelDefault, IMaterialVar** ppVars, int nFindContext )
  981. {
  982. IMaterialVar* pNewVar;
  983. bool pOverride[256];
  984. bool bWasConditional[256];
  985. int overrideMask = 0;
  986. int flagMask = 0;
  987. memset( ppVars, 0, 256 * sizeof(IMaterialVar*) );
  988. memset( pOverride, 0, sizeof( pOverride ) );
  989. memset( bWasConditional, 0, sizeof( bWasConditional ) );
  990. // Create the flag var...
  991. // Set model mode if we fell back from a model mode shader
  992. int modelFlag = modelDefault ? MATERIAL_VAR_MODEL : 0;
  993. ppVars[FLAGS] = IMaterialVar::Create( this, "$flags", modelFlag );
  994. ppVars[FLAGS_DEFINED] = IMaterialVar::Create( this, "$flags_defined", modelFlag );
  995. ppVars[FLAGS2] = IMaterialVar::Create( this, "$flags2", 0 );
  996. ppVars[FLAGS_DEFINED2] = IMaterialVar::Create( this, "$flags_defined2", 0 );
  997. int numParams = pShader ? pShader->GetNumParams() : 0;
  998. int varCount = numParams;
  999. bool parsingOverrides = (pOverrideKeyValues != 0);
  1000. KeyValues* pVar = pOverrideKeyValues ? pOverrideKeyValues->GetFirstSubKey() : keyValues.GetFirstSubKey();
  1001. const char *pszMatName = pVar ? pVar->GetString() : "Unknown";
  1002. while( pVar )
  1003. {
  1004. bool bProcessThisOne = true;
  1005. bool bIsConditionalVar;
  1006. const char *pszVarName = GetVarName( pVar );
  1007. if ( (nFindContext == MATERIAL_FINDCONTEXT_ISONAMODEL) && pszVarName && pszVarName[0] )
  1008. {
  1009. // Prevent ignorez models
  1010. // Should we do 'nofog' too? For now, decided not to.
  1011. if ( Q_stristr(pszVarName,"$ignorez") )
  1012. {
  1013. Warning("Ignoring material flag '%s' on material '%s'.\n", pszVarName, pszMatName );
  1014. goto nextVar;
  1015. }
  1016. }
  1017. // See if the var is a flag...
  1018. if (
  1019. ShouldSkipVar( pVar, &bIsConditionalVar ) || // should skip?
  1020. ((pVar->GetName()[0] == '%') && (g_pShaderDevice->IsUsingGraphics()) && (!MaterialSystem()->CanUseEditorMaterials() ) ) || // is an editor var?
  1021. ParseMaterialFlag( pVar, ppVars[FLAGS], ppVars[FLAGS_DEFINED], parsingOverrides, flagMask, overrideMask ) || // is a flag?
  1022. ParseMaterialFlag( pVar, ppVars[FLAGS2], ppVars[FLAGS_DEFINED2], parsingOverrides, flagMask, overrideMask )
  1023. )
  1024. bProcessThisOne = false;
  1025. if ( bProcessThisOne )
  1026. {
  1027. // See if the var is one of the shader params
  1028. int varIdx = FindMaterialVar( pShader, pszVarName );
  1029. // Check for multiply defined or overridden
  1030. if (varIdx >= 0)
  1031. {
  1032. if (ppVars[varIdx] && (! bIsConditionalVar ) )
  1033. {
  1034. if ( !pOverride[varIdx] || parsingOverrides )
  1035. {
  1036. Warning("Error! Variable \"%s\" is multiply defined in material \"%s\"!\n", pVar->GetName(), GetName() );
  1037. }
  1038. goto nextVar;
  1039. }
  1040. }
  1041. else
  1042. {
  1043. int i;
  1044. for ( i = numParams; i < varCount; ++i)
  1045. {
  1046. Assert( ppVars[i] );
  1047. if (!stricmp( ppVars[i]->GetName(), pVar->GetName() ))
  1048. break;
  1049. }
  1050. if (i != varCount)
  1051. {
  1052. if ( !pOverride[i] || parsingOverrides )
  1053. {
  1054. Warning("Error! Variable \"%s\" is multiply defined in material \"%s\"!\n", pVar->GetName(), GetName() );
  1055. }
  1056. goto nextVar;
  1057. }
  1058. }
  1059. // Create a material var for this dudely dude; could be zero...
  1060. pNewVar = CreateMaterialVarFromKeyValue( this, pVar );
  1061. if (!pNewVar)
  1062. goto nextVar;
  1063. if (varIdx < 0)
  1064. {
  1065. varIdx = varCount++;
  1066. }
  1067. if ( ppVars[varIdx] )
  1068. {
  1069. IMaterialVar::Destroy( ppVars[varIdx] );
  1070. }
  1071. ppVars[varIdx] = pNewVar;
  1072. if (parsingOverrides)
  1073. pOverride[varIdx] = true;
  1074. bWasConditional[varIdx] = bIsConditionalVar;
  1075. }
  1076. nextVar:
  1077. pVar = pVar->GetNextKey();
  1078. if (!pVar && parsingOverrides)
  1079. {
  1080. pVar = keyValues.GetFirstSubKey();
  1081. parsingOverrides = false;
  1082. }
  1083. }
  1084. // Create undefined vars for all the actual material vars
  1085. for (int i = 0; i < numParams; ++i)
  1086. {
  1087. if (!ppVars[i])
  1088. ppVars[i] = IMaterialVar::Create( this, pShader->GetParamName(i) );
  1089. }
  1090. return varCount;
  1091. }
  1092. static KeyValues *CheckConditionalFakeShaderName( char const *pShaderName, char const *pSuffixName,
  1093. KeyValues *pKeyValues )
  1094. {
  1095. KeyValues *pFallbackSection = pKeyValues->FindKey( pSuffixName );
  1096. if (pFallbackSection)
  1097. return pFallbackSection;
  1098. char nameBuf[256];
  1099. V_snprintf( nameBuf, sizeof(nameBuf), "%s_%s", pShaderName, pSuffixName );
  1100. pFallbackSection = pKeyValues->FindKey( nameBuf );
  1101. if (pFallbackSection)
  1102. return pFallbackSection;
  1103. return NULL;
  1104. }
  1105. static KeyValues *FindBuiltinFallbackBlock( char const *pShaderName, KeyValues *pKeyValues )
  1106. {
  1107. // handle "fake" shader fallbacks which are conditional upon mode. like _hdr_dx9, etc
  1108. if ( HardwareConfig()->GetDXSupportLevel() < 90 )
  1109. {
  1110. KeyValues *pRet = CheckConditionalFakeShaderName( pShaderName,"<DX90", pKeyValues );
  1111. if ( pRet )
  1112. return pRet;
  1113. }
  1114. if ( HardwareConfig()->GetDXSupportLevel() < 95 )
  1115. {
  1116. KeyValues *pRet = CheckConditionalFakeShaderName( pShaderName,"<DX95", pKeyValues );
  1117. if ( pRet )
  1118. return pRet;
  1119. }
  1120. if ( HardwareConfig()->GetDXSupportLevel() < 90 || !HardwareConfig()->SupportsPixelShaders_2_b() )
  1121. {
  1122. KeyValues *pRet = CheckConditionalFakeShaderName( pShaderName,"<DX90_20b", pKeyValues );
  1123. if ( pRet )
  1124. return pRet;
  1125. }
  1126. if ( HardwareConfig()->GetDXSupportLevel() >= 90 && HardwareConfig()->SupportsPixelShaders_2_b() )
  1127. {
  1128. KeyValues *pRet = CheckConditionalFakeShaderName( pShaderName,">=DX90_20b", pKeyValues );
  1129. if ( pRet )
  1130. return pRet;
  1131. }
  1132. if ( HardwareConfig()->GetDXSupportLevel() <= 90 )
  1133. {
  1134. KeyValues *pRet = CheckConditionalFakeShaderName( pShaderName,"<=DX90", pKeyValues );
  1135. if ( pRet )
  1136. return pRet;
  1137. }
  1138. if ( HardwareConfig()->GetDXSupportLevel() >= 90 )
  1139. {
  1140. KeyValues *pRet = CheckConditionalFakeShaderName( pShaderName,">=DX90", pKeyValues );
  1141. if ( pRet )
  1142. return pRet;
  1143. }
  1144. if ( HardwareConfig()->GetDXSupportLevel() > 90 )
  1145. {
  1146. KeyValues *pRet = CheckConditionalFakeShaderName( pShaderName,">DX90", pKeyValues );
  1147. if ( pRet )
  1148. return pRet;
  1149. }
  1150. if ( HardwareConfig()->GetHDRType() != HDR_TYPE_NONE )
  1151. {
  1152. KeyValues *pRet = CheckConditionalFakeShaderName( pShaderName,"hdr_dx9", pKeyValues );
  1153. if ( pRet )
  1154. return pRet;
  1155. pRet = CheckConditionalFakeShaderName( pShaderName,"hdr", pKeyValues );
  1156. if ( pRet )
  1157. return pRet;
  1158. }
  1159. else
  1160. {
  1161. KeyValues *pRet = CheckConditionalFakeShaderName( pShaderName,"ldr", pKeyValues );
  1162. if ( pRet )
  1163. return pRet;
  1164. }
  1165. if ( HardwareConfig()->UsesSRGBCorrectBlending() )
  1166. {
  1167. KeyValues *pRet = CheckConditionalFakeShaderName( pShaderName,"srgb", pKeyValues );
  1168. if ( pRet )
  1169. return pRet;
  1170. }
  1171. if ( HardwareConfig()->GetDXSupportLevel() >= 90 )
  1172. {
  1173. KeyValues *pRet = CheckConditionalFakeShaderName( pShaderName,"dx9", pKeyValues );
  1174. if ( pRet )
  1175. return pRet;
  1176. }
  1177. return NULL;
  1178. }
  1179. inline const char *MissingShaderName()
  1180. {
  1181. return (IsWindows() && !IsEmulatingGL()) ? "Wireframe_DX8" : "Wireframe_DX9";
  1182. }
  1183. //-----------------------------------------------------------------------------
  1184. // Hooks up the shader
  1185. //-----------------------------------------------------------------------------
  1186. KeyValues* CMaterial::InitializeShader( KeyValues &keyValues, KeyValues &patchKeyValues, int nFindContext )
  1187. {
  1188. MaterialLock_t hMaterialLock = MaterialSystem()->Lock();
  1189. KeyValues* pCurrentFallback = &keyValues;
  1190. KeyValues* pFallbackSection = 0;
  1191. char szShaderName[MAX_PATH];
  1192. char const* pShaderName = pCurrentFallback->GetName();
  1193. if ( !pShaderName )
  1194. {
  1195. // I'm not quite sure how this can happen, but we'll see...
  1196. Warning( "Shader not specified in material %s\nUsing wireframe instead...\n", GetName() );
  1197. Assert( 0 );
  1198. pShaderName = MissingShaderName();
  1199. }
  1200. else
  1201. {
  1202. // can't pass a stable reference to the key values name around
  1203. // naive leaf functions can cause KV system to re-alloc
  1204. V_strncpy( szShaderName, pShaderName, sizeof( szShaderName ) );
  1205. pShaderName = szShaderName;
  1206. }
  1207. IShader* pShader;
  1208. IMaterialVar* ppVars[256];
  1209. char pFallbackShaderNameBuf[256];
  1210. char pFallbackMaterialNameBuf[256];
  1211. int varCount = 0;
  1212. bool modelDefault = false;
  1213. // Keep going until there's no more fallbacks...
  1214. while( true )
  1215. {
  1216. // Find the shader for this material. Note that this may not be
  1217. // the actual shader we use due to fallbacks...
  1218. pShader = ShaderSystem()->FindShader( pShaderName );
  1219. if ( !pShader )
  1220. {
  1221. if ( g_pShaderDevice->IsUsingGraphics() )
  1222. {
  1223. Warning( "Error: Material \"%s\" uses unknown shader \"%s\"\n", GetName(), pShaderName );
  1224. //hushed Assert( 0 );
  1225. }
  1226. pShaderName = MissingShaderName();
  1227. pShader = ShaderSystem()->FindShader( pShaderName );
  1228. if ( !HushAsserts() )
  1229. {
  1230. AssertMsg( pShader, "pShader==NULL. Shader: %s", GetName() );
  1231. }
  1232. #ifndef DEDICATED
  1233. if ( !pShader )
  1234. {
  1235. #ifdef LINUX
  1236. // Exit out here. We're running into issues where this material is returned in a horribly broken
  1237. // state and you wind up crashing in LockMesh() because the vertex and index buffer pointers
  1238. // are NULL. You can repro this by not dying here and showing the intro movie. This happens on
  1239. // Linux when you run from a symlink'd SteamApps directory.
  1240. Error( "Shader '%s' for material '%s' not found.\n",
  1241. pCurrentFallback->GetName() ? pCurrentFallback->GetName() : pShaderName, GetName() );
  1242. #endif
  1243. MaterialSystem()->Unlock( hMaterialLock );
  1244. return NULL;
  1245. }
  1246. #endif
  1247. }
  1248. bool bHasBuiltinFallbackBlock = false;
  1249. if ( !pFallbackSection )
  1250. {
  1251. pFallbackSection = FindBuiltinFallbackBlock( pShaderName, &keyValues );
  1252. if( pFallbackSection )
  1253. {
  1254. bHasBuiltinFallbackBlock = true;
  1255. pFallbackSection->ChainKeyValue( &keyValues );
  1256. pCurrentFallback = pFallbackSection;
  1257. }
  1258. }
  1259. // Here we must set up all flags + material vars that the shader needs
  1260. // because it may look at them when choosing shader fallback.
  1261. varCount = ParseMaterialVars( pShader, keyValues, pFallbackSection, modelDefault, ppVars, nFindContext );
  1262. if ( !pShader )
  1263. break;
  1264. // Make sure we set default values before the fallback is looked for
  1265. ShaderSystem()->InitShaderParameters( pShader, ppVars, GetName() );
  1266. // Now that the material vars are parsed, see if there's a fallback
  1267. // But only if we're not in the tools
  1268. /*
  1269. if (!g_pShaderAPI->IsUsingGraphics())
  1270. break;
  1271. */
  1272. // Check for a fallback; if not, we're done
  1273. pShaderName = pShader->GetFallbackShader( ppVars );
  1274. if (!pShaderName)
  1275. {
  1276. break;
  1277. }
  1278. // Copy off the shader name, as it may be in a materialvar in the shader
  1279. // because we're about to delete all materialvars
  1280. Q_strncpy( pFallbackShaderNameBuf, pShaderName, 256 );
  1281. pShaderName = pFallbackShaderNameBuf;
  1282. // Remember the model flag if we're on dx7 or higher...
  1283. if (HardwareConfig()->SupportsVertexAndPixelShaders())
  1284. {
  1285. modelDefault = ( ppVars[FLAGS]->GetIntValue() & MATERIAL_VAR_MODEL ) != 0;
  1286. }
  1287. // Try to get the section associated with the fallback shader
  1288. // Then chain it to the base data so it can override the
  1289. // values if it wants to
  1290. if( !bHasBuiltinFallbackBlock )
  1291. {
  1292. pFallbackSection = keyValues.FindKey( pShaderName );
  1293. if (pFallbackSection)
  1294. {
  1295. pFallbackSection->ChainKeyValue( &keyValues );
  1296. pCurrentFallback = pFallbackSection;
  1297. }
  1298. }
  1299. // Now, blow away all of the material vars + try again...
  1300. for (int i = 0; i < varCount; ++i)
  1301. {
  1302. Assert( ppVars[i] );
  1303. IMaterialVar::Destroy( ppVars[i] );
  1304. }
  1305. // Check the KeyValues for '$fallbackmaterial'
  1306. // Note we have to do this *after* we chain the keyvalues
  1307. // based on the fallback shader since the names of the fallback material
  1308. // must lie within the shader-specific block usually.
  1309. const char *pFallbackMaterial = pCurrentFallback->GetString( "$fallbackmaterial" );
  1310. if ( pFallbackMaterial[0] )
  1311. {
  1312. // Don't fallback to ourselves
  1313. if ( Q_stricmp( GetName(), pFallbackMaterial ) )
  1314. {
  1315. // Gotta copy it off; clearing the keyvalues will blow the string away
  1316. Q_strncpy( pFallbackMaterialNameBuf, pFallbackMaterial, 256 );
  1317. keyValues.Clear();
  1318. if( !LoadVMTFile( keyValues, patchKeyValues, pFallbackMaterialNameBuf, UsesUNCFileName(), NULL ) )
  1319. {
  1320. Warning( "CMaterial::PrecacheVars: error loading vmt file %s for %s\n", pFallbackMaterialNameBuf, GetName() );
  1321. keyValues = *(((CMaterial *)g_pErrorMaterial)->m_pVMTKeyValues);
  1322. }
  1323. }
  1324. else
  1325. {
  1326. Warning( "CMaterial::PrecacheVars: fallback material for vmt file %s is itself!\n", GetName() );
  1327. keyValues = *(((CMaterial *)g_pErrorMaterial)->m_pVMTKeyValues);
  1328. }
  1329. pCurrentFallback = &keyValues;
  1330. pFallbackSection = NULL;
  1331. // I'm not quite sure how this can happen, but we'll see...
  1332. pShaderName = pCurrentFallback->GetName();
  1333. if (!pShaderName)
  1334. {
  1335. Warning("Shader not specified in material %s (fallback %s)\nUsing wireframe instead...\n", GetName(), pFallbackMaterialNameBuf );
  1336. pShaderName = MissingShaderName();
  1337. }
  1338. }
  1339. }
  1340. // Store off the shader
  1341. m_pShader = pShader;
  1342. // Store off the material vars + flags
  1343. m_VarCount = varCount;
  1344. m_pShaderParams = (IMaterialVar**)malloc( varCount * sizeof(IMaterialVar*) );
  1345. memcpy( m_pShaderParams, ppVars, varCount * sizeof(IMaterialVar*) );
  1346. #ifdef _DEBUG
  1347. for (int i = 0; i < varCount; ++i)
  1348. {
  1349. Assert( ppVars[i] );
  1350. }
  1351. #endif
  1352. MaterialSystem()->Unlock( hMaterialLock );
  1353. return pCurrentFallback;
  1354. }
  1355. //-----------------------------------------------------------------------------
  1356. // Gets the texturemap size
  1357. //-----------------------------------------------------------------------------
  1358. void CMaterial::PrecacheMappingDimensions( )
  1359. {
  1360. // Cache mapping width and mapping height
  1361. if (!m_representativeTexture)
  1362. {
  1363. #ifdef PARANOID
  1364. Warning( "No representative texture on material: \"%s\"\n", GetName() );
  1365. #endif
  1366. m_MappingWidth = 64;
  1367. m_MappingHeight = 64;
  1368. }
  1369. else
  1370. {
  1371. m_MappingWidth = m_representativeTexture->GetMappingWidth();
  1372. m_MappingHeight = m_representativeTexture->GetMappingHeight();
  1373. }
  1374. }
  1375. //-----------------------------------------------------------------------------
  1376. // Initialize the state snapshot
  1377. //-----------------------------------------------------------------------------
  1378. bool CMaterial::InitializeStateSnapshots()
  1379. {
  1380. if (IsPrecached())
  1381. {
  1382. if ( MaterialSystem()->GetCurrentMaterial() == this)
  1383. {
  1384. g_pShaderAPI->FlushBufferedPrimitives();
  1385. }
  1386. // Default state
  1387. CleanUpStateSnapshots();
  1388. if ( m_pShader && !ShaderSystem()->InitRenderState( m_pShader, m_VarCount, m_pShaderParams, &m_ShaderRenderState, GetName() ))
  1389. {
  1390. m_Flags &= ~MATERIAL_VALID_RENDERSTATE;
  1391. return false;
  1392. }
  1393. m_Flags |= MATERIAL_VALID_RENDERSTATE;
  1394. }
  1395. return true;
  1396. }
  1397. void CMaterial::CleanUpStateSnapshots()
  1398. {
  1399. if (IsValidRenderState())
  1400. {
  1401. ShaderSystem()->CleanupRenderState(&m_ShaderRenderState);
  1402. // -- THIS CANNOT BE HERE: m_Flags &= ~MATERIAL_VALID_RENDERSTATE;
  1403. // -- because it will cause a crash when main thread asks for material
  1404. // -- sort group it can temporarily see material in invalid render state
  1405. // -- and crash in DecalSurfaceAdd(msurface2_t*, int)
  1406. }
  1407. }
  1408. //-----------------------------------------------------------------------------
  1409. // This sets up a debugging/error shader...
  1410. //-----------------------------------------------------------------------------
  1411. void CMaterial::SetupErrorShader()
  1412. {
  1413. // Preserve the model flags
  1414. int flags = 0;
  1415. if ( m_pShaderParams && m_pShaderParams[FLAGS] )
  1416. {
  1417. flags = (m_pShaderParams[FLAGS]->GetIntValue() & MATERIAL_VAR_MODEL);
  1418. }
  1419. CleanUpShaderParams();
  1420. CleanUpMaterialProxy();
  1421. // We had a failure; replace it with a valid shader...
  1422. m_pShader = ShaderSystem()->FindShader( MissingShaderName() );
  1423. Assert( m_pShader );
  1424. // Create undefined vars for all the actual material vars
  1425. m_VarCount = m_pShader->GetNumParams();
  1426. m_pShaderParams = (IMaterialVar**)malloc( m_VarCount * sizeof(IMaterialVar*) );
  1427. for (int i = 0; i < m_VarCount; ++i)
  1428. {
  1429. m_pShaderParams[i] = IMaterialVar::Create( this, m_pShader->GetParamName(i) );
  1430. }
  1431. // Store the model flags
  1432. SetMaterialVarFlags( flags, true );
  1433. // Set the default values
  1434. ShaderSystem()->InitShaderParameters( m_pShader, m_pShaderParams, "Error" );
  1435. // Invokes the SHADER_INIT block in the various shaders,
  1436. ShaderSystem()->InitShaderInstance( m_pShader, m_pShaderParams, "Error", GetTextureGroupName() );
  1437. #ifdef DBGFLAG_ASSERT
  1438. bool ok =
  1439. #endif
  1440. InitializeStateSnapshots();
  1441. m_QueueFriendlyVersion.UpdateToRealTime();
  1442. Assert(ok);
  1443. }
  1444. //-----------------------------------------------------------------------------
  1445. // This computes the state snapshots for this material
  1446. //-----------------------------------------------------------------------------
  1447. void CMaterial::RecomputeStateSnapshots()
  1448. {
  1449. CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
  1450. if ( pCallQueue )
  1451. {
  1452. pCallQueue->QueueCall( this, &CMaterial::RecomputeStateSnapshots );
  1453. return;
  1454. }
  1455. bool ok = InitializeStateSnapshots();
  1456. // compute the state snapshots
  1457. if (!ok)
  1458. {
  1459. SetupErrorShader();
  1460. }
  1461. }
  1462. //-----------------------------------------------------------------------------
  1463. // Are we valid
  1464. //-----------------------------------------------------------------------------
  1465. inline bool CMaterial::IsValidRenderState() const
  1466. {
  1467. return (m_Flags & MATERIAL_VALID_RENDERSTATE) != 0;
  1468. }
  1469. //-----------------------------------------------------------------------------
  1470. // Gets/sets material var flags
  1471. //-----------------------------------------------------------------------------
  1472. inline int CMaterial::GetMaterialVarFlags() const
  1473. {
  1474. if ( m_pShaderParams && m_pShaderParams[FLAGS] )
  1475. {
  1476. return m_pShaderParams[FLAGS]->GetIntValueFast();
  1477. }
  1478. else
  1479. {
  1480. return 0;
  1481. }
  1482. }
  1483. inline void CMaterial::SetMaterialVarFlags( int flags, bool on )
  1484. {
  1485. if ( !m_pShaderParams )
  1486. {
  1487. Assert( 0 ); // are we hanging onto a material that has been cleaned up or isn't ready?
  1488. return;
  1489. }
  1490. if (on)
  1491. {
  1492. m_pShaderParams[FLAGS]->SetIntValue( GetMaterialVarFlags() | flags );
  1493. }
  1494. else
  1495. {
  1496. m_pShaderParams[FLAGS]->SetIntValue( GetMaterialVarFlags() & (~flags) );
  1497. }
  1498. // Mark it as being defined...
  1499. m_pShaderParams[FLAGS_DEFINED]->SetIntValue( m_pShaderParams[FLAGS_DEFINED]->GetIntValueFast() | flags );
  1500. }
  1501. inline int CMaterial::GetMaterialVarFlags2() const
  1502. {
  1503. if ( m_pShaderParams && m_VarCount > FLAGS2 && m_pShaderParams[FLAGS2] )
  1504. {
  1505. return m_pShaderParams[FLAGS2]->GetIntValueFast();
  1506. }
  1507. else
  1508. {
  1509. return 0;
  1510. }
  1511. }
  1512. inline void CMaterial::SetMaterialVarFlags2( int flags, bool on )
  1513. {
  1514. if ( m_pShaderParams && m_VarCount > FLAGS2 && m_pShaderParams[FLAGS2] )
  1515. {
  1516. if (on)
  1517. m_pShaderParams[FLAGS2]->SetIntValue( GetMaterialVarFlags2() | flags );
  1518. else
  1519. m_pShaderParams[FLAGS2]->SetIntValue( GetMaterialVarFlags2() & (~flags) );
  1520. }
  1521. if ( m_pShaderParams && m_VarCount > FLAGS_DEFINED2 && m_pShaderParams[FLAGS_DEFINED2] )
  1522. {
  1523. // Mark it as being defined...
  1524. m_pShaderParams[FLAGS_DEFINED2]->SetIntValue(
  1525. m_pShaderParams[FLAGS_DEFINED2]->GetIntValueFast() | flags );
  1526. }
  1527. }
  1528. //-----------------------------------------------------------------------------
  1529. // Gets the morph format
  1530. //-----------------------------------------------------------------------------
  1531. MorphFormat_t CMaterial::GetMorphFormat() const
  1532. {
  1533. const_cast<CMaterial*>(this)->Precache();
  1534. Assert( IsValidRenderState() );
  1535. return m_ShaderRenderState.m_MorphFormat;
  1536. }
  1537. //-----------------------------------------------------------------------------
  1538. // Gets the vertex format
  1539. //-----------------------------------------------------------------------------
  1540. VertexFormat_t CMaterial::GetVertexFormat() const
  1541. {
  1542. Assert( IsValidRenderState() );
  1543. return m_ShaderRenderState.m_VertexFormat;
  1544. }
  1545. VertexFormat_t CMaterial::GetVertexUsage() const
  1546. {
  1547. Assert( IsValidRenderState() );
  1548. return m_ShaderRenderState.m_VertexUsage;
  1549. }
  1550. bool CMaterial::PerformDebugTrace() const
  1551. {
  1552. return IsValidRenderState() && ((GetMaterialVarFlags() & MATERIAL_VAR_DEBUG ) != 0);
  1553. }
  1554. //-----------------------------------------------------------------------------
  1555. // Are we suppressed?
  1556. //-----------------------------------------------------------------------------
  1557. bool CMaterial::IsSuppressed() const
  1558. {
  1559. if ( !IsValidRenderState() )
  1560. return true;
  1561. return ((GetMaterialVarFlags() & MATERIAL_VAR_NO_DRAW) != 0);
  1562. }
  1563. void CMaterial::ToggleSuppression()
  1564. {
  1565. if (IsValidRenderState())
  1566. {
  1567. if ((GetMaterialVarFlags() & MATERIAL_VAR_NO_DEBUG_OVERRIDE) != 0)
  1568. return;
  1569. SetMaterialVarFlags( MATERIAL_VAR_NO_DRAW,
  1570. (GetMaterialVarFlags() & MATERIAL_VAR_NO_DRAW) == 0 );
  1571. }
  1572. }
  1573. void CMaterial::ToggleDebugTrace()
  1574. {
  1575. if (IsValidRenderState())
  1576. {
  1577. SetMaterialVarFlags( MATERIAL_VAR_DEBUG,
  1578. (GetMaterialVarFlags() & MATERIAL_VAR_DEBUG) == 0 );
  1579. }
  1580. }
  1581. //-----------------------------------------------------------------------------
  1582. // Can we override this material in debug?
  1583. //-----------------------------------------------------------------------------
  1584. bool CMaterial::NoDebugOverride() const
  1585. {
  1586. return IsValidRenderState() && (GetMaterialVarFlags() & MATERIAL_VAR_NO_DEBUG_OVERRIDE) != 0;
  1587. }
  1588. //-----------------------------------------------------------------------------
  1589. // Material Var flags
  1590. //-----------------------------------------------------------------------------
  1591. void CMaterial::SetMaterialVarFlag( MaterialVarFlags_t flag, bool on )
  1592. {
  1593. CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
  1594. if ( pCallQueue )
  1595. {
  1596. pCallQueue->QueueCall( this, &CMaterial::SetMaterialVarFlag, flag, on );
  1597. return;
  1598. }
  1599. bool oldOn = (GetMaterialVarFlags( ) & flag) != 0;
  1600. if (oldOn != on)
  1601. {
  1602. SetMaterialVarFlags( flag, on );
  1603. // This is going to be called from client code; recompute snapshots!
  1604. RecomputeStateSnapshots();
  1605. }
  1606. }
  1607. bool CMaterial::GetMaterialVarFlag( MaterialVarFlags_t flag ) const
  1608. {
  1609. return (GetMaterialVarFlags() & flag) != 0;
  1610. }
  1611. //-----------------------------------------------------------------------------
  1612. // Do we use the env_cubemap entity to get cubemaps from the level?
  1613. //-----------------------------------------------------------------------------
  1614. bool CMaterial::UsesEnvCubemap( void )
  1615. {
  1616. Precache();
  1617. if( !m_pShader )
  1618. {
  1619. if ( !HushAsserts() )
  1620. {
  1621. AssertMsg( m_pShader, "m_pShader==NULL. Shader: %s", GetName() );
  1622. }
  1623. return false;
  1624. }
  1625. Assert( m_pShaderParams );
  1626. return IsFlag2Set( m_pShaderParams, MATERIAL_VAR2_USES_ENV_CUBEMAP );
  1627. }
  1628. //-----------------------------------------------------------------------------
  1629. // Do we need a tangent space at the vertex level?
  1630. //-----------------------------------------------------------------------------
  1631. bool CMaterial::NeedsTangentSpace( void )
  1632. {
  1633. Precache();
  1634. if( !m_pShader )
  1635. {
  1636. if ( !HushAsserts() )
  1637. {
  1638. AssertMsg( m_pShader, "m_pShader==NULL. Shader: %s", GetName() );
  1639. }
  1640. return false;
  1641. }
  1642. Assert( m_pShaderParams );
  1643. return IsFlag2Set( m_pShaderParams, MATERIAL_VAR2_NEEDS_TANGENT_SPACES );
  1644. }
  1645. bool CMaterial::NeedsPowerOfTwoFrameBufferTexture( bool bCheckSpecificToThisFrame )
  1646. {
  1647. PrecacheVars();
  1648. if( !m_pShader )
  1649. {
  1650. if ( !HushAsserts() )
  1651. {
  1652. AssertMsg( m_pShader, "m_pShader==NULL. Shader: %s", GetName() );
  1653. }
  1654. return false;
  1655. }
  1656. Assert( m_pShaderParams );
  1657. return m_pShader->NeedsPowerOfTwoFrameBufferTexture( m_pShaderParams, bCheckSpecificToThisFrame );
  1658. }
  1659. bool CMaterial::NeedsFullFrameBufferTexture( bool bCheckSpecificToThisFrame )
  1660. {
  1661. PrecacheVars();
  1662. if( !m_pShader )
  1663. {
  1664. if ( !HushAsserts() )
  1665. {
  1666. AssertMsg( m_pShader, "m_pShader==NULL. Shader: %s", GetName() );
  1667. }
  1668. return false;
  1669. }
  1670. Assert( m_pShaderParams );
  1671. return m_pShader->NeedsFullFrameBufferTexture( m_pShaderParams, bCheckSpecificToThisFrame );
  1672. }
  1673. // GR - Is lightmap alpha needed?
  1674. bool CMaterial::NeedsLightmapBlendAlpha( void )
  1675. {
  1676. Precache();
  1677. return (GetMaterialVarFlags2() & MATERIAL_VAR2_BLEND_WITH_LIGHTMAP_ALPHA ) != 0;
  1678. }
  1679. //-----------------------------------------------------------------------------
  1680. // Do we need software skinning?
  1681. //-----------------------------------------------------------------------------
  1682. bool CMaterial::NeedsSoftwareSkinning( void )
  1683. {
  1684. Precache();
  1685. Assert( m_pShader );
  1686. if( !m_pShader )
  1687. {
  1688. return false;
  1689. }
  1690. Assert( m_pShaderParams );
  1691. return IsFlagSet( m_pShaderParams, MATERIAL_VAR_NEEDS_SOFTWARE_SKINNING );
  1692. }
  1693. //-----------------------------------------------------------------------------
  1694. // Do we need software lighting?
  1695. //-----------------------------------------------------------------------------
  1696. bool CMaterial::NeedsSoftwareLighting( void )
  1697. {
  1698. Precache();
  1699. Assert( m_pShader );
  1700. if( !m_pShader )
  1701. {
  1702. return false;
  1703. }
  1704. Assert( m_pShaderParams );
  1705. return IsFlag2Set( m_pShaderParams, MATERIAL_VAR2_NEEDS_SOFTWARE_LIGHTING );
  1706. }
  1707. //-----------------------------------------------------------------------------
  1708. // Alpha/color modulation
  1709. //-----------------------------------------------------------------------------
  1710. void CMaterial::AlphaModulate( float alpha )
  1711. {
  1712. Precache();
  1713. if ( m_VarCount > ALPHA )
  1714. m_pShaderParams[ALPHA]->SetFloatValue(alpha);
  1715. }
  1716. void CMaterial::ColorModulate( float r, float g, float b )
  1717. {
  1718. Precache();
  1719. if ( m_VarCount > COLOR )
  1720. m_pShaderParams[COLOR]->SetVecValue( r, g, b );
  1721. }
  1722. float CMaterial::GetAlphaModulation()
  1723. {
  1724. Precache();
  1725. if ( m_VarCount > ALPHA )
  1726. return m_pShaderParams[ALPHA]->GetFloatValue();
  1727. return 0.0f;
  1728. }
  1729. void CMaterial::GetColorModulation( float *r, float *g, float *b )
  1730. {
  1731. Precache();
  1732. float pColor[3] = { 0.0f, 0.0f, 0.0f };
  1733. if ( m_VarCount > COLOR )
  1734. m_pShaderParams[COLOR]->GetVecValue( pColor, 3 );
  1735. *r = pColor[0];
  1736. *g = pColor[1];
  1737. *b = pColor[2];
  1738. }
  1739. //-----------------------------------------------------------------------------
  1740. // Do we use fog?
  1741. //-----------------------------------------------------------------------------
  1742. bool CMaterial::UseFog() const
  1743. {
  1744. Assert( m_VarCount > 0 );
  1745. return IsValidRenderState() && ((GetMaterialVarFlags() & MATERIAL_VAR_NOFOG) == 0);
  1746. }
  1747. //-----------------------------------------------------------------------------
  1748. // diffuse bump?
  1749. //-----------------------------------------------------------------------------
  1750. bool CMaterial::IsUsingDiffuseBumpedLighting() const
  1751. {
  1752. return (GetMaterialVarFlags2() & MATERIAL_VAR2_LIGHTING_BUMPED_LIGHTMAP ) != 0;
  1753. }
  1754. //-----------------------------------------------------------------------------
  1755. // lightmap?
  1756. //-----------------------------------------------------------------------------
  1757. bool CMaterial::IsUsingLightmap() const
  1758. {
  1759. return (GetMaterialVarFlags2() & MATERIAL_VAR2_LIGHTING_LIGHTMAP ) != 0;
  1760. }
  1761. bool CMaterial::IsManuallyCreated() const
  1762. {
  1763. return (m_Flags & MATERIAL_IS_MANUALLY_CREATED) != 0;
  1764. }
  1765. bool CMaterial::UsesUNCFileName() const
  1766. {
  1767. return (m_Flags & MATERIAL_USES_UNC_FILENAME) != 0;
  1768. }
  1769. void CMaterial::DecideShouldReloadFromWhitelist( IFileList *pFilesToReload )
  1770. {
  1771. m_bShouldReloadFromWhitelist = false;
  1772. if ( IsManuallyCreated() || !IsPrecached() )
  1773. return;
  1774. // Materials loaded with an absolute pathname are usually debug materials.
  1775. if ( V_IsAbsolutePath( GetName() ) )
  1776. return;
  1777. char vmtFilename[MAX_PATH];
  1778. V_ComposeFileName( "materials", GetName(), vmtFilename, sizeof( vmtFilename ) );
  1779. V_strncat( vmtFilename, ".vmt", sizeof( vmtFilename ) );
  1780. // Check if either this file or any of the files it included need to be reloaded.
  1781. bool bShouldReload = pFilesToReload->IsFileInList( vmtFilename );
  1782. if ( !bShouldReload )
  1783. {
  1784. for ( int i=0; i < m_VMTIncludes.Count(); i++ )
  1785. {
  1786. g_pFullFileSystem->String( m_VMTIncludes[i], vmtFilename, sizeof( vmtFilename ) );
  1787. if ( pFilesToReload->IsFileInList( vmtFilename ) )
  1788. {
  1789. bShouldReload = true;
  1790. break;
  1791. }
  1792. }
  1793. }
  1794. m_bShouldReloadFromWhitelist = bShouldReload;
  1795. }
  1796. void CMaterial::ReloadFromWhitelistIfMarked()
  1797. {
  1798. if ( !m_bShouldReloadFromWhitelist )
  1799. return;
  1800. #ifdef PURE_SERVER_DEBUG_SPEW
  1801. {
  1802. char vmtFilename[MAX_PATH];
  1803. V_ComposeFileName( "materials", GetName(), vmtFilename, sizeof( vmtFilename ) );
  1804. V_strncat( vmtFilename, ".vmt", sizeof( vmtFilename ) );
  1805. Msg( "Reloading %s due to pure server whitelist change\n", GetName() );
  1806. }
  1807. #endif
  1808. Uncache();
  1809. Precache();
  1810. if ( !GetShader() )
  1811. {
  1812. // We can get in here if we previously loaded this material off disk and now the whitelist
  1813. // says to get it out of Steam but it's not in Steam. So just setup a wireframe thingy
  1814. // to draw the material with.
  1815. m_Flags |= MATERIAL_IS_PRECACHED | MATERIAL_VARS_IS_PRECACHED;
  1816. #if DEBUG
  1817. if (IsOSX())
  1818. {
  1819. printf("\n ##### CMaterial::ReloadFromWhitelistIfMarked: GetShader failed on %s, calling SetupErrorShader", m_pDebugName );
  1820. }
  1821. #endif
  1822. SetupErrorShader();
  1823. }
  1824. }
  1825. bool CMaterial::WasReloadedFromWhitelist()
  1826. {
  1827. return m_bShouldReloadFromWhitelist;
  1828. }
  1829. //-----------------------------------------------------------------------------
  1830. // Loads the material vars
  1831. //-----------------------------------------------------------------------------
  1832. bool CMaterial::PrecacheVars( KeyValues *pVMTKeyValues, KeyValues *pPatchKeyValues, CUtlVector<FileNameHandle_t> *pIncludes, int nFindContext )
  1833. {
  1834. // We should get both parameters or neither
  1835. Assert( ( pVMTKeyValues == NULL ) ? ( pPatchKeyValues == NULL ) : ( pPatchKeyValues != NULL ) );
  1836. // Don't bother if we're already precached
  1837. if( IsPrecachedVars() )
  1838. return true;
  1839. if ( pIncludes )
  1840. m_VMTIncludes = *pIncludes;
  1841. else
  1842. m_VMTIncludes.Purge();
  1843. MaterialLock_t hMaterialLock = MaterialSystem()->Lock();
  1844. bool bOk = false;
  1845. bool bError = false;
  1846. KeyValues *vmtKeyValues = NULL;
  1847. KeyValues *patchKeyValues = NULL;
  1848. if ( m_pVMTKeyValues )
  1849. {
  1850. // Use the procedural KeyValues
  1851. vmtKeyValues = m_pVMTKeyValues;
  1852. patchKeyValues = new KeyValues( "vmt_patches" );
  1853. // The caller should not be passing in KeyValues if we have procedural ones
  1854. Assert( ( pVMTKeyValues == NULL ) && ( pPatchKeyValues == NULL ) );
  1855. }
  1856. else if ( pVMTKeyValues )
  1857. {
  1858. // Use the passed-in (already-loaded) KeyValues
  1859. vmtKeyValues = pVMTKeyValues;
  1860. patchKeyValues = pPatchKeyValues;
  1861. }
  1862. else
  1863. {
  1864. m_VMTIncludes.Purge();
  1865. // load data from the vmt file
  1866. vmtKeyValues = new KeyValues( "vmt" );
  1867. patchKeyValues = new KeyValues( "vmt_patches" );
  1868. if( !LoadVMTFile( *vmtKeyValues, *patchKeyValues, GetName(), UsesUNCFileName(), &m_VMTIncludes ) )
  1869. {
  1870. Warning( "CMaterial::PrecacheVars: error loading vmt file for %s\n", GetName() );
  1871. bError = true;
  1872. }
  1873. }
  1874. if ( ! bError )
  1875. {
  1876. // Needed to prevent re-entrancy
  1877. m_Flags |= MATERIAL_VARS_IS_PRECACHED;
  1878. // Create shader and the material vars...
  1879. KeyValues *pFallbackKeyValues = InitializeShader( *vmtKeyValues, *patchKeyValues, nFindContext );
  1880. if ( pFallbackKeyValues )
  1881. {
  1882. // Gotta initialize the proxies too, using the fallback proxies
  1883. InitializeMaterialProxy(pFallbackKeyValues);
  1884. bOk = true;
  1885. }
  1886. }
  1887. // Clean up
  1888. if ( ( vmtKeyValues != m_pVMTKeyValues ) && ( vmtKeyValues != pVMTKeyValues ) )
  1889. {
  1890. vmtKeyValues->deleteThis();
  1891. }
  1892. if ( patchKeyValues != pPatchKeyValues )
  1893. {
  1894. patchKeyValues->deleteThis();
  1895. }
  1896. MaterialSystem()->Unlock( hMaterialLock );
  1897. return bOk;
  1898. }
  1899. //-----------------------------------------------------------------------------
  1900. // Loads the material info from the VMT file
  1901. //-----------------------------------------------------------------------------
  1902. void CMaterial::Precache()
  1903. {
  1904. // Don't bother if we're already precached
  1905. if ( IsPrecached() )
  1906. return;
  1907. // load data from the vmt file
  1908. if ( !PrecacheVars() )
  1909. return;
  1910. MaterialLock_t hMaterialLock = MaterialSystem()->Lock();
  1911. m_Flags |= MATERIAL_IS_PRECACHED;
  1912. // Invokes the SHADER_INIT block in the various shaders,
  1913. if ( m_pShader )
  1914. {
  1915. ShaderSystem()->InitShaderInstance( m_pShader, m_pShaderParams, GetName(), GetTextureGroupName() );
  1916. }
  1917. // compute the state snapshots
  1918. RecomputeStateSnapshots();
  1919. FindRepresentativeTexture();
  1920. // Reads in the texture width and height from the material var
  1921. PrecacheMappingDimensions();
  1922. Assert( IsValidRenderState() );
  1923. if( m_pShaderParams )
  1924. m_QueueFriendlyVersion.UpdateToRealTime();
  1925. MaterialSystem()->Unlock( hMaterialLock );
  1926. }
  1927. //-----------------------------------------------------------------------------
  1928. // Unloads the material data from memory
  1929. //-----------------------------------------------------------------------------
  1930. void CMaterial::Uncache( bool bPreserveVars )
  1931. {
  1932. MaterialLock_t hMaterialLock = MaterialSystem()->Lock();
  1933. // Don't bother if we're not cached
  1934. if ( IsPrecached() )
  1935. {
  1936. // Clean up the state snapshots
  1937. CleanUpStateSnapshots();
  1938. m_Flags &= ~MATERIAL_VALID_RENDERSTATE;
  1939. m_Flags &= ~MATERIAL_IS_PRECACHED;
  1940. }
  1941. if ( !bPreserveVars )
  1942. {
  1943. if ( IsPrecachedVars() )
  1944. {
  1945. // Clean up the shader + params
  1946. CleanUpShaderParams();
  1947. m_pShader = 0;
  1948. // Clean up the material proxy
  1949. CleanUpMaterialProxy();
  1950. m_Flags &= ~MATERIAL_VARS_IS_PRECACHED;
  1951. }
  1952. }
  1953. MaterialSystem()->Unlock( hMaterialLock );
  1954. // Whether we just now did it, or we were already unloaded,
  1955. // notify the pure system that the material is unloaded,
  1956. // so it doesn't caue us to fail sv_pure checks
  1957. if ( ( m_Flags & ( MATERIAL_VARS_IS_PRECACHED | MATERIAL_IS_MANUALLY_CREATED | MATERIAL_USES_UNC_FILENAME ) ) == 0 )
  1958. {
  1959. char szName[ MAX_PATH ];
  1960. V_sprintf_safe( szName, "materials/%s.vmt", GetName() );
  1961. g_pFullFileSystem->NotifyFileUnloaded( szName, "GAME" );
  1962. }
  1963. }
  1964. //-----------------------------------------------------------------------------
  1965. // reload all textures used by this materals
  1966. //-----------------------------------------------------------------------------
  1967. void CMaterial::ReloadTextures( void )
  1968. {
  1969. Precache();
  1970. int i;
  1971. int nParams = ShaderParamCount();
  1972. IMaterialVar **ppVars = GetShaderParams();
  1973. for( i = 0; i < nParams; i++ )
  1974. {
  1975. if( ppVars[i] )
  1976. {
  1977. if( ppVars[i]->IsTexture() )
  1978. {
  1979. ITextureInternal *pTexture = ( ITextureInternal * )ppVars[i]->GetTextureValue();
  1980. if( !IsTextureInternalEnvCubemap( pTexture ) )
  1981. {
  1982. pTexture->Download();
  1983. }
  1984. }
  1985. }
  1986. }
  1987. }
  1988. //-----------------------------------------------------------------------------
  1989. // Meant to be used with materials created using CreateMaterial
  1990. // It updates the materials to reflect the current values stored in the material vars
  1991. //-----------------------------------------------------------------------------
  1992. void CMaterial::Refresh()
  1993. {
  1994. if ( g_pShaderDevice->IsUsingGraphics() )
  1995. {
  1996. Uncache();
  1997. Precache();
  1998. }
  1999. }
  2000. void CMaterial::RefreshPreservingMaterialVars()
  2001. {
  2002. if ( g_pShaderDevice->IsUsingGraphics() )
  2003. {
  2004. Uncache( true );
  2005. Precache();
  2006. }
  2007. }
  2008. //-----------------------------------------------------------------------------
  2009. // Gets the material name
  2010. //-----------------------------------------------------------------------------
  2011. char const* CMaterial::GetName() const
  2012. {
  2013. return m_Name.String();
  2014. }
  2015. char const* CMaterial::GetTextureGroupName() const
  2016. {
  2017. return m_TextureGroupName.String();
  2018. }
  2019. //-----------------------------------------------------------------------------
  2020. // Material dimensions
  2021. //-----------------------------------------------------------------------------
  2022. int CMaterial::GetMappingWidth( )
  2023. {
  2024. Precache();
  2025. return m_MappingWidth;
  2026. }
  2027. int CMaterial::GetMappingHeight( )
  2028. {
  2029. Precache();
  2030. return m_MappingHeight;
  2031. }
  2032. //-----------------------------------------------------------------------------
  2033. // Animated material info
  2034. //-----------------------------------------------------------------------------
  2035. int CMaterial::GetNumAnimationFrames( )
  2036. {
  2037. Precache();
  2038. if( m_representativeTexture )
  2039. {
  2040. return m_representativeTexture->GetNumAnimationFrames();
  2041. }
  2042. else
  2043. {
  2044. #ifndef POSIX
  2045. Warning( "CMaterial::GetNumAnimationFrames:\nno representative texture for material %s\n", GetName() );
  2046. #endif
  2047. return 1;
  2048. }
  2049. }
  2050. //-----------------------------------------------------------------------------
  2051. // Purpose:
  2052. //-----------------------------------------------------------------------------
  2053. void CMaterial::GetMaterialOffset( float *pOffset )
  2054. {
  2055. // Identity.
  2056. pOffset[0] = 0.0f;
  2057. pOffset[1] = 0.0f;
  2058. }
  2059. //-----------------------------------------------------------------------------
  2060. // Purpose:
  2061. //-----------------------------------------------------------------------------
  2062. void CMaterial::GetMaterialScale( float *pScale )
  2063. {
  2064. // Identity.
  2065. pScale[0] = 1.0f;
  2066. pScale[1] = 1.0f;
  2067. }
  2068. //-----------------------------------------------------------------------------
  2069. // Reference count
  2070. //-----------------------------------------------------------------------------
  2071. void CMaterial::IncrementReferenceCount( )
  2072. {
  2073. ++m_RefCount;
  2074. }
  2075. void CMaterial::DecrementReferenceCount( )
  2076. {
  2077. --m_RefCount;
  2078. }
  2079. int CMaterial::GetReferenceCount( ) const
  2080. {
  2081. return m_RefCount;
  2082. }
  2083. //-----------------------------------------------------------------------------
  2084. // Sets the shader associated with the material
  2085. //-----------------------------------------------------------------------------
  2086. void CMaterial::SetShader( const char *pShaderName )
  2087. {
  2088. Assert( pShaderName );
  2089. int i;
  2090. IShader* pShader;
  2091. IMaterialVar* ppVars[256];
  2092. int iVarCount = 0;
  2093. // Clean up existing state
  2094. Uncache();
  2095. // Keep going until there's no more fallbacks...
  2096. while( true )
  2097. {
  2098. // Find the shader for this material. Note that this may not be
  2099. // the actual shader we use due to fallbacks...
  2100. pShader = ShaderSystem()->FindShader( pShaderName );
  2101. if (!pShader)
  2102. {
  2103. // Couldn't find the shader we wanted to use; it's not defined...
  2104. Warning( "SetShader: Couldn't find shader %s for material %s!\n", pShaderName, GetName() );
  2105. pShaderName = MissingShaderName();
  2106. pShader = ShaderSystem()->FindShader( pShaderName );
  2107. Assert( pShader );
  2108. }
  2109. // Create undefined vars for all the actual material vars
  2110. iVarCount = pShader->GetNumParams();
  2111. for (i = 0; i < iVarCount; ++i)
  2112. {
  2113. ppVars[i] = IMaterialVar::Create( this, pShader->GetParamName(i) );
  2114. }
  2115. // Make sure we set default values before the fallback is looked for
  2116. ShaderSystem()->InitShaderParameters( pShader, ppVars, pShaderName );
  2117. // Now that the material vars are parsed, see if there's a fallback
  2118. // But only if we're not in the tools
  2119. if (!g_pShaderDevice->IsUsingGraphics())
  2120. break;
  2121. // Check for a fallback; if not, we're done
  2122. pShaderName = pShader->GetFallbackShader( ppVars );
  2123. if (!pShaderName)
  2124. break;
  2125. // Now, blow away all of the material vars + try again...
  2126. for (i = 0; i < iVarCount; ++i)
  2127. {
  2128. Assert( ppVars[i] );
  2129. IMaterialVar::Destroy( ppVars[i] );
  2130. }
  2131. }
  2132. // Store off the shader
  2133. m_pShader = pShader;
  2134. // Store off the material vars + flags
  2135. m_VarCount = iVarCount;
  2136. m_pShaderParams = (IMaterialVar**)malloc( iVarCount * sizeof(IMaterialVar*) );
  2137. memcpy( m_pShaderParams, ppVars, iVarCount * sizeof(IMaterialVar*) );
  2138. // Invokes the SHADER_INIT block in the various shaders,
  2139. ShaderSystem()->InitShaderInstance( m_pShader, m_pShaderParams, GetName(), GetTextureGroupName() );
  2140. // Precache our initial state...
  2141. // NOTE: What happens here for textures???
  2142. // Pretend that we precached our material vars; we certainly don't have any!
  2143. m_Flags |= MATERIAL_VARS_IS_PRECACHED;
  2144. // NOTE: The caller has to call 'Refresh' for the shader to be ready...
  2145. }
  2146. const char *CMaterial::GetShaderName() const
  2147. {
  2148. const_cast< CMaterial* >( this )->PrecacheVars();
  2149. return m_pShader ? m_pShader->GetName() : "shader_error";
  2150. }
  2151. //-----------------------------------------------------------------------------
  2152. // Enumeration ID
  2153. //-----------------------------------------------------------------------------
  2154. int CMaterial::GetEnumerationID( ) const
  2155. {
  2156. return m_iEnumerationID;
  2157. }
  2158. void CMaterial::SetEnumerationID( int id )
  2159. {
  2160. m_iEnumerationID = id;
  2161. }
  2162. //-----------------------------------------------------------------------------
  2163. // Preview image
  2164. //-----------------------------------------------------------------------------
  2165. char const* CMaterial::GetPreviewImageName( void )
  2166. {
  2167. if ( IsConsole() )
  2168. {
  2169. // not supporting
  2170. return NULL;
  2171. }
  2172. PrecacheVars();
  2173. bool found;
  2174. IMaterialVar *pRepresentativeTextureVar;
  2175. FindVar( "%noToolTexture", &found, false );
  2176. if (found)
  2177. return NULL;
  2178. pRepresentativeTextureVar = FindVar( "%toolTexture", &found, false );
  2179. if( found )
  2180. {
  2181. if (pRepresentativeTextureVar->GetType() == MATERIAL_VAR_TYPE_STRING )
  2182. return pRepresentativeTextureVar->GetStringValue();
  2183. if (pRepresentativeTextureVar->GetType() == MATERIAL_VAR_TYPE_TEXTURE )
  2184. return pRepresentativeTextureVar->GetTextureValue()->GetName();
  2185. }
  2186. pRepresentativeTextureVar = FindVar( "$baseTexture", &found, false );
  2187. if( found )
  2188. {
  2189. if (pRepresentativeTextureVar->GetType() == MATERIAL_VAR_TYPE_STRING )
  2190. return pRepresentativeTextureVar->GetStringValue();
  2191. if (pRepresentativeTextureVar->GetType() == MATERIAL_VAR_TYPE_TEXTURE )
  2192. return pRepresentativeTextureVar->GetTextureValue()->GetName();
  2193. }
  2194. return GetName();
  2195. }
  2196. char const* CMaterial::GetPreviewImageFileName( void ) const
  2197. {
  2198. char const* pName = const_cast<CMaterial*>(this)->GetPreviewImageName();
  2199. if( !pName )
  2200. return NULL;
  2201. static char vtfFilename[MATERIAL_MAX_PATH];
  2202. if( Q_strlen( pName ) >= MATERIAL_MAX_PATH - 5 )
  2203. {
  2204. Warning( "MATERIAL_MAX_PATH to short for %s.vtf\n", pName );
  2205. return NULL;
  2206. }
  2207. if ( !UsesUNCFileName() )
  2208. {
  2209. Q_snprintf( vtfFilename, sizeof( vtfFilename ), "materials/%s.vtf", pName );
  2210. }
  2211. else
  2212. {
  2213. Q_snprintf( vtfFilename, sizeof( vtfFilename ), "%s.vtf", pName );
  2214. }
  2215. return vtfFilename;
  2216. }
  2217. PreviewImageRetVal_t CMaterial::GetPreviewImageProperties( int *width, int *height,
  2218. ImageFormat *imageFormat, bool* isTranslucent ) const
  2219. {
  2220. char const* pFileName = GetPreviewImageFileName();
  2221. if ( IsX360() || !pFileName )
  2222. {
  2223. *width = *height = 0;
  2224. *imageFormat = IMAGE_FORMAT_RGBA8888;
  2225. *isTranslucent = false;
  2226. return MATERIAL_NO_PREVIEW_IMAGE;
  2227. }
  2228. int nHeaderSize = VTFFileHeaderSize( VTF_MAJOR_VERSION );
  2229. unsigned char *pMem = (unsigned char *)stackalloc( nHeaderSize );
  2230. CUtlBuffer buf( pMem, nHeaderSize );
  2231. if( !g_pFullFileSystem->ReadFile( pFileName, NULL, buf, nHeaderSize ) )
  2232. {
  2233. Warning( "\"%s\" - \"%s\": cached version doesn't exist\n", GetName(), pFileName );
  2234. return MATERIAL_PREVIEW_IMAGE_BAD;
  2235. }
  2236. IVTFTexture *pVTFTexture = CreateVTFTexture();
  2237. if (!pVTFTexture->Unserialize( buf, true ))
  2238. {
  2239. Warning( "Error reading material \"%s\"\n", pFileName );
  2240. DestroyVTFTexture( pVTFTexture );
  2241. return MATERIAL_PREVIEW_IMAGE_BAD;
  2242. }
  2243. *width = pVTFTexture->Width();
  2244. *height = pVTFTexture->Height();
  2245. *imageFormat = pVTFTexture->Format();
  2246. *isTranslucent = (pVTFTexture->Flags() & (TEXTUREFLAGS_ONEBITALPHA | TEXTUREFLAGS_EIGHTBITALPHA)) != 0;
  2247. DestroyVTFTexture( pVTFTexture );
  2248. return MATERIAL_PREVIEW_IMAGE_OK;
  2249. }
  2250. PreviewImageRetVal_t CMaterial::GetPreviewImage( unsigned char *pData, int width, int height,
  2251. ImageFormat imageFormat ) const
  2252. {
  2253. CUtlBuffer buf;
  2254. int nHeaderSize;
  2255. int nImageOffset, nImageSize;
  2256. char const* pFileName = GetPreviewImageFileName();
  2257. if ( IsX360() || !pFileName )
  2258. {
  2259. return MATERIAL_NO_PREVIEW_IMAGE;
  2260. }
  2261. IVTFTexture *pVTFTexture = CreateVTFTexture();
  2262. FileHandle_t fileHandle = g_pFullFileSystem->Open( pFileName, "rb" );
  2263. if( !fileHandle )
  2264. {
  2265. Warning( "\"%s\": cached version doesn't exist\n", pFileName );
  2266. goto fail;
  2267. }
  2268. nHeaderSize = VTFFileHeaderSize( VTF_MAJOR_VERSION );
  2269. buf.EnsureCapacity( nHeaderSize );
  2270. // read the header first.. it's faster!!
  2271. int nBytesRead; // GCC won't let this be initialized right away
  2272. nBytesRead = g_pFullFileSystem->Read( buf.Base(), nHeaderSize, fileHandle );
  2273. buf.SeekPut( CUtlBuffer::SEEK_HEAD, nBytesRead );
  2274. // Unserialize the header
  2275. if (!pVTFTexture->Unserialize( buf, true ))
  2276. {
  2277. Warning( "Error reading material \"%s\"\n", pFileName );
  2278. goto fail;
  2279. }
  2280. // FIXME: Make sure the preview image size requested is the same
  2281. // size as mip level 0 of the texture
  2282. Assert( (width == pVTFTexture->Width()) && (height == pVTFTexture->Height()) );
  2283. // Determine where in the file to start reading (frame 0, face 0, mip 0)
  2284. pVTFTexture->ImageFileInfo( 0, 0, 0, &nImageOffset, &nImageSize );
  2285. if ( nImageSize == 0 )
  2286. {
  2287. Warning( "Couldn't determine offset and size of material \"%s\"\n", pFileName );
  2288. goto fail;
  2289. }
  2290. // Prep the utlbuffer for reading
  2291. buf.EnsureCapacity( nImageSize );
  2292. buf.SeekPut( CUtlBuffer::SEEK_HEAD, 0 );
  2293. // Read in the bits at the specified location
  2294. g_pFullFileSystem->Seek( fileHandle, nImageOffset, FILESYSTEM_SEEK_HEAD );
  2295. g_pFullFileSystem->Read( buf.Base(), nImageSize, fileHandle );
  2296. g_pFullFileSystem->Close( fileHandle );
  2297. // Convert from the format read in to the requested format
  2298. ImageLoader::ConvertImageFormat( (unsigned char*)buf.Base(), pVTFTexture->Format(),
  2299. pData, imageFormat, width, height );
  2300. DestroyVTFTexture( pVTFTexture );
  2301. return MATERIAL_PREVIEW_IMAGE_OK;
  2302. fail:
  2303. if( fileHandle )
  2304. {
  2305. g_pFullFileSystem->Close( fileHandle );
  2306. }
  2307. int nSize = ImageLoader::GetMemRequired( width, height, 1, imageFormat, false );
  2308. memset( pData, 0xff, nSize );
  2309. DestroyVTFTexture( pVTFTexture );
  2310. return MATERIAL_PREVIEW_IMAGE_BAD;
  2311. }
  2312. //-----------------------------------------------------------------------------
  2313. // Material variables
  2314. //-----------------------------------------------------------------------------
  2315. IMaterialVar *CMaterial::FindVar( char const *pVarName, bool *pFound, bool complain )
  2316. {
  2317. PrecacheVars();
  2318. // FIXME: Could look for flags here too...
  2319. MaterialVarSym_t sym = IMaterialVar::FindSymbol(pVarName);
  2320. if ( sym != UTL_INVAL_SYMBOL )
  2321. {
  2322. for (int i = m_VarCount; --i >= 0; )
  2323. {
  2324. if (m_pShaderParams[i]->GetNameAsSymbol() == sym)
  2325. {
  2326. if( pFound )
  2327. *pFound = true;
  2328. return m_pShaderParams[i];
  2329. }
  2330. }
  2331. }
  2332. if( pFound )
  2333. *pFound = false;
  2334. if( complain )
  2335. {
  2336. static int complainCount = 0;
  2337. if( complainCount < 100 )
  2338. {
  2339. Warning( "No such variable \"%s\" for material \"%s\"\n", pVarName, GetName() );
  2340. complainCount++;
  2341. }
  2342. }
  2343. return GetDummyVariable();
  2344. }
  2345. struct tokencache_t
  2346. {
  2347. unsigned short symbol;
  2348. unsigned char varIndex;
  2349. unsigned char cached;
  2350. };
  2351. IMaterialVar *CMaterial::FindVarFast( char const *pVarName, unsigned int *pCacheData )
  2352. {
  2353. tokencache_t *pToken = reinterpret_cast<tokencache_t *>(pCacheData);
  2354. PrecacheVars();
  2355. if ( pToken->cached )
  2356. {
  2357. if ( pToken->varIndex < m_VarCount && m_pShaderParams[pToken->varIndex]->GetNameAsSymbol() == pToken->symbol )
  2358. return m_pShaderParams[pToken->varIndex];
  2359. // FIXME: Could look for flags here too...
  2360. if ( !IMaterialVar::SymbolMatches(pVarName, pToken->symbol) )
  2361. {
  2362. pToken->symbol = IMaterialVar::FindSymbol(pVarName);
  2363. }
  2364. }
  2365. else
  2366. {
  2367. pToken->cached = true;
  2368. pToken->symbol = IMaterialVar::FindSymbol(pVarName);
  2369. }
  2370. if ( pToken->symbol != UTL_INVAL_SYMBOL )
  2371. {
  2372. for (int i = m_VarCount; --i >= 0; )
  2373. {
  2374. if (m_pShaderParams[i]->GetNameAsSymbol() == pToken->symbol)
  2375. {
  2376. pToken->varIndex = i;
  2377. return m_pShaderParams[i];
  2378. }
  2379. }
  2380. }
  2381. return NULL;
  2382. }
  2383. //-----------------------------------------------------------------------------
  2384. // Lovely material properties
  2385. //-----------------------------------------------------------------------------
  2386. void CMaterial::GetReflectivity( Vector& reflect )
  2387. {
  2388. Precache();
  2389. reflect = m_Reflectivity;
  2390. }
  2391. bool CMaterial::GetPropertyFlag( MaterialPropertyTypes_t type )
  2392. {
  2393. Precache();
  2394. if (!IsValidRenderState())
  2395. return false;
  2396. switch( type )
  2397. {
  2398. case MATERIAL_PROPERTY_NEEDS_LIGHTMAP:
  2399. return IsUsingLightmap();
  2400. case MATERIAL_PROPERTY_NEEDS_BUMPED_LIGHTMAPS:
  2401. return IsUsingDiffuseBumpedLighting();
  2402. }
  2403. return false;
  2404. }
  2405. //-----------------------------------------------------------------------------
  2406. // Is the material visible from both sides?
  2407. //-----------------------------------------------------------------------------
  2408. bool CMaterial::IsTwoSided()
  2409. {
  2410. PrecacheVars();
  2411. return GetMaterialVarFlag(MATERIAL_VAR_NOCULL);
  2412. }
  2413. //-----------------------------------------------------------------------------
  2414. // Are we translucent?
  2415. //-----------------------------------------------------------------------------
  2416. bool CMaterial::IsTranslucent()
  2417. {
  2418. Precache();
  2419. if ( m_VarCount > ALPHA )
  2420. return IsTranslucentInternal( m_pShaderParams? m_pShaderParams[ALPHA]->GetFloatValue() : 0.0 );
  2421. return false;
  2422. }
  2423. bool CMaterial::IsTranslucentInternal( float fAlphaModulation ) const
  2424. {
  2425. if (m_pShader && IsValidRenderState())
  2426. {
  2427. // I have to check for alpha modulation here because it isn't
  2428. // factored into the shader's notion of whether or not it's transparent
  2429. return ::IsTranslucent(&m_ShaderRenderState) ||
  2430. (fAlphaModulation < 1.0f) ||
  2431. m_pShader->IsTranslucent( m_pShaderParams );
  2432. }
  2433. return false;
  2434. }
  2435. //-----------------------------------------------------------------------------
  2436. // Are we alphatested?
  2437. //-----------------------------------------------------------------------------
  2438. bool CMaterial::IsAlphaTested()
  2439. {
  2440. Precache();
  2441. if (m_pShader && IsValidRenderState())
  2442. {
  2443. return ::IsAlphaTested(&m_ShaderRenderState) ||
  2444. GetMaterialVarFlag( MATERIAL_VAR_ALPHATEST );
  2445. }
  2446. return false;
  2447. }
  2448. //-----------------------------------------------------------------------------
  2449. // Are we vertex lit?
  2450. //-----------------------------------------------------------------------------
  2451. bool CMaterial::IsVertexLit()
  2452. {
  2453. Precache();
  2454. if (IsValidRenderState())
  2455. {
  2456. return ( GetMaterialVarFlags2() & MATERIAL_VAR2_LIGHTING_VERTEX_LIT ) != 0;
  2457. }
  2458. return false;
  2459. }
  2460. //-----------------------------------------------------------------------------
  2461. // Is the shader a sprite card shader?
  2462. //-----------------------------------------------------------------------------
  2463. bool CMaterial::IsSpriteCard()
  2464. {
  2465. Precache();
  2466. if (IsValidRenderState())
  2467. {
  2468. return ( GetMaterialVarFlags2() & MATERIAL_VAR2_IS_SPRITECARD ) != 0;
  2469. }
  2470. return false;
  2471. }
  2472. //-----------------------------------------------------------------------------
  2473. // Proxies
  2474. //-----------------------------------------------------------------------------
  2475. void CMaterial::CallBindProxy( void *proxyData )
  2476. {
  2477. CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
  2478. bool bIsThreaded = ( pCallQueue != NULL );
  2479. switch (g_config.proxiesTestMode)
  2480. {
  2481. case 0:
  2482. {
  2483. // Make sure we call the proxies in the order in which they show up
  2484. // in the .vmt file
  2485. if ( m_ProxyInfo.Count() )
  2486. {
  2487. if ( bIsThreaded )
  2488. {
  2489. EnableThreadedMaterialVarAccess( true, m_pShaderParams, m_VarCount );
  2490. }
  2491. for( int i = 0; i < m_ProxyInfo.Count(); ++i )
  2492. {
  2493. m_ProxyInfo[i]->OnBind( proxyData );
  2494. }
  2495. if ( bIsThreaded )
  2496. {
  2497. EnableThreadedMaterialVarAccess( false, m_pShaderParams, m_VarCount );
  2498. }
  2499. }
  2500. }
  2501. break;
  2502. case 2:
  2503. // alpha mod all....
  2504. {
  2505. float value = ( sin( 2.0f * M_PI * Plat_FloatTime() / 10.0f ) * 0.5f ) + 0.5f;
  2506. m_pShaderParams[ALPHA]->SetFloatValue( value );
  2507. }
  2508. break;
  2509. case 3:
  2510. // color mod all...
  2511. {
  2512. float value = ( sin( 2.0f * M_PI * Plat_FloatTime() / 10.0f ) * 0.5f ) + 0.5f;
  2513. m_pShaderParams[COLOR]->SetVecValue( value, 1.0f, 1.0f );
  2514. }
  2515. break;
  2516. }
  2517. }
  2518. IMaterial *CMaterial::CheckProxyReplacement( void *proxyData )
  2519. {
  2520. if ( m_pReplacementProxy != NULL )
  2521. {
  2522. IMaterial *pReplaceMaterial = m_pReplacementProxy->GetMaterial();
  2523. if ( pReplaceMaterial )
  2524. {
  2525. return pReplaceMaterial;
  2526. }
  2527. }
  2528. return this;
  2529. }
  2530. bool CMaterial::HasProxy( ) const
  2531. {
  2532. const_cast< CMaterial* >( this )->PrecacheVars();
  2533. return m_ProxyInfo.Count() > 0;
  2534. }
  2535. //-----------------------------------------------------------------------------
  2536. // Main draw method
  2537. //-----------------------------------------------------------------------------
  2538. #ifdef _WIN32
  2539. #pragma warning (disable: 4189)
  2540. #endif
  2541. void CMaterial::DrawMesh( VertexCompressionType_t vertexCompression )
  2542. {
  2543. if ( m_pShader )
  2544. {
  2545. #ifdef _DEBUG
  2546. if ( GetMaterialVarFlags() & MATERIAL_VAR_DEBUG )
  2547. {
  2548. // Putcher breakpoint here to catch the rendering of a material
  2549. // marked for debugging ($debug = 1 in a .vmt file) dynamic state version
  2550. int x = 0;
  2551. }
  2552. #endif
  2553. if ((GetMaterialVarFlags() & MATERIAL_VAR_NO_DRAW) == 0)
  2554. {
  2555. const char *pName = m_pShader->GetName();
  2556. ShaderSystem()->DrawElements( m_pShader, m_pShaderParams, &m_ShaderRenderState, vertexCompression, m_ChangeID ^ g_nDebugVarsSignature );
  2557. }
  2558. }
  2559. else
  2560. {
  2561. Warning( "CMaterial::DrawElements: No bound shader\n" );
  2562. }
  2563. }
  2564. #ifdef _WIN32
  2565. #pragma warning (default: 4189)
  2566. #endif
  2567. IShader *CMaterial::GetShader( ) const
  2568. {
  2569. return m_pShader;
  2570. }
  2571. IMaterialVar *CMaterial::GetShaderParam( int id )
  2572. {
  2573. return m_pShaderParams[id];
  2574. }
  2575. //-----------------------------------------------------------------------------
  2576. // Adds a material variable to the material
  2577. //-----------------------------------------------------------------------------
  2578. void CMaterial::AddMaterialVar( IMaterialVar *pMaterialVar )
  2579. {
  2580. ++m_VarCount;
  2581. m_pShaderParams = (IMaterialVar**)realloc( m_pShaderParams, m_VarCount * sizeof( IMaterialVar*) );
  2582. m_pShaderParams[m_VarCount-1] = pMaterialVar;
  2583. }
  2584. bool CMaterial::IsErrorMaterial() const
  2585. {
  2586. extern IMaterialInternal *g_pErrorMaterial;
  2587. const IMaterialInternal *pThis = this;
  2588. return g_pErrorMaterial == pThis;
  2589. }
  2590. void CMaterial::FindRepresentativeTexture( void )
  2591. {
  2592. Precache();
  2593. // First try to find the base texture...
  2594. bool found;
  2595. IMaterialVar *textureVar = FindVar( "$baseTexture", &found, false );
  2596. if( found && textureVar->GetType() == MATERIAL_VAR_TYPE_TEXTURE )
  2597. {
  2598. ITextureInternal *pTexture = ( ITextureInternal * )textureVar->GetTextureValue();
  2599. if( pTexture )
  2600. {
  2601. pTexture->GetReflectivity( m_Reflectivity );
  2602. }
  2603. }
  2604. if( !found || textureVar->GetType() != MATERIAL_VAR_TYPE_TEXTURE )
  2605. {
  2606. // Try the env map mask if the base texture doesn't work...
  2607. // this is needed for specular decals
  2608. textureVar = FindVar( "$envmapmask", &found, false );
  2609. if( !found || textureVar->GetType() != MATERIAL_VAR_TYPE_TEXTURE )
  2610. {
  2611. // Try the bumpmap
  2612. textureVar = FindVar( "$bumpmap", &found, false );
  2613. if( !found || textureVar->GetType() != MATERIAL_VAR_TYPE_TEXTURE )
  2614. {
  2615. textureVar = FindVar( "$dudvmap", &found, false );
  2616. if( !found || textureVar->GetType() != MATERIAL_VAR_TYPE_TEXTURE )
  2617. {
  2618. textureVar = FindVar( "$normalmap", &found, false );
  2619. if( !found || textureVar->GetType() != MATERIAL_VAR_TYPE_TEXTURE )
  2620. {
  2621. // Warning( "Can't find representative texture for material \"%s\"\n", GetName() );
  2622. m_representativeTexture = TextureManager()->ErrorTexture();
  2623. return;
  2624. }
  2625. }
  2626. }
  2627. }
  2628. }
  2629. m_representativeTexture = static_cast<ITextureInternal *>( textureVar->GetTextureValue() );
  2630. if (m_representativeTexture)
  2631. {
  2632. m_representativeTexture->Precache();
  2633. }
  2634. else
  2635. {
  2636. m_representativeTexture = TextureManager()->ErrorTexture();
  2637. Assert( m_representativeTexture );
  2638. }
  2639. }
  2640. void CMaterial::GetLowResColorSample( float s, float t, float *color ) const
  2641. {
  2642. if( !m_representativeTexture )
  2643. {
  2644. return;
  2645. }
  2646. m_representativeTexture->GetLowResColorSample( s, t, color);
  2647. }
  2648. //-----------------------------------------------------------------------------
  2649. // Lightmap-related methods
  2650. //-----------------------------------------------------------------------------
  2651. void CMaterial::SetMinLightmapPageID( int pageID )
  2652. {
  2653. m_minLightmapPageID = pageID;
  2654. }
  2655. void CMaterial::SetMaxLightmapPageID( int pageID )
  2656. {
  2657. m_maxLightmapPageID = pageID;
  2658. }
  2659. int CMaterial::GetMinLightmapPageID( ) const
  2660. {
  2661. return m_minLightmapPageID;
  2662. }
  2663. int CMaterial::GetMaxLightmapPageID( ) const
  2664. {
  2665. return m_maxLightmapPageID;
  2666. }
  2667. void CMaterial::SetNeedsWhiteLightmap( bool val )
  2668. {
  2669. if ( val )
  2670. m_Flags |= MATERIAL_NEEDS_WHITE_LIGHTMAP;
  2671. else
  2672. m_Flags &= ~MATERIAL_NEEDS_WHITE_LIGHTMAP;
  2673. }
  2674. bool CMaterial::GetNeedsWhiteLightmap( ) const
  2675. {
  2676. return (m_Flags & MATERIAL_NEEDS_WHITE_LIGHTMAP) != 0;
  2677. }
  2678. void CMaterial::MarkAsPreloaded( bool bSet )
  2679. {
  2680. if ( bSet )
  2681. {
  2682. m_Flags |= MATERIAL_IS_PRELOADED;
  2683. }
  2684. else
  2685. {
  2686. m_Flags &= ~MATERIAL_IS_PRELOADED;
  2687. }
  2688. }
  2689. bool CMaterial::IsPreloaded() const
  2690. {
  2691. return ( m_Flags & MATERIAL_IS_PRELOADED ) != 0;
  2692. }
  2693. void CMaterial::ArtificialAddRef( void )
  2694. {
  2695. if ( m_Flags & MATERIAL_ARTIFICIAL_REFCOUNT )
  2696. {
  2697. // already done
  2698. return;
  2699. }
  2700. m_Flags |= MATERIAL_ARTIFICIAL_REFCOUNT;
  2701. m_RefCount++;
  2702. }
  2703. void CMaterial::ArtificialRelease( void )
  2704. {
  2705. if ( !( m_Flags & MATERIAL_ARTIFICIAL_REFCOUNT ) )
  2706. {
  2707. return;
  2708. }
  2709. m_Flags &= ~MATERIAL_ARTIFICIAL_REFCOUNT;
  2710. m_RefCount--;
  2711. }
  2712. //-----------------------------------------------------------------------------
  2713. // Return the shader params
  2714. //-----------------------------------------------------------------------------
  2715. IMaterialVar **CMaterial::GetShaderParams( void )
  2716. {
  2717. return m_pShaderParams;
  2718. }
  2719. int CMaterial::ShaderParamCount() const
  2720. {
  2721. return m_VarCount;
  2722. }
  2723. //-----------------------------------------------------------------------------
  2724. // VMT parser
  2725. //-----------------------------------------------------------------------------
  2726. void InsertKeyValues( KeyValues& dst, KeyValues& src, bool bCheckForExistence, bool bRecursive )
  2727. {
  2728. KeyValues *pSrcVar = src.GetFirstSubKey();
  2729. while( pSrcVar )
  2730. {
  2731. if ( !bCheckForExistence || dst.FindKey( pSrcVar->GetName() ) )
  2732. {
  2733. switch( pSrcVar->GetDataType() )
  2734. {
  2735. case KeyValues::TYPE_STRING:
  2736. dst.SetString( pSrcVar->GetName(), pSrcVar->GetString() );
  2737. break;
  2738. case KeyValues::TYPE_INT:
  2739. dst.SetInt( pSrcVar->GetName(), pSrcVar->GetInt() );
  2740. break;
  2741. case KeyValues::TYPE_FLOAT:
  2742. dst.SetFloat( pSrcVar->GetName(), pSrcVar->GetFloat() );
  2743. break;
  2744. case KeyValues::TYPE_PTR:
  2745. dst.SetPtr( pSrcVar->GetName(), pSrcVar->GetPtr() );
  2746. break;
  2747. case KeyValues::TYPE_NONE:
  2748. {
  2749. // Subkey. Recurse.
  2750. KeyValues *pNewDest = dst.FindKey( pSrcVar->GetName(), true );
  2751. Assert( pNewDest );
  2752. InsertKeyValues( *pNewDest, *pSrcVar, bCheckForExistence, true );
  2753. }
  2754. break;
  2755. }
  2756. }
  2757. pSrcVar = pSrcVar->GetNextKey();
  2758. }
  2759. if ( bRecursive && !dst.GetFirstSubKey() )
  2760. {
  2761. // Insert a dummy key to an empty subkey to make sure it doesn't get removed
  2762. dst.SetInt( "__vmtpatchdummy", 1 );
  2763. }
  2764. if( bCheckForExistence )
  2765. {
  2766. for( KeyValues *pScan = dst.GetFirstTrueSubKey(); pScan; pScan = pScan->GetNextTrueSubKey() )
  2767. {
  2768. KeyValues *pTmp = src.FindKey( pScan->GetName() );
  2769. if( !pTmp )
  2770. continue;
  2771. // make sure that this is a subkey.
  2772. if( pTmp->GetDataType() != KeyValues::TYPE_NONE )
  2773. continue;
  2774. InsertKeyValues( *pScan, *pTmp, bCheckForExistence );
  2775. }
  2776. }
  2777. }
  2778. void WriteKeyValuesToFile( const char *pFileName, KeyValues& keyValues )
  2779. {
  2780. keyValues.SaveToFile( g_pFullFileSystem, pFileName );
  2781. }
  2782. void ApplyPatchKeyValues( KeyValues &keyValues, KeyValues &patchKeyValues )
  2783. {
  2784. KeyValues *pInsertSection = patchKeyValues.FindKey( "insert" );
  2785. KeyValues *pReplaceSection = patchKeyValues.FindKey( "replace" );
  2786. if ( pInsertSection )
  2787. {
  2788. InsertKeyValues( keyValues, *pInsertSection, false );
  2789. }
  2790. if ( pReplaceSection )
  2791. {
  2792. InsertKeyValues( keyValues, *pReplaceSection, true );
  2793. }
  2794. // Could add other commands here, like "delete", "rename", etc.
  2795. }
  2796. //-----------------------------------------------------------------------------
  2797. // Adds keys from srcKeys to destKeys, overwriting any keys that are already
  2798. // there.
  2799. //-----------------------------------------------------------------------------
  2800. void MergeKeyValues( KeyValues &srcKeys, KeyValues &destKeys )
  2801. {
  2802. for( KeyValues *pKV = srcKeys.GetFirstValue(); pKV; pKV = pKV->GetNextValue() )
  2803. {
  2804. switch( pKV->GetDataType() )
  2805. {
  2806. case KeyValues::TYPE_STRING:
  2807. destKeys.SetString( pKV->GetName(), pKV->GetString() );
  2808. break;
  2809. case KeyValues::TYPE_INT:
  2810. destKeys.SetInt( pKV->GetName(), pKV->GetInt() );
  2811. break;
  2812. case KeyValues::TYPE_FLOAT:
  2813. destKeys.SetFloat( pKV->GetName(), pKV->GetFloat() );
  2814. break;
  2815. case KeyValues::TYPE_PTR:
  2816. destKeys.SetPtr( pKV->GetName(), pKV->GetPtr() );
  2817. break;
  2818. }
  2819. }
  2820. for( KeyValues *pKV = srcKeys.GetFirstTrueSubKey(); pKV; pKV = pKV->GetNextTrueSubKey() )
  2821. {
  2822. KeyValues *pDestKV = destKeys.FindKey( pKV->GetName(), true );
  2823. MergeKeyValues( *pKV, *pDestKV );
  2824. }
  2825. }
  2826. //-----------------------------------------------------------------------------
  2827. //-----------------------------------------------------------------------------
  2828. void AccumulatePatchKeyValues( KeyValues &srcKeyValues, KeyValues &patchKeyValues )
  2829. {
  2830. KeyValues *pDestInsertSection = patchKeyValues.FindKey( "insert" );
  2831. if ( pDestInsertSection == NULL )
  2832. {
  2833. pDestInsertSection = new KeyValues( "insert" );
  2834. patchKeyValues.AddSubKey( pDestInsertSection );
  2835. }
  2836. KeyValues *pDestReplaceSection = patchKeyValues.FindKey( "replace" );
  2837. if ( pDestReplaceSection == NULL )
  2838. {
  2839. pDestReplaceSection = new KeyValues( "replace" );
  2840. patchKeyValues.AddSubKey( pDestReplaceSection );
  2841. }
  2842. KeyValues *pSrcInsertSection = srcKeyValues.FindKey( "insert" );
  2843. if ( pSrcInsertSection )
  2844. {
  2845. MergeKeyValues( *pSrcInsertSection, *pDestInsertSection );
  2846. }
  2847. KeyValues *pSrcReplaceSection = srcKeyValues.FindKey( "replace" );
  2848. if ( pSrcReplaceSection )
  2849. {
  2850. MergeKeyValues( *pSrcReplaceSection, *pDestReplaceSection );
  2851. }
  2852. }
  2853. //-----------------------------------------------------------------------------
  2854. //-----------------------------------------------------------------------------
  2855. bool AccumulateRecursiveVmtPatches( KeyValues &patchKeyValuesOut, KeyValues **ppBaseKeyValuesOut, const KeyValues& keyValues, const char *pPathID, CUtlVector<FileNameHandle_t> *pIncludes )
  2856. {
  2857. if ( pIncludes )
  2858. {
  2859. pIncludes->Purge();
  2860. }
  2861. patchKeyValuesOut.Clear();
  2862. if ( V_stricmp( keyValues.GetName(), "patch" ) != 0 )
  2863. {
  2864. // Not a patch file, nothing to do
  2865. if ( ppBaseKeyValuesOut )
  2866. {
  2867. // flag to the caller that the passed in keyValues are in fact final non-patch values
  2868. *ppBaseKeyValuesOut = NULL;
  2869. }
  2870. return true;
  2871. }
  2872. KeyValues *pCurrentKeyValues = keyValues.MakeCopy();
  2873. // Recurse down through all patch files:
  2874. int nCount = 0;
  2875. while( ( nCount < 10 ) && ( V_stricmp( pCurrentKeyValues->GetName(), "patch" ) == 0 ) )
  2876. {
  2877. // Accumulate the new patch keys from this file
  2878. AccumulatePatchKeyValues( *pCurrentKeyValues, patchKeyValuesOut );
  2879. // Load the included file
  2880. const char *pIncludeFileName = pCurrentKeyValues->GetString( "include" );
  2881. if ( pIncludeFileName == NULL )
  2882. {
  2883. // A patch file without an include key? Not good...
  2884. Warning( "VMT patch file has no include key - invalid!\n" );
  2885. Assert( pIncludeFileName );
  2886. break;
  2887. }
  2888. CUtlString includeFileName( pIncludeFileName ); // copy off the string before we clear the keyvalues it lives in
  2889. pCurrentKeyValues->Clear();
  2890. bool bSuccess = pCurrentKeyValues->LoadFromFile( g_pFullFileSystem, includeFileName, pPathID );
  2891. if( bSuccess )
  2892. {
  2893. if ( pIncludes )
  2894. {
  2895. // Remember that we included this file for the pure server stuff.
  2896. pIncludes->AddToTail( g_pFullFileSystem->FindOrAddFileName( includeFileName ) );
  2897. }
  2898. }
  2899. else
  2900. {
  2901. pCurrentKeyValues->deleteThis();
  2902. #ifndef DEDICATED
  2903. Warning( "Failed to load $include VMT file (%s)\n", includeFileName.String() );
  2904. #endif
  2905. if ( !HushAsserts() )
  2906. {
  2907. AssertMsg( false, "Failed to load $include VMT file (%s)", includeFileName.String() );
  2908. }
  2909. return false;
  2910. }
  2911. nCount++;
  2912. }
  2913. if ( ppBaseKeyValuesOut )
  2914. {
  2915. *ppBaseKeyValuesOut = pCurrentKeyValues;
  2916. }
  2917. else
  2918. {
  2919. pCurrentKeyValues->deleteThis();
  2920. }
  2921. if( nCount >= 10 )
  2922. {
  2923. Warning( "Infinite recursion in patch file?\n" );
  2924. }
  2925. return true;
  2926. }
  2927. //-----------------------------------------------------------------------------
  2928. //-----------------------------------------------------------------------------
  2929. void ExpandPatchFile( KeyValues& keyValues, KeyValues &patchKeyValues, const char *pPathID, CUtlVector<FileNameHandle_t> *pIncludes )
  2930. {
  2931. KeyValues *pNonPatchKeyValues = NULL;
  2932. if ( !patchKeyValues.IsEmpty() )
  2933. {
  2934. pNonPatchKeyValues = keyValues.MakeCopy();
  2935. }
  2936. else
  2937. {
  2938. bool bSuccess = AccumulateRecursiveVmtPatches( patchKeyValues, &pNonPatchKeyValues, keyValues, pPathID, pIncludes );
  2939. if ( !bSuccess )
  2940. {
  2941. return;
  2942. }
  2943. }
  2944. if ( pNonPatchKeyValues != NULL )
  2945. {
  2946. // We're dealing with a patch file. Apply accumulated patches to final vmt
  2947. ApplyPatchKeyValues( *pNonPatchKeyValues, patchKeyValues );
  2948. keyValues = *pNonPatchKeyValues;
  2949. pNonPatchKeyValues->deleteThis();
  2950. }
  2951. }
  2952. bool LoadVMTFile( KeyValues &vmtKeyValues, KeyValues &patchKeyValues, const char *pMaterialName, bool bAbsolutePath, CUtlVector<FileNameHandle_t> *pIncludes )
  2953. {
  2954. char pFileName[MAX_PATH];
  2955. const char *pPathID = "GAME";
  2956. if ( !bAbsolutePath )
  2957. {
  2958. Q_snprintf( pFileName, sizeof( pFileName ), "materials/%s.vmt", pMaterialName );
  2959. }
  2960. else
  2961. {
  2962. Q_snprintf( pFileName, sizeof( pFileName ), "%s.vmt", pMaterialName );
  2963. if ( pMaterialName[0] == '/' && pMaterialName[1] == '/' && pMaterialName[2] != '/' )
  2964. {
  2965. // UNC, do full search
  2966. pPathID = NULL;
  2967. }
  2968. }
  2969. if ( !vmtKeyValues.LoadFromFile( g_pFullFileSystem, pFileName, pPathID ) )
  2970. {
  2971. return false;
  2972. }
  2973. ExpandPatchFile( vmtKeyValues, patchKeyValues, pPathID, pIncludes );
  2974. return true;
  2975. }
  2976. int CMaterial::GetNumPasses( void )
  2977. {
  2978. Precache();
  2979. // int mod = m_ShaderRenderState.m_Modulation;
  2980. int mod = 0;
  2981. return m_ShaderRenderState.m_pSnapshots[mod].m_nPassCount;
  2982. }
  2983. int CMaterial::GetTextureMemoryBytes( void )
  2984. {
  2985. Precache();
  2986. int bytes = 0;
  2987. int i;
  2988. for( i = 0; i < m_VarCount; i++ )
  2989. {
  2990. IMaterialVar *pVar = m_pShaderParams[i];
  2991. if( pVar->GetType() == MATERIAL_VAR_TYPE_TEXTURE )
  2992. {
  2993. ITexture *pTexture = pVar->GetTextureValue();
  2994. if( pTexture && pTexture != ( ITexture * )0xffffffff )
  2995. {
  2996. bytes += pTexture->GetApproximateVidMemBytes();
  2997. }
  2998. }
  2999. }
  3000. return bytes;
  3001. }
  3002. void CMaterial::SetUseFixedFunctionBakedLighting( bool bEnable )
  3003. {
  3004. SetMaterialVarFlags2( MATERIAL_VAR2_USE_FIXED_FUNCTION_BAKED_LIGHTING, bEnable );
  3005. }
  3006. bool CMaterial::NeedsFixedFunctionFlashlight() const
  3007. {
  3008. return ( GetMaterialVarFlags2() & MATERIAL_VAR2_NEEDS_FIXED_FUNCTION_FLASHLIGHT ) &&
  3009. MaterialSystem()->InFlashlightMode();
  3010. }
  3011. bool CMaterial::IsUsingVertexID( ) const
  3012. {
  3013. return ( GetMaterialVarFlags2() & MATERIAL_VAR2_USES_VERTEXID ) != 0;
  3014. }
  3015. void CMaterial::DeleteIfUnreferenced()
  3016. {
  3017. if ( m_RefCount > 0 )
  3018. return;
  3019. IMaterialVar::DeleteUnreferencedTextures( true );
  3020. IMaterialInternal::DestroyMaterial( this );
  3021. IMaterialVar::DeleteUnreferencedTextures( false );
  3022. }