Counter Strike : Global Offensive Source Code
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.
 
 
 
 
 
 

446 lines
11 KiB

//========= Copyright © 1996-2013, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
//#include "cbase.h"
#include "custom_material.h"
#include "composite_texture.h"
#include "materialsystem/base_visuals_data_processor.h"
#include "materialsystem_global.h"
#include "keyvalues.h"
#include "tier0/vprof.h"
#ifndef DEDICATED
#include "filesystem.h"
#endif
//#define DEBUG_CUSTOMMATERIALS
int CCustomMaterial::m_nMaterialCount = 0;
//
// Material applied to the item to give it a custom look
//
CCustomMaterial::CCustomMaterial( KeyValues *pKeyValues )
: m_bValid( false )
, m_nModelMaterialIndex( -1 )
, m_szBaseMaterialName( NULL)
{
// we need to copy this, because the passed in one was allocated outside materialsystem.dll
m_pVMTKeyValues = ( pKeyValues != NULL ) ? pKeyValues->MakeCopy() : NULL;
}
CCustomMaterial::~CCustomMaterial()
{
Shutdown();
}
void CCustomMaterial::AddTexture( ICompositeTexture * pTextureInterface )
{
CCompositeTexture *pTexture = dynamic_cast< CCompositeTexture * >( pTextureInterface );
if ( pTexture )
{
m_pTextures.AddToTail( pTexture );
pTexture->AddRef();
}
}
ICompositeTexture *CCustomMaterial::GetTexture( int nIndex )
{
if ( nIndex >= 0 && nIndex < m_pTextures.Count() )
{
return m_pTextures[ nIndex ];
}
return NULL;
}
bool CCustomMaterial::CheckRegenerate( int nSize )
{
if ( m_pTextures.Count() > 0 )
{
return nSize != ( 2048 >> m_pTextures[ 0 ]->Size() );
}
return false;
}
void CCustomMaterial::SetBaseMaterialName( const char* szName )
{
Assert( !m_szBaseMaterialName );
m_szBaseMaterialName = strdup( szName );
}
void CCustomMaterial::Shutdown()
{
DestroyProceduralMaterial();
if ( m_pVMTKeyValues != NULL )
{
m_pVMTKeyValues->deleteThis();
m_pVMTKeyValues = NULL;
}
for ( int i = 0; i < m_pTextures.Count(); i++ )
{
if ( m_pTextures[ i ] )
{
m_pTextures[ i ]->Release();
m_pTextures[ i ] = NULL;
}
}
if ( m_szBaseMaterialName )
free( ( void * ) m_szBaseMaterialName );
m_pTextures.RemoveAll();
}
void CCustomMaterial::Usage( int& nTextures, int& nBackingTextures )
{
for ( int i = 0; i < m_pTextures.Count(); i++ )
{
m_pTextures[ i ]->Usage( nTextures, nBackingTextures );
}
}
bool CCustomMaterial::TexturesReady() const
{
if ( m_pTextures.Count() == 0 )
{
return false;
}
for ( int i = 0; i < m_pTextures.Count(); i++ )
{
if ( !m_pTextures[ i ]->IsReady() )
{
return false;
}
}
return true;
}
void CCustomMaterial::RegenerateTextures()
{
for ( int i = 0; i < m_pTextures.Count(); i++ )
{
m_pTextures[ i ]->ForceRegenerate();
}
}
bool CCustomMaterial::ShouldRelease()
{
return ( ( GetRefCount() == 1 ) && IsValid() );
}
bool CCustomMaterial::Compare( const CUtlVector< SCompositeTextureInfo > &vecTextures )
{
if ( m_pTextures.Count() == vecTextures.Count() )
{
FOR_EACH_VEC( m_pTextures, i )
{
if ( !m_pTextures[ i ]->Compare( vecTextures[ i ] ) )
{
return false;
}
}
return true;
}
return false;
}
void CCustomMaterial::Finalize()
{
char szUniqueMaterialName[ 256 ];
V_snprintf( szUniqueMaterialName, sizeof( szUniqueMaterialName ), "cs_custom_material_%i", m_nMaterialCount++ );
DestroyProceduralMaterial();
if ( m_pVMTKeyValues != NULL )
{
CreateProceduralMaterial( szUniqueMaterialName, m_pVMTKeyValues->MakeCopy() );
}
else
{
// Create default material key values
m_pVMTKeyValues = new KeyValues( "VertexLitGeneric" );
m_pVMTKeyValues->SetInt( "$phongalbedoboost", 45 );
m_pVMTKeyValues->SetInt( "$phong", 1 );
m_pVMTKeyValues->SetFloat( "$phongboost", 0.4 );
m_pVMTKeyValues->SetString( "$phongfresnelranges", "[.8 .8 1]" );
m_pVMTKeyValues->SetInt( "$basemapalphaphongmask", 1 );
m_pVMTKeyValues->SetString( "$envmap", "env_cubemap" );
m_pVMTKeyValues->SetInt( "$envmapfresnel", 1 );
m_pVMTKeyValues->SetString( "$envmapFresnelMinMaxExp", "[0 5 .4]" );
m_pVMTKeyValues->SetString( "$envmaptint", "[.02 .02 .02]" );
m_pVMTKeyValues->SetInt( "$phongalbedotint", 1 );
CreateProceduralMaterial( szUniqueMaterialName, m_pVMTKeyValues->MakeCopy() );
}
}
void CCustomMaterial::CreateProceduralMaterial( const char *pMaterialName, KeyValues *pVMTKeyValues )
{
// Replace parts of existing material key values
// loop over m_pTextures and set the material params
for ( int i = 0; i < m_pTextures.Count(); i++ )
{
pVMTKeyValues->SetString( g_szMaterialParamNames[ m_pTextures[ i ]->GetMaterialParamNameId() ], m_pTextures[ i ]->GetName() );
}
m_Material.Init( pMaterialName, pVMTKeyValues );
m_Material->Refresh();
}
void CCustomMaterial::DestroyProceduralMaterial()
{
m_Material.Shutdown( true );
}
//
// global custom material manager
// the game uses this to make/get a custom material
//
CCustomMaterialManager::CCustomMaterialManager()
{
#ifndef DEDICATED
m_pCustomMaterials.EnsureCapacity( 128 );
m_mapVMTKeyValues.SetLessFunc( StringLessThan );
#endif
}
CCustomMaterialManager::~CCustomMaterialManager()
{
}
// this is called at the end of each frame
bool ProcessDynamicCustomMaterialGenerator()
{
return MaterialSystem()->GetCustomMaterialManager()->Process();
}
bool CCustomMaterialManager::Init()
{
#ifndef DEDICATED
MaterialSystem()->AddEndFramePriorToNextContextFunc( ::ProcessDynamicCustomMaterialGenerator );
KeyValues *pVMTCache = new KeyValues( "VMTCache" );
if ( pVMTCache->LoadFromFile( g_pFullFileSystem, "resource/vmtcache.txt", "MOD" ) )
{
KeyValues *pValue = pVMTCache->GetFirstValue();
while ( pValue )
{
const char *pszVMTToCache = pValue->GetString();
if ( pszVMTToCache && pszVMTToCache[0] != 0 )
{
KeyValues *pVMTKeyValues = new KeyValues( "VertexLitGeneric" );
bool bVMTExists = pVMTKeyValues->LoadFromFile( g_pFullFileSystem , pszVMTToCache, "MOD" );
if ( bVMTExists )
{
m_mapVMTKeyValues.Insert( pszVMTToCache, pVMTKeyValues );
DevMsg( "CustomMaterialManager: Cached KeyValues %s.\n", pszVMTToCache );
}
else
{
DevMsg( "Failed to load VMT: %s\n", pszVMTToCache );
}
}
pValue = pValue->GetNextValue();
}
}
#endif
return true;
}
void CCustomMaterialManager::Shutdown()
{
#ifndef DEDICATED
MaterialSystem()->RemoveEndFramePriorToNextContextFunc( ::ProcessDynamicCustomMaterialGenerator );
FOR_EACH_MAP_FAST( m_mapVMTKeyValues, i )
{
m_mapVMTKeyValues[ i ]->deleteThis();
}
m_mapVMTKeyValues.Purge();
DestroyMaterials();
#endif
}
bool CCustomMaterialManager::GetVMTKeyValues( const char *pszVMTName, KeyValues **ppVMTKeyValues )
{
// lookup VMT KeyValues in container
int nIndex = m_mapVMTKeyValues.Find( pszVMTName );
if ( nIndex != m_mapVMTKeyValues.InvalidIndex() )
{
// need to return a copy here, since we don't want the caller to change our copy.
*ppVMTKeyValues = m_mapVMTKeyValues[ nIndex ]->MakeCopy();
return true;
}
return false;
}
// handles finalizing materials once all the textures are ready, swapping materials that are pending swap and ready, and cleans up materials that are no longer used
bool CCustomMaterialManager::Process()
{
//TM_ZONE( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
for ( int i = 0; i < m_pCustomMaterials.Count(); ++i )
{
if ( m_pCustomMaterials[ i ] &&
m_pCustomMaterials[ i ]->TexturesReady() &&
!m_pCustomMaterials[ i ]->IsValid() )
{
m_pCustomMaterials[ i ]->Finalize();
m_pCustomMaterials[ i ]->SetValid( true );
#ifdef DEBUG_CUSTOMMATERIALS
DevMsg( "Finalized custom material: %s \n", m_pCustomMaterials[ i ]->GetMaterial() ? m_pCustomMaterials[ i ]->GetMaterial()->GetName() : "*unknown*" );
#endif
}
else if ( !m_pCustomMaterials[ i ]->TexturesReady() &&
m_pCustomMaterials[ i ]->IsValid() )
{
// this happens when textures regenerate because of mat_picmip changes
m_pCustomMaterials[ i ]->SetValid( false );
}
}
for ( int i = m_pCustomMaterials.Count() - 1; i >= 0; i-- )
{
if ( m_pCustomMaterials[ i ] )
{
// clean up materials that are no longer used (we are the only reference)
if ( m_pCustomMaterials[ i ]->ShouldRelease() )
{
#ifdef DEBUG_CUSTOMMATERIALS
DevMsg( "Releasing custom material: %s \n", m_pCustomMaterials[ i ]->GetMaterial()->GetName() );
#endif
m_pCustomMaterials[ i ]->Release();
m_pCustomMaterials[ i ] = NULL;
m_pCustomMaterials.Remove( i );
}
}
}
return false;
}
ICustomMaterial * CCustomMaterialManager::GetOrCreateCustomMaterial( KeyValues *pKeyValues, const CUtlVector< SCompositeTextureInfo > &vecTextureInfos, bool bIgnorePicMip /*= false */ )
{
#if defined( DEDICATED ) || defined( DISABLE_CUSTOM_MATERIAL_GENERATION )
return NULL;
#endif
TM_MESSAGE( TELEMETRY_LEVEL0, TMMF_ICON_NOTE | TMMF_SEVERITY_WARNING, "%s %d", __FUNCTION__, vecTextureInfos[0].m_size );
for ( int i = 0; i < m_pCustomMaterials.Count(); ++i )
{
CCustomMaterial *pMaterial = m_pCustomMaterials[ i ];
if ( pMaterial && pMaterial->Compare( vecTextureInfos ) )
{
return pMaterial;
}
}
CCustomMaterial *pMaterial = new CCustomMaterial( pKeyValues );
pMaterial->SetValid( false );
pMaterial->SetBaseMaterialName( vecTextureInfos[ 0 ].m_pVisualsDataProcessor->GetOriginalMaterialBaseName() );
FOR_EACH_VEC( vecTextureInfos, i )
{
ICompositeTexture *pTexture = g_pMaterialSystem->GetCompositeTextureGenerator()->GetCompositeTexture( vecTextureInfos[ i ] );
if ( pTexture )
{
pMaterial->AddTexture( pTexture );
}
else
{
AssertMsg( pTexture != NULL, "Unable to get/create composite texture for custom material!" );
pMaterial->Release();
return NULL;
}
}
m_pCustomMaterials.AddToTail( pMaterial );
#ifdef DEBUG_CUSTOMMATERIALS
DevMsg( "Created custom material for: %s \n", pVisualsDataProcessor->GetOriginalMaterialName() );
#endif
// The material may not be complete yet, but it will be completed over the next few frames via Process()
return pMaterial;
}
void CCustomMaterialManager::ReloadAllMaterials( const CCommand &args )
{
for ( int i = 0; i < m_pCustomMaterials.Count(); ++i )
{
CCustomMaterial *pMaterial = m_pCustomMaterials[ i ];
if ( pMaterial )
{
pMaterial->RegenerateTextures();
}
}
}
void CCustomMaterialManager::ReloadVmtCache( const CCommand &args )
{
}
int CCustomMaterialManager::DebugGetNumActiveCustomMaterials( )
{
int nActive = 0;
for ( int i = 0; i < m_pCustomMaterials.Count(); ++i )
{
CCustomMaterial *pMaterial = m_pCustomMaterials[ i ];
if ( pMaterial && pMaterial->IsValid() )
{
nActive++;
}
}
return nActive;
}
void CCustomMaterialManager::Usage( const CCommand &args )
{
int nTextures = 0;
int nBackingTextures = 0;
int nActive = 0;
for ( int i = 0; i < m_pCustomMaterials.Count(); ++i )
{
CCustomMaterial *pMaterial = m_pCustomMaterials[ i ];
if ( pMaterial && pMaterial->IsValid() )
{
int nBackingTexture = 0;
pMaterial->Usage( nTextures, nBackingTexture );
Msg( "%2d. %s, %d \n", i, pMaterial->GetMaterial() ? pMaterial->GetMaterial()->GetName() : "*pending*", nBackingTexture );
nBackingTextures += nBackingTexture;
nActive++;
}
}
Msg( "Custom Weapon Material Usage: Total: %d Active: %d Textures: %d BackingTextures: %d \n", m_pCustomMaterials.Count(), nActive, nTextures, nBackingTextures );
}
void CCustomMaterialManager::DestroyMaterials( void )
{
for ( int i = 0; i < m_pCustomMaterials.Count(); ++i )
{
if ( m_pCustomMaterials[ i ] )
{
m_pCustomMaterials[ i ]->Release();
m_pCustomMaterials[ i ] = NULL;
}
}
m_pCustomMaterials.RemoveAll();
}