summaryrefslogtreecommitdiff
path: root/materialsystem/cmaterialvar.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'materialsystem/cmaterialvar.cpp')
-rw-r--r--materialsystem/cmaterialvar.cpp1631
1 files changed, 1631 insertions, 0 deletions
diff --git a/materialsystem/cmaterialvar.cpp b/materialsystem/cmaterialvar.cpp
new file mode 100644
index 0000000..ce86b92
--- /dev/null
+++ b/materialsystem/cmaterialvar.cpp
@@ -0,0 +1,1631 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//===========================================================================//
+
+#include "materialsystem/imaterialvar.h"
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/itexture.h"
+#include <string.h>
+#include "materialsystem_global.h"
+#include <stdlib.h>
+#include "shaderapi/ishaderapi.h"
+#include "imaterialinternal.h"
+#include "utlsymbol.h"
+#include "mempool.h"
+#include "itextureinternal.h"
+#include "tier0/dbg.h"
+#include "tier1/callqueue.h"
+#include "mathlib/vmatrix.h"
+#include "tier1/strtools.h"
+#include "texturemanager.h"
+
+#define MATERIALVAR_CHAR_BUF_SIZE 512
+
+#if !defined( _X360 )
+#pragma pack (1)
+#endif
+
+ConVar mat_texture_tracking( "mat_texture_tracking", IsDebug() ? "1" : "0" );
+CUtlMap<ITexture*, CInterlockedInt> s_TextureRefList( DefLessFunc( ITexture* ) );
+CUtlMap<ITexture*, CInterlockedInt> *g_pTextureRefList = &s_TextureRefList;
+
+struct MaterialVarMatrix_t
+{
+ VMatrix m_Matrix;
+ bool m_bIsIdent;
+};
+
+class CMaterialVar : public IMaterialVar
+{
+public:
+ // stuff from IMaterialVar
+ virtual const char * GetName( void ) const;
+ virtual MaterialVarSym_t GetNameAsSymbol() const;
+ virtual void SetFloatValue( float val );
+ virtual void SetIntValue( int val );
+ virtual void SetStringValue( const char *val );
+ virtual const char * GetStringValue( void ) const;
+ virtual void SetMatrixValue( VMatrix const& matrix );
+ virtual VMatrix const& GetMatrixValue( );
+ virtual bool MatrixIsIdentity( void ) const;
+ virtual void SetVecValue( const float* pVal, int numComps );
+ virtual void SetVecValue( float x, float y );
+ virtual void SetVecValue( float x, float y, float z );
+ virtual void SetVecValue( float x, float y, float z, float w );
+ void SetVecValueInternal( const Vector4D &vec, int nComps );
+ virtual void SetVecComponentValue( float fVal, int nComponent );
+ virtual void GetLinearVecValue( float *val, int numComps ) const;
+ virtual void SetFourCCValue( FourCC type, void *pData );
+ virtual void GetFourCCValue( FourCC *type, void **ppData );
+ virtual int GetIntValueInternal( void ) const;
+ virtual float GetFloatValueInternal( void ) const;
+ virtual float const* GetVecValueInternal( ) const;
+ virtual void GetVecValueInternal( float *val, int numcomps ) const;
+ virtual int VectorSizeInternal() const;
+
+ // revisit: is this a good interface for textures?
+
+ virtual ITexture * GetTextureValue( void );
+ virtual void SetTextureValue( ITexture * );
+ void SetTextureValueQueued( ITexture *texture );
+
+ virtual IMaterial * GetMaterialValue( void );
+ virtual void SetMaterialValue( IMaterial * );
+
+ virtual operator ITexture *() { return GetTextureValue(); }
+ virtual bool IsDefined() const;
+ virtual void SetUndefined();
+
+ virtual void CopyFrom( IMaterialVar *pMaterialVar );
+
+ FORCEINLINE void Init( void )
+ {
+ m_nNumVectorComps = 4;
+ m_VecVal.Init();
+ m_pStringVal = NULL;
+ m_intVal = 0;
+ m_nTempIndex = 0xFF;
+ m_bFakeMaterialVar = false;
+ m_Type = MATERIAL_VAR_TYPE_INT;
+ }
+
+ // stuff that is only visible inside of the material system
+ CMaterialVar();
+ CMaterialVar( IMaterial* pMaterial, const char *key, VMatrix const& matrix );
+ CMaterialVar( IMaterial* pMaterial, const char *key, const char *val );
+ CMaterialVar( IMaterial* pMaterial, const char *key, float* pVal, int numcomps );
+ CMaterialVar( IMaterial* pMaterial, const char *key, float val );
+ CMaterialVar( IMaterial* pMaterial, const char *key, int val );
+ CMaterialVar( IMaterial* pMaterial, const char *key );
+ virtual ~CMaterialVar();
+
+ virtual void SetValueAutodetectType( const char *val );
+
+ virtual IMaterial * GetOwningMaterial() { return m_pMaterial; }
+
+private:
+ // Cleans up material var data
+ CMaterialVar *AllocThreadVar();
+ void CleanUpData();
+
+ // NOTE: Dummy vars have no backlink so we have to check the pointer here
+ void VarChanged() { if ( m_pMaterial ) m_pMaterial->ReportVarChanged(this); }
+
+ // class data
+ static char s_CharBuf[MATERIALVAR_CHAR_BUF_SIZE];
+ static ITextureInternal *m_dummyTexture;
+
+ // Fixed-size allocator
+#ifdef NO_SBH // not needed if tier0 small block heap enabled
+ DECLARE_FIXEDSIZE_ALLOCATOR( CMaterialVar );
+#endif
+
+ // Owning material....
+ IMaterialInternal* m_pMaterial;
+
+ // Only using one of these at a time...
+ struct FourCC_t
+ {
+ FourCC m_FourCC;
+ void *m_pFourCCData;
+ };
+
+ FourCC_t *AllocFourCC();
+
+ union
+ {
+ IMaterialInternal* m_pMaterialValue;
+ ITextureInternal *m_pTexture;
+ MaterialVarMatrix_t* m_pMatrix;
+ FourCC_t *m_pFourCC;
+ };
+};
+
+// Has to exist *after* fixed size allocator declaration
+#include "tier0/memdbgon.h"
+
+typedef CMaterialVar *CMaterialVarPtr;
+
+#ifdef NO_SBH // not needed if tier0 small block heap enabled
+DEFINE_FIXEDSIZE_ALLOCATOR( CMaterialVar, 1024, true );
+#endif
+
+// Stores symbols for the material vars
+static CUtlSymbolTableMT s_MaterialVarSymbols( 0, 32, true );
+
+static bool g_bDeleteUnreferencedTexturesEnabled = false;
+
+
+//-----------------------------------------------------------------------------
+// Used to make GetIntValue thread safe from within proxy calls
+//-----------------------------------------------------------------------------
+static CMaterialVar s_pTempMaterialVar[254];
+static MaterialVarMatrix_t s_pTempMatrix[254];
+static bool s_bEnableThreadedAccess = false;
+static int s_nTempVarsUsed = 0;
+static int s_nOverflowTempVars = 0;
+
+
+//-----------------------------------------------------------------------------
+// Global methods related to material vars
+//-----------------------------------------------------------------------------
+void EnableThreadedMaterialVarAccess( bool bEnable, IMaterialVar **ppParams, int nVarCount )
+{
+ if ( s_bEnableThreadedAccess == bEnable )
+ return;
+
+ s_bEnableThreadedAccess = bEnable;
+ if ( !s_bEnableThreadedAccess )
+ {
+ // Necessary to free up reference counts
+ Assert( s_nTempVarsUsed <= Q_ARRAYSIZE(s_pTempMaterialVar) );
+ for ( int i = 0; i < s_nTempVarsUsed; ++i )
+ {
+ s_pTempMaterialVar[i].SetUndefined();
+ }
+ for ( int i = 0; i < nVarCount; ++i )
+ {
+ ppParams[i]->SetTempIndex( 0xFF );
+ }
+ s_nTempVarsUsed = 0;
+ if ( s_nOverflowTempVars )
+ {
+ Warning("Overflowed %d temp material vars!\n", s_nOverflowTempVars );
+ s_nOverflowTempVars = 0;
+ }
+ }
+}
+
+
+CMaterialVar *CMaterialVar::AllocThreadVar()
+{
+ if ( s_bEnableThreadedAccess )
+ {
+ if ( m_nTempIndex == 0xFF )
+ {
+ if ( s_nTempVarsUsed >= Q_ARRAYSIZE(s_pTempMaterialVar) )
+ {
+ s_nOverflowTempVars++;
+ return NULL;
+ }
+ m_nTempIndex = s_nTempVarsUsed++;
+ }
+
+ return &s_pTempMaterialVar[m_nTempIndex];
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Static method
+// Input : enable -
+//-----------------------------------------------------------------------------
+void IMaterialVar::DeleteUnreferencedTextures( bool enable )
+{
+ g_bDeleteUnreferencedTexturesEnabled = enable;
+}
+
+//-----------------------------------------------------------------------------
+// class factory methods
+//-----------------------------------------------------------------------------
+IMaterialVar* IMaterialVar::Create( IMaterial* pMaterial, const char* pKey, VMatrix const& matrix )
+{
+ return new CMaterialVar( pMaterial, pKey, matrix );
+}
+
+IMaterialVar* IMaterialVar::Create( IMaterial* pMaterial, const char* pKey, const char* pVal )
+{
+ return new CMaterialVar( pMaterial, pKey, pVal );
+}
+
+IMaterialVar* IMaterialVar::Create( IMaterial* pMaterial, const char* pKey, float* pVal, int numComps )
+{
+ return new CMaterialVar( pMaterial, pKey, pVal, numComps );
+}
+
+IMaterialVar* IMaterialVar::Create( IMaterial* pMaterial, const char* pKey, float val )
+{
+ return new CMaterialVar( pMaterial, pKey, val );
+}
+
+IMaterialVar* IMaterialVar::Create( IMaterial* pMaterial, const char* pKey, int val )
+{
+ return new CMaterialVar( pMaterial, pKey, val );
+}
+
+IMaterialVar* IMaterialVar::Create( IMaterial* pMaterial, const char* pKey )
+{
+ return new CMaterialVar( pMaterial, pKey );
+}
+
+void IMaterialVar::Destroy( IMaterialVar* pVar )
+{
+ if (pVar)
+ {
+ CMaterialVar* pVarImp = static_cast<CMaterialVar*>(pVar);
+ delete pVarImp;
+ }
+}
+
+MaterialVarSym_t IMaterialVar::GetSymbol( const char* pName )
+{
+ if (!pName)
+ return UTL_INVAL_SYMBOL;
+
+ char temp[1024];
+ Q_strncpy( temp, pName, sizeof( temp ) );
+ Q_strlower( temp );
+ return s_MaterialVarSymbols.AddString( temp );
+}
+
+MaterialVarSym_t IMaterialVar::FindSymbol( const char* pName )
+{
+ if (!pName)
+ return UTL_INVAL_SYMBOL;
+
+ return s_MaterialVarSymbols.Find( pName );
+}
+
+bool IMaterialVar::SymbolMatches( const char* pName, MaterialVarSym_t symbol )
+{
+ return !Q_stricmp( s_MaterialVarSymbols.String(symbol), pName );
+}
+
+
+//-----------------------------------------------------------------------------
+// class globals
+//-----------------------------------------------------------------------------
+char CMaterialVar::s_CharBuf[MATERIALVAR_CHAR_BUF_SIZE];
+
+
+//-----------------------------------------------------------------------------
+// constructors
+//-----------------------------------------------------------------------------
+inline CMaterialVar::FourCC_t *CMaterialVar::AllocFourCC()
+{
+ return new FourCC_t;
+}
+
+
+//-----------------------------------------------------------------------------
+// NOTE: This constructor is only used by the "fake" material vars
+// used to get thread mode working
+//-----------------------------------------------------------------------------
+CMaterialVar::CMaterialVar()
+{
+ Init();
+ m_pMaterial = NULL;
+ m_bFakeMaterialVar = true;
+}
+
+//-------------------------------------
+
+CMaterialVar::CMaterialVar( IMaterial* pMaterial, const char *pKey, VMatrix const& matrix )
+{
+ Init();
+ Assert( pKey );
+
+ m_pMaterial = static_cast<IMaterialInternal*>(pMaterial);
+ m_Name = GetSymbol( pKey );
+ Assert( m_Name != UTL_INVAL_SYMBOL );
+ m_Type = MATERIAL_VAR_TYPE_MATRIX;
+ m_pMatrix = new MaterialVarMatrix_t;
+ Assert( m_pMatrix );
+ MatrixCopy( matrix, m_pMatrix->m_Matrix );
+ m_pMatrix->m_bIsIdent = matrix.IsIdentity();
+ m_intVal = 0;
+ m_VecVal.Init();
+
+}
+
+CMaterialVar::CMaterialVar( IMaterial* pMaterial, const char *pKey, const char *pVal )
+{
+ Init();
+ Assert( pVal && pKey );
+
+ m_pMaterial = static_cast<IMaterialInternal*>(pMaterial);
+ m_Name = GetSymbol( pKey );
+ Assert( m_Name != UTL_INVAL_SYMBOL );
+ int len = Q_strlen( pVal ) + 1;
+ m_pStringVal = new char[ len ];
+ Q_strncpy( m_pStringVal, pVal, len );
+ m_Type = MATERIAL_VAR_TYPE_STRING;
+ m_VecVal[0] = m_VecVal[1] = m_VecVal[2] = m_VecVal[3] = atof( m_pStringVal );
+ m_intVal = int( atof( m_pStringVal ) );
+}
+
+CMaterialVar::CMaterialVar( IMaterial* pMaterial, const char *pKey, float* pVal, int numComps )
+{
+ Init();
+ Assert( pVal && pKey && (numComps <= 4) );
+
+ m_pMaterial = static_cast<IMaterialInternal*>(pMaterial);;
+ m_Name = GetSymbol( pKey );
+ Assert( m_Name != UTL_INVAL_SYMBOL );
+ m_Type = MATERIAL_VAR_TYPE_VECTOR;
+ memcpy( m_VecVal.Base(), pVal, numComps * sizeof(float) );
+ for (int i = numComps; i < 4; ++i)
+ m_VecVal[i] = 0.0f;
+
+ m_intVal = ( int ) m_VecVal[0];
+ m_nNumVectorComps = numComps;
+}
+
+CMaterialVar::CMaterialVar( IMaterial* pMaterial, const char *pKey, float val )
+{
+ Init();
+ m_pMaterial = static_cast<IMaterialInternal*>(pMaterial);
+ m_Name = GetSymbol( pKey );
+ Assert( m_Name != UTL_INVAL_SYMBOL );
+ m_Type = MATERIAL_VAR_TYPE_FLOAT;
+ m_VecVal[0] = m_VecVal[1] = m_VecVal[2] = m_VecVal[3] = val;
+ m_intVal = (int) val;
+
+}
+
+CMaterialVar::CMaterialVar( IMaterial* pMaterial, const char *pKey, int val )
+{
+ Init();
+ m_pMaterial = static_cast<IMaterialInternal*>(pMaterial);
+ m_Name = GetSymbol( pKey );
+ Assert( m_Name != UTL_INVAL_SYMBOL );
+ m_Type = MATERIAL_VAR_TYPE_INT;
+ m_VecVal[0] = m_VecVal[1] = m_VecVal[2] = m_VecVal[3] = (float) val;
+ m_intVal = val;
+}
+
+CMaterialVar::CMaterialVar( IMaterial* pMaterial, const char *pKey )
+{
+ Init();
+ m_pMaterial = static_cast<IMaterialInternal*>(pMaterial);
+ m_Name = GetSymbol( pKey );
+ Assert( m_Name != UTL_INVAL_SYMBOL );
+ m_Type = MATERIAL_VAR_TYPE_UNDEFINED;
+}
+
+
+//-----------------------------------------------------------------------------
+// destructor
+//-----------------------------------------------------------------------------
+CMaterialVar::~CMaterialVar()
+{
+ CleanUpData();
+}
+
+
+//-----------------------------------------------------------------------------
+// Cleans up material var allocated data if necessary
+//-----------------------------------------------------------------------------
+void CMaterialVar::CleanUpData()
+{
+ switch ( m_Type )
+ {
+ case MATERIAL_VAR_TYPE_STRING:
+ delete [] m_pStringVal;
+ m_pStringVal = NULL;
+ break;
+
+ case MATERIAL_VAR_TYPE_TEXTURE:
+ // garymcthack
+ if( m_pTexture && !IsTextureInternalEnvCubemap( m_pTexture ) )
+ {
+ m_pTexture->DecrementReferenceCount();
+ if ( g_bDeleteUnreferencedTexturesEnabled )
+ {
+ m_pTexture->DeleteIfUnreferenced();
+ }
+ m_pTexture = NULL;
+ }
+ break;
+
+ case MATERIAL_VAR_TYPE_MATERIAL:
+ if( m_pMaterialValue != NULL )
+ {
+ m_pMaterialValue->DecrementReferenceCount();
+ m_pMaterialValue = NULL;
+ }
+ break;
+
+ case MATERIAL_VAR_TYPE_MATRIX:
+ delete m_pMatrix;
+ m_pMatrix = NULL;
+ break;
+
+ case MATERIAL_VAR_TYPE_FOURCC:
+ delete m_pFourCC;
+ m_pFourCC = NULL;
+ break;
+
+ case MATERIAL_VAR_TYPE_VECTOR:
+ case MATERIAL_VAR_TYPE_INT:
+ case MATERIAL_VAR_TYPE_FLOAT:
+ default:
+ break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// name + type
+//-----------------------------------------------------------------------------
+MaterialVarSym_t CMaterialVar::GetNameAsSymbol() const
+{
+ return m_Name;
+}
+
+const char *CMaterialVar::GetName( ) const
+{
+ if( !m_Name.IsValid() )
+ {
+ Warning( "m_pName is NULL for CMaterialVar\n" );
+ return "";
+ }
+ return s_MaterialVarSymbols.String( m_Name );
+}
+
+
+//-----------------------------------------------------------------------------
+// Thread-safe versions
+//-----------------------------------------------------------------------------
+int CMaterialVar::GetIntValueInternal( void ) const
+{
+ CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
+ if ( pCallQueue && !m_bFakeMaterialVar )
+ {
+ if ( !s_bEnableThreadedAccess )
+ {
+ //DevMsg( 2, "Non-thread safe call to CMaterialVar %s!\n", GetName() );
+ }
+
+ if ( m_nTempIndex != 0xFF )
+ return s_pTempMaterialVar[m_nTempIndex].GetIntValueInternal();
+ }
+
+ // Set methods for float and vector update this
+ return m_intVal;
+}
+
+float CMaterialVar::GetFloatValueInternal( void ) const
+{
+ CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
+ if ( pCallQueue && !m_bFakeMaterialVar )
+ {
+ if ( !s_bEnableThreadedAccess )
+ {
+ //DevMsg( 2, "Non-thread safe call to CMaterialVar %s!\n", GetName() );
+ }
+
+ if ( m_nTempIndex != 0xFF )
+ return s_pTempMaterialVar[m_nTempIndex].GetFloatValueInternal();
+ }
+
+ return m_VecVal[0];
+}
+
+float const* CMaterialVar::GetVecValueInternal( ) const
+{
+ CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
+ if ( pCallQueue && !m_bFakeMaterialVar )
+ {
+ if ( !s_bEnableThreadedAccess )
+ {
+ //DevMsg( 2, "Non-thread safe call to CMaterialVar %s!\n", GetName() );
+ }
+
+ if ( m_nTempIndex != 0xFF )
+ return s_pTempMaterialVar[m_nTempIndex].GetVecValueInternal();
+ }
+
+ return m_VecVal.Base();
+}
+
+void CMaterialVar::GetVecValueInternal( float *val, int numcomps ) const
+{
+ CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
+ if ( pCallQueue && !m_bFakeMaterialVar )
+ {
+ if ( !s_bEnableThreadedAccess )
+ {
+ //DevMsg( 2, "Non-thread safe call to CMaterialVar %s!\n", GetName() );
+ }
+
+ if ( m_nTempIndex != 0xFF )
+ {
+ s_pTempMaterialVar[m_nTempIndex].GetVecValueInternal( val, numcomps );
+ return;
+ }
+ }
+
+ Assert( ( numcomps >0 ) && ( numcomps <= 4 ) );
+ for( int i=0 ; i < numcomps; i++ )
+ {
+ val[i] = m_VecVal[ i ];
+ }
+}
+
+int CMaterialVar::VectorSizeInternal() const
+{
+ CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
+ if ( pCallQueue && !m_bFakeMaterialVar )
+ {
+ if ( !s_bEnableThreadedAccess )
+ {
+ //DevMsg( 2, "Non-thread safe call to CMaterialVar %s!\n", GetName() );
+ }
+
+ if ( m_nTempIndex != 0xFF )
+ return s_pTempMaterialVar[m_nTempIndex].VectorSizeInternal( );
+ }
+
+ return m_nNumVectorComps;
+}
+
+// Don't want to be grabbing the dummy var and changing it's value. That usually means badness.
+#define ASSERT_NOT_DUMMY_VAR() AssertMsg( m_bFakeMaterialVar || ( V_stricmp( GetName(), "$dummyvar" ) != 0 ), "TRYING TO MODIFY $dummyvar, WHICH IS BAD, MMMKAY!" )
+
+//-----------------------------------------------------------------------------
+// float
+//-----------------------------------------------------------------------------
+void CMaterialVar::SetFloatValue( float val )
+{
+ ASSERT_NOT_DUMMY_VAR();
+ CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
+ if ( !m_bFakeMaterialVar && pCallQueue )
+ {
+ CMaterialVar *pThreadVar = AllocThreadVar();
+ if ( pThreadVar )
+ {
+ pThreadVar->SetFloatValue( val );
+ }
+ pCallQueue->QueueCall( this, &CMaterialVar::SetFloatValue, val );
+ return;
+ }
+
+ // Suppress all this if we're not actually changing anything
+ if ((m_Type == MATERIAL_VAR_TYPE_FLOAT) && (m_VecVal[0] == val))
+ return;
+
+ // Gotta flush if we've changed state and this is the current material
+ if ( !m_bFakeMaterialVar && m_pMaterial && (m_pMaterial == ( IMaterialInternal* )MaterialSystem()->GetCurrentMaterial()))
+ g_pShaderAPI->FlushBufferedPrimitives();
+
+ CleanUpData();
+ m_VecVal[0] = m_VecVal[1] = m_VecVal[2] = m_VecVal[3] = val;
+ m_intVal = (int) val;
+ m_Type = MATERIAL_VAR_TYPE_FLOAT;
+ VarChanged();
+}
+
+
+//-----------------------------------------------------------------------------
+// int
+//-----------------------------------------------------------------------------
+void CMaterialVar::SetIntValue( int val )
+{
+ ASSERT_NOT_DUMMY_VAR();
+ CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
+ if ( !m_bFakeMaterialVar && pCallQueue )
+ {
+ CMaterialVar *pThreadVar = AllocThreadVar();
+ if ( pThreadVar )
+ {
+ pThreadVar->SetIntValue( val );
+ }
+ pCallQueue->QueueCall( this, &CMaterialVar::SetIntValue, val );
+ return;
+ }
+
+ // Suppress all this if we're not actually changing anything
+ if ((m_Type == MATERIAL_VAR_TYPE_INT) && (m_intVal == val))
+ return;
+
+ // Gotta flush if we've changed state and this is the current material
+ if ( !m_bFakeMaterialVar && m_pMaterial && (m_pMaterial == ( IMaterialInternal* )MaterialSystem()->GetCurrentMaterial()))
+ g_pShaderAPI->FlushBufferedPrimitives();
+
+ CleanUpData();
+ m_intVal = val;
+ m_VecVal[0] = m_VecVal[1] = m_VecVal[2] = m_VecVal[3] = (float) val;
+ m_Type = MATERIAL_VAR_TYPE_INT;
+ VarChanged();
+}
+
+
+//-----------------------------------------------------------------------------
+// string
+//-----------------------------------------------------------------------------
+const char *CMaterialVar::GetStringValue( void ) const
+{
+ CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
+ if ( pCallQueue && !m_bFakeMaterialVar )
+ {
+ if ( !s_bEnableThreadedAccess )
+ {
+ //DevMsg( 2, "Non-thread safe call to CMaterialVar %s!\n", GetName() );
+ }
+
+ if ( m_nTempIndex != 0xFF )
+ return s_pTempMaterialVar[m_nTempIndex].GetStringValue();
+ }
+
+ switch( m_Type )
+ {
+ case MATERIAL_VAR_TYPE_STRING:
+ return m_pStringVal;
+
+ case MATERIAL_VAR_TYPE_INT:
+ Q_snprintf( s_CharBuf, sizeof( s_CharBuf ), "%d", m_intVal );
+ return s_CharBuf;
+
+ case MATERIAL_VAR_TYPE_FLOAT:
+ Q_snprintf( s_CharBuf, sizeof( s_CharBuf ), "%f", m_VecVal[0] );
+ return s_CharBuf;
+
+ case MATERIAL_VAR_TYPE_VECTOR:
+ {
+ s_CharBuf[0] = '[';
+ s_CharBuf[1] = ' ';
+ int len = 2;
+ for (int i = 0; i < m_nNumVectorComps; ++i)
+ {
+ if (len < sizeof( s_CharBuf ))
+ {
+ Q_snprintf( s_CharBuf + len, sizeof( s_CharBuf ) - len, "%f ", m_VecVal[i] );
+ len += strlen( s_CharBuf + len );
+ }
+ }
+ if (len < sizeof( s_CharBuf ) - 1)
+ {
+ s_CharBuf[len] = ']';
+ s_CharBuf[len+1] = '\0';
+ }
+ else
+ {
+ s_CharBuf[sizeof( s_CharBuf )-1] = '\0';
+ }
+ return s_CharBuf;
+ }
+
+ case MATERIAL_VAR_TYPE_MATRIX:
+ {
+ s_CharBuf[0] = '[';
+ s_CharBuf[1] = ' ';
+ int len = 2;
+ for (int i = 0; i < 4; ++i)
+ {
+ for (int j = 0; j < 4; ++j)
+ {
+ if (len < sizeof( s_CharBuf ))
+ len += Q_snprintf( s_CharBuf + len, sizeof( s_CharBuf ) - len, "%.3f ", m_pMatrix->m_Matrix[j][i] );
+ }
+ }
+ if (len < sizeof( s_CharBuf ) - 1)
+ {
+ s_CharBuf[len] = ']';
+ s_CharBuf[len+1] = '\0';
+ }
+ else
+ {
+ s_CharBuf[sizeof( s_CharBuf )-1] = '\0';
+ }
+ return s_CharBuf;
+ }
+
+ case MATERIAL_VAR_TYPE_TEXTURE:
+ // check for env_cubemap
+ if( IsTextureInternalEnvCubemap( m_pTexture ) )
+ {
+ return "env_cubemap";
+ }
+ else
+ {
+ Q_snprintf( s_CharBuf, sizeof( s_CharBuf ), "%s", m_pTexture->GetName() );
+ return s_CharBuf;
+ }
+ case MATERIAL_VAR_TYPE_MATERIAL:
+ Q_snprintf( s_CharBuf, sizeof( s_CharBuf ), "%s", ( m_pMaterialValue ? m_pMaterialValue->GetName() : "" ) );
+ return s_CharBuf;
+
+ case MATERIAL_VAR_TYPE_UNDEFINED:
+ return "<UNDEFINED>";
+
+ default:
+ Warning( "CMaterialVar::GetStringValue: Unknown material var type\n" );
+ return "";
+ }
+}
+
+void CMaterialVar::SetStringValue( const char *val )
+{
+ ASSERT_NOT_DUMMY_VAR();
+ CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
+ if ( !m_bFakeMaterialVar && pCallQueue )
+ {
+ CMaterialVar *pThreadVar = AllocThreadVar();
+ if ( pThreadVar )
+ {
+ pThreadVar->SetStringValue( val );
+ }
+ pCallQueue->QueueCall( this, &CMaterialVar::SetStringValue, CUtlEnvelope<const char *>(val) );
+ return;
+ }
+
+ // Gotta flush if we've changed state and this is the current material
+ if ( !m_bFakeMaterialVar && m_pMaterial && (m_pMaterial == ( IMaterialInternal* )MaterialSystem()->GetCurrentMaterial()))
+ g_pShaderAPI->FlushBufferedPrimitives();
+
+ CleanUpData();
+ int len = Q_strlen( val ) + 1;
+ m_pStringVal = new char[len];
+ Q_strncpy( m_pStringVal, val, len );
+ m_Type = MATERIAL_VAR_TYPE_STRING;
+ m_intVal = atoi( val );
+ m_VecVal[0] = m_VecVal[1] = m_VecVal[2] = m_VecVal[3] = atof( m_pStringVal );
+ VarChanged();
+}
+
+void CMaterialVar::SetFourCCValue( FourCC type, void *pData )
+{
+ ASSERT_NOT_DUMMY_VAR();
+ CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
+ if ( !m_bFakeMaterialVar && pCallQueue )
+ {
+ CMaterialVar *pThreadVar = AllocThreadVar();
+ if ( pThreadVar )
+ {
+ pThreadVar->SetFourCCValue( type, pData );
+ }
+ pCallQueue->QueueCall( this, &CMaterialVar::SetFourCCValue, type, pData );
+ return;
+ }
+
+ // Suppress all this if we're not actually changing anything
+ if ((m_Type == MATERIAL_VAR_TYPE_FOURCC) && m_pFourCC->m_FourCC == type && m_pFourCC->m_pFourCCData == pData )
+ return;
+
+ // Gotta flush if we've changed state and this is the current material
+ if ( !m_bFakeMaterialVar && m_pMaterial && (m_pMaterial == ( IMaterialInternal* )MaterialSystem()->GetCurrentMaterial()))
+ g_pShaderAPI->FlushBufferedPrimitives();
+
+ CleanUpData();
+
+ m_pFourCC = AllocFourCC();
+ m_pFourCC->m_FourCC = type;
+ m_pFourCC->m_pFourCCData = pData;
+ m_Type = MATERIAL_VAR_TYPE_FOURCC;
+ m_VecVal.Init();
+ m_intVal = 0;
+ VarChanged();
+}
+
+void CMaterialVar::GetFourCCValue( FourCC *type, void **ppData )
+{
+ CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
+ if ( pCallQueue && !m_bFakeMaterialVar )
+ {
+ if ( !s_bEnableThreadedAccess )
+ {
+ //DevMsg( 2, "Non-thread safe call to CMaterialVar %s!\n", GetName() );
+ }
+
+ if ( m_nTempIndex != 0xFF )
+ return s_pTempMaterialVar[m_nTempIndex].GetFourCCValue( type, ppData );
+ }
+
+ if( m_Type == MATERIAL_VAR_TYPE_FOURCC )
+ {
+ *type = m_pFourCC->m_FourCC;
+ *ppData = m_pFourCC->m_pFourCCData;
+ }
+ else
+ {
+ *type = FOURCC_UNKNOWN;
+ *ppData = 0;
+
+ static int bitchCount;
+ if( bitchCount < 10 )
+ {
+ Warning( "CMaterialVar::GetVecValue: trying to get a vec value for %s which is of type %d\n",
+ GetName(), ( int )m_Type );
+ bitchCount++;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// texture
+//-----------------------------------------------------------------------------
+ITexture *CMaterialVar::GetTextureValue( void )
+{
+ CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
+ if ( pCallQueue && !m_bFakeMaterialVar )
+ {
+ if ( !s_bEnableThreadedAccess )
+ {
+ //DevMsg( 2, "Non-thread safe call to CMaterialVar %s!\n", GetName() );
+ }
+
+ if ( m_nTempIndex != 0xFF )
+ return s_pTempMaterialVar[m_nTempIndex].GetTextureValue( );
+ }
+
+ ITexture *retVal = NULL;
+
+ if( m_pMaterial )
+ {
+ m_pMaterial->Precache();
+ }
+
+ if( m_Type == MATERIAL_VAR_TYPE_TEXTURE )
+ {
+ if ( !IsTextureInternalEnvCubemap( m_pTexture ) )
+ {
+ retVal = static_cast<ITexture *>( m_pTexture );
+ }
+ else
+ {
+ retVal = MaterialSystem()->GetLocalCubemap();
+ }
+ if( !retVal )
+ {
+ static int bitchCount = 0;
+ if( bitchCount < 10 )
+ {
+ Warning( "Invalid texture value in CMaterialVar::GetTextureValue\n" );
+ bitchCount++;
+ }
+ }
+ }
+ else
+ {
+ static int bitchCount = 0;
+ if( bitchCount < 10 )
+ {
+ Warning( "Requesting texture value from var \"%s\" which is "
+ "not a texture value (material: %s)\n", GetName(),
+ m_pMaterial ? m_pMaterial->GetName() : "NULL material" );
+ bitchCount++;
+ }
+ }
+
+ if( !retVal )
+ {
+ retVal = TextureManager()->ErrorTexture();
+ }
+ return retVal;
+}
+
+
+void CMaterialVar::SetTextureValueQueued( ITexture *texture )
+{
+ SetTextureValue( texture );
+
+ // Matches IncrementReferenceCount in SetTextureValue
+ if ( texture )
+ texture->DecrementReferenceCount();
+
+ // Debug
+ if ( mat_texture_tracking.GetBool() )
+ {
+ int iIndex = g_pTextureRefList->Find( texture );
+ Assert( iIndex != g_pTextureRefList->InvalidIndex() );
+ g_pTextureRefList->Element( iIndex )--;
+ }
+}
+
+static bool s_bInitTextureRefList = false;
+
+void CMaterialVar::SetTextureValue( ITexture *texture )
+{
+ if ( !s_bInitTextureRefList )
+ {
+ g_pTextureRefList->SetLessFunc( DefLessFunc( ITexture* ) );
+ s_bInitTextureRefList = true;
+ }
+
+ // Avoid the garymcthack in CShaderSystem::LoadCubeMap by ensuring we're not using
+ // the internal env cubemap.
+ if ( ThreadInMainThread() && !IsTextureInternalEnvCubemap( static_cast<ITextureInternal*>( texture ) ) )
+ {
+ ITextureInternal* pTexInternal = assert_cast<ITextureInternal *>( texture );
+ TextureManager()->RequestAllMipmaps( pTexInternal );
+ }
+
+ ASSERT_NOT_DUMMY_VAR();
+ CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
+ if ( !m_bFakeMaterialVar && pCallQueue )
+ {
+ // FIXME (toml): deal with reference count
+ CMaterialVar *pThreadVar = AllocThreadVar();
+ if ( pThreadVar )
+ {
+ pThreadVar->SetTextureValue( texture );
+ }
+
+ // Matches DecrementReferenceCount in SetTextureValueQueued
+ if ( texture )
+ texture->IncrementReferenceCount();
+
+ // Debug!
+ if ( mat_texture_tracking.GetBool() )
+ {
+ int iIndex = g_pTextureRefList->Find( texture );
+ if ( iIndex == g_pTextureRefList->InvalidIndex() )
+ {
+ g_pTextureRefList->Insert( texture, 1 );
+ }
+ else
+ {
+ g_pTextureRefList->Element( iIndex )++;
+ }
+ }
+
+ pCallQueue->QueueCall( this, &CMaterialVar::SetTextureValueQueued, texture );
+ return;
+ }
+
+ ITextureInternal* pTexImp = static_cast<ITextureInternal *>( texture );
+
+ // Suppress all this if we're not actually changing anything
+ if ((m_Type == MATERIAL_VAR_TYPE_TEXTURE) && (m_pTexture == pTexImp))
+ return;
+
+ // Gotta flush if we've changed state and this is the current material
+ if ( !m_bFakeMaterialVar && m_pMaterial && (m_pMaterial == MaterialSystem()->GetCurrentMaterial()))
+ g_pShaderAPI->FlushBufferedPrimitives();
+
+ if( pTexImp && !IsTextureInternalEnvCubemap( pTexImp ) )
+ {
+ pTexImp->IncrementReferenceCount();
+ }
+
+ CleanUpData();
+ m_pTexture = pTexImp;
+ m_Type = MATERIAL_VAR_TYPE_TEXTURE;
+ m_intVal = 0;
+ m_VecVal.Init();
+ VarChanged();
+}
+
+
+//-----------------------------------------------------------------------------
+// material
+//-----------------------------------------------------------------------------
+IMaterial *CMaterialVar::GetMaterialValue( void )
+{
+ CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
+ if ( pCallQueue && !m_bFakeMaterialVar )
+ {
+ if ( !s_bEnableThreadedAccess )
+ {
+ //DevMsg( 2, "Non-thread safe call to CMaterialVar %s!\n", GetName() );
+ }
+
+ if ( m_nTempIndex != 0xFF )
+ return s_pTempMaterialVar[m_nTempIndex].GetMaterialValue( );
+ }
+
+ IMaterial *retVal = NULL;
+
+ if( m_pMaterial )
+ {
+ m_pMaterial->Precache();
+ }
+
+ if( m_Type == MATERIAL_VAR_TYPE_MATERIAL )
+ {
+ retVal = static_cast<IMaterial *>( m_pMaterialValue );
+ }
+ else
+ {
+ static int bitchCount = 0;
+ if( bitchCount < 10 )
+ {
+ Warning( "Requesting material value from var \"%s\" which is "
+ "not a material value (material: %s)\n", GetName(),
+ m_pMaterial ? m_pMaterial->GetName() : "NULL material" );
+ bitchCount++;
+ }
+ }
+ return retVal;
+}
+
+void CMaterialVar::SetMaterialValue( IMaterial *pMaterial )
+{
+ ASSERT_NOT_DUMMY_VAR();
+ CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
+ if ( !m_bFakeMaterialVar && pCallQueue )
+ {
+ // FIXME (toml): deal with reference count
+ CMaterialVar *pThreadVar = AllocThreadVar();
+ if ( pThreadVar )
+ {
+ pThreadVar->SetMaterialValue( pMaterial );
+ }
+ pCallQueue->QueueCall( this, &CMaterialVar::SetMaterialValue, pMaterial );
+ return;
+ }
+
+ //HACKHACK: Only use the realtime material as the material value since converting it every time it's loaded could be forgotten, and chance of game code usage is low
+ if( pMaterial )
+ pMaterial = ((IMaterialInternal *)pMaterial)->GetRealTimeVersion();
+
+ IMaterialInternal* pMaterialImp = static_cast<IMaterialInternal *>( pMaterial );
+
+ // Suppress all this if we're not actually changing anything
+ if ((m_Type == MATERIAL_VAR_TYPE_MATERIAL) && (m_pMaterialValue == pMaterialImp))
+ return;
+
+ // Gotta flush if we've changed state and this is the current material
+ if ( !m_bFakeMaterialVar && m_pMaterial && (m_pMaterial == MaterialSystem()->GetCurrentMaterial()))
+ {
+ g_pShaderAPI->FlushBufferedPrimitives();
+ }
+
+ if( pMaterialImp != NULL )
+ {
+ pMaterialImp->IncrementReferenceCount();
+ }
+
+ CleanUpData();
+ m_pMaterialValue = pMaterialImp;
+ m_Type = MATERIAL_VAR_TYPE_MATERIAL;
+ m_intVal = 0;
+ m_VecVal.Init();
+ VarChanged();
+}
+
+
+//-----------------------------------------------------------------------------
+// Vector
+//-----------------------------------------------------------------------------
+void CMaterialVar::GetLinearVecValue( float *pVal, int numComps ) const
+{
+ CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
+ if ( pCallQueue && !m_bFakeMaterialVar )
+ {
+ if ( !s_bEnableThreadedAccess )
+ {
+ //DevMsg( 2, "Non-thread safe call to CMaterialVar %s!\n", GetName() );
+ }
+
+ if ( m_nTempIndex != 0xFF )
+ return s_pTempMaterialVar[m_nTempIndex].GetLinearVecValue( pVal, numComps );
+ }
+
+ Assert( numComps <= 4 );
+
+ switch( m_Type )
+ {
+ case MATERIAL_VAR_TYPE_VECTOR:
+ {
+ for ( int i = 0; i < numComps; ++i )
+ {
+ pVal[i] = GammaToLinear( m_VecVal[i] );
+ }
+ }
+ break;
+
+ case MATERIAL_VAR_TYPE_INT:
+ {
+ for ( int i = 0; i < numComps; ++i )
+ {
+ pVal[i] = GammaToLinear( m_intVal );
+ }
+ }
+ break;
+
+ case MATERIAL_VAR_TYPE_FLOAT:
+ {
+ for ( int i = 0; i < numComps; ++i )
+ {
+ pVal[i] = GammaToLinear( m_VecVal[0] );
+ }
+ }
+ break;
+
+ case MATERIAL_VAR_TYPE_MATRIX:
+ case MATERIAL_VAR_TYPE_UNDEFINED:
+ {
+ for ( int i = 0; i < numComps; ++i )
+ {
+ pVal[i] = 0.0f;
+ }
+ }
+ break;
+
+ default:
+ Warning( "CMaterialVar::GetVecValue: trying to get a vec value for %s which is of type %d\n",
+ GetName(), ( int )m_Type );
+ break;
+ }
+}
+
+void CMaterialVar::SetVecValueInternal( const Vector4D &vec, int nComps )
+{
+ ASSERT_NOT_DUMMY_VAR();
+ CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
+ if ( !m_bFakeMaterialVar && pCallQueue )
+ {
+ CMaterialVar *pThreadVar = AllocThreadVar();
+ if ( pThreadVar )
+ {
+ pThreadVar->SetVecValueInternal( vec, nComps );
+ }
+ pCallQueue->QueueCall( this, &CMaterialVar::SetVecValueInternal, RefToVal( vec ), nComps );
+ return;
+ }
+ // Suppress all this if we're not actually changing anything
+ if ((m_Type == MATERIAL_VAR_TYPE_VECTOR ) && (m_VecVal == vec ) )
+ return;
+ // Gotta flush if we've changed state and this is the current material
+ if ( !m_bFakeMaterialVar && m_pMaterial && (m_pMaterial == MaterialSystem()->GetCurrentMaterial()))
+ g_pShaderAPI->FlushBufferedPrimitives();
+
+ if ( m_Type != MATERIAL_VAR_TYPE_VECTOR )
+ {
+ CleanUpData();
+ m_Type = MATERIAL_VAR_TYPE_VECTOR;
+ }
+ Assert( nComps <= 4 );
+ m_nNumVectorComps = nComps;
+ memcpy( m_VecVal.Base(), vec.Base(), 4 * sizeof(float) );
+ m_intVal = ( int ) m_VecVal[0];
+
+#ifdef _DEBUG
+ for (int i = m_nNumVectorComps; i < 4; ++i )
+ Assert( m_VecVal[i] == 0.0f );
+#endif
+ VarChanged();
+}
+
+void CMaterialVar::SetVecValue( const float* pVal, int numComps )
+{
+ Vector4D vec;
+ memcpy( vec.Base(), pVal, numComps * sizeof(float) );
+ for (int i = numComps; i < 4; ++i )
+ {
+ vec[i] = 0.0f;
+ }
+ SetVecValueInternal( vec, numComps);
+}
+
+void CMaterialVar::SetVecValue( float x, float y )
+{
+ SetVecValueInternal( Vector4D( x, y, 0.0f, 0.0f ), 2 );
+}
+
+void CMaterialVar::SetVecValue( float x, float y, float z )
+{
+ SetVecValueInternal( Vector4D( x, y, z, 0.0f ), 3 );
+}
+
+void CMaterialVar::SetVecValue( float x, float y, float z, float w )
+{
+ SetVecValueInternal( Vector4D( x, y, z, w ), 4 );
+}
+
+
+void CMaterialVar::SetVecComponentValue( float fVal, int nComponent )
+{
+ ASSERT_NOT_DUMMY_VAR();
+
+#ifndef _CERT
+ // DIAF
+ if ( nComponent < 0 || nComponent > 3 )
+ {
+ Error( "Invalid vector component (%d) of variable %s referenced in material %s", nComponent, GetName(), GetOwningMaterial()->GetName() );
+ return;
+ }
+#endif
+
+ CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
+ if ( !m_bFakeMaterialVar && pCallQueue )
+ {
+ if ( s_bEnableThreadedAccess )
+ {
+ bool bInit = ( m_nTempIndex == 0xFF ) ? true : false;
+ CMaterialVar *pThreadVar = AllocThreadVar();
+ if ( pThreadVar )
+ {
+ if ( bInit )
+ {
+ pThreadVar->SetVecValue( m_VecVal.Base(), m_nNumVectorComps );
+ }
+ pThreadVar->SetVecComponentValue( fVal, nComponent );
+ }
+ }
+ pCallQueue->QueueCall( this, &CMaterialVar::SetVecComponentValue, fVal, nComponent );
+ return;
+ }
+
+ // Suppress all this if we're not actually changing anything
+ if ((m_Type == MATERIAL_VAR_TYPE_VECTOR ) && (m_VecVal[nComponent] == fVal ) )
+ return;
+
+ // Gotta flush if we've changed state and this is the current material
+ if ( !m_bFakeMaterialVar && m_pMaterial && (m_pMaterial == MaterialSystem()->GetCurrentMaterial()))
+ g_pShaderAPI->FlushBufferedPrimitives();
+
+ if ( m_Type != MATERIAL_VAR_TYPE_VECTOR )
+ {
+ CleanUpData();
+ m_Type = MATERIAL_VAR_TYPE_VECTOR;
+ }
+ Assert( nComponent <= 3 );
+
+ if( m_nNumVectorComps < nComponent )
+ {
+ //reset all undefined components to 0
+ for( int i = m_nNumVectorComps; i != nComponent; ++i )
+ m_VecVal[i] = 0.0f;
+
+ m_nNumVectorComps = nComponent;
+ }
+
+ m_VecVal[nComponent] = fVal;
+
+#ifdef _DEBUG
+ for (int i = m_nNumVectorComps; i < 4; ++i )
+ Assert( m_VecVal[i] == 0.0f );
+#endif
+ VarChanged();
+}
+
+
+//-----------------------------------------------------------------------------
+// Matrix
+//-----------------------------------------------------------------------------
+VMatrix const& CMaterialVar::GetMatrixValue( )
+{
+ CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
+ if ( pCallQueue && !m_bFakeMaterialVar )
+ {
+ if ( !s_bEnableThreadedAccess )
+ {
+ //DevMsg( 2, "Non-thread safe call to CMaterialVar %s!\n", GetName() );
+ }
+
+ if ( m_nTempIndex != 0xFF )
+ return s_pTempMaterialVar[m_nTempIndex].GetMatrixValue();
+ }
+
+ if (m_Type == MATERIAL_VAR_TYPE_MATRIX)
+ return m_pMatrix->m_Matrix;
+
+ static VMatrix identity( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 );
+ return identity;
+}
+
+void CMaterialVar::SetMatrixValue( VMatrix const& matrix )
+{
+ ASSERT_NOT_DUMMY_VAR();
+ CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
+ if ( !m_bFakeMaterialVar && pCallQueue )
+ {
+ CMaterialVar *pThreadVar = AllocThreadVar();
+ if ( pThreadVar )
+ {
+ pThreadVar->SetMatrixValue( matrix );
+ }
+ pCallQueue->QueueCall( this, &CMaterialVar::SetMatrixValue, RefToVal( matrix ) );
+ return;
+ }
+
+ // Gotta flush if we've changed state and this is the current material
+ if ( !m_bFakeMaterialVar && m_pMaterial && (m_pMaterial == MaterialSystem()->GetCurrentMaterial()))
+ g_pShaderAPI->FlushBufferedPrimitives();
+
+ CleanUpData();
+
+ // NOTE: This is necessary because the mempool MaterialVarMatrix_t uses is not threadsafe
+ m_pMatrix = new MaterialVarMatrix_t;
+
+ MatrixCopy( matrix, m_pMatrix->m_Matrix );
+ m_Type = MATERIAL_VAR_TYPE_MATRIX;
+ m_pMatrix->m_bIsIdent = matrix.IsIdentity();
+ m_VecVal.Init();
+ m_intVal = ( int ) m_VecVal[0];
+ VarChanged();
+}
+
+bool CMaterialVar::MatrixIsIdentity( void ) const
+{
+ if( m_Type != MATERIAL_VAR_TYPE_MATRIX )
+ {
+ return true;
+ }
+ return m_pMatrix->m_bIsIdent;
+}
+
+//-----------------------------------------------------------------------------
+// Undefined
+//-----------------------------------------------------------------------------
+bool CMaterialVar::IsDefined() const
+{
+ return m_Type != MATERIAL_VAR_TYPE_UNDEFINED;
+}
+
+void CMaterialVar::SetUndefined()
+{
+ ASSERT_NOT_DUMMY_VAR();
+ CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
+ if ( !m_bFakeMaterialVar && pCallQueue )
+ {
+ CMaterialVar *pThreadVar = AllocThreadVar();
+ if ( pThreadVar )
+ {
+ pThreadVar->SetUndefined( );
+ }
+ pCallQueue->QueueCall( this, &CMaterialVar::SetUndefined );
+ return;
+ }
+
+ if (m_Type == MATERIAL_VAR_TYPE_UNDEFINED)
+ return;
+
+ // Gotta flush if we've changed state and this is the current material
+ if ( !m_bFakeMaterialVar && m_pMaterial && (m_pMaterial == MaterialSystem()->GetCurrentMaterial()))
+ g_pShaderAPI->FlushBufferedPrimitives();
+
+ CleanUpData();
+ m_Type = MATERIAL_VAR_TYPE_UNDEFINED;
+ VarChanged();
+}
+
+
+//-----------------------------------------------------------------------------
+// Copy from another material var
+//-----------------------------------------------------------------------------
+void CMaterialVar::CopyFrom( IMaterialVar *pMaterialVar )
+{
+ CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
+ if ( !m_bFakeMaterialVar && pCallQueue )
+ {
+ CMaterialVar *pThreadVar = AllocThreadVar();
+ if ( pThreadVar )
+ {
+ pThreadVar->CopyFrom( pMaterialVar );
+ }
+ pCallQueue->QueueCall( this, &CMaterialVar::CopyFrom, pMaterialVar );
+ return;
+ }
+
+ switch( pMaterialVar->GetType() )
+ {
+ case MATERIAL_VAR_TYPE_FLOAT:
+ SetFloatValue( pMaterialVar->GetFloatValue() );
+ break;
+
+ case MATERIAL_VAR_TYPE_STRING:
+ SetStringValue( pMaterialVar->GetStringValue() );
+ break;
+
+ case MATERIAL_VAR_TYPE_VECTOR:
+ SetVecValue( pMaterialVar->GetVecValue(), pMaterialVar->VectorSize() );
+ break;
+
+ case MATERIAL_VAR_TYPE_TEXTURE:
+ SetTextureValue( pMaterialVar->GetTextureValue() );
+ break;
+
+ case MATERIAL_VAR_TYPE_INT:
+ SetIntValue( pMaterialVar->GetIntValue() );
+ break;
+
+ case MATERIAL_VAR_TYPE_FOURCC:
+ {
+ FourCC fourCC;
+ void *pData;
+ pMaterialVar->GetFourCCValue( &fourCC, &pData );
+ SetFourCCValue( fourCC, pData );
+ }
+ break;
+
+ case MATERIAL_VAR_TYPE_UNDEFINED:
+ SetUndefined();
+ break;
+
+ case MATERIAL_VAR_TYPE_MATRIX:
+ SetMatrixValue( pMaterialVar->GetMatrixValue() );
+ break;
+
+ case MATERIAL_VAR_TYPE_MATERIAL:
+ SetMaterialValue( pMaterialVar->GetMaterialValue() );
+ break;
+
+ default:
+ Assert(0);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// 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( const char* v )
+{
+ while (IsWhitespace(*v))
+ {
+ ++v;
+ if (IsEndline(*v))
+ return false;
+ }
+ return *v == '[' || *v == '{';
+}
+
+//-----------------------------------------------------------------------------
+// Creates a vector material var
+//-----------------------------------------------------------------------------
+static int ParseVectorFromKeyValueString( const char *pString, float vecVal[4] )
+{
+ const char* pScan = pString;
+ 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( "no ']' or '}' found in vector key in ParseVectorFromKeyValueString\n" );
+ }
+
+ // allow for vec2's, etc.
+ vecVal[i] = 0.0f;
+ break;
+ }
+
+ char* pEnd;
+
+ vecVal[i] = strtod( pScan, &pEnd );
+ if (pScan == pEnd)
+ {
+ Warning( "error parsing vector element in ParseVectorFromKeyValueString\n" );
+ 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;
+}
+
+void CMaterialVar::SetValueAutodetectType( const char *val )
+{
+ ASSERT_NOT_DUMMY_VAR();
+ int len = Q_strlen( val );
+
+ // Here, let's determine if we got a float or an int....
+ char* pIEnd; // pos where int scan ended
+ char* pFEnd; // pos where float scan ended
+ const char* pSEnd = val + len ; // pos where token ends
+
+ int ival = strtol( val, &pIEnd, 10 );
+ float fval = (float)strtod( val, &pFEnd );
+
+ if ( ( pFEnd > pIEnd ) && ( pFEnd == pSEnd ) )
+ {
+ SetFloatValue( fval );
+ return;
+ }
+
+ if ( pIEnd == pSEnd )
+ {
+ SetIntValue( ival );
+ return;
+ }
+
+ // Isn't an int or a float.
+
+ // Is it a matrix?
+
+ VMatrix mat;
+ int count = sscanf( val, " [ %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)
+ {
+ SetMatrixValue( mat );
+ return;
+ }
+
+ Vector2D scale, center;
+ float angle;
+ Vector2D translation;
+ count = sscanf( val, " 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)
+ {
+ 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 );
+ SetMatrixValue( mat );
+ return;
+ }
+
+ if( IsVector( val ) )
+ {
+ float vecVal[4];
+ int nDim = ParseVectorFromKeyValueString( val, vecVal );
+ if ( nDim > 0 )
+ {
+ SetVecValue( vecVal, nDim );
+ return;
+ }
+ }
+
+ SetStringValue( val );
+}