summaryrefslogtreecommitdiff
path: root/materialsystem/cmaterial.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /materialsystem/cmaterial.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'materialsystem/cmaterial.cpp')
-rw-r--r--materialsystem/cmaterial.cpp3599
1 files changed, 3599 insertions, 0 deletions
diff --git a/materialsystem/cmaterial.cpp b/materialsystem/cmaterial.cpp
new file mode 100644
index 0000000..90f53d6
--- /dev/null
+++ b/materialsystem/cmaterial.cpp
@@ -0,0 +1,3599 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Implementation of a material
+//
+//===========================================================================//
+
+#include "imaterialinternal.h"
+#include "bitmap/tgaloader.h"
+#include "colorspace.h"
+#include "materialsystem/imaterialvar.h"
+#include "materialsystem/itexture.h"
+#include <string.h>
+#include "materialsystem_global.h"
+#include "shaderapi/ishaderapi.h"
+#include "materialsystem/imaterialproxy.h"
+#include "shadersystem.h"
+#include "materialsystem/imaterialproxyfactory.h"
+#include "IHardwareConfigInternal.h"
+#include "utlsymbol.h"
+#include <malloc.h>
+#include "filesystem.h"
+#include <KeyValues.h>
+#include "mempool.h"
+#include "shaderapi/ishaderutil.h"
+#include "vtf/vtf.h"
+#include "tier1/strtools.h"
+#include <ctype.h>
+#include "utlbuffer.h"
+#include "mathlib/vmatrix.h"
+#include "texturemanager.h"
+#include "itextureinternal.h"
+#include "mempool.h"
+#include "tier1/callqueue.h"
+#include "cmaterial_queuefriendly.h"
+#include "ifilelist.h"
+#include "tier0/icommandline.h"
+#include "tier0/minidump.h"
+
+// #define PROXY_TRACK_NAMES
+
+//-----------------------------------------------------------------------------
+// Material implementation
+//-----------------------------------------------------------------------------
+class CMaterial : public IMaterialInternal
+{
+public:
+ // Members of the IMaterial interface
+ const char *GetName() const;
+ const char *GetTextureGroupName() const;
+
+ PreviewImageRetVal_t GetPreviewImageProperties( int *width, int *height,
+ ImageFormat *imageFormat, bool* isTranslucent ) const;
+ PreviewImageRetVal_t GetPreviewImage( unsigned char *data, int width, int height,
+ ImageFormat imageFormat ) const;
+
+ int GetMappingWidth( );
+ int GetMappingHeight( );
+ int GetNumAnimationFrames( );
+
+ bool InMaterialPage( void ) { return false; }
+ void GetMaterialOffset( float *pOffset );
+ void GetMaterialScale( float *pOffset );
+ IMaterial *GetMaterialPage( void ) { return NULL; }
+
+ void IncrementReferenceCount( );
+ void DecrementReferenceCount( );
+ int GetEnumerationID( ) const;
+ void GetLowResColorSample( float s, float t, float *color ) const;
+
+ IMaterialVar * FindVar( char const *varName, bool *found, bool complain = true );
+ IMaterialVar * FindVarFast( char const *pVarName, unsigned int *pToken );
+
+ // Sets new VMT shader parameters for the material
+ virtual void SetShaderAndParams( KeyValues *pKeyValues );
+
+ bool UsesEnvCubemap( void );
+ bool NeedsSoftwareSkinning( void );
+ virtual bool NeedsSoftwareLighting( void );
+ bool NeedsTangentSpace( void );
+ bool NeedsPowerOfTwoFrameBufferTexture( bool bCheckSpecificToThisFrame = true );
+ bool NeedsFullFrameBufferTexture( bool bCheckSpecificToThisFrame = true );
+ virtual bool IsUsingVertexID( ) const;
+
+ // GR - Is lightmap alpha needed?
+ bool NeedsLightmapBlendAlpha( void );
+
+ virtual void AlphaModulate( float alpha );
+ virtual void ColorModulate( float r, float g, float b );
+ virtual float GetAlphaModulation();
+ virtual void GetColorModulation( float *r, float *g, float *b );
+
+ // Gets the morph format
+ virtual MorphFormat_t GetMorphFormat() const;
+
+ void SetMaterialVarFlag( MaterialVarFlags_t flag, bool on );
+ bool GetMaterialVarFlag( MaterialVarFlags_t flag ) const;
+
+ bool IsTranslucent();
+ 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].
+ bool IsAlphaTested();
+ bool IsVertexLit();
+ virtual bool IsSpriteCard();
+
+ void GetReflectivity( Vector& reflect );
+ bool GetPropertyFlag( MaterialPropertyTypes_t type );
+
+ // Is the material visible from both sides?
+ bool IsTwoSided();
+
+ int GetNumPasses( void );
+ int GetTextureMemoryBytes( void );
+
+ void SetUseFixedFunctionBakedLighting( bool bEnable );
+
+ virtual bool IsPrecached( ) const;
+
+public:
+ // stuff that is visible only from within the material system
+
+ // constructor, destructor
+ CMaterial( char const* materialName, const char *pTextureGroupName, KeyValues *pVMTKeyValues );
+ virtual ~CMaterial();
+
+ void DrawMesh( VertexCompressionType_t vertexCompression );
+ int GetReferenceCount( ) const;
+ void Uncache( bool bPreserveVars = false );
+ void Precache();
+ void ReloadTextures( void );
+ // If provided, pKeyValues and pPatchKeyValues should come from LoadVMTFile()
+ bool PrecacheVars( KeyValues *pKeyValues = NULL, KeyValues *pPatchKeyValues = NULL, CUtlVector<FileNameHandle_t> *pIncludes = NULL, int nFindContext = MATERIAL_FINDCONTEXT_NONE );
+ void SetMinLightmapPageID( int pageID );
+ void SetMaxLightmapPageID( int pageID );
+ int GetMinLightmapPageID( ) const;
+ int GetMaxLightmapPageID( ) const;
+ void SetNeedsWhiteLightmap( bool val );
+ bool GetNeedsWhiteLightmap( ) const;
+ bool IsPrecachedVars( ) const;
+ IShader * GetShader() const;
+ const char *GetShaderName() const;
+
+ virtual void DeleteIfUnreferenced();
+
+ void SetEnumerationID( int id );
+ void CallBindProxy( void *proxyData );
+ virtual IMaterial *CheckProxyReplacement( void *proxyData );
+ bool HasProxy( void ) const;
+
+ // Sets the shader associated with the material
+ void SetShader( const char *pShaderName );
+
+ // Can we override this material in debug?
+ bool NoDebugOverride() const;
+
+ // Gets the vertex format
+ VertexFormat_t GetVertexFormat() const;
+
+ // diffuse bump lightmap?
+ bool IsUsingDiffuseBumpedLighting() const;
+
+ // lightmap?
+ bool IsUsingLightmap() const;
+
+ // Gets the vertex usage flags
+ VertexFormat_t GetVertexUsage() const;
+
+ // Debugs this material
+ bool PerformDebugTrace() const;
+
+ // Are we suppressed?
+ bool IsSuppressed() const;
+
+ // Do we use fog?
+ bool UseFog( void ) const;
+
+ // Should we draw?
+ void ToggleSuppression();
+ void ToggleDebugTrace();
+
+ // Refresh material based on current var values
+ void Refresh();
+ void RefreshPreservingMaterialVars();
+
+ // This computes the state snapshots for this material
+ void RecomputeStateSnapshots();
+
+ // Gets at the shader parameters
+ virtual int ShaderParamCount() const;
+ virtual IMaterialVar **GetShaderParams( void );
+
+ virtual void AddMaterialVar( IMaterialVar *pMaterialVar );
+
+ virtual bool IsErrorMaterial() const;
+
+ // Was this manually created (not read from a file?)
+ virtual bool IsManuallyCreated() const;
+
+ virtual bool NeedsFixedFunctionFlashlight() const;
+
+ virtual void MarkAsPreloaded( bool bSet );
+ virtual bool IsPreloaded() const;
+
+ virtual void ArtificialAddRef( void );
+ virtual void ArtificialRelease( void );
+
+ virtual void ReportVarChanged( IMaterialVar *pVar )
+ {
+ m_ChangeID++;
+ }
+ virtual void ClearContextData( void );
+
+ virtual uint32 GetChangeID() const { return m_ChangeID; }
+
+ virtual bool IsRealTimeVersion( void ) const { return true; }
+ virtual IMaterialInternal *GetRealTimeVersion( void ) { return this; }
+ virtual IMaterialInternal *GetQueueFriendlyVersion( void ) { return &m_QueueFriendlyVersion; }
+
+ void DecideShouldReloadFromWhitelist( IFileList *pFilesToReload );
+ void ReloadFromWhitelistIfMarked();
+ bool WasReloadedFromWhitelist();
+
+private:
+ // Initializes, cleans up the shader params
+ void CleanUpShaderParams();
+
+ // Sets up an error shader when we run into problems.
+ void SetupErrorShader();
+
+ // Does this material have a UNC-file name?
+ bool UsesUNCFileName() const;
+
+ // Prints material flags.
+ void PrintMaterialFlags( int flags, int flagsDefined );
+
+ // Parses material flags
+ bool ParseMaterialFlag( KeyValues* pParseValue, IMaterialVar* pFlagVar,
+ IMaterialVar* pFlagDefinedVar, bool parsingOverrides, int& flagMask, int& overrideMask );
+
+ // Computes the material vars for the shader
+ int ParseMaterialVars( IShader* pShader, KeyValues& keyValues,
+ KeyValues* pOverride, bool modelDefault, IMaterialVar** ppVars, int nFindContext = MATERIAL_FINDCONTEXT_NONE );
+
+ // Figures out the preview image for worldcraft
+ char const* GetPreviewImageName( );
+ char const* GetPreviewImageFileName( void ) const;
+
+ // Hooks up the shader, returns keyvalues of fallback that was used
+ KeyValues* InitializeShader( KeyValues &keyValues, KeyValues &patchKeyValues, int nFindContext = MATERIAL_FINDCONTEXT_NONE );
+
+ // Finds the flag associated with a particular flag name
+ int FindMaterialVarFlag( char const* pFlagName ) const;
+
+ // Initializes, cleans up the state snapshots
+ bool InitializeStateSnapshots();
+ void CleanUpStateSnapshots();
+
+ // Initializes, cleans up the material proxy
+ void InitializeMaterialProxy( KeyValues* pFallbackKeyValues );
+ void CleanUpMaterialProxy();
+ void DetermineProxyReplacements( KeyValues *pFallbackKeyValues );
+
+ // Creates, destroys snapshots
+ RenderPassList_t *CreateRenderPassList();
+ void DestroyRenderPassList( RenderPassList_t *pPassList );
+
+ // Grabs the texture width and height from the var list for faster access
+ void PrecacheMappingDimensions( );
+
+ // Gets the renderstate
+ virtual ShaderRenderState_t *GetRenderState();
+
+ // Do we have a valid renderstate?
+ bool IsValidRenderState() const;
+
+ // Get the material var flags
+ int GetMaterialVarFlags() const;
+ void SetMaterialVarFlags( int flags, bool on );
+ int GetMaterialVarFlags2() const;
+ void SetMaterialVarFlags2( int flags, bool on );
+
+ // Returns a dummy material variable
+ IMaterialVar* GetDummyVariable();
+
+ IMaterialVar* GetShaderParam( int id );
+
+ void FindRepresentativeTexture( void );
+
+ bool ShouldSkipVar( KeyValues *pMaterialVar, bool * pWasConditional );
+
+
+ // Fixed-size allocator
+ DECLARE_FIXEDSIZE_ALLOCATOR( CMaterial );
+
+private:
+ enum
+ {
+ MATERIAL_NEEDS_WHITE_LIGHTMAP = 0x1,
+ MATERIAL_IS_PRECACHED = 0x2,
+ MATERIAL_VARS_IS_PRECACHED = 0x4,
+ MATERIAL_VALID_RENDERSTATE = 0x8,
+ MATERIAL_IS_MANUALLY_CREATED = 0x10,
+ MATERIAL_USES_UNC_FILENAME = 0x20,
+ MATERIAL_IS_PRELOADED = 0x40,
+ MATERIAL_ARTIFICIAL_REFCOUNT = 0x80,
+ };
+
+ int m_iEnumerationID;
+
+ int m_minLightmapPageID;
+ int m_maxLightmapPageID;
+
+ unsigned short m_MappingWidth;
+ unsigned short m_MappingHeight;
+
+ IShader *m_pShader;
+
+ CUtlSymbol m_Name;
+ // Any textures created for this material go under this texture group.
+ CUtlSymbol m_TextureGroupName;
+
+ CInterlockedInt m_RefCount;
+ unsigned short m_Flags;
+
+ unsigned char m_VarCount;
+
+ CUtlVector< IMaterialProxy * > m_ProxyInfo;
+
+#ifdef PROXY_TRACK_NAMES
+ // Array to track names of above material proxies. Useful for tracking down issues with proxies.
+ CUtlVector< CUtlString > m_ProxyInfoNames;
+#endif
+
+ IMaterialVar** m_pShaderParams;
+ IMaterialProxy *m_pReplacementProxy;
+ ShaderRenderState_t m_ShaderRenderState;
+
+ // This remembers filenames of VMTs that we included so we can sv_pure/flush ourselves if any of them need to be reloaded.
+ CUtlVector<FileNameHandle_t> m_VMTIncludes;
+ bool m_bShouldReloadFromWhitelist; // Tells us if the material decided it should be reloaded due to sv_pure whitelist changes.
+
+ ITextureInternal *m_representativeTexture;
+ Vector m_Reflectivity;
+ uint32 m_ChangeID;
+
+ // Used only by procedural materials; it essentially is an in-memory .VMT file
+ KeyValues *m_pVMTKeyValues;
+
+#if defined( _DEBUG )
+ // Makes it easier to see what's going on
+ char *m_pDebugName;
+#endif
+
+protected:
+ CMaterial_QueueFriendly m_QueueFriendlyVersion;
+};
+
+
+// NOTE: This must be the last file included
+// Has to exist *after* fixed size allocator declaration
+#include "tier0/memdbgon.h"
+
+// Forward decls of helper functions for dealing with patch vmts.
+static void ApplyPatchKeyValues( KeyValues &keyValues, KeyValues &patchKeyValues );
+static bool AccumulateRecursiveVmtPatches( KeyValues &patchKeyValuesOut, KeyValues **ppBaseKeyValuesOut,
+ const KeyValues& keyValues, const char *pPathID, CUtlVector<FileNameHandle_t> *pIncludes );
+
+//-----------------------------------------------------------------------------
+// Parser utilities
+//-----------------------------------------------------------------------------
+static inline bool IsWhitespace( char c )
+{
+ return c == ' ' || c == '\t';
+}
+
+static inline bool IsEndline( char c )
+{
+ return c == '\n' || c == '\0';
+}
+
+static inline bool IsVector( char const* v )
+{
+ while (IsWhitespace(*v))
+ {
+ ++v;
+ if (IsEndline(*v))
+ return false;
+ }
+ return *v == '[' || *v == '{';
+}
+
+
+//-----------------------------------------------------------------------------
+// Methods to create state snapshots
+//-----------------------------------------------------------------------------
+#include "tier0/memdbgoff.h"
+
+#ifndef _CONSOLE
+struct EditorRenderStateList_t
+{
+ // Store combo of alpha, color, fixed-function baked lighting, flashlight, editor mode
+ RenderPassList_t m_Snapshots[SNAPSHOT_COUNT_EDITOR];
+
+ DECLARE_FIXEDSIZE_ALLOCATOR( EditorRenderStateList_t );
+};
+#endif
+
+struct StandardRenderStateList_t
+{
+ // Store combo of alpha, color, fixed-function baked lighting, flashlight
+ RenderPassList_t m_Snapshots[SNAPSHOT_COUNT_NORMAL];
+
+ DECLARE_FIXEDSIZE_ALLOCATOR( StandardRenderStateList_t );
+};
+
+#include "tier0/memdbgon.h"
+
+#ifndef _CONSOLE
+DEFINE_FIXEDSIZE_ALLOCATOR( EditorRenderStateList_t, 256, true );
+#endif
+DEFINE_FIXEDSIZE_ALLOCATOR( StandardRenderStateList_t, 256, true );
+
+
+//-----------------------------------------------------------------------------
+// class factory methods
+//-----------------------------------------------------------------------------
+DEFINE_FIXEDSIZE_ALLOCATOR( CMaterial, 256, true );
+
+IMaterialInternal* IMaterialInternal::CreateMaterial( char const* pMaterialName, const char *pTextureGroupName, KeyValues *pVMTKeyValues )
+{
+ MaterialLock_t hMaterialLock = MaterialSystem()->Lock();
+ IMaterialInternal *pResult = new CMaterial( pMaterialName, pTextureGroupName, pVMTKeyValues );
+ MaterialSystem()->Unlock( hMaterialLock );
+ return pResult;
+}
+
+void IMaterialInternal::DestroyMaterial( IMaterialInternal* pMaterial )
+{
+ MaterialLock_t hMaterialLock = MaterialSystem()->Lock();
+ if (pMaterial)
+ {
+ Assert( pMaterial->IsRealTimeVersion() );
+ CMaterial* pMatImp = static_cast<CMaterial*>(pMaterial);
+ // Deletion of the error material is deferred until after all other materials have been deleted.
+ // See CMaterialSystem::CleanUpErrorMaterial() in cmaterialsystem.cpp.
+ if ( !pMatImp->IsErrorMaterial() )
+ {
+ delete pMatImp;
+ }
+ }
+ MaterialSystem()->Unlock( hMaterialLock );
+}
+
+//-----------------------------------------------------------------------------
+// constructor, destructor
+//-----------------------------------------------------------------------------
+CMaterial::CMaterial( char const* materialName, const char *pTextureGroupName, KeyValues *pKeyValues )
+{
+ m_Reflectivity.Init( 0.2f, 0.2f, 0.2f );
+ int len = Q_strlen(materialName);
+ char* pTemp = (char*)_alloca( len + 1 );
+
+ // Strip off the extension
+ Q_StripExtension( materialName, pTemp, len+1 );
+ Q_strlower( pTemp );
+
+#if defined( _X360 )
+ // material names are expected to be forward slashed for correct sort and find behavior!
+ // assert now to track alternate or regressed path that is source of inconsistency
+ Assert( strchr( pTemp, '\\' ) == NULL );
+#endif
+
+ // Convert it to a symbol
+ m_Name = pTemp;
+
+#if defined( _DEBUG )
+ m_pDebugName = new char[strlen(pTemp) + 1];
+ Q_strncpy( m_pDebugName, pTemp, strlen(pTemp) + 1 );
+#endif
+
+ m_bShouldReloadFromWhitelist = false;
+ m_Flags = 0;
+ m_pShader = NULL;
+ m_pShaderParams = NULL;
+ m_RefCount = 0;
+ m_representativeTexture = NULL;
+ m_pReplacementProxy = NULL;
+ m_VarCount = 0;
+ m_MappingWidth = m_MappingHeight = 0;
+ m_iEnumerationID = 0;
+ m_minLightmapPageID = m_maxLightmapPageID = 0;
+ m_TextureGroupName = pTextureGroupName;
+ m_pVMTKeyValues = pKeyValues;
+ if (m_pVMTKeyValues)
+ {
+ m_Flags |= MATERIAL_IS_MANUALLY_CREATED;
+ }
+
+ if ( pTemp[0] == '/' && pTemp[1] == '/' && pTemp[2] != '/' )
+ {
+ m_Flags |= MATERIAL_USES_UNC_FILENAME;
+ }
+
+ // Initialize the renderstate to something indicating nothing should be drawn
+ m_ShaderRenderState.m_Flags = 0;
+ m_ShaderRenderState.m_VertexFormat = m_ShaderRenderState.m_VertexUsage = 0;
+ m_ShaderRenderState.m_MorphFormat = 0;
+ m_ShaderRenderState.m_pSnapshots = CreateRenderPassList();
+ m_ChangeID = 0;
+
+ m_QueueFriendlyVersion.SetRealTimeVersion( this );
+}
+
+CMaterial::~CMaterial()
+{
+ MaterialSystem()->UnbindMaterial( this );
+
+ Uncache();
+
+ if ( m_RefCount != 0 )
+ {
+ DevWarning( 2, "Reference Count for Material %s (%d) != 0\n", GetName(), (int) m_RefCount );
+ }
+
+ if ( m_pVMTKeyValues )
+ {
+ m_pVMTKeyValues->deleteThis();
+ m_pVMTKeyValues = NULL;
+ }
+
+ DestroyRenderPassList( m_ShaderRenderState.m_pSnapshots );
+
+ m_representativeTexture = NULL;
+
+#if defined( _DEBUG )
+ delete [] m_pDebugName;
+#endif
+
+ // Deliberately stomp our VTable so that we can detect cases where code tries to access freed materials.
+ int *p = (int *)this;
+ *p = 0xc0dedbad;
+}
+
+
+void CMaterial::ClearContextData( void )
+{
+ int nSnapshotCount = SnapshotTypeCount();
+ for( int i = 0 ; i < nSnapshotCount ; i++ )
+ for( int j = 0 ; j < m_ShaderRenderState.m_pSnapshots[i].m_nPassCount; j++ )
+ {
+ if ( m_ShaderRenderState.m_pSnapshots[i].m_pContextData[j] )
+ {
+ delete m_ShaderRenderState.m_pSnapshots[i].m_pContextData[j];
+ m_ShaderRenderState.m_pSnapshots[i].m_pContextData[j] = NULL;
+ }
+
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Sets new VMT shader parameters for the material
+//-----------------------------------------------------------------------------
+void CMaterial::SetShaderAndParams( KeyValues *pKeyValues )
+{
+ Uncache();
+
+ if ( m_pVMTKeyValues )
+ {
+ m_pVMTKeyValues->deleteThis();
+ m_pVMTKeyValues = NULL;
+ }
+
+ m_pVMTKeyValues = pKeyValues ? pKeyValues->MakeCopy() : NULL;
+ if (m_pVMTKeyValues)
+ {
+ m_Flags |= MATERIAL_IS_MANUALLY_CREATED;
+ }
+
+ // Apply patches
+ const char *pMaterialName = GetName();
+ char pFileName[MAX_PATH];
+ const char *pPathID = "GAME";
+ if ( !UsesUNCFileName() )
+ {
+ Q_snprintf( pFileName, sizeof( pFileName ), "materials/%s.vmt", pMaterialName );
+ }
+ else
+ {
+ Q_snprintf( pFileName, sizeof( pFileName ), "%s.vmt", pMaterialName );
+ if ( pMaterialName[0] == '/' && pMaterialName[1] == '/' && pMaterialName[2] != '/' )
+ {
+ // UNC, do full search
+ pPathID = NULL;
+ }
+ }
+
+ KeyValues *pLoadedKeyValues = new KeyValues( "vmt" );
+ if ( pLoadedKeyValues->LoadFromFile( g_pFullFileSystem, pFileName, pPathID ) )
+ {
+ // Load succeeded, check if it's a patch file
+ if ( V_stricmp( pLoadedKeyValues->GetName(), "patch" ) == 0 )
+ {
+ // it's a patch file, recursively build up patch keyvalues
+ KeyValues *pPatchKeyValues = new KeyValues( "vmt_patch" );
+ bool bSuccess = AccumulateRecursiveVmtPatches( *pPatchKeyValues, NULL, *pLoadedKeyValues, pPathID, NULL );
+ if ( bSuccess )
+ {
+ // Apply accumulated patches to final vmt
+ ApplyPatchKeyValues( *m_pVMTKeyValues, *pPatchKeyValues );
+ }
+ pPatchKeyValues->deleteThis();
+ }
+ }
+ pLoadedKeyValues->deleteThis();
+
+ if ( g_pShaderDevice->IsUsingGraphics() )
+ {
+ Precache();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates, destroys snapshots
+//-----------------------------------------------------------------------------
+RenderPassList_t *CMaterial::CreateRenderPassList()
+{
+ RenderPassList_t *pRenderPassList;
+ if ( IsConsole() || !MaterialSystem()->CanUseEditorMaterials() )
+ {
+ StandardRenderStateList_t *pList = new StandardRenderStateList_t;
+ pRenderPassList = (RenderPassList_t*)pList->m_Snapshots;
+ }
+#ifndef _CONSOLE
+ else
+ {
+ EditorRenderStateList_t *pList = new EditorRenderStateList_t;
+ pRenderPassList = (RenderPassList_t*)pList->m_Snapshots;
+ }
+#endif
+
+ int nSnapshotCount = SnapshotTypeCount();
+ memset( pRenderPassList, 0, nSnapshotCount * sizeof(RenderPassList_t) );
+ return pRenderPassList;
+}
+
+void CMaterial::DestroyRenderPassList( RenderPassList_t *pPassList )
+{
+ if ( !pPassList )
+ return;
+
+ int nSnapshotCount = SnapshotTypeCount();
+ for( int i = 0 ; i < nSnapshotCount ; i++ )
+ for( int j = 0 ; j < pPassList[i].m_nPassCount; j++ )
+ {
+ if ( pPassList[i].m_pContextData[j] )
+ {
+ delete pPassList[i].m_pContextData[j];
+ pPassList[i].m_pContextData[j] = NULL;
+ }
+
+ }
+ if ( IsConsole() || !MaterialSystem()->CanUseEditorMaterials() )
+ {
+ StandardRenderStateList_t *pList = (StandardRenderStateList_t*)pPassList;
+ delete pList;
+ }
+#ifndef _CONSOLE
+ else
+ {
+ EditorRenderStateList_t *pList = (EditorRenderStateList_t*)pPassList;
+ delete pList;
+ }
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets the renderstate
+//-----------------------------------------------------------------------------
+ShaderRenderState_t *CMaterial::GetRenderState()
+{
+ Precache();
+ return &m_ShaderRenderState;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns a dummy material variable
+//-----------------------------------------------------------------------------
+IMaterialVar* CMaterial::GetDummyVariable()
+{
+ static IMaterialVar* pDummyVar = 0;
+ if (!pDummyVar)
+ pDummyVar = IMaterialVar::Create( 0, "$dummyVar", 0 );
+
+ return pDummyVar;
+}
+
+
+//-----------------------------------------------------------------------------
+// Are vars precached?
+//-----------------------------------------------------------------------------
+bool CMaterial::IsPrecachedVars( ) const
+{
+ return (m_Flags & MATERIAL_VARS_IS_PRECACHED) != 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Are we precached?
+//-----------------------------------------------------------------------------
+bool CMaterial::IsPrecached( ) const
+{
+ return (m_Flags & MATERIAL_IS_PRECACHED) != 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Cleans up shader parameters
+//-----------------------------------------------------------------------------
+void CMaterial::CleanUpShaderParams()
+{
+ if( m_pShaderParams )
+ {
+ for (int i = 0; i < m_VarCount; ++i)
+ {
+ IMaterialVar::Destroy( m_pShaderParams[i] );
+ }
+
+ free( m_pShaderParams );
+ m_pShaderParams = 0;
+ }
+ m_VarCount = 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Initializes the material proxy
+//-----------------------------------------------------------------------------
+void CMaterial::InitializeMaterialProxy( KeyValues* pFallbackKeyValues )
+{
+ IMaterialProxyFactory *pMaterialProxyFactory;
+ pMaterialProxyFactory = MaterialSystem()->GetMaterialProxyFactory();
+ if( !pMaterialProxyFactory )
+ return;
+
+ DetermineProxyReplacements( pFallbackKeyValues );
+
+ if ( m_pReplacementProxy )
+ {
+ m_ProxyInfo.AddToTail( m_pReplacementProxy );
+#ifdef PROXY_TRACK_NAMES
+ m_ProxyInfoNames.AddToTail( "__replacementproxy" );
+#endif
+ }
+
+ // See if we've got a proxy section; obey fallbacks
+ KeyValues* pProxySection = pFallbackKeyValues->FindKey("Proxies");
+ if ( pProxySection )
+ {
+ // Iterate through the section + create all of the proxies
+ KeyValues* pProxyKey = pProxySection->GetFirstSubKey();
+
+ for ( ; pProxyKey; pProxyKey = pProxyKey->GetNextKey() )
+ {
+ // Each of the proxies should themselves be databases
+ IMaterialProxy* pProxy = pMaterialProxyFactory->CreateProxy( pProxyKey->GetName() );
+ if (!pProxy)
+ {
+ Warning( "Error: Material \"%s\" : proxy \"%s\" not found!\n", GetName(), pProxyKey->GetName() );
+ continue;
+ }
+
+ if (!pProxy->Init( this->GetQueueFriendlyVersion(), pProxyKey ))
+ {
+ pMaterialProxyFactory->DeleteProxy( pProxy );
+ Warning( "Error: Material \"%s\" : proxy \"%s\" unable to initialize!\n", GetName(), pProxyKey->GetName() );
+ }
+ else
+ {
+ m_ProxyInfo.AddToTail( pProxy );
+#ifdef PROXY_TRACK_NAMES
+ m_ProxyInfoNames.AddToTail( pProxyKey->GetName() );
+#endif
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Cleans up the material proxy
+//-----------------------------------------------------------------------------
+void CMaterial::CleanUpMaterialProxy()
+{
+ if ( !m_ProxyInfo.Count() )
+ return;
+
+ IMaterialProxyFactory *pMaterialProxyFactory;
+ pMaterialProxyFactory = MaterialSystem()->GetMaterialProxyFactory();
+ if ( !pMaterialProxyFactory )
+ return;
+
+ // Clean up material proxies
+ for ( int i = m_ProxyInfo.Count() - 1; i >= 0; i-- )
+ {
+ IMaterialProxy *pProxy = m_ProxyInfo[ i ];
+
+ pMaterialProxyFactory->DeleteProxy( pProxy );
+ }
+
+ m_ProxyInfo.RemoveAll();
+#ifdef PROXY_TRACK_NAMES
+ m_ProxyInfoNames.RemoveAll();
+#endif
+}
+
+
+void CMaterial::DetermineProxyReplacements( KeyValues *pFallbackKeyValues )
+{
+ m_pReplacementProxy = MaterialSystem()->DetermineProxyReplacements( this, pFallbackKeyValues );
+}
+
+
+static char const *GetVarName( KeyValues *pVar )
+{
+ char const *pVarName = pVar->GetName();
+ char const *pQuestion = strchr( pVarName, '?' );
+ if (! pQuestion )
+ return pVarName;
+ else
+ return pQuestion + 1;
+}
+
+//-----------------------------------------------------------------------------
+// Finds the index of the material var associated with a var
+//-----------------------------------------------------------------------------
+static int FindMaterialVar( IShader* pShader, char const* pVarName )
+{
+ if ( !pShader )
+ return -1;
+
+ // Strip preceeding spaces
+ pVarName += strspn( pVarName, " \t" );
+
+ for (int i = pShader->GetNumParams(); --i >= 0; )
+ {
+ // Makes the parser a little more lenient.. strips off bogus spaces in the var name.
+ const char *pParamName = pShader->GetParamName(i);
+ const char *pFound = Q_stristr( pVarName, pParamName );
+
+ // The found string had better start with the first non-whitespace character
+ if ( pFound != pVarName )
+ continue;
+
+ // Strip spaces at the end
+ int nLen = Q_strlen( pParamName );
+ pFound += nLen;
+ while ( true )
+ {
+ if ( !pFound[0] )
+ return i;
+
+ if ( !IsWhitespace( pFound[0] ) )
+ break;
+
+ ++pFound;
+ }
+ }
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates a vector material var
+//-----------------------------------------------------------------------------
+int ParseVectorFromKeyValueString( KeyValues *pKeyValue, const char *pMaterialName, float vecVal[4] )
+{
+ char const* pScan = pKeyValue->GetString();
+ bool divideBy255 = false;
+
+ // skip whitespace
+ while( IsWhitespace(*pScan) )
+ {
+ ++pScan;
+ }
+
+ if( *pScan == '{' )
+ {
+ divideBy255 = true;
+ }
+ else
+ {
+ Assert( *pScan == '[' );
+ }
+
+ // skip the '['
+ ++pScan;
+ int i;
+ for( i = 0; i < 4; i++ )
+ {
+ // skip whitespace
+ while( IsWhitespace(*pScan) )
+ {
+ ++pScan;
+ }
+
+ if( IsEndline(*pScan) || *pScan == ']' || *pScan == '}' )
+ {
+ if (*pScan != ']' && *pScan != '}')
+ {
+ Warning( "Warning in .VMT file (%s): no ']' or '}' found in vector key \"%s\".\n"
+ "Did you forget to surround the vector with \"s?\n", pMaterialName, pKeyValue->GetName() );
+ }
+
+ // allow for vec2's, etc.
+ vecVal[i] = 0.0f;
+ break;
+ }
+
+ char* pEnd;
+
+ vecVal[i] = strtod( pScan, &pEnd );
+ if (pScan == pEnd)
+ {
+ Warning( "Error in .VMT file: error parsing vector element \"%s\" in \"%s\"\n", pKeyValue->GetName(), pMaterialName );
+ return 0;
+ }
+
+ pScan = pEnd;
+ }
+
+ if( divideBy255 )
+ {
+ vecVal[0] *= ( 1.0f / 255.0f );
+ vecVal[1] *= ( 1.0f / 255.0f );
+ vecVal[2] *= ( 1.0f / 255.0f );
+ vecVal[3] *= ( 1.0f / 255.0f );
+ }
+
+ return i;
+}
+
+static IMaterialVar* CreateVectorMaterialVarFromKeyValue( IMaterial* pMaterial, KeyValues* pKeyValue )
+{
+ char const *pszName = GetVarName( pKeyValue );
+ float vecVal[4];
+ int nDim = ParseVectorFromKeyValueString( pKeyValue, pszName, vecVal );
+ if ( nDim == 0 )
+ return NULL;
+
+ // Create the variable!
+ return IMaterialVar::Create( pMaterial, pszName, vecVal, nDim );
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates a vector material var
+//-----------------------------------------------------------------------------
+static IMaterialVar* CreateMatrixMaterialVarFromKeyValue( IMaterial* pMaterial, KeyValues* pKeyValue )
+{
+ char const* pScan = pKeyValue->GetString();
+ char const *pszName = GetVarName( pKeyValue );
+
+ // Matrices can be specified one of two ways:
+ // [ # # # # # # # # # # # # # # # # ]
+ // or
+ // center # # scale # # rotate # translate # #
+
+ VMatrix mat;
+ int count = sscanf( pScan, " [ %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f ]",
+ &mat.m[0][0], &mat.m[0][1], &mat.m[0][2], &mat.m[0][3],
+ &mat.m[1][0], &mat.m[1][1], &mat.m[1][2], &mat.m[1][3],
+ &mat.m[2][0], &mat.m[2][1], &mat.m[2][2], &mat.m[2][3],
+ &mat.m[3][0], &mat.m[3][1], &mat.m[3][2], &mat.m[3][3] );
+ if (count == 16)
+ {
+ return IMaterialVar::Create( pMaterial, pszName, mat );
+ }
+
+ Vector2D scale, center;
+ float angle;
+ Vector2D translation;
+ count = sscanf( pScan, " center %f %f scale %f %f rotate %f translate %f %f",
+ &center.x, &center.y, &scale.x, &scale.y, &angle, &translation.x, &translation.y );
+ if (count != 7)
+ return NULL;
+
+ VMatrix temp;
+ MatrixBuildTranslation( mat, -center.x, -center.y, 0.0f );
+ MatrixBuildScale( temp, scale.x, scale.y, 1.0f );
+ MatrixMultiply( temp, mat, mat );
+ MatrixBuildRotateZ( temp, angle );
+ MatrixMultiply( temp, mat, mat );
+ MatrixBuildTranslation( temp, center.x + translation.x, center.y + translation.y, 0.0f );
+ MatrixMultiply( temp, mat, mat );
+
+ // Create the variable!
+ return IMaterialVar::Create( pMaterial, pszName, mat );
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates a material var from a key value
+//-----------------------------------------------------------------------------
+
+static IMaterialVar* CreateMaterialVarFromKeyValue( IMaterial* pMaterial, KeyValues* pKeyValue )
+{
+ char const *pszName = GetVarName( pKeyValue );
+ switch( pKeyValue->GetDataType() )
+ {
+ case KeyValues::TYPE_INT:
+ return IMaterialVar::Create( pMaterial, pszName, pKeyValue->GetInt() );
+
+ case KeyValues::TYPE_FLOAT:
+ return IMaterialVar::Create( pMaterial, pszName, pKeyValue->GetFloat() );
+
+ case KeyValues::TYPE_STRING:
+ {
+ char const* pString = pKeyValue->GetString();
+ if (!pString || !pString[0])
+ return 0;
+
+ // Look for matrices
+ IMaterialVar *pMatrixVar = CreateMatrixMaterialVarFromKeyValue( pMaterial, pKeyValue );
+ if (pMatrixVar)
+ return pMatrixVar;
+
+ // Look for vectors
+ if (!IsVector(pString))
+ return IMaterialVar::Create( pMaterial, pszName, pString );
+
+ // Parse the string as a vector...
+ return CreateVectorMaterialVarFromKeyValue( pMaterial, pKeyValue );
+ }
+ }
+
+ return 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Reads out common flags, prevents them from becoming material vars
+//-----------------------------------------------------------------------------
+int CMaterial::FindMaterialVarFlag( char const* pFlagName ) const
+{
+ // Strip preceeding spaces
+ while ( pFlagName[0] )
+ {
+ if ( !IsWhitespace( pFlagName[0] ) )
+ break;
+
+ ++pFlagName;
+ }
+
+ for( int i = 0; *ShaderSystem()->ShaderStateString(i); ++i )
+ {
+ const char *pStateString = ShaderSystem()->ShaderStateString(i);
+ const char *pFound = Q_stristr( pFlagName, pStateString );
+
+ // The found string had better start with the first non-whitespace character
+ if ( pFound != pFlagName )
+ continue;
+
+ // Strip spaces at the end
+ int nLen = Q_strlen( pStateString );
+ pFound += nLen;
+ while ( true )
+ {
+ if ( !pFound[0] )
+ return (1 << i);
+
+ if ( !IsWhitespace( pFound[0] ) )
+ break;
+
+ ++pFound;
+ }
+ }
+ return 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Print material flags
+//-----------------------------------------------------------------------------
+void CMaterial::PrintMaterialFlags( int flags, int flagsDefined )
+{
+ int i;
+ for( i = 0; *ShaderSystem()->ShaderStateString(i); i++ )
+ {
+ if( flags & ( 1<<i ) )
+ {
+ Warning( "%s|", ShaderSystem()->ShaderStateString(i) );
+ }
+ }
+ Warning( "\n" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Parses material flags
+//-----------------------------------------------------------------------------
+bool CMaterial::ParseMaterialFlag( KeyValues* pParseValue, IMaterialVar* pFlagVar,
+ IMaterialVar* pFlagDefinedVar, bool parsingOverrides, int& flagMask, int& overrideMask )
+{
+ // See if the var is a flag...
+ int flagbit = FindMaterialVarFlag( GetVarName( pParseValue ) );
+ if (!flagbit)
+ return false;
+
+ // Allow for flag override
+ int testMask = parsingOverrides ? overrideMask : flagMask;
+ if (testMask & flagbit)
+ {
+ Warning("Error! Flag \"%s\" is multiply defined in material \"%s\"!\n", pParseValue->GetName(), GetName() );
+ return true;
+ }
+
+ // Make sure overrides win
+ if (overrideMask & flagbit)
+ return true;
+
+ if (parsingOverrides)
+ overrideMask |= flagbit;
+ else
+ flagMask |= flagbit;
+
+ // If so, then set the flag bit
+ if (pParseValue->GetInt())
+ pFlagVar->SetIntValue( pFlagVar->GetIntValue() | flagbit );
+ else
+ pFlagVar->SetIntValue( pFlagVar->GetIntValue() & (~flagbit) );
+
+ // Mark the flag as being defined
+ pFlagDefinedVar->SetIntValue( pFlagDefinedVar->GetIntValue() | flagbit );
+
+/*
+ if( stristr( m_pDebugName, "glasswindow064a" ) )
+ {
+ Warning( "flags\n" );
+ PrintMaterialFlags( pFlagVar->GetIntValue(), pFlagDefinedVar->GetIntValue() );
+ }
+*/
+
+ return true;
+}
+
+
+ConVar mat_reduceparticles( "mat_reduceparticles", "0", FCVAR_ALLOWED_IN_COMPETITIVE );
+
+bool CMaterial::ShouldSkipVar( KeyValues *pVar, bool *pWasConditional )
+{
+ char const *pVarName = pVar->GetName();
+ char const *pQuestion = strchr( pVarName, '?' );
+ if ( ( ! pQuestion ) || (pQuestion == pVarName ) )
+ {
+ *pWasConditional = false; // unconditional var
+ return false;
+ }
+ else
+ {
+ bool bShouldSkip = true;
+ *pWasConditional = true;
+ // parse the conditional part
+ char pszConditionName[256];
+ V_strncpy( pszConditionName, pVarName, 1+pQuestion-pVarName );
+ char const *pCond = pszConditionName;
+ bool bToggle = false;
+ if ( pCond[0] == '!' )
+ {
+ pCond++;
+ bToggle = true;
+ }
+
+ if ( ! stricmp( pCond, "lowfill" ) )
+ {
+ bShouldSkip = !mat_reduceparticles.GetBool();
+ }
+ else if ( ! stricmp( pCond, "hdr" ) )
+ {
+ bShouldSkip = ( HardwareConfig()->GetHDRType() == HDR_TYPE_NONE );
+ }
+ else if ( ! stricmp( pCond, "srgb" ) )
+ {
+ bShouldSkip = ( !HardwareConfig()->UsesSRGBCorrectBlending() );
+ }
+ else if ( ! stricmp( pCond, "ldr" ) )
+ {
+ bShouldSkip = ( HardwareConfig()->GetHDRType() != HDR_TYPE_NONE );
+ }
+ else if ( ! stricmp( pCond, "360" ) )
+ {
+ bShouldSkip = !IsX360();
+ }
+ else
+ {
+ Warning( "unrecognized conditional test %s in %s\n", pVarName, GetName() );
+ }
+
+ return bShouldSkip ^ bToggle;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes the material vars for the shader
+//-----------------------------------------------------------------------------
+int CMaterial::ParseMaterialVars( IShader* pShader, KeyValues& keyValues,
+ KeyValues* pOverrideKeyValues, bool modelDefault, IMaterialVar** ppVars, int nFindContext )
+{
+ IMaterialVar* pNewVar;
+ bool pOverride[256];
+ bool bWasConditional[256];
+ int overrideMask = 0;
+ int flagMask = 0;
+
+ memset( ppVars, 0, 256 * sizeof(IMaterialVar*) );
+ memset( pOverride, 0, sizeof( pOverride ) );
+ memset( bWasConditional, 0, sizeof( bWasConditional ) );
+
+ // Create the flag var...
+ // Set model mode if we fell back from a model mode shader
+ int modelFlag = modelDefault ? MATERIAL_VAR_MODEL : 0;
+ ppVars[FLAGS] = IMaterialVar::Create( this, "$flags", modelFlag );
+ ppVars[FLAGS_DEFINED] = IMaterialVar::Create( this, "$flags_defined", modelFlag );
+ ppVars[FLAGS2] = IMaterialVar::Create( this, "$flags2", 0 );
+ ppVars[FLAGS_DEFINED2] = IMaterialVar::Create( this, "$flags_defined2", 0 );
+
+ int numParams = pShader ? pShader->GetNumParams() : 0;
+ int varCount = numParams;
+
+ bool parsingOverrides = (pOverrideKeyValues != 0);
+ KeyValues* pVar = pOverrideKeyValues ? pOverrideKeyValues->GetFirstSubKey() : keyValues.GetFirstSubKey();
+
+ const char *pszMatName = pVar ? pVar->GetString() : "Unknown";
+
+ while( pVar )
+ {
+ bool bProcessThisOne = true;
+
+ bool bIsConditionalVar;
+
+ const char *pszVarName = GetVarName( pVar );
+
+ if ( (nFindContext == MATERIAL_FINDCONTEXT_ISONAMODEL) && pszVarName && pszVarName[0] )
+ {
+ // Prevent ignorez models
+ // Should we do 'nofog' too? For now, decided not to.
+ if ( Q_stristr(pszVarName,"$ignorez") )
+ {
+ Warning("Ignoring material flag '%s' on material '%s'.\n", pszVarName, pszMatName );
+ goto nextVar;
+ }
+ }
+
+ // See if the var is a flag...
+ if (
+ ShouldSkipVar( pVar, &bIsConditionalVar ) || // should skip?
+ ((pVar->GetName()[0] == '%') && (g_pShaderDevice->IsUsingGraphics()) && (!MaterialSystem()->CanUseEditorMaterials() ) ) || // is an editor var?
+ ParseMaterialFlag( pVar, ppVars[FLAGS], ppVars[FLAGS_DEFINED], parsingOverrides, flagMask, overrideMask ) || // is a flag?
+ ParseMaterialFlag( pVar, ppVars[FLAGS2], ppVars[FLAGS_DEFINED2], parsingOverrides, flagMask, overrideMask )
+ )
+ bProcessThisOne = false;
+
+ if ( bProcessThisOne )
+ {
+ // See if the var is one of the shader params
+ int varIdx = FindMaterialVar( pShader, pszVarName );
+
+ // Check for multiply defined or overridden
+ if (varIdx >= 0)
+ {
+ if (ppVars[varIdx] && (! bIsConditionalVar ) )
+ {
+ if ( !pOverride[varIdx] || parsingOverrides )
+ {
+ Warning("Error! Variable \"%s\" is multiply defined in material \"%s\"!\n", pVar->GetName(), GetName() );
+ }
+ goto nextVar;
+ }
+ }
+ else
+ {
+ int i;
+ for ( i = numParams; i < varCount; ++i)
+ {
+ Assert( ppVars[i] );
+ if (!stricmp( ppVars[i]->GetName(), pVar->GetName() ))
+ break;
+ }
+ if (i != varCount)
+ {
+ if ( !pOverride[i] || parsingOverrides )
+ {
+ Warning("Error! Variable \"%s\" is multiply defined in material \"%s\"!\n", pVar->GetName(), GetName() );
+ }
+ goto nextVar;
+ }
+ }
+
+ // Create a material var for this dudely dude; could be zero...
+ pNewVar = CreateMaterialVarFromKeyValue( this, pVar );
+ if (!pNewVar)
+ goto nextVar;
+
+ if (varIdx < 0)
+ {
+ varIdx = varCount++;
+ }
+ if ( ppVars[varIdx] )
+ {
+ IMaterialVar::Destroy( ppVars[varIdx] );
+ }
+ ppVars[varIdx] = pNewVar;
+ if (parsingOverrides)
+ pOverride[varIdx] = true;
+ bWasConditional[varIdx] = bIsConditionalVar;
+
+ }
+
+nextVar:
+ pVar = pVar->GetNextKey();
+ if (!pVar && parsingOverrides)
+ {
+ pVar = keyValues.GetFirstSubKey();
+ parsingOverrides = false;
+ }
+ }
+
+ // Create undefined vars for all the actual material vars
+ for (int i = 0; i < numParams; ++i)
+ {
+ if (!ppVars[i])
+ ppVars[i] = IMaterialVar::Create( this, pShader->GetParamName(i) );
+ }
+
+ return varCount;
+}
+
+
+static KeyValues *CheckConditionalFakeShaderName( char const *pShaderName, char const *pSuffixName,
+ KeyValues *pKeyValues )
+{
+ KeyValues *pFallbackSection = pKeyValues->FindKey( pSuffixName );
+ if (pFallbackSection)
+ return pFallbackSection;
+
+ char nameBuf[256];
+ V_snprintf( nameBuf, sizeof(nameBuf), "%s_%s", pShaderName, pSuffixName );
+ pFallbackSection = pKeyValues->FindKey( nameBuf );
+
+ if (pFallbackSection)
+ return pFallbackSection;
+
+ return NULL;
+}
+
+
+static KeyValues *FindBuiltinFallbackBlock( char const *pShaderName, KeyValues *pKeyValues )
+{
+ // handle "fake" shader fallbacks which are conditional upon mode. like _hdr_dx9, etc
+ if ( HardwareConfig()->GetDXSupportLevel() < 90 )
+ {
+ KeyValues *pRet = CheckConditionalFakeShaderName( pShaderName,"<DX90", pKeyValues );
+ if ( pRet )
+ return pRet;
+ }
+ if ( HardwareConfig()->GetDXSupportLevel() < 95 )
+ {
+ KeyValues *pRet = CheckConditionalFakeShaderName( pShaderName,"<DX95", pKeyValues );
+ if ( pRet )
+ return pRet;
+ }
+ if ( HardwareConfig()->GetDXSupportLevel() < 90 || !HardwareConfig()->SupportsPixelShaders_2_b() )
+ {
+ KeyValues *pRet = CheckConditionalFakeShaderName( pShaderName,"<DX90_20b", pKeyValues );
+ if ( pRet )
+ return pRet;
+ }
+ if ( HardwareConfig()->GetDXSupportLevel() >= 90 && HardwareConfig()->SupportsPixelShaders_2_b() )
+ {
+ KeyValues *pRet = CheckConditionalFakeShaderName( pShaderName,">=DX90_20b", pKeyValues );
+ if ( pRet )
+ return pRet;
+ }
+ if ( HardwareConfig()->GetDXSupportLevel() <= 90 )
+ {
+ KeyValues *pRet = CheckConditionalFakeShaderName( pShaderName,"<=DX90", pKeyValues );
+ if ( pRet )
+ return pRet;
+ }
+ if ( HardwareConfig()->GetDXSupportLevel() >= 90 )
+ {
+ KeyValues *pRet = CheckConditionalFakeShaderName( pShaderName,">=DX90", pKeyValues );
+ if ( pRet )
+ return pRet;
+ }
+ if ( HardwareConfig()->GetDXSupportLevel() > 90 )
+ {
+ KeyValues *pRet = CheckConditionalFakeShaderName( pShaderName,">DX90", pKeyValues );
+ if ( pRet )
+ return pRet;
+ }
+ if ( HardwareConfig()->GetHDRType() != HDR_TYPE_NONE )
+ {
+ KeyValues *pRet = CheckConditionalFakeShaderName( pShaderName,"hdr_dx9", pKeyValues );
+ if ( pRet )
+ return pRet;
+ pRet = CheckConditionalFakeShaderName( pShaderName,"hdr", pKeyValues );
+ if ( pRet )
+ return pRet;
+ }
+ else
+ {
+ KeyValues *pRet = CheckConditionalFakeShaderName( pShaderName,"ldr", pKeyValues );
+ if ( pRet )
+ return pRet;
+ }
+ if ( HardwareConfig()->UsesSRGBCorrectBlending() )
+ {
+ KeyValues *pRet = CheckConditionalFakeShaderName( pShaderName,"srgb", pKeyValues );
+ if ( pRet )
+ return pRet;
+ }
+ if ( HardwareConfig()->GetDXSupportLevel() >= 90 )
+ {
+ KeyValues *pRet = CheckConditionalFakeShaderName( pShaderName,"dx9", pKeyValues );
+ if ( pRet )
+ return pRet;
+ }
+ return NULL;
+}
+
+inline const char *MissingShaderName()
+{
+ return (IsWindows() && !IsEmulatingGL()) ? "Wireframe_DX8" : "Wireframe_DX9";
+}
+
+//-----------------------------------------------------------------------------
+// Hooks up the shader
+//-----------------------------------------------------------------------------
+KeyValues* CMaterial::InitializeShader( KeyValues &keyValues, KeyValues &patchKeyValues, int nFindContext )
+{
+ MaterialLock_t hMaterialLock = MaterialSystem()->Lock();
+
+ KeyValues* pCurrentFallback = &keyValues;
+ KeyValues* pFallbackSection = 0;
+
+ char szShaderName[MAX_PATH];
+ char const* pShaderName = pCurrentFallback->GetName();
+ if ( !pShaderName )
+ {
+ // I'm not quite sure how this can happen, but we'll see...
+ Warning( "Shader not specified in material %s\nUsing wireframe instead...\n", GetName() );
+ Assert( 0 );
+ pShaderName = MissingShaderName();
+ }
+ else
+ {
+ // can't pass a stable reference to the key values name around
+ // naive leaf functions can cause KV system to re-alloc
+ V_strncpy( szShaderName, pShaderName, sizeof( szShaderName ) );
+ pShaderName = szShaderName;
+ }
+
+ IShader* pShader;
+ IMaterialVar* ppVars[256];
+ char pFallbackShaderNameBuf[256];
+ char pFallbackMaterialNameBuf[256];
+ int varCount = 0;
+ bool modelDefault = false;
+
+ // Keep going until there's no more fallbacks...
+ while( true )
+ {
+ // Find the shader for this material. Note that this may not be
+ // the actual shader we use due to fallbacks...
+ pShader = ShaderSystem()->FindShader( pShaderName );
+ if ( !pShader )
+ {
+ if ( g_pShaderDevice->IsUsingGraphics() )
+ {
+ Warning( "Error: Material \"%s\" uses unknown shader \"%s\"\n", GetName(), pShaderName );
+ //hushed Assert( 0 );
+ }
+
+ pShaderName = MissingShaderName();
+ pShader = ShaderSystem()->FindShader( pShaderName );
+
+ if ( !HushAsserts() )
+ {
+ AssertMsg( pShader, "pShader==NULL. Shader: %s", GetName() );
+ }
+
+#ifndef DEDICATED
+ if ( !pShader )
+ {
+#ifdef LINUX
+ // Exit out here. We're running into issues where this material is returned in a horribly broken
+ // state and you wind up crashing in LockMesh() because the vertex and index buffer pointers
+ // are NULL. You can repro this by not dying here and showing the intro movie. This happens on
+ // Linux when you run from a symlink'd SteamApps directory.
+ Error( "Shader '%s' for material '%s' not found.\n",
+ pCurrentFallback->GetName() ? pCurrentFallback->GetName() : pShaderName, GetName() );
+#endif
+ MaterialSystem()->Unlock( hMaterialLock );
+ return NULL;
+ }
+#endif
+
+ }
+
+ bool bHasBuiltinFallbackBlock = false;
+ if ( !pFallbackSection )
+ {
+ pFallbackSection = FindBuiltinFallbackBlock( pShaderName, &keyValues );
+ if( pFallbackSection )
+ {
+ bHasBuiltinFallbackBlock = true;
+ pFallbackSection->ChainKeyValue( &keyValues );
+ pCurrentFallback = pFallbackSection;
+ }
+ }
+
+ // Here we must set up all flags + material vars that the shader needs
+ // because it may look at them when choosing shader fallback.
+ varCount = ParseMaterialVars( pShader, keyValues, pFallbackSection, modelDefault, ppVars, nFindContext );
+
+ if ( !pShader )
+ break;
+
+ // Make sure we set default values before the fallback is looked for
+ ShaderSystem()->InitShaderParameters( pShader, ppVars, GetName() );
+
+ // Now that the material vars are parsed, see if there's a fallback
+ // But only if we're not in the tools
+/*
+ if (!g_pShaderAPI->IsUsingGraphics())
+ break;
+*/
+
+ // Check for a fallback; if not, we're done
+ pShaderName = pShader->GetFallbackShader( ppVars );
+ if (!pShaderName)
+ {
+ break;
+ }
+ // Copy off the shader name, as it may be in a materialvar in the shader
+ // because we're about to delete all materialvars
+ Q_strncpy( pFallbackShaderNameBuf, pShaderName, 256 );
+ pShaderName = pFallbackShaderNameBuf;
+
+ // Remember the model flag if we're on dx7 or higher...
+ if (HardwareConfig()->SupportsVertexAndPixelShaders())
+ {
+ modelDefault = ( ppVars[FLAGS]->GetIntValue() & MATERIAL_VAR_MODEL ) != 0;
+ }
+
+ // Try to get the section associated with the fallback shader
+ // Then chain it to the base data so it can override the
+ // values if it wants to
+ if( !bHasBuiltinFallbackBlock )
+ {
+ pFallbackSection = keyValues.FindKey( pShaderName );
+ if (pFallbackSection)
+ {
+ pFallbackSection->ChainKeyValue( &keyValues );
+ pCurrentFallback = pFallbackSection;
+ }
+ }
+
+ // Now, blow away all of the material vars + try again...
+ for (int i = 0; i < varCount; ++i)
+ {
+ Assert( ppVars[i] );
+ IMaterialVar::Destroy( ppVars[i] );
+ }
+
+ // Check the KeyValues for '$fallbackmaterial'
+ // Note we have to do this *after* we chain the keyvalues
+ // based on the fallback shader since the names of the fallback material
+ // must lie within the shader-specific block usually.
+ const char *pFallbackMaterial = pCurrentFallback->GetString( "$fallbackmaterial" );
+ if ( pFallbackMaterial[0] )
+ {
+ // Don't fallback to ourselves
+ if ( Q_stricmp( GetName(), pFallbackMaterial ) )
+ {
+ // Gotta copy it off; clearing the keyvalues will blow the string away
+ Q_strncpy( pFallbackMaterialNameBuf, pFallbackMaterial, 256 );
+ keyValues.Clear();
+ if( !LoadVMTFile( keyValues, patchKeyValues, pFallbackMaterialNameBuf, UsesUNCFileName(), NULL ) )
+ {
+ Warning( "CMaterial::PrecacheVars: error loading vmt file %s for %s\n", pFallbackMaterialNameBuf, GetName() );
+ keyValues = *(((CMaterial *)g_pErrorMaterial)->m_pVMTKeyValues);
+ }
+ }
+ else
+ {
+ Warning( "CMaterial::PrecacheVars: fallback material for vmt file %s is itself!\n", GetName() );
+ keyValues = *(((CMaterial *)g_pErrorMaterial)->m_pVMTKeyValues);
+ }
+ pCurrentFallback = &keyValues;
+ pFallbackSection = NULL;
+
+ // I'm not quite sure how this can happen, but we'll see...
+ pShaderName = pCurrentFallback->GetName();
+ if (!pShaderName)
+ {
+ Warning("Shader not specified in material %s (fallback %s)\nUsing wireframe instead...\n", GetName(), pFallbackMaterialNameBuf );
+ pShaderName = MissingShaderName();
+ }
+ }
+ }
+
+ // Store off the shader
+ m_pShader = pShader;
+
+ // Store off the material vars + flags
+ m_VarCount = varCount;
+ m_pShaderParams = (IMaterialVar**)malloc( varCount * sizeof(IMaterialVar*) );
+ memcpy( m_pShaderParams, ppVars, varCount * sizeof(IMaterialVar*) );
+
+#ifdef _DEBUG
+ for (int i = 0; i < varCount; ++i)
+ {
+ Assert( ppVars[i] );
+ }
+#endif
+
+ MaterialSystem()->Unlock( hMaterialLock );
+ return pCurrentFallback;
+}
+
+//-----------------------------------------------------------------------------
+// Gets the texturemap size
+//-----------------------------------------------------------------------------
+
+void CMaterial::PrecacheMappingDimensions( )
+{
+ // Cache mapping width and mapping height
+ if (!m_representativeTexture)
+ {
+#ifdef PARANOID
+ Warning( "No representative texture on material: \"%s\"\n", GetName() );
+#endif
+ m_MappingWidth = 64;
+ m_MappingHeight = 64;
+ }
+ else
+ {
+ m_MappingWidth = m_representativeTexture->GetMappingWidth();
+ m_MappingHeight = m_representativeTexture->GetMappingHeight();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Initialize the state snapshot
+//-----------------------------------------------------------------------------
+bool CMaterial::InitializeStateSnapshots()
+{
+ if (IsPrecached())
+ {
+ if ( MaterialSystem()->GetCurrentMaterial() == this)
+ {
+ g_pShaderAPI->FlushBufferedPrimitives();
+ }
+
+ // Default state
+ CleanUpStateSnapshots();
+
+ if ( m_pShader && !ShaderSystem()->InitRenderState( m_pShader, m_VarCount, m_pShaderParams, &m_ShaderRenderState, GetName() ))
+ {
+ m_Flags &= ~MATERIAL_VALID_RENDERSTATE;
+ return false;
+ }
+
+ m_Flags |= MATERIAL_VALID_RENDERSTATE;
+ }
+
+ return true;
+}
+
+void CMaterial::CleanUpStateSnapshots()
+{
+ if (IsValidRenderState())
+ {
+ ShaderSystem()->CleanupRenderState(&m_ShaderRenderState);
+ // -- THIS CANNOT BE HERE: m_Flags &= ~MATERIAL_VALID_RENDERSTATE;
+ // -- because it will cause a crash when main thread asks for material
+ // -- sort group it can temporarily see material in invalid render state
+ // -- and crash in DecalSurfaceAdd(msurface2_t*, int)
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// This sets up a debugging/error shader...
+//-----------------------------------------------------------------------------
+void CMaterial::SetupErrorShader()
+{
+ // Preserve the model flags
+ int flags = 0;
+ if ( m_pShaderParams && m_pShaderParams[FLAGS] )
+ {
+ flags = (m_pShaderParams[FLAGS]->GetIntValue() & MATERIAL_VAR_MODEL);
+ }
+
+ CleanUpShaderParams();
+ CleanUpMaterialProxy();
+
+ // We had a failure; replace it with a valid shader...
+
+ m_pShader = ShaderSystem()->FindShader( MissingShaderName() );
+ Assert( m_pShader );
+
+ // Create undefined vars for all the actual material vars
+ m_VarCount = m_pShader->GetNumParams();
+ m_pShaderParams = (IMaterialVar**)malloc( m_VarCount * sizeof(IMaterialVar*) );
+
+ for (int i = 0; i < m_VarCount; ++i)
+ {
+ m_pShaderParams[i] = IMaterialVar::Create( this, m_pShader->GetParamName(i) );
+ }
+
+ // Store the model flags
+ SetMaterialVarFlags( flags, true );
+
+ // Set the default values
+ ShaderSystem()->InitShaderParameters( m_pShader, m_pShaderParams, "Error" );
+
+ // Invokes the SHADER_INIT block in the various shaders,
+ ShaderSystem()->InitShaderInstance( m_pShader, m_pShaderParams, "Error", GetTextureGroupName() );
+
+#ifdef DBGFLAG_ASSERT
+ bool ok =
+#endif
+ InitializeStateSnapshots();
+
+ m_QueueFriendlyVersion.UpdateToRealTime();
+
+ Assert(ok);
+}
+
+
+//-----------------------------------------------------------------------------
+// This computes the state snapshots for this material
+//-----------------------------------------------------------------------------
+void CMaterial::RecomputeStateSnapshots()
+{
+ CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
+ if ( pCallQueue )
+ {
+ pCallQueue->QueueCall( this, &CMaterial::RecomputeStateSnapshots );
+ return;
+ }
+
+ bool ok = InitializeStateSnapshots();
+
+ // compute the state snapshots
+ if (!ok)
+ {
+ SetupErrorShader();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Are we valid
+//-----------------------------------------------------------------------------
+inline bool CMaterial::IsValidRenderState() const
+{
+ return (m_Flags & MATERIAL_VALID_RENDERSTATE) != 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets/sets material var flags
+//-----------------------------------------------------------------------------
+inline int CMaterial::GetMaterialVarFlags() const
+{
+ if ( m_pShaderParams && m_pShaderParams[FLAGS] )
+ {
+ return m_pShaderParams[FLAGS]->GetIntValueFast();
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+inline void CMaterial::SetMaterialVarFlags( int flags, bool on )
+{
+ if ( !m_pShaderParams )
+ {
+ Assert( 0 ); // are we hanging onto a material that has been cleaned up or isn't ready?
+ return;
+ }
+
+ if (on)
+ {
+ m_pShaderParams[FLAGS]->SetIntValue( GetMaterialVarFlags() | flags );
+ }
+ else
+ {
+ m_pShaderParams[FLAGS]->SetIntValue( GetMaterialVarFlags() & (~flags) );
+ }
+
+ // Mark it as being defined...
+ m_pShaderParams[FLAGS_DEFINED]->SetIntValue( m_pShaderParams[FLAGS_DEFINED]->GetIntValueFast() | flags );
+}
+
+inline int CMaterial::GetMaterialVarFlags2() const
+{
+ if ( m_pShaderParams && m_VarCount > FLAGS2 && m_pShaderParams[FLAGS2] )
+ {
+ return m_pShaderParams[FLAGS2]->GetIntValueFast();
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+inline void CMaterial::SetMaterialVarFlags2( int flags, bool on )
+{
+ if ( m_pShaderParams && m_VarCount > FLAGS2 && m_pShaderParams[FLAGS2] )
+ {
+ if (on)
+ m_pShaderParams[FLAGS2]->SetIntValue( GetMaterialVarFlags2() | flags );
+ else
+ m_pShaderParams[FLAGS2]->SetIntValue( GetMaterialVarFlags2() & (~flags) );
+ }
+
+ if ( m_pShaderParams && m_VarCount > FLAGS_DEFINED2 && m_pShaderParams[FLAGS_DEFINED2] )
+ {
+ // Mark it as being defined...
+ m_pShaderParams[FLAGS_DEFINED2]->SetIntValue(
+ m_pShaderParams[FLAGS_DEFINED2]->GetIntValueFast() | flags );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets the morph format
+//-----------------------------------------------------------------------------
+MorphFormat_t CMaterial::GetMorphFormat() const
+{
+ const_cast<CMaterial*>(this)->Precache();
+ Assert( IsValidRenderState() );
+ return m_ShaderRenderState.m_MorphFormat;
+}
+
+//-----------------------------------------------------------------------------
+// Gets the vertex format
+//-----------------------------------------------------------------------------
+VertexFormat_t CMaterial::GetVertexFormat() const
+{
+ Assert( IsValidRenderState() );
+ return m_ShaderRenderState.m_VertexFormat;
+}
+
+VertexFormat_t CMaterial::GetVertexUsage() const
+{
+ Assert( IsValidRenderState() );
+ return m_ShaderRenderState.m_VertexUsage;
+}
+
+bool CMaterial::PerformDebugTrace() const
+{
+ return IsValidRenderState() && ((GetMaterialVarFlags() & MATERIAL_VAR_DEBUG ) != 0);
+}
+
+
+//-----------------------------------------------------------------------------
+// Are we suppressed?
+//-----------------------------------------------------------------------------
+bool CMaterial::IsSuppressed() const
+{
+ if ( !IsValidRenderState() )
+ return true;
+
+ return ((GetMaterialVarFlags() & MATERIAL_VAR_NO_DRAW) != 0);
+}
+
+void CMaterial::ToggleSuppression()
+{
+ if (IsValidRenderState())
+ {
+ if ((GetMaterialVarFlags() & MATERIAL_VAR_NO_DEBUG_OVERRIDE) != 0)
+ return;
+
+ SetMaterialVarFlags( MATERIAL_VAR_NO_DRAW,
+ (GetMaterialVarFlags() & MATERIAL_VAR_NO_DRAW) == 0 );
+ }
+}
+
+void CMaterial::ToggleDebugTrace()
+{
+ if (IsValidRenderState())
+ {
+ SetMaterialVarFlags( MATERIAL_VAR_DEBUG,
+ (GetMaterialVarFlags() & MATERIAL_VAR_DEBUG) == 0 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Can we override this material in debug?
+//-----------------------------------------------------------------------------
+
+bool CMaterial::NoDebugOverride() const
+{
+ return IsValidRenderState() && (GetMaterialVarFlags() & MATERIAL_VAR_NO_DEBUG_OVERRIDE) != 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Material Var flags
+//-----------------------------------------------------------------------------
+void CMaterial::SetMaterialVarFlag( MaterialVarFlags_t flag, bool on )
+{
+ CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
+ if ( pCallQueue )
+ {
+ pCallQueue->QueueCall( this, &CMaterial::SetMaterialVarFlag, flag, on );
+ return;
+ }
+
+ bool oldOn = (GetMaterialVarFlags( ) & flag) != 0;
+ if (oldOn != on)
+ {
+ SetMaterialVarFlags( flag, on );
+
+ // This is going to be called from client code; recompute snapshots!
+ RecomputeStateSnapshots();
+ }
+}
+
+bool CMaterial::GetMaterialVarFlag( MaterialVarFlags_t flag ) const
+{
+ return (GetMaterialVarFlags() & flag) != 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Do we use the env_cubemap entity to get cubemaps from the level?
+//-----------------------------------------------------------------------------
+bool CMaterial::UsesEnvCubemap( void )
+{
+ Precache();
+
+ if( !m_pShader )
+ {
+ if ( !HushAsserts() )
+ {
+ AssertMsg( m_pShader, "m_pShader==NULL. Shader: %s", GetName() );
+ }
+ return false;
+ }
+
+ Assert( m_pShaderParams );
+ return IsFlag2Set( m_pShaderParams, MATERIAL_VAR2_USES_ENV_CUBEMAP );
+}
+
+
+//-----------------------------------------------------------------------------
+// Do we need a tangent space at the vertex level?
+//-----------------------------------------------------------------------------
+bool CMaterial::NeedsTangentSpace( void )
+{
+ Precache();
+
+ if( !m_pShader )
+ {
+ if ( !HushAsserts() )
+ {
+ AssertMsg( m_pShader, "m_pShader==NULL. Shader: %s", GetName() );
+ }
+ return false;
+ }
+
+ Assert( m_pShaderParams );
+ return IsFlag2Set( m_pShaderParams, MATERIAL_VAR2_NEEDS_TANGENT_SPACES );
+}
+
+bool CMaterial::NeedsPowerOfTwoFrameBufferTexture( bool bCheckSpecificToThisFrame )
+{
+ PrecacheVars();
+ if( !m_pShader )
+ {
+ if ( !HushAsserts() )
+ {
+ AssertMsg( m_pShader, "m_pShader==NULL. Shader: %s", GetName() );
+ }
+ return false;
+ }
+
+ Assert( m_pShaderParams );
+ return m_pShader->NeedsPowerOfTwoFrameBufferTexture( m_pShaderParams, bCheckSpecificToThisFrame );
+}
+
+bool CMaterial::NeedsFullFrameBufferTexture( bool bCheckSpecificToThisFrame )
+{
+ PrecacheVars();
+ if( !m_pShader )
+ {
+ if ( !HushAsserts() )
+ {
+ AssertMsg( m_pShader, "m_pShader==NULL. Shader: %s", GetName() );
+ }
+ return false;
+ }
+
+ Assert( m_pShaderParams );
+ return m_pShader->NeedsFullFrameBufferTexture( m_pShaderParams, bCheckSpecificToThisFrame );
+}
+
+// GR - Is lightmap alpha needed?
+bool CMaterial::NeedsLightmapBlendAlpha( void )
+{
+ Precache();
+ return (GetMaterialVarFlags2() & MATERIAL_VAR2_BLEND_WITH_LIGHTMAP_ALPHA ) != 0;
+}
+
+//-----------------------------------------------------------------------------
+// Do we need software skinning?
+//-----------------------------------------------------------------------------
+bool CMaterial::NeedsSoftwareSkinning( void )
+{
+ Precache();
+ Assert( m_pShader );
+ if( !m_pShader )
+ {
+ return false;
+ }
+
+ Assert( m_pShaderParams );
+ return IsFlagSet( m_pShaderParams, MATERIAL_VAR_NEEDS_SOFTWARE_SKINNING );
+}
+
+
+//-----------------------------------------------------------------------------
+// Do we need software lighting?
+//-----------------------------------------------------------------------------
+bool CMaterial::NeedsSoftwareLighting( void )
+{
+ Precache();
+ Assert( m_pShader );
+ if( !m_pShader )
+ {
+ return false;
+ }
+ Assert( m_pShaderParams );
+ return IsFlag2Set( m_pShaderParams, MATERIAL_VAR2_NEEDS_SOFTWARE_LIGHTING );
+}
+
+//-----------------------------------------------------------------------------
+// Alpha/color modulation
+//-----------------------------------------------------------------------------
+void CMaterial::AlphaModulate( float alpha )
+{
+ Precache();
+ if ( m_VarCount > ALPHA )
+ m_pShaderParams[ALPHA]->SetFloatValue(alpha);
+}
+
+void CMaterial::ColorModulate( float r, float g, float b )
+{
+ Precache();
+ if ( m_VarCount > COLOR )
+ m_pShaderParams[COLOR]->SetVecValue( r, g, b );
+}
+
+float CMaterial::GetAlphaModulation()
+{
+ Precache();
+ if ( m_VarCount > ALPHA )
+ return m_pShaderParams[ALPHA]->GetFloatValue();
+ return 0.0f;
+}
+
+void CMaterial::GetColorModulation( float *r, float *g, float *b )
+{
+ Precache();
+
+ float pColor[3] = { 0.0f, 0.0f, 0.0f };
+ if ( m_VarCount > COLOR )
+ m_pShaderParams[COLOR]->GetVecValue( pColor, 3 );
+ *r = pColor[0];
+ *g = pColor[1];
+ *b = pColor[2];
+}
+
+
+//-----------------------------------------------------------------------------
+// Do we use fog?
+//-----------------------------------------------------------------------------
+bool CMaterial::UseFog() const
+{
+ Assert( m_VarCount > 0 );
+ return IsValidRenderState() && ((GetMaterialVarFlags() & MATERIAL_VAR_NOFOG) == 0);
+}
+
+
+//-----------------------------------------------------------------------------
+// diffuse bump?
+//-----------------------------------------------------------------------------
+bool CMaterial::IsUsingDiffuseBumpedLighting() const
+{
+ return (GetMaterialVarFlags2() & MATERIAL_VAR2_LIGHTING_BUMPED_LIGHTMAP ) != 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// lightmap?
+//-----------------------------------------------------------------------------
+bool CMaterial::IsUsingLightmap() const
+{
+ return (GetMaterialVarFlags2() & MATERIAL_VAR2_LIGHTING_LIGHTMAP ) != 0;
+}
+
+bool CMaterial::IsManuallyCreated() const
+{
+ return (m_Flags & MATERIAL_IS_MANUALLY_CREATED) != 0;
+}
+
+bool CMaterial::UsesUNCFileName() const
+{
+ return (m_Flags & MATERIAL_USES_UNC_FILENAME) != 0;
+}
+
+
+void CMaterial::DecideShouldReloadFromWhitelist( IFileList *pFilesToReload )
+{
+ m_bShouldReloadFromWhitelist = false;
+ if ( IsManuallyCreated() || !IsPrecached() )
+ return;
+
+ // Materials loaded with an absolute pathname are usually debug materials.
+ if ( V_IsAbsolutePath( GetName() ) )
+ return;
+
+ char vmtFilename[MAX_PATH];
+ V_ComposeFileName( "materials", GetName(), vmtFilename, sizeof( vmtFilename ) );
+ V_strncat( vmtFilename, ".vmt", sizeof( vmtFilename ) );
+
+ // Check if either this file or any of the files it included need to be reloaded.
+ bool bShouldReload = pFilesToReload->IsFileInList( vmtFilename );
+ if ( !bShouldReload )
+ {
+ for ( int i=0; i < m_VMTIncludes.Count(); i++ )
+ {
+ g_pFullFileSystem->String( m_VMTIncludes[i], vmtFilename, sizeof( vmtFilename ) );
+ if ( pFilesToReload->IsFileInList( vmtFilename ) )
+ {
+ bShouldReload = true;
+ break;
+ }
+ }
+ }
+
+ m_bShouldReloadFromWhitelist = bShouldReload;
+}
+
+void CMaterial::ReloadFromWhitelistIfMarked()
+{
+ if ( !m_bShouldReloadFromWhitelist )
+ return;
+
+ #ifdef PURE_SERVER_DEBUG_SPEW
+ {
+ char vmtFilename[MAX_PATH];
+ V_ComposeFileName( "materials", GetName(), vmtFilename, sizeof( vmtFilename ) );
+ V_strncat( vmtFilename, ".vmt", sizeof( vmtFilename ) );
+ Msg( "Reloading %s due to pure server whitelist change\n", GetName() );
+ }
+ #endif
+
+ Uncache();
+ Precache();
+ if ( !GetShader() )
+ {
+ // We can get in here if we previously loaded this material off disk and now the whitelist
+ // says to get it out of Steam but it's not in Steam. So just setup a wireframe thingy
+ // to draw the material with.
+ m_Flags |= MATERIAL_IS_PRECACHED | MATERIAL_VARS_IS_PRECACHED;
+ #if DEBUG
+ if (IsOSX())
+ {
+ printf("\n ##### CMaterial::ReloadFromWhitelistIfMarked: GetShader failed on %s, calling SetupErrorShader", m_pDebugName );
+ }
+ #endif
+
+ SetupErrorShader();
+ }
+}
+
+bool CMaterial::WasReloadedFromWhitelist()
+{
+ return m_bShouldReloadFromWhitelist;
+}
+
+//-----------------------------------------------------------------------------
+// Loads the material vars
+//-----------------------------------------------------------------------------
+bool CMaterial::PrecacheVars( KeyValues *pVMTKeyValues, KeyValues *pPatchKeyValues, CUtlVector<FileNameHandle_t> *pIncludes, int nFindContext )
+{
+ // We should get both parameters or neither
+ Assert( ( pVMTKeyValues == NULL ) ? ( pPatchKeyValues == NULL ) : ( pPatchKeyValues != NULL ) );
+
+ // Don't bother if we're already precached
+ if( IsPrecachedVars() )
+ return true;
+
+ if ( pIncludes )
+ m_VMTIncludes = *pIncludes;
+ else
+ m_VMTIncludes.Purge();
+
+ MaterialLock_t hMaterialLock = MaterialSystem()->Lock();
+
+ bool bOk = false;
+ bool bError = false;
+ KeyValues *vmtKeyValues = NULL;
+ KeyValues *patchKeyValues = NULL;
+ if ( m_pVMTKeyValues )
+ {
+ // Use the procedural KeyValues
+ vmtKeyValues = m_pVMTKeyValues;
+ patchKeyValues = new KeyValues( "vmt_patches" );
+
+ // The caller should not be passing in KeyValues if we have procedural ones
+ Assert( ( pVMTKeyValues == NULL ) && ( pPatchKeyValues == NULL ) );
+ }
+ else if ( pVMTKeyValues )
+ {
+ // Use the passed-in (already-loaded) KeyValues
+ vmtKeyValues = pVMTKeyValues;
+ patchKeyValues = pPatchKeyValues;
+ }
+ else
+ {
+ m_VMTIncludes.Purge();
+
+ // load data from the vmt file
+ vmtKeyValues = new KeyValues( "vmt" );
+ patchKeyValues = new KeyValues( "vmt_patches" );
+ if( !LoadVMTFile( *vmtKeyValues, *patchKeyValues, GetName(), UsesUNCFileName(), &m_VMTIncludes ) )
+ {
+ Warning( "CMaterial::PrecacheVars: error loading vmt file for %s\n", GetName() );
+ bError = true;
+ }
+ }
+
+ if ( ! bError )
+ {
+ // Needed to prevent re-entrancy
+ m_Flags |= MATERIAL_VARS_IS_PRECACHED;
+
+ // Create shader and the material vars...
+ KeyValues *pFallbackKeyValues = InitializeShader( *vmtKeyValues, *patchKeyValues, nFindContext );
+ if ( pFallbackKeyValues )
+ {
+ // Gotta initialize the proxies too, using the fallback proxies
+ InitializeMaterialProxy(pFallbackKeyValues);
+ bOk = true;
+ }
+ }
+
+ // Clean up
+ if ( ( vmtKeyValues != m_pVMTKeyValues ) && ( vmtKeyValues != pVMTKeyValues ) )
+ {
+ vmtKeyValues->deleteThis();
+ }
+ if ( patchKeyValues != pPatchKeyValues )
+ {
+ patchKeyValues->deleteThis();
+ }
+
+ MaterialSystem()->Unlock( hMaterialLock );
+
+ return bOk;
+}
+
+
+//-----------------------------------------------------------------------------
+// Loads the material info from the VMT file
+//-----------------------------------------------------------------------------
+void CMaterial::Precache()
+{
+ // Don't bother if we're already precached
+ if ( IsPrecached() )
+ return;
+
+ // load data from the vmt file
+ if ( !PrecacheVars() )
+ return;
+
+ MaterialLock_t hMaterialLock = MaterialSystem()->Lock();
+
+ m_Flags |= MATERIAL_IS_PRECACHED;
+
+ // Invokes the SHADER_INIT block in the various shaders,
+ if ( m_pShader )
+ {
+ ShaderSystem()->InitShaderInstance( m_pShader, m_pShaderParams, GetName(), GetTextureGroupName() );
+ }
+
+ // compute the state snapshots
+ RecomputeStateSnapshots();
+
+ FindRepresentativeTexture();
+
+ // Reads in the texture width and height from the material var
+ PrecacheMappingDimensions();
+
+ Assert( IsValidRenderState() );
+
+ if( m_pShaderParams )
+ m_QueueFriendlyVersion.UpdateToRealTime();
+
+ MaterialSystem()->Unlock( hMaterialLock );
+}
+
+
+//-----------------------------------------------------------------------------
+// Unloads the material data from memory
+//-----------------------------------------------------------------------------
+void CMaterial::Uncache( bool bPreserveVars )
+{
+ MaterialLock_t hMaterialLock = MaterialSystem()->Lock();
+
+ // Don't bother if we're not cached
+ if ( IsPrecached() )
+ {
+ // Clean up the state snapshots
+ CleanUpStateSnapshots();
+ m_Flags &= ~MATERIAL_VALID_RENDERSTATE;
+ m_Flags &= ~MATERIAL_IS_PRECACHED;
+ }
+
+ if ( !bPreserveVars )
+ {
+ if ( IsPrecachedVars() )
+ {
+ // Clean up the shader + params
+ CleanUpShaderParams();
+ m_pShader = 0;
+
+ // Clean up the material proxy
+ CleanUpMaterialProxy();
+
+ m_Flags &= ~MATERIAL_VARS_IS_PRECACHED;
+ }
+ }
+
+ MaterialSystem()->Unlock( hMaterialLock );
+
+ // Whether we just now did it, or we were already unloaded,
+ // notify the pure system that the material is unloaded,
+ // so it doesn't caue us to fail sv_pure checks
+ if ( ( m_Flags & ( MATERIAL_VARS_IS_PRECACHED | MATERIAL_IS_MANUALLY_CREATED | MATERIAL_USES_UNC_FILENAME ) ) == 0 )
+ {
+ char szName[ MAX_PATH ];
+ V_sprintf_safe( szName, "materials/%s.vmt", GetName() );
+ g_pFullFileSystem->NotifyFileUnloaded( szName, "GAME" );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// reload all textures used by this materals
+//-----------------------------------------------------------------------------
+void CMaterial::ReloadTextures( void )
+{
+ Precache();
+ int i;
+ int nParams = ShaderParamCount();
+ IMaterialVar **ppVars = GetShaderParams();
+ for( i = 0; i < nParams; i++ )
+ {
+ if( ppVars[i] )
+ {
+ if( ppVars[i]->IsTexture() )
+ {
+ ITextureInternal *pTexture = ( ITextureInternal * )ppVars[i]->GetTextureValue();
+ if( !IsTextureInternalEnvCubemap( pTexture ) )
+ {
+ pTexture->Download();
+ }
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Meant to be used with materials created using CreateMaterial
+// It updates the materials to reflect the current values stored in the material vars
+//-----------------------------------------------------------------------------
+void CMaterial::Refresh()
+{
+ if ( g_pShaderDevice->IsUsingGraphics() )
+ {
+ Uncache();
+ Precache();
+ }
+}
+
+void CMaterial::RefreshPreservingMaterialVars()
+{
+ if ( g_pShaderDevice->IsUsingGraphics() )
+ {
+ Uncache( true );
+ Precache();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Gets the material name
+//-----------------------------------------------------------------------------
+char const* CMaterial::GetName() const
+{
+ return m_Name.String();
+}
+
+
+char const* CMaterial::GetTextureGroupName() const
+{
+ return m_TextureGroupName.String();
+}
+
+
+//-----------------------------------------------------------------------------
+// Material dimensions
+//-----------------------------------------------------------------------------
+int CMaterial::GetMappingWidth( )
+{
+ Precache();
+ return m_MappingWidth;
+}
+
+int CMaterial::GetMappingHeight( )
+{
+ Precache();
+ return m_MappingHeight;
+}
+
+
+//-----------------------------------------------------------------------------
+// Animated material info
+//-----------------------------------------------------------------------------
+
+int CMaterial::GetNumAnimationFrames( )
+{
+ Precache();
+ if( m_representativeTexture )
+ {
+ return m_representativeTexture->GetNumAnimationFrames();
+ }
+ else
+ {
+#ifndef POSIX
+ Warning( "CMaterial::GetNumAnimationFrames:\nno representative texture for material %s\n", GetName() );
+#endif
+ return 1;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CMaterial::GetMaterialOffset( float *pOffset )
+{
+ // Identity.
+ pOffset[0] = 0.0f;
+ pOffset[1] = 0.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CMaterial::GetMaterialScale( float *pScale )
+{
+ // Identity.
+ pScale[0] = 1.0f;
+ pScale[1] = 1.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Reference count
+//-----------------------------------------------------------------------------
+void CMaterial::IncrementReferenceCount( )
+{
+ ++m_RefCount;
+}
+
+void CMaterial::DecrementReferenceCount( )
+{
+ --m_RefCount;
+}
+
+int CMaterial::GetReferenceCount( ) const
+{
+ return m_RefCount;
+}
+
+//-----------------------------------------------------------------------------
+// Sets the shader associated with the material
+//-----------------------------------------------------------------------------
+void CMaterial::SetShader( const char *pShaderName )
+{
+ Assert( pShaderName );
+
+ int i;
+ IShader* pShader;
+ IMaterialVar* ppVars[256];
+ int iVarCount = 0;
+
+ // Clean up existing state
+ Uncache();
+
+ // Keep going until there's no more fallbacks...
+ while( true )
+ {
+ // Find the shader for this material. Note that this may not be
+ // the actual shader we use due to fallbacks...
+ pShader = ShaderSystem()->FindShader( pShaderName );
+ if (!pShader)
+ {
+ // Couldn't find the shader we wanted to use; it's not defined...
+ Warning( "SetShader: Couldn't find shader %s for material %s!\n", pShaderName, GetName() );
+ pShaderName = MissingShaderName();
+ pShader = ShaderSystem()->FindShader( pShaderName );
+ Assert( pShader );
+ }
+
+ // Create undefined vars for all the actual material vars
+ iVarCount = pShader->GetNumParams();
+ for (i = 0; i < iVarCount; ++i)
+ {
+ ppVars[i] = IMaterialVar::Create( this, pShader->GetParamName(i) );
+ }
+
+ // Make sure we set default values before the fallback is looked for
+ ShaderSystem()->InitShaderParameters( pShader, ppVars, pShaderName );
+
+ // Now that the material vars are parsed, see if there's a fallback
+ // But only if we're not in the tools
+ if (!g_pShaderDevice->IsUsingGraphics())
+ break;
+
+ // Check for a fallback; if not, we're done
+ pShaderName = pShader->GetFallbackShader( ppVars );
+ if (!pShaderName)
+ break;
+
+ // Now, blow away all of the material vars + try again...
+ for (i = 0; i < iVarCount; ++i)
+ {
+ Assert( ppVars[i] );
+ IMaterialVar::Destroy( ppVars[i] );
+ }
+ }
+
+ // Store off the shader
+ m_pShader = pShader;
+
+ // Store off the material vars + flags
+ m_VarCount = iVarCount;
+ m_pShaderParams = (IMaterialVar**)malloc( iVarCount * sizeof(IMaterialVar*) );
+ memcpy( m_pShaderParams, ppVars, iVarCount * sizeof(IMaterialVar*) );
+
+ // Invokes the SHADER_INIT block in the various shaders,
+ ShaderSystem()->InitShaderInstance( m_pShader, m_pShaderParams, GetName(), GetTextureGroupName() );
+
+ // Precache our initial state...
+ // NOTE: What happens here for textures???
+
+ // Pretend that we precached our material vars; we certainly don't have any!
+ m_Flags |= MATERIAL_VARS_IS_PRECACHED;
+
+ // NOTE: The caller has to call 'Refresh' for the shader to be ready...
+}
+
+const char *CMaterial::GetShaderName() const
+{
+ const_cast< CMaterial* >( this )->PrecacheVars();
+ return m_pShader ? m_pShader->GetName() : "shader_error";
+}
+
+
+//-----------------------------------------------------------------------------
+// Enumeration ID
+//-----------------------------------------------------------------------------
+int CMaterial::GetEnumerationID( ) const
+{
+ return m_iEnumerationID;
+}
+
+void CMaterial::SetEnumerationID( int id )
+{
+ m_iEnumerationID = id;
+}
+
+//-----------------------------------------------------------------------------
+// Preview image
+//-----------------------------------------------------------------------------
+char const* CMaterial::GetPreviewImageName( void )
+{
+ if ( IsConsole() )
+ {
+ // not supporting
+ return NULL;
+ }
+
+ PrecacheVars();
+
+ bool found;
+ IMaterialVar *pRepresentativeTextureVar;
+
+ FindVar( "%noToolTexture", &found, false );
+ if (found)
+ return NULL;
+
+ pRepresentativeTextureVar = FindVar( "%toolTexture", &found, false );
+ if( found )
+ {
+ if (pRepresentativeTextureVar->GetType() == MATERIAL_VAR_TYPE_STRING )
+ return pRepresentativeTextureVar->GetStringValue();
+ if (pRepresentativeTextureVar->GetType() == MATERIAL_VAR_TYPE_TEXTURE )
+ return pRepresentativeTextureVar->GetTextureValue()->GetName();
+ }
+ pRepresentativeTextureVar = FindVar( "$baseTexture", &found, false );
+ if( found )
+ {
+ if (pRepresentativeTextureVar->GetType() == MATERIAL_VAR_TYPE_STRING )
+ return pRepresentativeTextureVar->GetStringValue();
+ if (pRepresentativeTextureVar->GetType() == MATERIAL_VAR_TYPE_TEXTURE )
+ return pRepresentativeTextureVar->GetTextureValue()->GetName();
+ }
+ return GetName();
+}
+
+char const* CMaterial::GetPreviewImageFileName( void ) const
+{
+ char const* pName = const_cast<CMaterial*>(this)->GetPreviewImageName();
+ if( !pName )
+ return NULL;
+
+ static char vtfFilename[MATERIAL_MAX_PATH];
+ if( Q_strlen( pName ) >= MATERIAL_MAX_PATH - 5 )
+ {
+ Warning( "MATERIAL_MAX_PATH to short for %s.vtf\n", pName );
+ return NULL;
+ }
+
+ if ( !UsesUNCFileName() )
+ {
+ Q_snprintf( vtfFilename, sizeof( vtfFilename ), "materials/%s.vtf", pName );
+ }
+ else
+ {
+ Q_snprintf( vtfFilename, sizeof( vtfFilename ), "%s.vtf", pName );
+ }
+
+ return vtfFilename;
+}
+
+PreviewImageRetVal_t CMaterial::GetPreviewImageProperties( int *width, int *height,
+ ImageFormat *imageFormat, bool* isTranslucent ) const
+{
+ char const* pFileName = GetPreviewImageFileName();
+ if ( IsX360() || !pFileName )
+ {
+ *width = *height = 0;
+ *imageFormat = IMAGE_FORMAT_RGBA8888;
+ *isTranslucent = false;
+ return MATERIAL_NO_PREVIEW_IMAGE;
+ }
+
+ int nHeaderSize = VTFFileHeaderSize( VTF_MAJOR_VERSION );
+ unsigned char *pMem = (unsigned char *)stackalloc( nHeaderSize );
+ CUtlBuffer buf( pMem, nHeaderSize );
+ if( !g_pFullFileSystem->ReadFile( pFileName, NULL, buf, nHeaderSize ) )
+ {
+ Warning( "\"%s\" - \"%s\": cached version doesn't exist\n", GetName(), pFileName );
+ return MATERIAL_PREVIEW_IMAGE_BAD;
+ }
+
+ IVTFTexture *pVTFTexture = CreateVTFTexture();
+ if (!pVTFTexture->Unserialize( buf, true ))
+ {
+ Warning( "Error reading material \"%s\"\n", pFileName );
+ DestroyVTFTexture( pVTFTexture );
+ return MATERIAL_PREVIEW_IMAGE_BAD;
+ }
+
+ *width = pVTFTexture->Width();
+ *height = pVTFTexture->Height();
+ *imageFormat = pVTFTexture->Format();
+ *isTranslucent = (pVTFTexture->Flags() & (TEXTUREFLAGS_ONEBITALPHA | TEXTUREFLAGS_EIGHTBITALPHA)) != 0;
+ DestroyVTFTexture( pVTFTexture );
+ return MATERIAL_PREVIEW_IMAGE_OK;
+}
+
+PreviewImageRetVal_t CMaterial::GetPreviewImage( unsigned char *pData, int width, int height,
+ ImageFormat imageFormat ) const
+{
+ CUtlBuffer buf;
+ int nHeaderSize;
+ int nImageOffset, nImageSize;
+
+ char const* pFileName = GetPreviewImageFileName();
+ if ( IsX360() || !pFileName )
+ {
+ return MATERIAL_NO_PREVIEW_IMAGE;
+ }
+
+ IVTFTexture *pVTFTexture = CreateVTFTexture();
+ FileHandle_t fileHandle = g_pFullFileSystem->Open( pFileName, "rb" );
+ if( !fileHandle )
+ {
+ Warning( "\"%s\": cached version doesn't exist\n", pFileName );
+ goto fail;
+ }
+
+ nHeaderSize = VTFFileHeaderSize( VTF_MAJOR_VERSION );
+ buf.EnsureCapacity( nHeaderSize );
+
+ // read the header first.. it's faster!!
+ int nBytesRead; // GCC won't let this be initialized right away
+ nBytesRead = g_pFullFileSystem->Read( buf.Base(), nHeaderSize, fileHandle );
+ buf.SeekPut( CUtlBuffer::SEEK_HEAD, nBytesRead );
+
+ // Unserialize the header
+ if (!pVTFTexture->Unserialize( buf, true ))
+ {
+ Warning( "Error reading material \"%s\"\n", pFileName );
+ goto fail;
+ }
+
+ // FIXME: Make sure the preview image size requested is the same
+ // size as mip level 0 of the texture
+ Assert( (width == pVTFTexture->Width()) && (height == pVTFTexture->Height()) );
+
+ // Determine where in the file to start reading (frame 0, face 0, mip 0)
+ pVTFTexture->ImageFileInfo( 0, 0, 0, &nImageOffset, &nImageSize );
+
+ if ( nImageSize == 0 )
+ {
+ Warning( "Couldn't determine offset and size of material \"%s\"\n", pFileName );
+ goto fail;
+ }
+
+ // Prep the utlbuffer for reading
+ buf.EnsureCapacity( nImageSize );
+ buf.SeekPut( CUtlBuffer::SEEK_HEAD, 0 );
+
+ // Read in the bits at the specified location
+ g_pFullFileSystem->Seek( fileHandle, nImageOffset, FILESYSTEM_SEEK_HEAD );
+ g_pFullFileSystem->Read( buf.Base(), nImageSize, fileHandle );
+ g_pFullFileSystem->Close( fileHandle );
+
+ // Convert from the format read in to the requested format
+ ImageLoader::ConvertImageFormat( (unsigned char*)buf.Base(), pVTFTexture->Format(),
+ pData, imageFormat, width, height );
+
+ DestroyVTFTexture( pVTFTexture );
+ return MATERIAL_PREVIEW_IMAGE_OK;
+
+fail:
+ if( fileHandle )
+ {
+ g_pFullFileSystem->Close( fileHandle );
+ }
+ int nSize = ImageLoader::GetMemRequired( width, height, 1, imageFormat, false );
+ memset( pData, 0xff, nSize );
+ DestroyVTFTexture( pVTFTexture );
+ return MATERIAL_PREVIEW_IMAGE_BAD;
+}
+
+//-----------------------------------------------------------------------------
+// Material variables
+//-----------------------------------------------------------------------------
+IMaterialVar *CMaterial::FindVar( char const *pVarName, bool *pFound, bool complain )
+{
+ PrecacheVars();
+
+ // FIXME: Could look for flags here too...
+
+ MaterialVarSym_t sym = IMaterialVar::FindSymbol(pVarName);
+ if ( sym != UTL_INVAL_SYMBOL )
+ {
+ for (int i = m_VarCount; --i >= 0; )
+ {
+ if (m_pShaderParams[i]->GetNameAsSymbol() == sym)
+ {
+ if( pFound )
+ *pFound = true;
+ return m_pShaderParams[i];
+ }
+ }
+ }
+
+ if( pFound )
+ *pFound = false;
+
+ if( complain )
+ {
+ static int complainCount = 0;
+ if( complainCount < 100 )
+ {
+ Warning( "No such variable \"%s\" for material \"%s\"\n", pVarName, GetName() );
+ complainCount++;
+ }
+ }
+ return GetDummyVariable();
+}
+
+struct tokencache_t
+{
+ unsigned short symbol;
+ unsigned char varIndex;
+ unsigned char cached;
+};
+
+IMaterialVar *CMaterial::FindVarFast( char const *pVarName, unsigned int *pCacheData )
+{
+ tokencache_t *pToken = reinterpret_cast<tokencache_t *>(pCacheData);
+ PrecacheVars();
+
+ if ( pToken->cached )
+ {
+ if ( pToken->varIndex < m_VarCount && m_pShaderParams[pToken->varIndex]->GetNameAsSymbol() == pToken->symbol )
+ return m_pShaderParams[pToken->varIndex];
+ // FIXME: Could look for flags here too...
+ if ( !IMaterialVar::SymbolMatches(pVarName, pToken->symbol) )
+ {
+ pToken->symbol = IMaterialVar::FindSymbol(pVarName);
+ }
+ }
+ else
+ {
+ pToken->cached = true;
+ pToken->symbol = IMaterialVar::FindSymbol(pVarName);
+ }
+
+ if ( pToken->symbol != UTL_INVAL_SYMBOL )
+ {
+ for (int i = m_VarCount; --i >= 0; )
+ {
+ if (m_pShaderParams[i]->GetNameAsSymbol() == pToken->symbol)
+ {
+ pToken->varIndex = i;
+ return m_pShaderParams[i];
+ }
+ }
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Lovely material properties
+//-----------------------------------------------------------------------------
+void CMaterial::GetReflectivity( Vector& reflect )
+{
+ Precache();
+
+ reflect = m_Reflectivity;
+}
+
+
+bool CMaterial::GetPropertyFlag( MaterialPropertyTypes_t type )
+{
+ Precache();
+
+ if (!IsValidRenderState())
+ return false;
+
+ switch( type )
+ {
+ case MATERIAL_PROPERTY_NEEDS_LIGHTMAP:
+ return IsUsingLightmap();
+
+ case MATERIAL_PROPERTY_NEEDS_BUMPED_LIGHTMAPS:
+ return IsUsingDiffuseBumpedLighting();
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Is the material visible from both sides?
+//-----------------------------------------------------------------------------
+bool CMaterial::IsTwoSided()
+{
+ PrecacheVars();
+ return GetMaterialVarFlag(MATERIAL_VAR_NOCULL);
+}
+
+
+//-----------------------------------------------------------------------------
+// Are we translucent?
+//-----------------------------------------------------------------------------
+bool CMaterial::IsTranslucent()
+{
+ Precache();
+ if ( m_VarCount > ALPHA )
+ return IsTranslucentInternal( m_pShaderParams? m_pShaderParams[ALPHA]->GetFloatValue() : 0.0 );
+ return false;
+}
+
+
+bool CMaterial::IsTranslucentInternal( float fAlphaModulation ) const
+{
+ if (m_pShader && IsValidRenderState())
+ {
+ // I have to check for alpha modulation here because it isn't
+ // factored into the shader's notion of whether or not it's transparent
+ return ::IsTranslucent(&m_ShaderRenderState) ||
+ (fAlphaModulation < 1.0f) ||
+ m_pShader->IsTranslucent( m_pShaderParams );
+ }
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Are we alphatested?
+//-----------------------------------------------------------------------------
+bool CMaterial::IsAlphaTested()
+{
+ Precache();
+ if (m_pShader && IsValidRenderState())
+ {
+ return ::IsAlphaTested(&m_ShaderRenderState) ||
+ GetMaterialVarFlag( MATERIAL_VAR_ALPHATEST );
+ }
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Are we vertex lit?
+//-----------------------------------------------------------------------------
+bool CMaterial::IsVertexLit()
+{
+ Precache();
+ if (IsValidRenderState())
+ {
+ return ( GetMaterialVarFlags2() & MATERIAL_VAR2_LIGHTING_VERTEX_LIT ) != 0;
+ }
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Is the shader a sprite card shader?
+//-----------------------------------------------------------------------------
+bool CMaterial::IsSpriteCard()
+{
+ Precache();
+ if (IsValidRenderState())
+ {
+ return ( GetMaterialVarFlags2() & MATERIAL_VAR2_IS_SPRITECARD ) != 0;
+ }
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Proxies
+//-----------------------------------------------------------------------------
+void CMaterial::CallBindProxy( void *proxyData )
+{
+ CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
+ bool bIsThreaded = ( pCallQueue != NULL );
+ switch (g_config.proxiesTestMode)
+ {
+ case 0:
+ {
+ // Make sure we call the proxies in the order in which they show up
+ // in the .vmt file
+ if ( m_ProxyInfo.Count() )
+ {
+ if ( bIsThreaded )
+ {
+ EnableThreadedMaterialVarAccess( true, m_pShaderParams, m_VarCount );
+ }
+ for( int i = 0; i < m_ProxyInfo.Count(); ++i )
+ {
+ m_ProxyInfo[i]->OnBind( proxyData );
+ }
+ if ( bIsThreaded )
+ {
+ EnableThreadedMaterialVarAccess( false, m_pShaderParams, m_VarCount );
+ }
+ }
+ }
+ break;
+
+ case 2:
+ // alpha mod all....
+ {
+ float value = ( sin( 2.0f * M_PI * Plat_FloatTime() / 10.0f ) * 0.5f ) + 0.5f;
+ m_pShaderParams[ALPHA]->SetFloatValue( value );
+ }
+ break;
+
+ case 3:
+ // color mod all...
+ {
+ float value = ( sin( 2.0f * M_PI * Plat_FloatTime() / 10.0f ) * 0.5f ) + 0.5f;
+ m_pShaderParams[COLOR]->SetVecValue( value, 1.0f, 1.0f );
+ }
+ break;
+ }
+}
+
+
+IMaterial *CMaterial::CheckProxyReplacement( void *proxyData )
+{
+ if ( m_pReplacementProxy != NULL )
+ {
+ IMaterial *pReplaceMaterial = m_pReplacementProxy->GetMaterial();
+
+ if ( pReplaceMaterial )
+ {
+ return pReplaceMaterial;
+ }
+ }
+
+ return this;
+}
+
+
+bool CMaterial::HasProxy( ) const
+{
+ const_cast< CMaterial* >( this )->PrecacheVars();
+ return m_ProxyInfo.Count() > 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Main draw method
+//-----------------------------------------------------------------------------
+
+#ifdef _WIN32
+#pragma warning (disable: 4189)
+#endif
+
+void CMaterial::DrawMesh( VertexCompressionType_t vertexCompression )
+{
+ if ( m_pShader )
+ {
+#ifdef _DEBUG
+ if ( GetMaterialVarFlags() & MATERIAL_VAR_DEBUG )
+ {
+ // Putcher breakpoint here to catch the rendering of a material
+ // marked for debugging ($debug = 1 in a .vmt file) dynamic state version
+ int x = 0;
+ }
+#endif
+ if ((GetMaterialVarFlags() & MATERIAL_VAR_NO_DRAW) == 0)
+ {
+ const char *pName = m_pShader->GetName();
+ ShaderSystem()->DrawElements( m_pShader, m_pShaderParams, &m_ShaderRenderState, vertexCompression, m_ChangeID ^ g_nDebugVarsSignature );
+ }
+ }
+ else
+ {
+ Warning( "CMaterial::DrawElements: No bound shader\n" );
+ }
+}
+
+#ifdef _WIN32
+#pragma warning (default: 4189)
+#endif
+
+IShader *CMaterial::GetShader( ) const
+{
+ return m_pShader;
+}
+
+IMaterialVar *CMaterial::GetShaderParam( int id )
+{
+ return m_pShaderParams[id];
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds a material variable to the material
+//-----------------------------------------------------------------------------
+void CMaterial::AddMaterialVar( IMaterialVar *pMaterialVar )
+{
+ ++m_VarCount;
+ m_pShaderParams = (IMaterialVar**)realloc( m_pShaderParams, m_VarCount * sizeof( IMaterialVar*) );
+ m_pShaderParams[m_VarCount-1] = pMaterialVar;
+}
+
+
+bool CMaterial::IsErrorMaterial() const
+{
+ extern IMaterialInternal *g_pErrorMaterial;
+ const IMaterialInternal *pThis = this;
+ return g_pErrorMaterial == pThis;
+}
+
+
+void CMaterial::FindRepresentativeTexture( void )
+{
+ Precache();
+
+ // First try to find the base texture...
+ bool found;
+ IMaterialVar *textureVar = FindVar( "$baseTexture", &found, false );
+ if( found && textureVar->GetType() == MATERIAL_VAR_TYPE_TEXTURE )
+ {
+ ITextureInternal *pTexture = ( ITextureInternal * )textureVar->GetTextureValue();
+ if( pTexture )
+ {
+ pTexture->GetReflectivity( m_Reflectivity );
+ }
+ }
+ if( !found || textureVar->GetType() != MATERIAL_VAR_TYPE_TEXTURE )
+ {
+ // Try the env map mask if the base texture doesn't work...
+ // this is needed for specular decals
+ textureVar = FindVar( "$envmapmask", &found, false );
+ if( !found || textureVar->GetType() != MATERIAL_VAR_TYPE_TEXTURE )
+ {
+ // Try the bumpmap
+ textureVar = FindVar( "$bumpmap", &found, false );
+ if( !found || textureVar->GetType() != MATERIAL_VAR_TYPE_TEXTURE )
+ {
+ textureVar = FindVar( "$dudvmap", &found, false );
+ if( !found || textureVar->GetType() != MATERIAL_VAR_TYPE_TEXTURE )
+ {
+ textureVar = FindVar( "$normalmap", &found, false );
+ if( !found || textureVar->GetType() != MATERIAL_VAR_TYPE_TEXTURE )
+ {
+ // Warning( "Can't find representative texture for material \"%s\"\n", GetName() );
+ m_representativeTexture = TextureManager()->ErrorTexture();
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ m_representativeTexture = static_cast<ITextureInternal *>( textureVar->GetTextureValue() );
+ if (m_representativeTexture)
+ {
+ m_representativeTexture->Precache();
+ }
+ else
+ {
+ m_representativeTexture = TextureManager()->ErrorTexture();
+ Assert( m_representativeTexture );
+ }
+}
+
+
+void CMaterial::GetLowResColorSample( float s, float t, float *color ) const
+{
+ if( !m_representativeTexture )
+ {
+ return;
+ }
+ m_representativeTexture->GetLowResColorSample( s, t, color);
+}
+
+
+//-----------------------------------------------------------------------------
+// Lightmap-related methods
+//-----------------------------------------------------------------------------
+
+void CMaterial::SetMinLightmapPageID( int pageID )
+{
+ m_minLightmapPageID = pageID;
+}
+
+void CMaterial::SetMaxLightmapPageID( int pageID )
+{
+ m_maxLightmapPageID = pageID;
+}
+
+int CMaterial::GetMinLightmapPageID( ) const
+{
+ return m_minLightmapPageID;
+}
+
+int CMaterial::GetMaxLightmapPageID( ) const
+{
+ return m_maxLightmapPageID;
+}
+
+void CMaterial::SetNeedsWhiteLightmap( bool val )
+{
+ if ( val )
+ m_Flags |= MATERIAL_NEEDS_WHITE_LIGHTMAP;
+ else
+ m_Flags &= ~MATERIAL_NEEDS_WHITE_LIGHTMAP;
+}
+
+bool CMaterial::GetNeedsWhiteLightmap( ) const
+{
+ return (m_Flags & MATERIAL_NEEDS_WHITE_LIGHTMAP) != 0;
+}
+
+void CMaterial::MarkAsPreloaded( bool bSet )
+{
+ if ( bSet )
+ {
+ m_Flags |= MATERIAL_IS_PRELOADED;
+ }
+ else
+ {
+ m_Flags &= ~MATERIAL_IS_PRELOADED;
+ }
+}
+
+bool CMaterial::IsPreloaded() const
+{
+ return ( m_Flags & MATERIAL_IS_PRELOADED ) != 0;
+}
+
+void CMaterial::ArtificialAddRef( void )
+{
+ if ( m_Flags & MATERIAL_ARTIFICIAL_REFCOUNT )
+ {
+ // already done
+ return;
+ }
+
+ m_Flags |= MATERIAL_ARTIFICIAL_REFCOUNT;
+ m_RefCount++;
+}
+
+void CMaterial::ArtificialRelease( void )
+{
+ if ( !( m_Flags & MATERIAL_ARTIFICIAL_REFCOUNT ) )
+ {
+ return;
+ }
+
+ m_Flags &= ~MATERIAL_ARTIFICIAL_REFCOUNT;
+ m_RefCount--;
+}
+
+//-----------------------------------------------------------------------------
+// Return the shader params
+//-----------------------------------------------------------------------------
+IMaterialVar **CMaterial::GetShaderParams( void )
+{
+ return m_pShaderParams;
+}
+
+int CMaterial::ShaderParamCount() const
+{
+ return m_VarCount;
+}
+
+
+//-----------------------------------------------------------------------------
+// VMT parser
+//-----------------------------------------------------------------------------
+void InsertKeyValues( KeyValues& dst, KeyValues& src, bool bCheckForExistence, bool bRecursive )
+{
+ KeyValues *pSrcVar = src.GetFirstSubKey();
+ while( pSrcVar )
+ {
+ if ( !bCheckForExistence || dst.FindKey( pSrcVar->GetName() ) )
+ {
+ switch( pSrcVar->GetDataType() )
+ {
+ case KeyValues::TYPE_STRING:
+ dst.SetString( pSrcVar->GetName(), pSrcVar->GetString() );
+ break;
+ case KeyValues::TYPE_INT:
+ dst.SetInt( pSrcVar->GetName(), pSrcVar->GetInt() );
+ break;
+ case KeyValues::TYPE_FLOAT:
+ dst.SetFloat( pSrcVar->GetName(), pSrcVar->GetFloat() );
+ break;
+ case KeyValues::TYPE_PTR:
+ dst.SetPtr( pSrcVar->GetName(), pSrcVar->GetPtr() );
+ break;
+ case KeyValues::TYPE_NONE:
+ {
+ // Subkey. Recurse.
+ KeyValues *pNewDest = dst.FindKey( pSrcVar->GetName(), true );
+ Assert( pNewDest );
+ InsertKeyValues( *pNewDest, *pSrcVar, bCheckForExistence, true );
+ }
+ break;
+ }
+ }
+ pSrcVar = pSrcVar->GetNextKey();
+ }
+
+ if ( bRecursive && !dst.GetFirstSubKey() )
+ {
+ // Insert a dummy key to an empty subkey to make sure it doesn't get removed
+ dst.SetInt( "__vmtpatchdummy", 1 );
+ }
+
+ if( bCheckForExistence )
+ {
+ for( KeyValues *pScan = dst.GetFirstTrueSubKey(); pScan; pScan = pScan->GetNextTrueSubKey() )
+ {
+ KeyValues *pTmp = src.FindKey( pScan->GetName() );
+ if( !pTmp )
+ continue;
+ // make sure that this is a subkey.
+ if( pTmp->GetDataType() != KeyValues::TYPE_NONE )
+ continue;
+ InsertKeyValues( *pScan, *pTmp, bCheckForExistence );
+ }
+ }
+}
+
+void WriteKeyValuesToFile( const char *pFileName, KeyValues& keyValues )
+{
+ keyValues.SaveToFile( g_pFullFileSystem, pFileName );
+}
+
+void ApplyPatchKeyValues( KeyValues &keyValues, KeyValues &patchKeyValues )
+{
+ KeyValues *pInsertSection = patchKeyValues.FindKey( "insert" );
+ KeyValues *pReplaceSection = patchKeyValues.FindKey( "replace" );
+
+ if ( pInsertSection )
+ {
+ InsertKeyValues( keyValues, *pInsertSection, false );
+ }
+
+ if ( pReplaceSection )
+ {
+ InsertKeyValues( keyValues, *pReplaceSection, true );
+ }
+
+ // Could add other commands here, like "delete", "rename", etc.
+}
+
+//-----------------------------------------------------------------------------
+// Adds keys from srcKeys to destKeys, overwriting any keys that are already
+// there.
+//-----------------------------------------------------------------------------
+void MergeKeyValues( KeyValues &srcKeys, KeyValues &destKeys )
+{
+ for( KeyValues *pKV = srcKeys.GetFirstValue(); pKV; pKV = pKV->GetNextValue() )
+ {
+ switch( pKV->GetDataType() )
+ {
+ case KeyValues::TYPE_STRING:
+ destKeys.SetString( pKV->GetName(), pKV->GetString() );
+ break;
+ case KeyValues::TYPE_INT:
+ destKeys.SetInt( pKV->GetName(), pKV->GetInt() );
+ break;
+ case KeyValues::TYPE_FLOAT:
+ destKeys.SetFloat( pKV->GetName(), pKV->GetFloat() );
+ break;
+ case KeyValues::TYPE_PTR:
+ destKeys.SetPtr( pKV->GetName(), pKV->GetPtr() );
+ break;
+ }
+ }
+ for( KeyValues *pKV = srcKeys.GetFirstTrueSubKey(); pKV; pKV = pKV->GetNextTrueSubKey() )
+ {
+ KeyValues *pDestKV = destKeys.FindKey( pKV->GetName(), true );
+ MergeKeyValues( *pKV, *pDestKV );
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void AccumulatePatchKeyValues( KeyValues &srcKeyValues, KeyValues &patchKeyValues )
+{
+ KeyValues *pDestInsertSection = patchKeyValues.FindKey( "insert" );
+ if ( pDestInsertSection == NULL )
+ {
+ pDestInsertSection = new KeyValues( "insert" );
+ patchKeyValues.AddSubKey( pDestInsertSection );
+ }
+
+ KeyValues *pDestReplaceSection = patchKeyValues.FindKey( "replace" );
+ if ( pDestReplaceSection == NULL )
+ {
+ pDestReplaceSection = new KeyValues( "replace" );
+ patchKeyValues.AddSubKey( pDestReplaceSection );
+ }
+
+ KeyValues *pSrcInsertSection = srcKeyValues.FindKey( "insert" );
+ if ( pSrcInsertSection )
+ {
+ MergeKeyValues( *pSrcInsertSection, *pDestInsertSection );
+ }
+
+ KeyValues *pSrcReplaceSection = srcKeyValues.FindKey( "replace" );
+ if ( pSrcReplaceSection )
+ {
+ MergeKeyValues( *pSrcReplaceSection, *pDestReplaceSection );
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool AccumulateRecursiveVmtPatches( KeyValues &patchKeyValuesOut, KeyValues **ppBaseKeyValuesOut, const KeyValues& keyValues, const char *pPathID, CUtlVector<FileNameHandle_t> *pIncludes )
+{
+ if ( pIncludes )
+ {
+ pIncludes->Purge();
+ }
+
+ patchKeyValuesOut.Clear();
+
+ if ( V_stricmp( keyValues.GetName(), "patch" ) != 0 )
+ {
+ // Not a patch file, nothing to do
+ if ( ppBaseKeyValuesOut )
+ {
+ // flag to the caller that the passed in keyValues are in fact final non-patch values
+ *ppBaseKeyValuesOut = NULL;
+ }
+ return true;
+ }
+
+ KeyValues *pCurrentKeyValues = keyValues.MakeCopy();
+
+ // Recurse down through all patch files:
+ int nCount = 0;
+ while( ( nCount < 10 ) && ( V_stricmp( pCurrentKeyValues->GetName(), "patch" ) == 0 ) )
+ {
+ // Accumulate the new patch keys from this file
+ AccumulatePatchKeyValues( *pCurrentKeyValues, patchKeyValuesOut );
+
+ // Load the included file
+ const char *pIncludeFileName = pCurrentKeyValues->GetString( "include" );
+
+ if ( pIncludeFileName == NULL )
+ {
+ // A patch file without an include key? Not good...
+ Warning( "VMT patch file has no include key - invalid!\n" );
+ Assert( pIncludeFileName );
+ break;
+ }
+
+ CUtlString includeFileName( pIncludeFileName ); // copy off the string before we clear the keyvalues it lives in
+ pCurrentKeyValues->Clear();
+ bool bSuccess = pCurrentKeyValues->LoadFromFile( g_pFullFileSystem, includeFileName, pPathID );
+ if( bSuccess )
+ {
+ if ( pIncludes )
+ {
+ // Remember that we included this file for the pure server stuff.
+ pIncludes->AddToTail( g_pFullFileSystem->FindOrAddFileName( includeFileName ) );
+ }
+ }
+ else
+ {
+ pCurrentKeyValues->deleteThis();
+#ifndef DEDICATED
+ Warning( "Failed to load $include VMT file (%s)\n", includeFileName.String() );
+#endif
+ if ( !HushAsserts() )
+ {
+ AssertMsg( false, "Failed to load $include VMT file (%s)", includeFileName.String() );
+ }
+ return false;
+ }
+
+ nCount++;
+ }
+
+ if ( ppBaseKeyValuesOut )
+ {
+ *ppBaseKeyValuesOut = pCurrentKeyValues;
+ }
+ else
+ {
+ pCurrentKeyValues->deleteThis();
+ }
+
+ if( nCount >= 10 )
+ {
+ Warning( "Infinite recursion in patch file?\n" );
+ }
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void ExpandPatchFile( KeyValues& keyValues, KeyValues &patchKeyValues, const char *pPathID, CUtlVector<FileNameHandle_t> *pIncludes )
+{
+ KeyValues *pNonPatchKeyValues = NULL;
+ if ( !patchKeyValues.IsEmpty() )
+ {
+ pNonPatchKeyValues = keyValues.MakeCopy();
+ }
+ else
+ {
+ bool bSuccess = AccumulateRecursiveVmtPatches( patchKeyValues, &pNonPatchKeyValues, keyValues, pPathID, pIncludes );
+ if ( !bSuccess )
+ {
+ return;
+ }
+ }
+
+ if ( pNonPatchKeyValues != NULL )
+ {
+ // We're dealing with a patch file. Apply accumulated patches to final vmt
+ ApplyPatchKeyValues( *pNonPatchKeyValues, patchKeyValues );
+ keyValues = *pNonPatchKeyValues;
+ pNonPatchKeyValues->deleteThis();
+ }
+}
+
+bool LoadVMTFile( KeyValues &vmtKeyValues, KeyValues &patchKeyValues, const char *pMaterialName, bool bAbsolutePath, CUtlVector<FileNameHandle_t> *pIncludes )
+{
+ char pFileName[MAX_PATH];
+ const char *pPathID = "GAME";
+ if ( !bAbsolutePath )
+ {
+ Q_snprintf( pFileName, sizeof( pFileName ), "materials/%s.vmt", pMaterialName );
+ }
+ else
+ {
+ Q_snprintf( pFileName, sizeof( pFileName ), "%s.vmt", pMaterialName );
+ if ( pMaterialName[0] == '/' && pMaterialName[1] == '/' && pMaterialName[2] != '/' )
+ {
+ // UNC, do full search
+ pPathID = NULL;
+ }
+ }
+
+ if ( !vmtKeyValues.LoadFromFile( g_pFullFileSystem, pFileName, pPathID ) )
+ {
+ return false;
+ }
+ ExpandPatchFile( vmtKeyValues, patchKeyValues, pPathID, pIncludes );
+
+ return true;
+}
+
+int CMaterial::GetNumPasses( void )
+{
+ Precache();
+// int mod = m_ShaderRenderState.m_Modulation;
+ int mod = 0;
+ return m_ShaderRenderState.m_pSnapshots[mod].m_nPassCount;
+}
+
+int CMaterial::GetTextureMemoryBytes( void )
+{
+ Precache();
+ int bytes = 0;
+ int i;
+ for( i = 0; i < m_VarCount; i++ )
+ {
+ IMaterialVar *pVar = m_pShaderParams[i];
+ if( pVar->GetType() == MATERIAL_VAR_TYPE_TEXTURE )
+ {
+ ITexture *pTexture = pVar->GetTextureValue();
+ if( pTexture && pTexture != ( ITexture * )0xffffffff )
+ {
+ bytes += pTexture->GetApproximateVidMemBytes();
+ }
+ }
+ }
+ return bytes;
+}
+
+void CMaterial::SetUseFixedFunctionBakedLighting( bool bEnable )
+{
+ SetMaterialVarFlags2( MATERIAL_VAR2_USE_FIXED_FUNCTION_BAKED_LIGHTING, bEnable );
+}
+
+bool CMaterial::NeedsFixedFunctionFlashlight() const
+{
+ return ( GetMaterialVarFlags2() & MATERIAL_VAR2_NEEDS_FIXED_FUNCTION_FLASHLIGHT ) &&
+ MaterialSystem()->InFlashlightMode();
+}
+
+bool CMaterial::IsUsingVertexID( ) const
+{
+ return ( GetMaterialVarFlags2() & MATERIAL_VAR2_USES_VERTEXID ) != 0;
+}
+
+void CMaterial::DeleteIfUnreferenced()
+{
+ if ( m_RefCount > 0 )
+ return;
+ IMaterialVar::DeleteUnreferencedTextures( true );
+ IMaterialInternal::DestroyMaterial( this );
+ IMaterialVar::DeleteUnreferencedTextures( false );
+}