|
|
//========= 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", ¢er.x, ¢er.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 ); }
|