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