|
|
//========= 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(); }
|