source-engine/materialsystem/CMaterialSubRect.cpp

1070 lines
34 KiB
C++
Raw Permalink Normal View History

2020-04-22 16:56:21 +00:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=====================================================================================//
#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"
2022-02-23 11:56:29 +00:00
#include <stdlib.h>
2020-04-22 16:56:21 +00:00
#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 "cmaterial_queuefriendly.h"
#include "mempool.h"
static IMaterialVar *CreateMaterialVarFromKeyValue( IMaterial* pMaterial, KeyValues* pKeyValue );
//-----------------------------------------------------------------------------
// Material SubRect implementation
//-----------------------------------------------------------------------------
class CMaterialSubRect : public IMaterialInternal
{
public:
// pVMTKeyValues and pPatchKeyValues should come from LoadVMTFile()
CMaterialSubRect( char const *pMaterialName, const char *pTextureGroupName, KeyValues *pVMTKeyValues, KeyValues *pPatchKeyValues, bool bAssumeCreateFromFile );
virtual ~CMaterialSubRect();
// IMaterial Interface
const char *GetName() const;
const char *GetTextureGroupName() const;
int GetMappingWidth();
int GetMappingHeight();
bool InMaterialPage( void ) { return true; }
void GetMaterialOffset( float *pOffset );
void GetMaterialScale( float *pScale );
IMaterial *GetMaterialPage( void ) { return m_pMaterialPage; }
void IncrementReferenceCount( void );
void DecrementReferenceCount( void );
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 );
int GetEnumerationID() const;
// Maybe!
void GetReflectivity( Vector& reflect ) { m_pMaterialPage->GetReflectivity( reflect ); }
// IMaterialInternal Interface
int GetReferenceCount( void ) const;
void Precache();
void Uncache( bool bPreserveVars = false );
// 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 );
bool IsPrecached() const;
bool IsPrecachedVars( ) const;
bool IsManuallyCreated() const;
void SetEnumerationID( int id );
void AddMaterialVar( IMaterialVar *pMaterialVar );
void MarkAsPreloaded( bool bSet );
bool IsPreloaded() const;
void ArtificialAddRef();
void ArtificialRelease();
//=============================
// Chained to the material page.
//=============================
// IMaterial Interface.
PreviewImageRetVal_t GetPreviewImageProperties( int *width, int *height, ImageFormat *imageFormat, bool* isTranslucent ) const
{ return m_pMaterialPage->GetPreviewImageProperties( width, height, imageFormat, isTranslucent ); }
PreviewImageRetVal_t GetPreviewImage( unsigned char *data, int width, int height, ImageFormat imageFormat ) const
{ return m_pMaterialPage->GetPreviewImage( data, width, height, imageFormat ); }
ShaderRenderState_t *GetRenderState() { return m_pMaterialPage->GetRenderState(); }
int GetNumAnimationFrames() { return m_pMaterialPage->GetNumAnimationFrames(); }
void GetLowResColorSample( float s, float t, float *color ) const
{
if ( m_pMaterialPage )
m_pMaterialPage->GetLowResColorSample( s, t, color );
else
color[ 0 ] = color[ 1 ] = color[ 2 ] = 0.0f;
}
bool UsesEnvCubemap( void ) { return m_pMaterialPage->UsesEnvCubemap(); }
bool NeedsSoftwareSkinning( void ) { return m_pMaterialPage->NeedsSoftwareSkinning(); }
bool NeedsSoftwareLighting( void ) { return m_pMaterialPage->NeedsSoftwareLighting(); }
bool NeedsTangentSpace( void ) { return m_pMaterialPage->NeedsTangentSpace(); }
bool NeedsPowerOfTwoFrameBufferTexture( bool bCheckSpecificToThisFrame = true ) { return m_pMaterialPage->NeedsPowerOfTwoFrameBufferTexture( bCheckSpecificToThisFrame ); }
bool NeedsFullFrameBufferTexture( bool bCheckSpecificToThisFrame = true ) { return m_pMaterialPage->NeedsFullFrameBufferTexture( bCheckSpecificToThisFrame ); }
bool NeedsLightmapBlendAlpha( void ) { return m_pMaterialPage->NeedsLightmapBlendAlpha(); }
void AlphaModulate( float alpha ) { m_pMaterialPage->AlphaModulate( alpha ); }
void ColorModulate( float r, float g, float b ) { m_pMaterialPage->ColorModulate( r, g, b ); }
float GetAlphaModulation( ) { return m_pMaterialPage->GetAlphaModulation( ); }
void GetColorModulation( float *r, float *g, float *b ) { m_pMaterialPage->GetColorModulation( r, g, b ); }
void SetMaterialVarFlag( MaterialVarFlags_t flag, bool on ) { m_pMaterialPage->SetMaterialVarFlag( flag, on ); }
bool GetMaterialVarFlag( MaterialVarFlags_t flag ) const { return m_pMaterialPage->GetMaterialVarFlag( flag ); }
bool IsTranslucent() { return m_pMaterialPage->IsTranslucent(); }
bool IsTranslucentInternal( float fAlphaModulation ) const { return m_pMaterialPage->IsTranslucentInternal( fAlphaModulation ); }
bool IsAlphaTested() { return m_pMaterialPage->IsAlphaTested(); }
bool IsVertexLit() { return m_pMaterialPage->IsVertexLit(); }
bool GetPropertyFlag( MaterialPropertyTypes_t type ) { return m_pMaterialPage->GetPropertyFlag( type ); }
bool IsTwoSided() { return m_pMaterialPage->IsTwoSided(); }
int GetNumPasses( void ) { return m_pMaterialPage->GetNumPasses(); }
int GetTextureMemoryBytes( void ) { return m_pMaterialPage->GetTextureMemoryBytes(); }
// IMaterialInternal Interface.
void DrawMesh( VertexCompressionType_t vertexCompression ) { m_pMaterialPage->DrawMesh( vertexCompression ); }
void ReloadTextures( void ) { m_pMaterialPage->ReloadTextures(); }
void SetMinLightmapPageID( int pageID )
{
m_pMaterialPage->SetMinLightmapPageID( pageID );
}
void SetMaxLightmapPageID( int pageID )
{
m_pMaterialPage->SetMaxLightmapPageID( pageID );
}
int GetMinLightmapPageID( ) const { return m_pMaterialPage->GetMinLightmapPageID(); }
int GetMaxLightmapPageID( ) const { return m_pMaterialPage->GetMaxLightmapPageID(); }
void SetNeedsWhiteLightmap( bool val )
{
m_pMaterialPage->SetNeedsWhiteLightmap( val );
}
bool GetNeedsWhiteLightmap( ) const { return m_pMaterialPage->GetNeedsWhiteLightmap(); }
IShader * GetShader() const { return m_pMaterialPage->GetShader(); }
void CallBindProxy( void *proxyData ) { m_pMaterialPage->CallBindProxy( proxyData ); }
IMaterial *CheckProxyReplacement( void *proxyData ) { return m_pMaterialPage->CheckProxyReplacement( proxyData ); }
bool HasProxy( void ) const { return m_pMaterialPage->HasProxy(); }
// Sets the shader associated with the material
void SetShader( const char *pShaderName ) { m_pMaterialPage->SetShader( pShaderName ); }
const char * GetShaderName() const { return m_pMaterialPage->GetShaderName(); }
virtual void DeleteIfUnreferenced();
virtual bool IsSpriteCard() { return m_pMaterialPage->IsSpriteCard(); }
// Can we override this material in debug?
bool NoDebugOverride() const { return m_pMaterialPage->NoDebugOverride(); }
// Gets the vertex format
VertexFormat_t GetVertexFormat() const { return m_pMaterialPage->GetVertexFormat(); }
// Gets the morph format
virtual MorphFormat_t GetMorphFormat() const { return m_pMaterialPage->GetMorphFormat(); }
// diffuse bump lightmap?
// bool IsUsingDiffuseBumpedLighting() const { return m_pChainMaterial->IsUsingDiffuseBumpedLighting(); }
// lightmap?
// bool IsUsingLightmap() const { return m_pChainMaterial->IsUsingLightmap(); }
// Gets the vertex usage flags
VertexFormat_t GetVertexUsage() const { return m_pMaterialPage->GetVertexUsage(); }
// Debugs this material
bool PerformDebugTrace() const { return m_pMaterialPage->PerformDebugTrace(); }
// Are we suppressed?
bool IsSuppressed() const { return m_pMaterialPage->IsSuppressed(); }
// Do we use fog?
bool UseFog( void ) const { return m_pMaterialPage->UseFog(); }
// Should we draw?
void ToggleSuppression() { m_pMaterialPage->ToggleSuppression(); }
void ToggleDebugTrace() { m_pMaterialPage->ToggleDebugTrace(); }
// Refresh material based on current var values
void Refresh() { m_pMaterialPage->Refresh(); }
void RefreshPreservingMaterialVars() { m_pMaterialPage->RefreshPreservingMaterialVars(); }
// This computes the state snapshots for this material
void RecomputeStateSnapshots() { m_pMaterialPage->RecomputeStateSnapshots(); }
// Gets at the shader parameters
int ShaderParamCount() const { return m_pMaterialPage->ShaderParamCount(); }
IMaterialVar **GetShaderParams( void ) { return m_pMaterialPage->GetShaderParams(); }
bool IsErrorMaterial() const { return false; }
void SetUseFixedFunctionBakedLighting( bool bEnable ) { m_pMaterialPage->SetUseFixedFunctionBakedLighting( bEnable ); }
bool NeedsFixedFunctionFlashlight() const { return m_pMaterialPage->NeedsFixedFunctionFlashlight(); }
virtual void DecideShouldReloadFromWhitelist( IFileList *pFileList ) { m_pMaterialPage->DecideShouldReloadFromWhitelist( pFileList ); }
virtual void ReloadFromWhitelistIfMarked() { return m_pMaterialPage->ReloadFromWhitelistIfMarked(); }
virtual bool WasReloadedFromWhitelist() { return m_pMaterialPage->WasReloadedFromWhitelist(); }
bool IsUsingVertexID( ) const { return m_pMaterialPage->IsUsingVertexID(); }
virtual void ReportVarChanged( IMaterialVar *pVar ) { m_pMaterialPage->ReportVarChanged(pVar); }
virtual uint32 GetChangeID() const { return m_pMaterialPage->GetChangeID(); }
virtual bool IsRealTimeVersion( void ) const { return true; }
virtual IMaterialInternal *GetRealTimeVersion( void ) { return this; }
virtual IMaterialInternal *GetQueueFriendlyVersion( void ) { return &m_QueueFriendlyVersion; };
virtual void PrecacheMappingDimensions( void ) { m_pMaterialPage->PrecacheMappingDimensions(); }
virtual void FindRepresentativeTexture( void ) { m_pMaterialPage->FindRepresentativeTexture(); }
private:
void ParseMaterialVars( KeyValues &keyValues );
void SetupMaterialVars( void );
// Do we use a UNC-specified materal name?
bool UsesUNCFileName() const;
IMaterialVar *GetDummyMaterialVar();
private:
enum
{
MATERIALSUBRECT_IS_PRECACHED = 0x1,
MATERIALSUBRECT_VARS_IS_PRECACHED = 0x2,
MATERIALSUBRECT_IS_MANUALLY_CREATED = 0x4,
MATERIALSUBRECT_USES_UNC_FILENAME = 0x20,
MATERIALSUBRECT_IS_PRELOADED = 0x40,
MATERIALSUBRECT_ARTIFICIAL_REFCOUNT = 0x80,
};
// Fixed-size allocator
DECLARE_FIXEDSIZE_ALLOCATOR( CMaterialSubRect );
IMaterialInternal *m_pMaterialPage;
int m_iEnumID;
CUtlSymbol m_symName;
CUtlSymbol m_symTextureGroupName;
Vector2D m_vecOffset;
Vector2D m_vecScale;
Vector2D m_vecSize;
short m_nRefCount;
unsigned int m_fLocal; // Local flags - precached etc...
CUtlVector<IMaterialVar*> m_aMaterialVars;
// Used only by procedural materials; it essentially is an in-memory .VMT file
KeyValues *m_pVMTKeyValues;
#ifdef _DEBUG
// Makes it easier to see what's going on
char* m_pDebugName;
#endif
CMaterial_QueueFriendly m_QueueFriendlyVersion;
};
// NOTE: This must be the last file included
// Has to exist *after* fixed size allocator declaration
#include "tier0/memdbgon.h"
DEFINE_FIXEDSIZE_ALLOCATOR( CMaterialSubRect, 256, true );
//-----------------------------------------------------------------------------
// Purpose: Static create method for material subrect.
//-----------------------------------------------------------------------------
IMaterialInternal* IMaterialInternal::CreateMaterialSubRect( char const* pMaterialName, const char *pTextureGroupName,
KeyValues *pVMTKeyValues, KeyValues *pPatchKeyValues, bool bAssumeCreateFromFile )
{
return new CMaterialSubRect( pMaterialName, pTextureGroupName, pVMTKeyValues, pPatchKeyValues, bAssumeCreateFromFile );
}
//-----------------------------------------------------------------------------
// Purpose: Static destroy method for material subrect.
//-----------------------------------------------------------------------------
void IMaterialInternal::DestroyMaterialSubRect( IMaterialInternal* pMaterial )
{
if ( pMaterial )
{
CMaterialSubRect* pMat = static_cast<CMaterialSubRect*>( pMaterial );
delete pMat;
}
}
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CMaterialSubRect::CMaterialSubRect( const char *pMaterialName, const char *pTextureGroupName,
KeyValues *pVMTKeyValues, KeyValues *pPatchKeyValues, bool bAssumeCreateFromFile )
{
m_QueueFriendlyVersion.SetRealTimeVersion( this );
// Name with extension stripped off.
int len = Q_strlen( pMaterialName );
char* pTemp = ( char* )_alloca( len + 1 );
Q_strncpy( pTemp, pMaterialName, len + 1 );
Q_strlower( pTemp );
pTemp[ len - 4 ] = '\0';
m_symName = pTemp;
#ifdef _DEBUG
m_pDebugName = new char[Q_strlen( pTemp ) + 1];
Q_strncpy( m_pDebugName, pTemp, Q_strlen( pTemp ) + 1 );
#endif
m_pMaterialPage = NULL;
m_iEnumID = 0;
m_symTextureGroupName = pTextureGroupName;
m_vecOffset.Init();
m_vecScale.Init();
m_vecSize.Init();
m_nRefCount = 0;
m_fLocal = 0;
m_aMaterialVars.Purge();
if ( pTemp[0] == '/' && pTemp[1] == '/' && pTemp[2] != '/' )
{
m_fLocal |= MATERIALSUBRECT_USES_UNC_FILENAME;
}
if ( !bAssumeCreateFromFile )
{
m_pVMTKeyValues = pVMTKeyValues;
if (m_pVMTKeyValues)
{
m_fLocal |= MATERIALSUBRECT_IS_MANUALLY_CREATED;
}
// Precache immediately. We need the material page immediately.
Precache();
}
else
{
m_pVMTKeyValues = NULL;
PrecacheVars( pVMTKeyValues, pPatchKeyValues );
Precache();
}
Assert( m_pMaterialPage );
// Increment the material page usage counter.
m_pMaterialPage->IncrementReferenceCount();
}
//-----------------------------------------------------------------------------
// Purpose: Deconstructor
//-----------------------------------------------------------------------------
CMaterialSubRect::~CMaterialSubRect()
{
Uncache( );
if( m_nRefCount != 0 )
{
DevWarning( 2, "Reference Count for Material %s (%d) != 0\n", GetName(), m_nRefCount );
}
if ( m_pMaterialPage )
{
m_pMaterialPage->DecrementReferenceCount();
m_pMaterialPage = NULL;
}
if ( m_pVMTKeyValues )
{
m_pVMTKeyValues->deleteThis();
m_pVMTKeyValues = NULL;
}
// m_aMaterialVars is freed, purged, and lit on fire in Uncache() above.
#ifdef _DEBUG
if ( m_pDebugName )
{
delete[] m_pDebugName;
m_pDebugName = NULL;
}
#endif
}
//-----------------------------------------------------------------------------
// Sets new VMT shader parameters for the material
//-----------------------------------------------------------------------------
void CMaterialSubRect::SetShaderAndParams( KeyValues *pKeyValues )
{
Uncache();
if ( m_pVMTKeyValues )
{
m_pVMTKeyValues->deleteThis();
m_pVMTKeyValues = NULL;
}
m_pVMTKeyValues = pKeyValues ? pKeyValues->MakeCopy() : NULL;
if (m_pVMTKeyValues)
{
m_fLocal |= MATERIALSUBRECT_IS_MANUALLY_CREATED;
}
if ( g_pShaderDevice->IsUsingGraphics() )
{
Precache();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *CMaterialSubRect::GetName() const
{
return m_symName.String();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *CMaterialSubRect::GetTextureGroupName() const
{
return m_symTextureGroupName.String();
}
//-----------------------------------------------------------------------------
// Purpose: Return the size of the subrect not the texture page size (width).
//-----------------------------------------------------------------------------
int CMaterialSubRect::GetMappingWidth()
{
return int( m_vecSize.x );
}
//-----------------------------------------------------------------------------
// Purpose: Return the size of the subrect not the texture page size (height).
//-----------------------------------------------------------------------------
int CMaterialSubRect::GetMappingHeight()
{
return int( m_vecSize.y );
}
//-----------------------------------------------------------------------------
// Purpose: Return the texture offset into the texture page.
//-----------------------------------------------------------------------------
void CMaterialSubRect::GetMaterialOffset( float *pOffset )
{
pOffset[0] = m_vecOffset.x;
pOffset[1] = m_vecOffset.y;
}
//-----------------------------------------------------------------------------
// Purpose: Return the texture scale (size) within the texture page.
//-----------------------------------------------------------------------------
void CMaterialSubRect::GetMaterialScale( float *pScale )
{
pScale[0] = m_vecScale.x;
pScale[1] = m_vecScale.y;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMaterialSubRect::IncrementReferenceCount( void )
{
++m_nRefCount;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMaterialSubRect::DecrementReferenceCount( void )
{
--m_nRefCount;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CMaterialSubRect::GetReferenceCount( void ) const
{
return m_nRefCount;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CMaterialSubRect::IsPrecached() const
{
return ( m_fLocal & MATERIALSUBRECT_IS_PRECACHED ) != 0;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CMaterialSubRect::IsPrecachedVars( ) const
{
return ( m_fLocal & MATERIALSUBRECT_VARS_IS_PRECACHED ) != 0;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CMaterialSubRect::IsManuallyCreated() const
{
return ( m_fLocal & MATERIALSUBRECT_IS_MANUALLY_CREATED ) != 0;
}
//-----------------------------------------------------------------------------
// Do we use a UNC-specified materal name?
//-----------------------------------------------------------------------------
bool CMaterialSubRect::UsesUNCFileName() const
{
return ( m_fLocal & MATERIALSUBRECT_USES_UNC_FILENAME ) != 0;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMaterialSubRect::Precache()
{
// Are we already precached?
if( IsPrecached() )
return;
// Load data from the .vmt file.
if( !PrecacheVars() )
return;
m_QueueFriendlyVersion.UpdateToRealTime();
// Precached.
m_fLocal |= MATERIALSUBRECT_IS_PRECACHED;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CMaterialSubRect::PrecacheVars( KeyValues * pVMTKeyValues, KeyValues * pPatchKeyValues, CUtlVector<FileNameHandle_t> *pIncludes, int nFindContext )
{
// FIXME: Should call through to the parent material for all of this???
// We should get both parameters or neither
Assert( ( pVMTKeyValues == NULL ) ? ( pPatchKeyValues == NULL ) : ( pPatchKeyValues != NULL ) );
// Are we already precached?
if( IsPrecachedVars() )
return true;
// load data from the vmt file
bool bOk = 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
{
// load data from the vmt file
vmtKeyValues = new KeyValues( "vmt" );
patchKeyValues = new KeyValues( "vmt_patches" );
if( !LoadVMTFile( *vmtKeyValues, *patchKeyValues, GetName(), UsesUNCFileName(), NULL ) )
{
Warning( "CMaterialSubRect::PrecacheVars: error loading vmt file for %s\n", GetName() );
goto precacheVarsDone;
}
}
// Get the "Subrect" material vars.
ParseMaterialVars( *vmtKeyValues );
// Setup the "Subrect" material vars.
SetupMaterialVars();
// Vars are precached.
m_fLocal |= MATERIALSUBRECT_VARS_IS_PRECACHED;
bOk = true;
precacheVarsDone:
// Clean up
if ( ( vmtKeyValues != m_pVMTKeyValues ) && ( vmtKeyValues != pVMTKeyValues ) )
{
vmtKeyValues->deleteThis();
}
if ( patchKeyValues != pPatchKeyValues )
{
patchKeyValues->deleteThis();
}
return bOk;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMaterialSubRect::ParseMaterialVars( KeyValues &keyValues )
{
KeyValues *pKeyValues = &keyValues;
// I'm not quite sure how this can happen, but we'll see...
const char *pShaderName = pKeyValues->GetName();
if ( !pShaderName )
{
DevWarning( 1, "CMaterialSubRect::InitializeShader: Shader not specified in material %s.\n", GetName() );
Assert( 0 );
pShaderName = IsPC() && !IsEmulatingGL() ? "Wireframe_DX6" : "Wireframe_DX9";
}
// Verify we have the correct "shader." There is only one type.
// Needs to be case insensitive because we can't guarantee case specified in VMTs
if ( !Q_stricmp( pShaderName, "Subrect" ) )
{
KeyValues *pVar = pKeyValues->GetFirstSubKey();
while ( pVar )
{
if ( !Q_stricmp( pVar->GetName(), "$Pos" ) )
{
sscanf( pVar->GetString(), "%f %f", &m_vecOffset.x, &m_vecOffset.y );
}
else if ( !Q_stricmp( pVar->GetName(), "$Size" ) )
{
sscanf( pVar->GetString(), "%f %f", &m_vecSize.x, &m_vecSize.y );
}
else if ( !Q_stricmp( pVar->GetName(), "$Material" ) )
{
m_pMaterialPage = static_cast<IMaterialInternal*>( MaterialSystem()->FindMaterial( pVar->GetString(), TEXTURE_GROUP_DECAL ) );
m_pMaterialPage = m_pMaterialPage->GetRealTimeVersion(); //always work with the realtime material internally
}
// else if ( !Q_stricmp( pVar->GetName(), "$decalscale" ) )
// {
// m_flDecalScale = pVar->GetFloat();
// }
// Add var to list.
IMaterialVar *pNewVar = CreateMaterialVarFromKeyValue( this, pVar );
if ( pNewVar )
{
m_aMaterialVars.AddToTail( pNewVar );
}
// Continue getting the keys until they are all found.
pVar = pVar->GetNextKey();
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMaterialSubRect::SetupMaterialVars( void )
{
if ( !m_pMaterialPage )
{
DevWarning( 1, "CMaterialSubRect::SetupMaterialVars: Invalid Material Page!\n" );
return;
}
// Ask the material page for its size.
int nMaterialPageWidth = m_pMaterialPage->GetMappingWidth();
int nMaterialPageHeight = m_pMaterialPage->GetMappingHeight();
// Normalize the offset and scale.
float flOOWidth = 1.0f / static_cast<float>( nMaterialPageWidth );
float flOOHeight = 1.0f / static_cast<float>( nMaterialPageHeight );
// Add 0.5f to push the image "in" by 1/2 a texel, and subtract 1.0f to push it
// "in" by 1/2 a texel on the other side.
m_vecOffset.x += 1.0f;
m_vecOffset.y += 1.0f;
m_vecOffset.x *= flOOWidth;
m_vecOffset.y *= flOOHeight;
m_vecScale.x = ( m_vecSize.x - 2.0f ) * flOOWidth;
m_vecScale.y = ( m_vecSize.y - 2.0f ) * flOOHeight;
}
//-----------------------------------------------------------------------------
// Purpose: Look through
//-----------------------------------------------------------------------------
IMaterialVar *CMaterialSubRect::FindVar( char const *varName, bool *found, bool complain )
{
// Look for the var in the material page - it has precedence.
IMaterialVar *pVar = m_pMaterialPage->FindVar( varName, found, false );
if ( *found )
return pVar;
// Look for the var in the local list of vars.
MaterialVarSym_t symVar = IMaterialVar::FindSymbol( varName );
if ( symVar != UTL_INVAL_SYMBOL )
{
int nVarCount = m_aMaterialVars.Count();
for ( int iVar = 0; iVar < nVarCount; ++iVar )
{
if ( m_aMaterialVars[iVar]->GetNameAsSymbol() == symVar )
{
*found = true;
return m_aMaterialVars[iVar];
}
}
}
// Not found!
if( complain )
{
static int complainCount = 0;
if( complainCount < 100 )
{
DevWarning( 1, "No such variable \"%s\" for material \"%s\"\n", varName, GetName() );
complainCount++;
}
}
return GetDummyMaterialVar();
}
IMaterialVar *CMaterialSubRect::FindVarFast( char const *pVarName, unsigned int *pToken )
{
// Look for the var in the material page - it has precedence.
IMaterialVar *pVar = m_pMaterialPage->FindVarFast( pVarName, pToken );
if ( pVar )
return pVar;
if ( *pToken != UTL_INVAL_SYMBOL )
{
int nVarCount = m_aMaterialVars.Count();
for ( int iVar = 0; iVar < nVarCount; ++iVar )
{
if ( m_aMaterialVars[iVar]->GetNameAsSymbol() == *pToken )
return m_aMaterialVars[iVar];
}
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
IMaterialVar *CMaterialSubRect::GetDummyMaterialVar()
{
static IMaterialVar* pDummyVar = 0;
if ( !pDummyVar )
pDummyVar = IMaterialVar::Create( 0, "$dummyVar", 0 );
return pDummyVar;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CMaterialSubRect::GetEnumerationID() const
{
return m_iEnumID;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMaterialSubRect::SetEnumerationID( int id )
{
m_iEnumID = id;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMaterialSubRect::Uncache( bool bPreserveVars )
{
MaterialLock_t hMaterialLock = MaterialSystem()->Lock();
// Don't bother if we're not cached
if ( IsPrecached() )
{
m_fLocal &= ~MATERIALSUBRECT_IS_PRECACHED;
}
if ( !bPreserveVars )
{
if ( IsPrecachedVars() )
{
for ( int i = 0; i < m_aMaterialVars.Count(); ++i )
{
IMaterialVar::Destroy( m_aMaterialVars[i] );
}
m_aMaterialVars.Purge();
m_fLocal &= ~MATERIALSUBRECT_VARS_IS_PRECACHED;
}
}
MaterialSystem()->Unlock( hMaterialLock );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMaterialSubRect::AddMaterialVar( IMaterialVar *pMaterialVar )
{
m_aMaterialVars.AddToTail( pMaterialVar );
}
void CMaterialSubRect::MarkAsPreloaded( bool bSet )
{
if ( bSet )
{
m_fLocal |= MATERIALSUBRECT_IS_PRELOADED;
}
else
{
m_fLocal &= ~MATERIALSUBRECT_IS_PRELOADED;
}
}
bool CMaterialSubRect::IsPreloaded() const
{
return ( m_fLocal & MATERIALSUBRECT_IS_PRELOADED ) != 0;
}
void CMaterialSubRect::ArtificialAddRef( void )
{
if ( m_fLocal & MATERIALSUBRECT_ARTIFICIAL_REFCOUNT )
{
// already done
return;
}
m_fLocal |= MATERIALSUBRECT_ARTIFICIAL_REFCOUNT;
m_nRefCount++;
}
void CMaterialSubRect::ArtificialRelease( void )
{
if ( !( m_fLocal & MATERIALSUBRECT_ARTIFICIAL_REFCOUNT ) )
{
return;
}
m_fLocal &= ~MATERIALSUBRECT_ARTIFICIAL_REFCOUNT;
m_nRefCount--;
}
//-----------------------------------------------------------------------------
// 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 == '{';
}
//-----------------------------------------------------------------------------
// Creates a vector material var
//-----------------------------------------------------------------------------
static IMaterialVar* CreateVectorMaterialVarFromKeyValue( IMaterial* pMaterial, KeyValues* pKeyValue )
{
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", pMaterial->GetName(), 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(), pMaterial->GetName() );
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 );
}
// Create the variable!
return IMaterialVar::Create( pMaterial, pKeyValue->GetName(), vecVal, i );
}
//-----------------------------------------------------------------------------
// Creates a vector material var
//-----------------------------------------------------------------------------
static IMaterialVar* CreateMatrixMaterialVarFromKeyValue( IMaterial* pMaterial, KeyValues* pKeyValue )
{
char const* pScan = pKeyValue->GetString();
// 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, pKeyValue->GetName(), mat );
}
Vector2D scale, center;
float angle;
Vector2D translation;
count = sscanf( pScan, " center %f %f scale %f %f rotate %f translate %f %f",
&center.x, &center.y, &scale.x, &scale.y, &angle, &translation.x, &translation.y );
if (count != 7)
return NULL;
VMatrix temp;
MatrixBuildTranslation( mat, -center.x, -center.y, 0.0f );
MatrixBuildScale( temp, scale.x, scale.y, 1.0f );
MatrixMultiply( temp, mat, mat );
MatrixBuildRotateZ( temp, angle );
MatrixMultiply( temp, mat, mat );
MatrixBuildTranslation( temp, center.x + translation.x, center.y + translation.y, 0.0f );
MatrixMultiply( temp, mat, mat );
// Create the variable!
return IMaterialVar::Create( pMaterial, pKeyValue->GetName(), mat );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
static IMaterialVar *CreateMaterialVarFromKeyValue( IMaterial* pMaterial, KeyValues* pKeyValue )
{
switch( pKeyValue->GetDataType() )
{
case KeyValues::TYPE_INT:
{
return IMaterialVar::Create( pMaterial, pKeyValue->GetName(), pKeyValue->GetInt() );
}
case KeyValues::TYPE_FLOAT:
{
return IMaterialVar::Create( pMaterial, pKeyValue->GetName(), 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, pKeyValue->GetName(), pString );
// Parse the string as a vector...
return CreateVectorMaterialVarFromKeyValue( pMaterial, pKeyValue );
}
}
return 0;
}
void CMaterialSubRect::DeleteIfUnreferenced()
{
if ( m_nRefCount > 0 )
return;
MaterialSystem()->RemoveMaterialSubRect( this );
}