diff options
Diffstat (limited to 'materialsystem/cmaterialvar.cpp')
| -rw-r--r-- | materialsystem/cmaterialvar.cpp | 1631 |
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", + ¢er.x, ¢er.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 ); +} |