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.
 
 
 
 
 
 

633 lines
20 KiB

//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "gameuidynamictextures.h"
#include "gamelayer.h"
#include "gamerect.h"
#include "tier1/utlstring.h"
#include "tier1/utlstringmap.h"
#include "tier1/utlbuffer.h"
#include "gameuisystemmgr.h"
#include "tier1/fmtstr.h"
#include "materialsystem/imaterial.h"
#include "materialsystem/imaterialvar.h"
#include "materialsystem/imaterialsystem.h"
#include "materialsystem/imesh.h"
#include "rendersystem/irenderdevice.h"
#include "rendersystem/irendercontext.h"
#include "tier1/keyvalues.h"
#include "materialsystem/IMaterialProxy.h"
#include "materialsystem/imaterialproxyfactory.h"
#include "gameuisystemmgr.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define GAMEUI_DYNAMIC_TEXTURE_SHEET_WIDTH 2048
#define GAMEUI_DYNAMIC_TEXTURE_SHEET_HEIGHT 2048
//-----------------------------------------------------------------------------
// A material proxy that resets the base texture to use the dynamic texture
//-----------------------------------------------------------------------------
class CGameControlsProxy : public IMaterialProxy
{
public:
CGameControlsProxy();
virtual ~CGameControlsProxy(){};
virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
virtual void OnBind( void *pProxyData );
virtual void Release( void )
{
delete this;
}
virtual IMaterial *GetMaterial();
private:
IMaterialVar* m_BaseTextureVar;
};
CGameControlsProxy::CGameControlsProxy(): m_BaseTextureVar( NULL )
{
}
bool CGameControlsProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
{
bool bFoundVar;
m_BaseTextureVar = pMaterial->FindVar( "$basetexture", &bFoundVar, false );
return bFoundVar;
}
void CGameControlsProxy::OnBind( void *pProxyData )
{
const char *pBaseTextureName = ( const char * )pProxyData;
ITexture *pTexture = g_pMaterialSystem->FindTexture( pBaseTextureName, TEXTURE_GROUP_OTHER, true );
m_BaseTextureVar->SetTextureValue( pTexture );
}
IMaterial *CGameControlsProxy::GetMaterial()
{
return m_BaseTextureVar->GetOwningMaterial();
}
//-----------------------------------------------------------------------------
// Factory to create dynamic material. ( Return in this case. )
//-----------------------------------------------------------------------------
class CMaterialProxyFactory : public IMaterialProxyFactory
{
public:
IMaterialProxy *CreateProxy( const char *proxyName );
void DeleteProxy( IMaterialProxy *pProxy );
CreateInterfaceFn GetFactory();
};
static CMaterialProxyFactory s_DynamicMaterialProxyFactory;
IMaterialProxy *CMaterialProxyFactory::CreateProxy( const char *proxyName )
{
if ( Q_strcmp( proxyName, "GameControlsProxy" ) == NULL )
{
return static_cast< IMaterialProxy * >( new CGameControlsProxy );
}
return NULL;
}
void CMaterialProxyFactory::DeleteProxy( IMaterialProxy *pProxy )
{
}
CreateInterfaceFn CMaterialProxyFactory::GetFactory()
{
return Sys_GetFactoryThis();
}
//-----------------------------------------------------------------------------
// Constructor / Destructor.
//-----------------------------------------------------------------------------
CGameUIDynamicTextures::CGameUIDynamicTextures()
{
m_pDynamicTexturePacker = NULL;
m_RenderMaterial = NULL;
m_bRegenerate = false;
}
CGameUIDynamicTextures::~CGameUIDynamicTextures()
{
Shutdown();
}
//-----------------------------------------------------------------------------
// Init any render targets needed by the UI.
//-----------------------------------------------------------------------------
void CGameUIDynamicTextures::InitRenderTargets()
{
if ( !m_TexturePage.IsValid() )
{
m_TexturePage.InitRenderTarget( GAMEUI_DYNAMIC_TEXTURE_SHEET_WIDTH, GAMEUI_DYNAMIC_TEXTURE_SHEET_HEIGHT,
RT_SIZE_NO_CHANGE, IMAGE_FORMAT_ARGB8888,
MATERIAL_RT_DEPTH_NONE, false, "_rt_DynamicUI" );
}
if ( m_TexturePage.IsValid() )
{
int nSheetWidth = m_TexturePage->GetActualWidth();
int nSheetHeight = m_TexturePage->GetActualHeight();
m_pDynamicTexturePacker = new CTexturePacker( nSheetWidth, nSheetHeight, IsGameConsole() ? 0 : 1 );
KeyValues *pVMTKeyValues = new KeyValues( "GameControls" );
pVMTKeyValues->SetString( "$basetexture", "_rt_DynamicUI" );
CMaterialReference material;
// Material names must be different
CFmtStr materialName;
materialName.sprintf( "dynamictx_%s", "_rt_DynamicUI" );
material.Init( materialName, TEXTURE_GROUP_OTHER, pVMTKeyValues );
material->Refresh();
{
ImageAliasData_t imageData;
imageData.m_Width = nSheetWidth;
imageData.m_Height = nSheetHeight;
imageData.m_Material = material;
m_ImageAliasMap[ "_rt_DynamicUI" ] = imageData;
}
{
CTextureReference pErrorTexture;
pErrorTexture.Init( "error", TEXTURE_GROUP_OTHER, true );
ImageAliasData_t imageData;
imageData.m_Width = pErrorTexture->GetActualWidth();
imageData.m_Height = pErrorTexture->GetActualHeight();
imageData.m_szBaseTextureName = "error";
imageData.m_Material = NULL;
m_ImageAliasMap[ "errorImageAlias" ] = imageData;
}
pVMTKeyValues = new KeyValues( "GameControls" );
pVMTKeyValues->SetInt( "$ignorez", 1 );
KeyValues *pProxies = new KeyValues( "Proxies" );
pProxies->AddSubKey( new KeyValues( "GameControlsProxy" ) );
pVMTKeyValues->AddSubKey( pProxies );
m_RenderMaterial.Init( "dynamic_render_texture", TEXTURE_GROUP_OTHER, pVMTKeyValues );
m_RenderMaterial->Refresh();
g_pGameUISystemMgrImpl->InitImageAlias( "defaultImageAlias" );
g_pGameUISystemMgrImpl->LoadImageAliasTexture( "defaultImageAlias", "vguiedit/pixel" );
}
}
IMaterialProxy *CGameUIDynamicTextures::CreateProxy( const char *proxyName )
{
return s_DynamicMaterialProxyFactory.CreateProxy( proxyName );
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CGameUIDynamicTextures::Shutdown()
{
m_RenderMaterial.Shutdown();
m_TexturePage.Shutdown();
if ( m_pDynamicTexturePacker )
{
delete m_pDynamicTexturePacker;
}
m_pDynamicTexturePacker = NULL;
m_RenderMaterial = NULL;
m_bRegenerate = false;
}
void CGameUIDynamicTextures::SetImageEntry( const char *pEntryName, ImageAliasData_t &imageData )
{
m_ImageAliasMap[ pEntryName ] = imageData;
}
//-----------------------------------------------------------------------------
// Associate this image alias name with a .vtf texture.
//-----------------------------------------------------------------------------
void CGameUIDynamicTextures::LoadImageAlias( const char *pAlias, const char *pBaseTextureName )
{
if ( !pAlias || !*pAlias )
return;
if ( !pBaseTextureName || !*pBaseTextureName )
return;
CFmtStr texturePath;
texturePath.sprintf( "materials\\%s.vtf", pBaseTextureName );
if ( !g_pFullFileSystem->FileExists( texturePath.Access(), IsGameConsole() ? "MOD" : "GAME" ) )
{
Warning( "Unable to find game ui dynamic texture \"%s\"\n", texturePath.Access() );
}
if ( Q_strcmp( pBaseTextureName, "" ) == 0 )
{
ImageAliasData_t *pImageData = GetImageAliasData( "errorImageAlias" );
ImageAliasData_t imageData;
imageData.Init();
imageData.m_XPos = pImageData->m_XPos;
imageData.m_YPos = pImageData->m_YPos;
imageData.m_Width = pImageData->m_Width;
imageData.m_Height = pImageData->m_Height;
imageData.m_szBaseTextureName = pImageData->m_szBaseTextureName;
imageData.m_Material = pImageData->m_Material;
imageData.m_bIsInSheet = pImageData->m_bIsInSheet;
imageData.m_nNodeIndex = pImageData->m_nNodeIndex;
SetImageEntry( pAlias, imageData );
return;
}
Msg( "Loading Alias %s Texture %s\n", pAlias, pBaseTextureName );
CTextureReference pTexture;
pTexture.Init( pBaseTextureName, TEXTURE_GROUP_OTHER, true );
ImageAliasData_t *pImageData = GetImageAliasData( pAlias );
int nodeIndex = -1;
if ( m_pDynamicTexturePacker )
{
if ( !IsErrorImageAliasData( pImageData ) )
{
if ( pImageData->m_nNodeIndex != -1 )
{
// We already had something packed in there for this alias.
// Remove the old alias texture
m_pDynamicTexturePacker->RemoveRect( pImageData->m_nNodeIndex );
}
// Set up new imagedata values
pImageData->m_Width = pTexture->GetActualWidth();
pImageData->m_Height = pTexture->GetActualHeight();
pImageData->m_szBaseTextureName = pBaseTextureName;
}
else
{
ImageAliasData_t imageData;
imageData.m_Width = pTexture->GetActualWidth();
imageData.m_Height = pTexture->GetActualHeight();
imageData.m_szBaseTextureName = pBaseTextureName;
imageData.m_nRefCount = pImageData->m_nRefCount;
SetImageEntry( pAlias, imageData );
pImageData = GetImageAliasData( pAlias );
Assert( !IsErrorImageAliasData( pImageData ) );
}
int nSheetWidth = 0;
int nSheetHeight = 0;
GetDynamicSheetSize( nSheetWidth, nSheetHeight );
Assert( nSheetWidth != 0 );
Assert( nSheetHeight != 0 );
// If our texture is huge in some way, don't bother putting it in the packed texture.
if ( ( pTexture->GetActualWidth() << 1 ) < nSheetWidth || ( pTexture->GetActualHeight() << 1 ) < nSheetHeight )
{
// Put this texture into the packed render target texture.
Rect_t texRect;
texRect.x = 0;
texRect.y = 0;
texRect.width = pTexture->GetActualWidth();
texRect.height = pTexture->GetActualHeight();
nodeIndex = m_pDynamicTexturePacker->InsertRect( texRect );
}
}
// For debugging this will make no packing happen. Each texture is one draw call
//nodeIndex = -1;
pImageData->m_nNodeIndex = nodeIndex;
LoadImageAliasTexture( pAlias, pBaseTextureName );
}
//-----------------------------------------------------------------------------
// Release the entry for this alias in the packer.
//-----------------------------------------------------------------------------
void CGameUIDynamicTextures::ReleaseImageAlias( const char *pAlias )
{
if ( Q_strcmp( pAlias, "_rt_DynamicUI" ) == 0 )
return;
if ( Q_strcmp( pAlias, "errorImageAlias" ) == 0 )
return;
if ( Q_strcmp( pAlias, "defaultImageAlias" ) == 0 )
return;
ImageAliasData_t *pImageData = GetImageAliasData( pAlias );
if ( pImageData )
{
Assert( pImageData->m_nRefCount > 0 );
pImageData->m_nRefCount--;
if ( pImageData->m_nRefCount == 0 )
{
if ( pImageData->m_bIsInSheet )
{
Msg( "Releasing Alias %s\n", pAlias );
m_pDynamicTexturePacker->RemoveRect( pImageData->m_nNodeIndex );
}
pImageData->Init();
}
}
}
//-----------------------------------------------------------------------------
// Associate this image alias name with a .vtf texture.
// This fxn loads the texture into the GameControls shader and makes a material
// for you
//-----------------------------------------------------------------------------
void CGameUIDynamicTextures::LoadImageAliasTexture( const char *pAlias, const char *pBaseTextureName )
{
if ( !pBaseTextureName || !*pBaseTextureName )
return;
CTextureReference pTexture;
pTexture.Init( pBaseTextureName, TEXTURE_GROUP_OTHER, true );
ImageAliasData_t *pImageData = GetImageAliasData( pAlias );
// Now set up the material for the new imagedata.
if ( pImageData->m_nNodeIndex == -1 )// it won't fit in the map, give it its own material, it is its own draw call.
{
// Material names must be different
CUtlVector<char *> words;
V_SplitString( pImageData->m_szBaseTextureName, "/", words );
CFmtStr materialName;
materialName.sprintf( "dynamictx_%s", words[words.Count()-1] );
KeyValues *pVMTKeyValues = new KeyValues( "GameControls" );
pVMTKeyValues->SetString( "$basetexture", pImageData->m_szBaseTextureName );
CMaterialReference material;
material.Init( materialName, TEXTURE_GROUP_OTHER, pVMTKeyValues );
material->Refresh();
pImageData->m_Material = material;
pImageData->m_bIsInSheet = false;
}
else // Do this if it fit in the packed texture.
{
if ( !m_RenderMaterial )
return;
pImageData->m_Material.Init( GetImageAliasMaterial( "_rt_DynamicUI" ) );
Assert( pImageData->m_Material );
pImageData->m_bIsInSheet = true;
const CTexturePacker::TreeEntry_t &newEntry = m_pDynamicTexturePacker->GetEntry( pImageData->m_nNodeIndex );
Assert( newEntry.rc.width == pImageData->m_Width );
Assert( newEntry.rc.height == pImageData->m_Height );
pImageData->m_XPos = newEntry.rc.x;
pImageData->m_YPos = newEntry.rc.y;
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
pRenderContext->PushRenderTargetAndViewport( m_TexturePage, NULL, pImageData->m_XPos, pImageData->m_YPos, pImageData->m_Width, pImageData->m_Height );
pRenderContext->MatrixMode( MATERIAL_PROJECTION );
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
pRenderContext->Scale( 1, -1, 1 );
float flPixelOffsetX = 0.5f;
float flPixelOffsetY = 0.5f;
pRenderContext->Ortho( flPixelOffsetX, flPixelOffsetY, pImageData->m_Width + flPixelOffsetX, pImageData->m_Height + flPixelOffsetY, -1.0f, 1.0f );
//pRenderContext->Ortho( 0, 0, pImageData->m_Width, pImageData->m_Height, -1.0f, 1.0f );
// Clear to random color. Useful for testing.
//pRenderContext->ClearColor3ub( rand()%256, rand()%256, rand()%256 );
pRenderContext->ClearColor4ub( 0, 0, 0, 255 );
pRenderContext->ClearBuffers( true, false );
// make sure there is no translation and rotation laying around
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
pRenderContext->MatrixMode( MATERIAL_VIEW );
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
pRenderContext->Bind( m_RenderMaterial, (void *)pImageData->m_szBaseTextureName.Get() );
int x = 0;
int y = 0;
int wide = pImageData->m_Width;
int tall = pImageData->m_Height;
unsigned char drawcolor[4];
drawcolor[0] = 255;
drawcolor[1] = 255;
drawcolor[2] = 255;
drawcolor[3] = 255;
IMesh *pMesh = pRenderContext->GetDynamicMesh( true );
if ( pMesh )
{
CMeshBuilder meshBuilder;
meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
meshBuilder.Position3f( x, y, 0 );
meshBuilder.Color4ubv( drawcolor );
meshBuilder.TexCoord2f( 0, 0, 0 );
meshBuilder.AdvanceVertexF<VTX_HAVEPOS | VTX_HAVECOLOR, 1>();
meshBuilder.Position3f( wide, y, 0 );
meshBuilder.Color4ubv( drawcolor );
meshBuilder.TexCoord2f( 0, 1, 0 );
meshBuilder.AdvanceVertexF<VTX_HAVEPOS | VTX_HAVECOLOR, 1>();
meshBuilder.Position3f( wide, tall, 0 );
meshBuilder.Color4ubv( drawcolor );
meshBuilder.TexCoord2f( 0, 1, 1 );
meshBuilder.AdvanceVertexF<VTX_HAVEPOS | VTX_HAVECOLOR, 1>();
meshBuilder.Position3f( x, tall, 0 );
meshBuilder.Color4ubv( drawcolor );
meshBuilder.TexCoord2f( 0, 0, 1 );
meshBuilder.AdvanceVertexF<VTX_HAVEPOS | VTX_HAVECOLOR, 1>();
meshBuilder.End();
pMesh->Draw();
}
// Restore the matrices
pRenderContext->MatrixMode( MATERIAL_PROJECTION );
pRenderContext->PopMatrix();
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PopMatrix();
pRenderContext->MatrixMode( MATERIAL_VIEW );
pRenderContext->PopMatrix();
pRenderContext->PopRenderTargetAndViewport();
}
}
//-----------------------------------------------------------------------------
// Return the material bound to this image alias.
// If the texture was not found you will see the purple and black checkerboard
// error texture.
//-----------------------------------------------------------------------------
IMaterial *CGameUIDynamicTextures::GetImageAliasMaterial( const char *pAlias )
{
if ( m_ImageAliasMap.Defined( pAlias ) )
{
return m_ImageAliasMap[pAlias].m_Material;
}
return m_ImageAliasMap[ "errorImageAlias" ].m_Material;
}
//-----------------------------------------------------------------------------
// Return the data bound to this image alias.
// If the alias was not found you will see the purple and black checkerboard
// error texture.
//-----------------------------------------------------------------------------
ImageAliasData_t *CGameUIDynamicTextures::GetImageAliasData( const char *pAlias )
{
if ( m_ImageAliasMap.Defined( pAlias ) )
{
return &m_ImageAliasMap[pAlias];
}
return &m_ImageAliasMap[ "errorImageAlias" ];
}
//-----------------------------------------------------------------------------
// Return true if this data is the error alias
//-----------------------------------------------------------------------------
bool CGameUIDynamicTextures::IsErrorImageAliasData( ImageAliasData_t *pData )
{
return ( &m_ImageAliasMap[ "errorImageAlias" ] ) == pData;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CGameUIDynamicTextures::GetDynamicSheetSize( int &nWidth, int &nHeight )
{
nWidth = m_TexturePage->GetActualWidth();
nHeight = m_TexturePage->GetActualHeight();
}
//-----------------------------------------------------------------------------
// Draw the dynamic texture associated with this alias at x, y
// It is drawn full size. This fxn is modeled off of CGameUISystemSurface::DrawFontTexture()
//-----------------------------------------------------------------------------
void CGameUIDynamicTextures::DrawDynamicTexture( const char *pAlias, int x, int y )
{
if ( !pAlias || !*pAlias )
return;
ImageAliasData_t *pImageData = GetImageAliasData( pAlias );
int wide = 0;
int tall = 0;
if ( !pImageData->m_bIsInSheet )
{
wide = pImageData->m_Width;
tall = pImageData->m_Height;
}
else
{
GetDynamicSheetSize( wide, tall );
}
color32 drawcolor;
drawcolor.r = 255;
drawcolor.g = 255;
drawcolor.b = 255;
drawcolor.a = 255;
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
IMesh *pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, pImageData->m_Material );
if ( !pMesh )
return;
CMeshBuilder meshBuilder;
meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
meshBuilder.Position3f( x, y, 0 );
meshBuilder.Color4ub( drawcolor.r, drawcolor.g, drawcolor.b, drawcolor.a );
meshBuilder.TexCoord3f( 0, 0, 0, 0 );
meshBuilder.AdvanceVertex();
meshBuilder.Position3f( x + wide, y, 0 );
meshBuilder.Color4ub( drawcolor.r, drawcolor.g, drawcolor.b, drawcolor.a );
meshBuilder.TexCoord3f( 0, 1, 0, 0 );
meshBuilder.AdvanceVertex();
meshBuilder.Position3f( x + wide, y + tall, 0 );
meshBuilder.Color4ub( drawcolor.r, drawcolor.g, drawcolor.b, drawcolor.a );
meshBuilder.TexCoord3f( 0, 1, 1, 0 );
meshBuilder.AdvanceVertex();
meshBuilder.Position3f( x, y + tall, 0 );
meshBuilder.Color4ub( drawcolor.r, drawcolor.g, drawcolor.b, drawcolor.a );
meshBuilder.TexCoord3f( 0, 0, 1, 0 );
meshBuilder.AdvanceVertex();
meshBuilder.End();
pMesh->Draw();
}
//-----------------------------------------------------------------------------
// Reload all textures into the rendertarget.
//-----------------------------------------------------------------------------
void CGameUIDynamicTextures::OnRestore( int nChangeFlags )
{
m_bRegenerate = true;
}
//-----------------------------------------------------------------------------
// Reload all textures into the rendertarget.
//-----------------------------------------------------------------------------
void CGameUIDynamicTextures::RegenerateTexture( int nChangeFlags )
{
if ( !m_bRegenerate )
return;
int numEntries = m_ImageAliasMap.GetNumStrings();
for ( int i = 0; i < numEntries; ++i )
{
const char *pAlias = m_ImageAliasMap.String( i );
ImageAliasData_t *pImageData = GetImageAliasData( pAlias );
if ( IsErrorImageAliasData( pImageData ) )
continue;
if ( Q_strcmp( pAlias, "_rt_DynamicUI") == 0 )
continue;
if ( !pImageData->m_szBaseTextureName || !*pImageData->m_szBaseTextureName )
continue;
ITexture *pTexture = g_pMaterialSystem->FindTexture( pImageData->m_szBaseTextureName.Get(), TEXTURE_GROUP_OTHER, true );
Assert( pTexture );
pTexture->Download();
LoadImageAliasTexture( pAlias, pImageData->m_szBaseTextureName );
}
}