source-engine/materialsystem/cmaterialvar.cpp

1615 lines
42 KiB
C++

//========= 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
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 )
{
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:
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 ( strcmp(m_pTexture->GetName(), "bitch_cubemap") == 0 )
retVal = MaterialSystem()->GetLocalCubemap();
else
retVal = static_cast<ITexture *>( m_pTexture );
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() )
{
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 )
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 );
}