//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #include "client_pch.h" #include "ivideomode.h" #include "client_class.h" #include "icliententitylist.h" #include "vgui_basepanel.h" #include #include #include #include #include #include #include #include #include #include #include "tier0/vprof.h" #include "KeyValues.h" #include "vgui_helpers.h" #include "utlsymbol.h" #include "tier1/UtlStringMap.h" #include "bitvec.h" #include "utldict.h" #include "vgui/ILocalize.h" #include "con_nprint.h" #include "gl_matsysiface.h" #include "VGuiMatSurface/IMatSystemSurface.h" #include "materialsystem/imaterial.h" #include "materialsystem/imaterialvar.h" #include "materialsystem/imesh.h" #include "materialsystem/idebugtextureinfo.h" #include "materialsystem/itexture.h" #include "vtf/vtf.h" #include "tier2/tier2.h" #include "smartptr.h" #include "igame.h" #include "client.h" #include "tier2/p4helpers.h" #include "p4lib/ip4.h" #include "ivtex.h" #include "tier2/fileutils.h" // For character manipulations isupper/tolower #include // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" #define KEYNAME_NAME "Name" #define KEYNAME_PATH "Path" #define KEYNAME_BINDS_MAX "BindsMax" #define KEYNAME_BINDS_FRAME "BindsFrame" #define KEYNAME_SIZE "Size" #define KEYNAME_FORMAT "Format" #define KEYNAME_WIDTH "Width" #define KEYNAME_HEIGHT "Height" #define KEYNAME_TEXTURE_GROUP "TexGroup" #define COPYTOCLIPBOARD_CMDNAME "CopyToClipboard" #if defined( _X360 ) CON_COMMAND( mat_get_textures, "VXConsole command" ) { g_pMaterialSystemDebugTextureInfo->EnableDebugTextureList( true ); g_pMaterialSystemDebugTextureInfo->EnableGetAllTextures( args.ArgC() >= 2 && ( Q_stricmp( args[1], "all" ) == 0 ) ); } #endif static ConVar mat_texture_list( "mat_texture_list", "0", FCVAR_CHEAT, "For debugging, show a list of used textures per frame" ); static enum TxListPanelRequest { TXR_NONE, TXR_SHOW, TXR_RUNNING, TXR_HIDE } s_eTxListPanelRequest = TXR_NONE; IVTex* VTex_Load( CSysModule** pModule ); void VTex_Unload( CSysModule *pModule ); void* CubemapsFSFactory( const char *pName, int *pReturnCode ); bool StripDirName( char *pFilename ); // These are here so you can bind a key to +mat_texture_list and toggle it on and off. void mat_texture_list_on_f(); void mat_texture_list_off_f(); ConCommand mat_texture_list_on( "+mat_texture_list", mat_texture_list_on_f ); ConCommand mat_texture_list_off( "-mat_texture_list", mat_texture_list_off_f ); ConVar mat_texture_list_all( "mat_texture_list_all", "0", FCVAR_NEVER_AS_STRING|FCVAR_CHEAT, "If this is nonzero, then the texture list panel will show all currently-loaded textures." ); ConVar mat_texture_list_view( "mat_texture_list_view", "1", FCVAR_NEVER_AS_STRING|FCVAR_CHEAT, "If this is nonzero, then the texture list panel will render thumbnails of currently-loaded textures." ); ConVar mat_show_texture_memory_usage( "mat_show_texture_memory_usage", "0", FCVAR_NEVER_AS_STRING|FCVAR_CHEAT, "Display the texture memory usage on the HUD." ); static int g_warn_texkbytes = 1499; static int g_warn_texdimensions = 1024; static bool g_warn_enable = true; static bool g_cursorset = false; class CTextureListPanel; static CTextureListPanel *g_pTextureListPanel = NULL; static bool g_bRecursiveRequestToShowTextureList = false; static int g_nSaveQueueState = INT_MIN; // // A class to mimic a list view // namespace vgui { class TileViewPanelEx : public Panel { DECLARE_CLASS_SIMPLE( TileViewPanelEx, Panel ); public: TileViewPanelEx( Panel *parent, const char *panelName ); ~TileViewPanelEx(); public: virtual void SetFont( HFont hFont ); virtual HFont GetFont(); public: enum HitTest_t { HT_NOTHING = 0, HT_TILE }; virtual int HitTest( int x, int y, int &iTile ); virtual bool GetTileOrg( int iTile, int &x, int &y ); protected: // Overrides for contents virtual int GetNumTiles() = 0; virtual void GetTileSize( int &wide, int &tall ) = 0; virtual void RenderTile( int iTile, int x, int y ) = 0; protected: // Handlers virtual void OnMouseWheeled( int delta ); virtual void OnSizeChanged( int wide, int tall ); virtual void PerformLayout(); virtual void Paint(); virtual void ApplySchemeSettings( IScheme *pScheme ); MESSAGE_FUNC( OnSliderMoved, "ScrollBarSliderMoved" ); protected: virtual bool ComputeLayoutInfo(); int m_li_numTiles; //!< Total number of tiles int m_li_numVisibleTiles; //!< Number of tiles fitting in the client area int m_li_wide, m_li_tall; //!< Tile area width/height int m_li_wideItem, m_li_tallItem; //!< Every item width/height int m_li_colVisible, m_li_rowVisible; //!< Num columns/rows fitting in the client area int m_li_rowNeeded; //!< Num rows required to fit all numTiles int m_li_startTile, m_li_endTile; //!< Start/end tile that are rendered in the client area private: ScrollBar *m_hbar; HFont m_hFont; }; // // Implementation details // TileViewPanelEx::TileViewPanelEx( Panel *parent, const char *panelName ) : Panel( parent, panelName ), m_hbar( NULL ), m_hFont( INVALID_FONT ) { m_hbar = new ScrollBar( this, "VerticalScrollBar", true ); m_hbar->AddActionSignalTarget( this ); m_hbar->SetVisible( true ); } TileViewPanelEx::~TileViewPanelEx() { delete m_hbar; } void TileViewPanelEx::SetFont( HFont hFont ) { m_hFont = hFont; Repaint(); } HFont TileViewPanelEx::GetFont() { return m_hFont; } int TileViewPanelEx::HitTest( int x, int y, int &iTile ) { iTile = -1; if ( !ComputeLayoutInfo() ) return HT_NOTHING; int hitCol = ( x / m_li_wideItem ); int hitRow = ( y / m_li_tallItem ); if ( hitCol >= m_li_colVisible ) return HT_NOTHING; if ( hitRow > m_li_rowVisible ) return HT_NOTHING; int hitTile = m_li_startTile + hitCol + hitRow * m_li_colVisible; if ( hitTile >= m_li_endTile ) return HT_NOTHING; // Hit tile iTile = hitTile; return HT_TILE; } bool TileViewPanelEx::GetTileOrg( int iTile, int &x, int &y ) { if ( m_li_colVisible <= 0 ) return false; if ( iTile < m_li_startTile || iTile >= m_li_endTile ) return false; x = 0 + ( ( iTile - m_li_startTile ) % m_li_colVisible ) * m_li_wideItem; y = 0 + ( ( iTile - m_li_startTile ) / m_li_colVisible ) * m_li_tallItem; return true; } void TileViewPanelEx::OnMouseWheeled( int delta ) { if ( m_hbar->IsVisible() ) { int val = m_hbar->GetValue(); val -= delta; m_hbar->SetValue( val ); } } void TileViewPanelEx::OnSizeChanged( int wide, int tall ) { BaseClass::OnSizeChanged( wide, tall ); InvalidateLayout(); Repaint(); } void TileViewPanelEx::PerformLayout() { int numTiles = GetNumTiles(); m_hbar->SetVisible( false ); int wide, tall; GetSize( wide, tall ); wide -= m_hbar->GetWide(); m_hbar->SetPos( wide - 2, 0 ); m_hbar->SetTall( tall ); if ( !numTiles ) return; int wideItem = 1, tallItem = 1; GetTileSize( wideItem, tallItem ); if ( !wideItem || !tallItem ) return; int colVisible = wide / wideItem; int rowVisible = tall / tallItem; if ( rowVisible <= 0 || colVisible <= 0 ) return; int rowNeeded = ( numTiles + colVisible - 1 ) / colVisible; int startTile = 0; if ( rowNeeded > rowVisible ) { m_hbar->SetRange( 0, rowNeeded ); m_hbar->SetRangeWindow( rowVisible ); m_hbar->SetButtonPressedScrollValue( 1 ); m_hbar->SetVisible( true ); m_hbar->InvalidateLayout(); int val = m_hbar->GetValue(); startTile = val * colVisible; } } bool TileViewPanelEx::ComputeLayoutInfo() { m_li_numTiles = GetNumTiles(); if ( !m_li_numTiles ) return false; GetSize( m_li_wide, m_li_tall ); m_li_wide -= m_hbar->GetWide(); m_li_wideItem = 1, m_li_tallItem = 1; GetTileSize( m_li_wideItem, m_li_tallItem ); if ( !m_li_wideItem || !m_li_tallItem ) return false; m_li_colVisible = m_li_wide / m_li_wideItem; m_li_rowVisible = m_li_tall / m_li_tallItem; if ( m_li_rowVisible <= 0 || m_li_colVisible <= 0 ) return false; m_li_rowNeeded = ( m_li_numTiles + m_li_colVisible - 1 ) / m_li_colVisible; m_li_numVisibleTiles = m_li_colVisible * m_li_rowVisible; m_li_startTile = 0; if ( m_li_rowNeeded > m_li_rowVisible ) { int val = m_hbar->GetValue(); m_li_startTile = val * m_li_colVisible; } if ( m_li_startTile >= m_li_numTiles ) m_li_startTile = m_li_numTiles - m_li_numVisibleTiles; if ( m_li_startTile < 0 ) m_li_startTile = 0; m_li_endTile = m_li_startTile + m_li_numVisibleTiles + m_li_colVisible; // draw the partly visible items if ( m_li_endTile > m_li_numTiles ) m_li_endTile = m_li_numTiles; return true; } void TileViewPanelEx::Paint() { BaseClass::Paint(); // Now render all our tiles if ( !ComputeLayoutInfo() ) return; for ( int iRenderTile = m_li_startTile; iRenderTile < m_li_endTile; ++ iRenderTile ) { int x = 0 + ( ( iRenderTile - m_li_startTile ) % m_li_colVisible ) * m_li_wideItem; int y = 0 + ( ( iRenderTile - m_li_startTile ) / m_li_colVisible ) * m_li_tallItem; RenderTile( iRenderTile, x, y ); } } void TileViewPanelEx::ApplySchemeSettings( IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); SetBgColor( GetSchemeColor( "ListPanel.BgColor", pScheme ) ); SetBorder( pScheme->GetBorder("ButtonDepressedBorder" ) ); SetFont( pScheme->GetFont( "Default", IsProportional() ) ); } void TileViewPanelEx::OnSliderMoved() { InvalidateLayout(); Repaint(); } }; // namespace vgui ////////////////////////////////////////////////////////////////////////// // // Material access // ////////////////////////////////////////////////////////////////////////// class CAutoMatSysDebugMode { public: CAutoMatSysDebugMode(); ~CAutoMatSysDebugMode(); void ScheduleCleanupTextureVar( IMaterialVar *pVar ); protected: bool bOldDebugMode; CUtlRBTree< IMaterialVar * > arrCleanupVars; }; CAutoMatSysDebugMode::CAutoMatSysDebugMode() : arrCleanupVars( DefLessFunc(IMaterialVar *) ) { g_pMaterialSystem->Flush(); bOldDebugMode = g_pMaterialSystemDebugTextureInfo->SetDebugTextureRendering( true ); } CAutoMatSysDebugMode::~CAutoMatSysDebugMode() { g_pMaterialSystem->Flush(); g_pMaterialSystemDebugTextureInfo->SetDebugTextureRendering( bOldDebugMode ); for ( uint32 k = 0; k < arrCleanupVars.Count(); ++ k ) { IMaterialVar *pVar = arrCleanupVars.Element( k ); pVar->SetUndefined(); } } void CAutoMatSysDebugMode::ScheduleCleanupTextureVar( IMaterialVar *pVar ) { if ( !pVar ) return; arrCleanupVars.InsertIfNotFound( pVar ); } static IMaterial * UseDebugMaterial( char const *szMaterial, ITexture *pMatTexture, CAutoMatSysDebugMode *pRestoreVars ) { if ( !szMaterial || !pMatTexture ) return NULL; bool foundVar; IMaterial *pMaterial = materials->FindMaterial( szMaterial, TEXTURE_GROUP_OTHER, false ); // IMaterial *pMaterial = materials->FindMaterial( "debug/debugempty", TEXTURE_GROUP_OTHER, false ); if ( !pMaterial ) return NULL; IMaterialVar *BaseTextureVar = pMaterial->FindVar( "$basetexture", &foundVar, false ); // IMaterialVar *BaseTextureVar = pMaterial->FindVar( "$basetexture", &foundVar, false ); if ( !foundVar || !BaseTextureVar ) return NULL; IMaterialVar *FrameVar = pMaterial->FindVar( "$frame", &foundVar, false ); if ( foundVar && FrameVar ) { int numAnimFrames = pMatTexture->GetNumAnimationFrames(); if ( pMatTexture->IsRenderTarget() || pMatTexture->IsProcedural() ) numAnimFrames = 0; // Cannot use the stencil parts of the render targets and shouldn't use the other frames of procedural textures FrameVar->SetIntValue( ( numAnimFrames > 0 ) ? ( g_ClientGlobalVariables.tickcount % numAnimFrames ) : 0 ); } BaseTextureVar->SetTextureValue( pMatTexture ); if ( pRestoreVars ) { pRestoreVars->ScheduleCleanupTextureVar( BaseTextureVar ); } return pMaterial; } static void RenderTexturedRect( vgui::Panel *pPanel, IMaterial *pMaterial, int x, int y, int x1, int y1, int xoff = 0, int yoff = 0 ) { CAutoMatSysDebugMode auto_matsysdebugmode; int tall = pPanel->GetTall(); float fHeightUV = 1.0f; if ( y1 > tall ) { fHeightUV = float( tall - y ) / float( y1 - y ); y1 = tall; } if ( y1 <= y ) return; pPanel->LocalToScreen( x, y ); pPanel->LocalToScreen( x1, y1 ); x += xoff; x1 += xoff; y += yoff; y1 += yoff; CMatRenderContextPtr pRenderContext( materials ); pRenderContext->Bind( pMaterial ); IMesh* pMesh = pRenderContext->GetDynamicMesh( true ); CMeshBuilder meshBuilder; meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 ); meshBuilder.Position3f( x, y, 0.0f ); meshBuilder.TexCoord2f( 0, 0.0f, 0.0f ); meshBuilder.AdvanceVertex(); meshBuilder.Position3f( x1, y, 0.0f ); meshBuilder.TexCoord2f( 0, 1.0f, 0.0f ); meshBuilder.AdvanceVertex(); meshBuilder.Position3f( x1, y1, 0.0f ); meshBuilder.TexCoord2f( 0, 1.0f, fHeightUV ); meshBuilder.AdvanceVertex(); meshBuilder.Position3f( x, y1, 0.0f ); meshBuilder.TexCoord2f( 0, 0.0f, fHeightUV ); meshBuilder.AdvanceVertex(); meshBuilder.End(); pMesh->Draw(); } static bool ShallWarnTx( KeyValues *kv, ITexture *tx ) { if ( !tx ) { return false; } if ( tx->GetFlags() & ( TEXTUREFLAGS_NOLOD | TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_ONEBITALPHA ) ) return true; if ( stricmp( "DXT1", kv->GetString( KEYNAME_FORMAT ) ) && stricmp( "DXT5", kv->GetString( KEYNAME_FORMAT ) ) && stricmp( "ATI1N", kv->GetString( KEYNAME_FORMAT ) ) && stricmp( "ATI2N", kv->GetString( KEYNAME_FORMAT ) ) && stricmp( "DXT5_RUNTIME", kv->GetString( KEYNAME_FORMAT ) ) ) return true; if ( kv->GetInt( KEYNAME_SIZE ) > g_warn_texkbytes ) return true; if ( kv->GetInt( KEYNAME_WIDTH ) > g_warn_texdimensions ) return true; if ( kv->GetInt( KEYNAME_HEIGHT ) > g_warn_texdimensions ) return true; return false; } static void FmtCommaNumber( char *pchBuffer, unsigned int uiNumber ) { pchBuffer[0] = 0; for ( unsigned int uiDivisor = 1000 * 1000 * 1000; uiDivisor > 0; uiDivisor /= 1000 ) { if ( uiNumber > uiDivisor ) { unsigned int uiPrint = ( uiNumber / uiDivisor ) % 1000; sprintf( pchBuffer + strlen( pchBuffer ), ( uiNumber / uiDivisor < 1000 ) ? "%d," : "%03d,", uiPrint ); } } int len = strlen( pchBuffer ); if ( !len ) sprintf( pchBuffer, "0" ); else if ( pchBuffer[ len - 1 ] == ',' ) pchBuffer[ len - 1 ] = 0; } namespace { char s_chLastViewedTextureBuffer[ 0x200 ] = {0}; }; // end `anonymous` namespace bool CanAdjustTextureSize( char const *szTextureName, bool bMoveSizeUp ) { ITexture *pMatTexture = materials->FindTexture( szTextureName, "", false ); if ( !pMatTexture ) return false; // Determine the texture current size if ( !bMoveSizeUp ) { return ( pMatTexture->GetActualWidth() > 4 || pMatTexture->GetActualHeight() > 4 ); } else { return ( pMatTexture->GetActualWidth() < pMatTexture->GetMappingWidth() || pMatTexture->GetActualHeight() < pMatTexture->GetMappingHeight() ); } } bool AdjustTextureSize( char const *szTextureName, bool bMoveSizeUp ) { ITexture *pMatTexture = materials->FindTexture( szTextureName, "", false ); if ( !pMatTexture ) return false; pMatTexture->ForceLODOverride( bMoveSizeUp ? +1 : -1 ); return true; } CON_COMMAND_F( mat_texture_list_txlod, "Adjust LOD of the last viewed texture +1 to inc resolution, -1 to dec resolution", FCVAR_DONTRECORD ) { if ( args.ArgC() == 2 ) { int iAdjustment = atoi( args.Arg( 1 ) ); switch ( iAdjustment ) { case 1: case -1: if ( !CanAdjustTextureSize( s_chLastViewedTextureBuffer, iAdjustment > 0 ) ) Warning( "mat_texture_list_txlod cannot adjust lod for '%s'\n", s_chLastViewedTextureBuffer ); else if ( !AdjustTextureSize( s_chLastViewedTextureBuffer, iAdjustment > 0 ) ) Warning( "mat_texture_list_txlod failed adjusting lod for '%s'\n", s_chLastViewedTextureBuffer ); else Msg( "mat_texture_list_txlod adjusted lod %c1 for '%s'\n", ( iAdjustment > 0 ) ? '+' : '-' , s_chLastViewedTextureBuffer ); return; default: break; } } Warning( "Usage: 'mat_texture_list_txlod +1' to inc lod | 'mat_texture_list_txlod -1' to dec lod\n" ); } static bool SaveTextureImage( char const *szTextureName ) { bool bRet; char fileName[ MAX_PATH ]; char baseName[ MAX_PATH ]; ITexture *pMatTexture = materials->FindTexture( szTextureName, "", false ); if ( !pMatTexture ) { Msg( "materials->FindTexture( '%s' ) failed.\n", szTextureName ); return false; } V_FileBase( szTextureName, baseName, ARRAYSIZE( baseName ) ); Q_snprintf( fileName, ARRAYSIZE( fileName ), "//MOD/tex_%s.tga", baseName ); bRet = pMatTexture->SaveToFile( fileName ); Msg( "SaveTextureImage( '%s' ): %s\n", fileName, bRet ? "succeeded" : "failed" ); return bRet; } namespace MatViewOverride { // // View override parameters request // struct ViewParamsReq { CUtlVector< UtlSymId_t > lstMaterials; } s_viewParamsReq; void RequestSelectNone( void ) { s_viewParamsReq.lstMaterials.RemoveAll(); } void RequestSelected( int nCount, UtlSymId_t const *pNameIds ) { s_viewParamsReq.lstMaterials.AddMultipleToTail( nCount, pNameIds ); } // // View override last parameters // struct ViewParamsLast { ViewParamsLast() : lstMaterials( DefLessFunc( UtlSymId_t ) ), flTime( 0.f ), bHighlighted( false ) {} float flTime; bool bHighlighted; struct TxInfo { ITexture *pTx; CUtlSymbol name; }; struct VarMap : public CUtlMap< UtlSymId_t, TxInfo > { VarMap() : CUtlMap< UtlSymId_t, TxInfo >( DefLessFunc( UtlSymId_t ) ) {} VarMap( VarMap const &x ) { const_cast< VarMap & >( x ).Swap( *this ); m_matInfo = x.m_matInfo; } // Fast-swap data struct MatInfo { // MatInfo() : ignorez( false ) {} // bool ignorez; MatInfo() {} } m_matInfo; }; CUtlMap< UtlSymId_t, VarMap > lstMaterials; } s_viewParamsLast; // // DisplaySelectedTextures // Executed every frame to toggle the selected/unselected // textures for a list of materials. // void DisplaySelectedTextures() { // Nothing selected if ( !s_viewParamsLast.lstMaterials.Count() && !s_viewParamsReq.lstMaterials.Count() ) return; if ( !s_viewParamsLast.lstMaterials.Count() && s_viewParamsReq.lstMaterials.Count() ) { // First time selection s_viewParamsLast.flTime = Plat_FloatTime(); s_viewParamsLast.bHighlighted = false; } else { // Wait for the flash-time float fCurTime = Plat_FloatTime(); if ( fCurTime >= s_viewParamsLast.flTime && fCurTime < s_viewParamsLast.flTime + 0.4 ) return; s_viewParamsLast.flTime = fCurTime; s_viewParamsLast.bHighlighted = !s_viewParamsLast.bHighlighted; } // Find the empty texture ITexture *txEmpty = materials->FindTexture( "debugempty", "", false ); typedef unsigned short MapIdx; // Now walk over all the materials in the req list and push them to the params for ( int k = 0, kEnd = s_viewParamsReq.lstMaterials.Count(); k < kEnd; ++ k ) { UtlSymId_t idMat = s_viewParamsReq.lstMaterials[ k ]; MapIdx idx = s_viewParamsLast.lstMaterials.Find( idMat ); if ( idx == s_viewParamsLast.lstMaterials.InvalidIndex() ) { // Insert the new material idx = s_viewParamsLast.lstMaterials.Insert( idMat ); ViewParamsLast::VarMap &vars = s_viewParamsLast.lstMaterials.Element( idx ); // Locate the material char const *szMaterialName = CUtlSymbol( idMat ).String(); IMaterial *pMat = materials->FindMaterial( szMaterialName, TEXTURE_GROUP_OTHER, false ); if ( pMat ) { int numParams = pMat->ShaderParamCount(); IMaterialVar **arrVars = pMat->GetShaderParams(); for ( int idxParam = 0; idxParam < numParams; ++ idxParam ) { // if ( !stricmp( arrVars[ idxParam ]->GetName(), "$ignorez" ) ) // { // vars.m_matInfo.ignorez = arrVars[ idxParam ]->GetIntValue() ? true : false; // arrVars[ idxParam ]->SetIntValue( 1 ); // continue; // } if ( !arrVars[ idxParam ]->IsTexture() ) continue; ITexture *pTex = arrVars[ idxParam ]->GetTextureValue(); if ( !pTex || pTex->IsError() ) continue; ViewParamsLast::TxInfo txinfo; txinfo.name = CUtlSymbol( pTex->GetName() ); txinfo.pTx = pTex; vars.Insert( CUtlSymbol( arrVars[ idxParam ]->GetName() ), txinfo ); } } } } // Now walk over all the materials that we have to highlight or unhighlight for ( MapIdx idx = s_viewParamsLast.lstMaterials.FirstInorder(); idx != s_viewParamsLast.lstMaterials.InvalidIndex(); /* advance inside */ ) { UtlSymId_t idMat = s_viewParamsLast.lstMaterials.Key( idx ); ViewParamsLast::VarMap &vars = s_viewParamsLast.lstMaterials.Element( idx ); bool bRemovedSelection = ( s_viewParamsReq.lstMaterials.Find( idMat ) < 0 ); char const *szMaterialName = CUtlSymbol( idMat ).String(); IMaterial *pMat = materials->FindMaterial( szMaterialName, TEXTURE_GROUP_OTHER, false ); if ( pMat ) { int numParams = pMat->ShaderParamCount(); IMaterialVar **arrVars = pMat->GetShaderParams(); for ( int idxParam = 0; idxParam < numParams; ++ idxParam ) { // if ( bRemovedSelection && !stricmp( arrVars[ idxParam ]->GetName(), "$ignorez" ) ) // { // arrVars[ idxParam ]->SetIntValue( vars.m_matInfo.ignorez ? 1 : 0 ); // continue; // } char const *szVarName = arrVars[ idxParam ]->GetName(); CUtlSymbol symVarName( szVarName ); MapIdx idxVarsTxInfo = vars.Find( symVarName ); if ( idxVarsTxInfo != vars.InvalidIndex() ) { ViewParamsLast::TxInfo &ti = vars.Element( idxVarsTxInfo ); ITexture *pTx = ( s_viewParamsLast.bHighlighted && !bRemovedSelection ) ? txEmpty : ti.pTx; Assert( ti.pTx && !ti.pTx->IsError() ); arrVars[ idxParam ]->SetTextureValue( pTx ); } } } // Index advance if ( bRemovedSelection ) { MapIdx idxRemove = idx; idx = s_viewParamsLast.lstMaterials.NextInorder( idx ); s_viewParamsLast.lstMaterials.RemoveAt( idxRemove ); } else { idx = s_viewParamsLast.lstMaterials.NextInorder( idx ); } } } }; // end namespace MatViewOverride ////////////////////////////////////////////////////////////////////////// // // Custom text entry with "Open" for vmt's // ////////////////////////////////////////////////////////////////////////// class CVmtTextEntry : public vgui::TextEntry { DECLARE_CLASS_SIMPLE( CVmtTextEntry, vgui::TextEntry ); public: CVmtTextEntry( vgui::Panel *parent, char const *szName ) : BaseClass( parent, szName ) {} public: MESSAGE_FUNC( OpenVmtSelected, "DoOpenVmtSelected" ); virtual void OpenEditMenu(); }; void CVmtTextEntry::OpenEditMenu() { vgui::Menu *pEditMenu = BaseClass::GetEditMenu(); int pos = pEditMenu->AddMenuItem( "Open VMT", new KeyValues("DoOpenVmtSelected"), this ); pEditMenu->MoveMenuItem( pos, 0 ); int x0, x1; pEditMenu->SetItemEnabled( "Open VMT", GetSelectedRange(x0, x1) ); BaseClass::OpenEditMenu(); } void CVmtTextEntry::OpenVmtSelected() { int x0, x1; if ( !GetSelectedRange(x0, x1) ) return; CUtlVector buf; buf.SetCount( x1 - x0 + 1 ); GetTextRange( buf.Base(), x0, x1 - x0 ); for ( char *pchName = buf.Base(), *pchNext = NULL; pchName; pchName = pchNext ) { if ( ( pchNext = strchr( pchName, '\n' ) ) != NULL ) *( pchNext ++ ) = 0; char chResolveName[ 256 ] = {0}, chResolveNameArg[ 256 ] = {0}; Q_snprintf( chResolveNameArg, sizeof( chResolveNameArg ) - 1, "materials/%s.vmt", pchName ); char const *szResolvedName = g_pFileSystem->RelativePathToFullPath( chResolveNameArg, "game", chResolveName, sizeof( chResolveName ) - 1 ); if ( szResolvedName ) vgui::system()->ShellExecuteEx( "open", szResolvedName, "" ); } } #ifdef IS_WINDOWS_PC bool IsSpaceOrQuote( char val ) { return (isspace(val) || val == '\"'); } static bool SetBufferValue( char *chTxtFileBuffer, char const *szLookupKey, char const *szNewValue ) { bool bResult = false; size_t lenTmp = strlen( szNewValue ); size_t nTxtFileBufferLen = strlen( chTxtFileBuffer ); for ( char *pch = chTxtFileBuffer; ( NULL != ( pch = strstr( pch, szLookupKey ) ) ); ++ pch ) { char *val = pch + strlen( szLookupKey ); if ( !IsSpaceOrQuote( *val ) ) continue; else ++ val; // Skip over the whitespace and quotes while ( *val && IsSpaceOrQuote( *val ) ) ++ val; char *pValStart = val; // Okay, here comes the value while ( *val && IsSpaceOrQuote( *val ) ) ++ val; while ( *val && !IsSpaceOrQuote( *val ) ) ++ val; char *pValEnd = val; // Okay, here ends the value memmove( pValStart + lenTmp, pValEnd, chTxtFileBuffer + nTxtFileBufferLen + 1 - pValEnd ); memcpy( pValStart, szNewValue, lenTmp ); nTxtFileBufferLen += ( lenTmp - ( pValEnd - pValStart ) ); bResult = true; } if ( !bResult ) { char *pchAdd = chTxtFileBuffer + nTxtFileBufferLen; strcpy( pchAdd + strlen( pchAdd ), "\n" ); strcpy( pchAdd + strlen( pchAdd ), szLookupKey ); strcpy( pchAdd + strlen( pchAdd ), " " ); strcpy( pchAdd + strlen( pchAdd ), szNewValue ); strcpy( pchAdd + strlen( pchAdd ), "\n" ); bResult = true; } return bResult; } // Replaces the first occurrence of "szFindData" with "szNewData" // Returns the remaining buffer past the replaced data or NULL if // no replacement occurred. static char * BufferReplace( char *buf, char const *szFindData, char const *szNewData ) { size_t len = strlen( buf ), lFind = strlen( szFindData ), lNew = strlen( szNewData ); if ( char *pBegin = strstr( buf, szFindData ) ) { memmove( pBegin + lNew, pBegin + lFind, buf + len - ( pBegin + lFind ) ); memmove( pBegin, szNewData, lNew ); return pBegin + lNew; } return NULL; } class CP4Requirement { public: CP4Requirement(); ~CP4Requirement(); protected: bool m_bLoadedModule; CSysModule *m_pP4Module; }; CP4Requirement::CP4Requirement() : m_bLoadedModule( false ), m_pP4Module( NULL ) { #ifdef STAGING_ONLY if ( p4 ) return; // load the p4 lib m_pP4Module = Sys_LoadModule( "p4lib" ); m_bLoadedModule = true; if ( m_pP4Module ) { CreateInterfaceFn factory = Sys_GetFactory( m_pP4Module ); if ( factory ) { p4 = ( IP4 * )factory( P4_INTERFACE_VERSION, NULL ); if ( p4 ) { extern CreateInterfaceFn g_AppSystemFactory; p4->Connect( g_AppSystemFactory ); p4->Init(); } } } #endif // STAGING_ONLY if ( !p4 ) { Warning( "Can't load p4lib.dll\n" ); } } CP4Requirement::~CP4Requirement() { if ( m_bLoadedModule && m_pP4Module ) { if ( p4 ) { p4->Shutdown(); p4->Disconnect(); } Sys_UnloadModule( m_pP4Module ); m_pP4Module = NULL; p4 = NULL; } } #endif // #ifdef IS_WINDOWS_PC ////////////////////////////////////////////////////////////////////////// // // Render textures edit panel // ////////////////////////////////////////////////////////////////////////// class CRenderTextureEditor : public vgui::Frame { DECLARE_CLASS_SIMPLE( CRenderTextureEditor, vgui::Frame ); public: CRenderTextureEditor( vgui::Panel *parent, char const *szName ); ~CRenderTextureEditor(); public: virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); virtual void PerformLayout(); virtual void OnCommand( const char *command ); virtual void SetFont( vgui::HFont hFont ) { m_hFont = hFont; Repaint(); } virtual vgui::HFont GetFont() { return m_hFont; } public: virtual void OnMousePressed( vgui::MouseCode code ); virtual void OnMouseDoublePressed( vgui::MouseCode code ) { OnMousePressed( code ); } virtual void Activate(); virtual void Close(); public: void SetDispInfo( KeyValues *kv, int iHint ); void GetDispInfo( KeyValues *&kv, int &iHint ); public: virtual void Paint(); enum { TILE_BORDER = 10, TILE_SIZE = 550, TILE_TEXTURE_SIZE = 256, TILE_TEXT = 70 }; protected: vgui::HFont m_hFont; CVmtTextEntry *m_pMaterials; vgui::Button *m_pExplore; vgui::Button *m_pReload; vgui::Button *m_pRebuild; vgui::Button *m_pToggleNoMip; vgui::Button *m_pCopyTxt; #ifndef POSIX vgui::Button *m_pCopyImg; #endif vgui::Button *m_pSaveImg; vgui::Button *m_pSizeControls[2]; vgui::Button *m_pFlashBtn; KeyValues *m_pInfo; CUtlBuffer m_bufInfoText; CUtlVector< UtlSymId_t > m_lstMaterials; int m_iInfoHint; }; CRenderTextureEditor::CRenderTextureEditor( vgui::Panel *parent, char const *szName ) : BaseClass( parent, szName ), m_pInfo( NULL ), m_iInfoHint( 0 ), m_hFont( vgui::INVALID_FONT ), m_bufInfoText( 0, 0, CUtlBuffer::TEXT_BUFFER ) { m_pMaterials = vgui::SETUP_PANEL( new CVmtTextEntry( this, "Materials" ) ); m_pMaterials->SetMultiline( true ); m_pMaterials->SetEditable( false ); m_pMaterials->SetEnabled( false ); m_pMaterials->SetVerticalScrollbar( true ); m_pMaterials->SetVisible( true ); m_pExplore = vgui::SETUP_PANEL( new vgui::Button( this, "Explore", "Open", this, "Explore" ) ); m_pExplore->SetVisible( true ); m_pReload = vgui::SETUP_PANEL( new vgui::Button( this, "Reload", "Reload", this, "Reload" ) ); m_pReload->SetVisible( true ); m_pRebuild = vgui::SETUP_PANEL( new vgui::Button( this, "RebuildVTF", "Rebuild VTF", this, "RebuildVTF" ) ); m_pRebuild->SetVisible( true ); m_pToggleNoMip = vgui::SETUP_PANEL( new vgui::Button( this, "ToggleNoMip", "ToggleNoMip", this, "ToggleNoMip" ) ); m_pToggleNoMip->SetVisible( true ); m_pCopyTxt = vgui::SETUP_PANEL( new vgui::Button( this, "CopyTxt", "Copy Text", this, "CopyTxt" ) ); m_pCopyTxt->SetVisible( true ); #ifndef POSIX m_pCopyImg = vgui::SETUP_PANEL( new vgui::Button( this, "CopyImg", "Copy Image", this, "CopyImg" ) ); m_pCopyImg->SetVisible( true ); #endif m_pSaveImg = vgui::SETUP_PANEL( new vgui::Button( this, "SaveImg", "Save Image", this, "SaveImg" ) ); m_pSaveImg->SetVisible( true ); m_pFlashBtn = vgui::SETUP_PANEL( new vgui::Button( this, "FlashBtn", "Flash in Game", this, "FlashBtn" ) ); m_pFlashBtn->SetVisible( true ); m_pSizeControls[0] = vgui::SETUP_PANEL( new vgui::Button( this, "--", "--", this, "size-" ) ); m_pSizeControls[1] = vgui::SETUP_PANEL( new vgui::Button( this, "+", "+", this, "size+" ) ); } CRenderTextureEditor::~CRenderTextureEditor() { SetDispInfo( NULL, 0 ); } void CRenderTextureEditor::GetDispInfo( KeyValues *&kv, int &iHint ) { iHint = m_iInfoHint; kv = m_pInfo; } void CRenderTextureEditor::SetDispInfo( KeyValues *kv, int iHint ) { m_iInfoHint = iHint; if ( m_pInfo ) m_pInfo->deleteThis(); m_pInfo = kv ? kv->MakeCopy() : NULL; CUtlStringMap< bool > arrMaterials, arrMaterialsFullNames; if ( kv ) { char const *szTextureName = kv->GetString( KEYNAME_NAME ); for ( MaterialHandle_t hm = materials->FirstMaterial(); hm != materials->InvalidMaterial(); hm = materials->NextMaterial( hm ) ) { IMaterial *pMat = materials->GetMaterial( hm ); if ( !pMat ) continue; int numParams = pMat->ShaderParamCount(); IMaterialVar **arrVars = pMat->GetShaderParams(); for ( int idxParam = 0; idxParam < numParams; ++ idxParam ) { if ( !arrVars[ idxParam ]->IsTexture() ) continue; ITexture *pTex = arrVars[ idxParam ]->GetTextureValue(); if ( !pTex || pTex->IsError() ) continue; if ( !stricmp( pTex->GetName(), szTextureName ) ) { bool bRealMaterial = true; if ( StringHasPrefix( pMat->GetName(), "debug/debugtexture" ) ) bRealMaterial = false; if ( StringHasPrefix( pMat->GetName(), "maps/" ) ) { // Format: maps/mapname/[matname]_x_y_z bRealMaterial = false; char chName[ MAX_PATH ]; Q_strncpy( chName, pMat->GetName() + 5, sizeof( chName ) - 1 ); if ( char *szMatName = strchr( chName, '/' ) ) { ++ szMatName; for ( int k = 0; k < 3; ++ k ) { if ( char *rUnderscore = strrchr( szMatName, '_' ) ) *rUnderscore = 0; } sprintf( szMatName + strlen( szMatName ), " (cubemap)" ); arrMaterials[ szMatName ] = true; } arrMaterialsFullNames[ pMat->GetName() ] = true; } if ( bRealMaterial ) { arrMaterials[ pMat->GetName() ] = true; arrMaterialsFullNames[ pMat->GetName() ] = true; } break; } } } } // Now that we have a list of materials make a printable version CUtlBuffer bufText( 0, 0, CUtlBuffer::TEXT_BUFFER ); if ( !arrMaterials.GetNumStrings() ) { bufText.Printf( "-- no materials --" ); } else { int c = arrMaterials.GetNumStrings(); bufText.Printf( " %d material%s:", c, ( c%10 == 1 && c != 11 ) ? "" : "s" ); } for ( int k = 0; k < arrMaterials.GetNumStrings(); ++ k ) { bufText.Printf( "\n%s", arrMaterials.String( k ) ); } m_pSaveImg->SetVisible( false ); // Set text except for the cases when m_pInfo and no materials found if ( !( m_pInfo && !arrMaterials.GetNumStrings() ) ) { if ( kv ) { int iTxWidth = kv->GetInt( KEYNAME_WIDTH ); int iTxHeight = kv->GetInt( KEYNAME_HEIGHT ); char const *szTxFormat = kv->GetString( KEYNAME_FORMAT ); bufText.Printf( "\n%dx%d Format:%s", iTxWidth, iTxHeight, szTxFormat ); // We support saving the 8888 formats right now: "RGBA8888", "ABGR8888", "ARGB8888", "BGRA8888", "BGRX8888" if( Q_strstr( szTxFormat, "8888" ) ) { m_pSaveImg->SetVisible( true ); } } m_pMaterials->SetText( ( char const * ) bufText.Base() ); m_lstMaterials.RemoveAll(); m_lstMaterials.EnsureCapacity( arrMaterialsFullNames.GetNumStrings() ); for ( int k = 0; k < arrMaterialsFullNames.GetNumStrings(); ++ k ) { m_lstMaterials.AddToTail( CUtlSymbol( arrMaterialsFullNames.String( k ) ) ); } } m_bufInfoText.Clear(); InvalidateLayout(); } void CRenderTextureEditor::Close() { BaseClass::Close(); SetDispInfo( NULL, 0 ); } void CRenderTextureEditor::Activate() { BaseClass::Activate(); } void CRenderTextureEditor::PerformLayout() { BaseClass::PerformLayout(); int iRenderedHeight = 4 * TILE_BORDER + TILE_TEXT + TILE_SIZE; SetSize( 4 * TILE_BORDER + TILE_SIZE, iRenderedHeight + 90 + TILE_BORDER ); m_pMaterials->SetPos( TILE_BORDER, iRenderedHeight + 2 ); m_pMaterials->SetSize( 2 * TILE_BORDER + TILE_SIZE, 90 ); m_pExplore->SetPos( 2 * TILE_BORDER + TILE_SIZE - 50, 2 * TILE_BORDER ); m_pExplore->SetWide( 50 ); m_pReload->SetPos( 2 * TILE_BORDER + TILE_SIZE - 50 - 65, 2 * TILE_BORDER ); m_pReload->SetWide( 60 ); m_pReload->SetVisible( m_lstMaterials.Count() > 0 ); m_pRebuild->SetPos( 2 * TILE_BORDER + TILE_SIZE - 50 - 65 - 95, 2 * TILE_BORDER ); m_pRebuild->SetWide( 90 ); m_pRebuild->SetVisible( m_lstMaterials.Count() > 0 ); m_pToggleNoMip->SetPos( 2 * TILE_BORDER + TILE_SIZE - 50 - 95, (2 * TILE_BORDER) + m_pReload->GetTall() + 1 ); m_pToggleNoMip->SetWide( 90 ); m_pToggleNoMip->SetVisible( m_lstMaterials.Count() > 0 ); m_pExplore->SetVisible( false ); m_pSizeControls[0]->SetVisible( false ); m_pSizeControls[1]->SetVisible( false ); if ( m_pInfo ) { char chResolveName[ 256 ] = {0}, chResolveNameArg[ 256 ] = {0}; Q_snprintf( chResolveNameArg, sizeof( chResolveNameArg ) - 1, "materials/%s.vtf", m_pInfo->GetString( KEYNAME_NAME ) ); char const *szResolvedName = g_pFileSystem->RelativePathToFullPath( chResolveNameArg, "game", chResolveName, sizeof( chResolveName ) - 1 ); if ( szResolvedName ) { m_pExplore->SetVisible( true ); } if ( !m_pInfo->GetInt( "SpecialTx" ) ) { m_pSizeControls[0]->SetVisible( true ); m_pSizeControls[1]->SetVisible( true ); m_pSizeControls[0]->SetEnabled( CanAdjustTextureSize( m_pInfo->GetString( KEYNAME_NAME ), false ) ); m_pSizeControls[1]->SetEnabled( CanAdjustTextureSize( m_pInfo->GetString( KEYNAME_NAME ), true ) ); int posX, posY; m_pExplore->GetPos( posX, posY ); m_pSizeControls[0]->SetPos( posX, posY + m_pExplore->GetTall() + 1 ); m_pSizeControls[0]->SetWide( m_pExplore->GetWide() / 2 ); m_pSizeControls[1]->SetPos( posX + m_pSizeControls[0]->GetWide() + 1, posY + m_pExplore->GetTall() + 1 ); m_pSizeControls[1]->SetWide( m_pExplore->GetWide() - ( m_pSizeControls[0]->GetWide() + 1 ) ); } } { int posX, posY; m_pExplore->GetPos( posX, posY ); posY += m_pExplore->GetTall() * 2 + 2; posX += m_pExplore->GetWide(); posX -= 80; m_pSaveImg->SetPos( posX, posY ); m_pSaveImg->SetWide( 80 ); #ifndef POSIX posX -= 80 + 5; m_pCopyImg->SetPos( posX, posY ); m_pCopyImg->SetWide( 80 ); #endif posX -= 80 + 5; m_pCopyTxt->SetPos( posX, posY ); m_pCopyTxt->SetWide( 80 ); posX -= 95 + 5; m_pFlashBtn->SetPos( posX, posY ); m_pFlashBtn->SetWide( 95 ); } } #ifdef IS_WINDOWS_PC bool GetTextureContentPath( const char *pszVTF, char *szTextureContentPath, int nBufSize ) { char vhRelVTF[MAX_PATH]; Q_snprintf( vhRelVTF, sizeof( vhRelVTF ) - 1, "materials/%s.vtf", pszVTF ); ConVarRef mat_texture_list_content_path( "mat_texture_list_content_path" ); if ( !mat_texture_list_content_path.GetString()[0] ) { szTextureContentPath = const_cast< char * >( g_pFileSystem->RelativePathToFullPath( vhRelVTF, "game", szTextureContentPath, nBufSize - 1 ) ); if ( !szTextureContentPath ) { Warning( " texture '%s' is not loaded from file system.\n", pszVTF ); return false; } if ( !BufferReplace( szTextureContentPath, "\\game\\", "\\content\\" ) || !BufferReplace( szTextureContentPath, "\\materials\\", "\\materialsrc\\" ) ) { Warning( " texture '%s' cannot be mapped to content directory.\n", pszVTF ); return false; } } else { V_strncpy( szTextureContentPath, mat_texture_list_content_path.GetString(), MAX_PATH ); V_strncat( szTextureContentPath, "/", MAX_PATH ); V_strncat( szTextureContentPath, pszVTF, MAX_PATH ); V_strncat( szTextureContentPath, ".vtf", MAX_PATH ); } return true; } #endif // #ifdef IS_WINDOWS_PC void CRenderTextureEditor::OnCommand( const char *command ) { BaseClass::OnCommand( command ); if ( !stricmp( command, "Explore" ) && m_pInfo ) { char chResolveName[ 256 ] = {0}, chResolveNameArg[ 256 ] = {0}; Q_snprintf( chResolveNameArg, sizeof( chResolveNameArg ) - 1, "materials/%s.vtf", m_pInfo->GetString( KEYNAME_NAME ) ); char const *szResolvedName = g_pFileSystem->RelativePathToFullPath( chResolveNameArg, "game", chResolveName, sizeof( chResolveName ) - 1 ); char params[256]; Q_snprintf( params, sizeof( params ) - 1, "/E,/SELECT,%s", szResolvedName ); vgui::system()->ShellExecuteEx( "open", "explorer.exe", params ); } if ( !stricmp( command, "Reload" ) && m_lstMaterials.Count() ) { CUtlBuffer bufCommand( 0, 0, CUtlBuffer::TEXT_BUFFER ); int idxMaterial = 0; // // Issue the console command several times in case we overflow the // console command buffer size. // Cbuf_Execute(); for ( ; idxMaterial < m_lstMaterials.Count(); ) { int iOldIdx = idxMaterial; bufCommand.Printf( "mat_reloadmaterial \"" ); for ( ; idxMaterial < m_lstMaterials.Count(); ++ idxMaterial ) { int iOldSize = bufCommand.TellPut(); int const iSizeLimit = CCommand::MaxCommandLength() - 3; char const *szString = CUtlSymbol( m_lstMaterials[idxMaterial] ).String(); bufCommand.Printf( "%s%s", ( idxMaterial > iOldIdx ) ? "*" : "", szString ); if ( bufCommand.TellPut() > iSizeLimit && idxMaterial > iOldIdx ) { bufCommand.SeekPut( CUtlBuffer::SEEK_HEAD, iOldSize ); break; } } bufCommand.Printf( "\"\n" ); bufCommand.PutChar( 0 ); Cbuf_AddText( ( char const * ) bufCommand.Base() ); bufCommand.Clear(); Cbuf_Execute(); } } if ( !stricmp( command, "CopyTxt" ) ) { char const *szName = ( char const * ) m_bufInfoText.Base(); if ( !m_bufInfoText.TellPut() || !szName ) szName = ""; vgui::system()->SetClipboardText( szName, strlen( szName ) + 1 ); } if ( !stricmp( command, "CopyImg" ) ) { int x = 0, y = 0; this->LocalToScreen( x, y ); void *pMainWnd = game->GetMainWindow(); vgui::system()->SetClipboardImage( pMainWnd, x, y, x + GetWide(), y + GetTall() ); } if ( !stricmp( command, "SaveImg" ) ) { SaveTextureImage( m_pInfo->GetString( KEYNAME_NAME ) ); } if ( !stricmp( command, "FlashBtn" ) ) { MatViewOverride::RequestSelectNone(); MatViewOverride::RequestSelected( m_lstMaterials.Count(), m_lstMaterials.Base() ); mat_texture_list_off_f(); } if ( ( !stricmp( command, "size-" ) || !stricmp( command, "size+" ) ) && m_pInfo ) { bool bSizeUp = ( command[4] == '+' ); bool bResult = AdjustTextureSize( m_pInfo->GetString( KEYNAME_NAME ), bSizeUp ); if ( bResult ) { CAutoPushPop auto_g_bRecursiveRequestToShowTextureList( g_bRecursiveRequestToShowTextureList, true ); mat_texture_list_on_f(); } InvalidateLayout(); } if ( !stricmp( command, "ToggleNoMip" ) ) { #ifdef IS_WINDOWS_PC CP4Requirement p4req; if ( !p4 ) g_p4factory->SetDummyMode( true ); char const *szTextureFile = m_pInfo->GetString( KEYNAME_NAME ); char const *szTextureGroup = m_pInfo->GetString( KEYNAME_TEXTURE_GROUP ); ITexture *pMatTexture = NULL; if ( *szTextureFile ) pMatTexture = materials->FindTexture( szTextureFile, szTextureGroup, false ); if ( !pMatTexture ) pMatTexture = materials->FindTexture( "debugempty", "", false ); bool bNewNoMip = !(pMatTexture->GetFlags() & TEXTUREFLAGS_NOMIP); char szFileName[MAX_PATH]; if ( !GetTextureContentPath( szTextureFile, szFileName, sizeof(szFileName) ) ) return; // Figure out what kind of source content is there: // 1. look for TGA - if found, get the txt file (if txt file missing, create one) // 2. otherwise look for PSD - affecting psdinfo // 3. else error char *pExtPut = szFileName + strlen( szFileName ) - strlen( ".vtf" ); // compensating the TEXTURE_FNAME_EXTENSION(.vtf) extension // 1.tga sprintf( pExtPut, ".tga" ); if ( g_pFullFileSystem->FileExists( szFileName ) ) { // Have tga - pump in the txt file sprintf( pExtPut, ".txt" ); CUtlBuffer bufTxtFileBuffer( 0, 0, CUtlBuffer::TEXT_BUFFER ); g_pFullFileSystem->ReadFile( szFileName, 0, bufTxtFileBuffer ); for ( int k = 0; k < 1024; ++ k ) bufTxtFileBuffer.PutChar( 0 ); // Now fix maxwidth/maxheight settings SetBufferValue( ( char * ) bufTxtFileBuffer.Base(), "nomip", bNewNoMip ? "1" : "0" ); bufTxtFileBuffer.SeekPut( CUtlBuffer::SEEK_HEAD, strlen( ( char * ) bufTxtFileBuffer.Base() ) ); // Check out or add the file g_p4factory->SetOpenFileChangeList( "Texture LOD Autocheckout" ); CP4AutoEditFile autop4_edit( szFileName ); // Save the file contents if ( g_pFullFileSystem->WriteFile( szFileName, 0, bufTxtFileBuffer ) ) { Msg(" '%s' : saved.\n", szFileName ); CP4AutoAddFile autop4_add( szFileName ); } else { Warning( " '%s' : failed to save - toggle \"nomip\" manually.\n", szFileName ); } } else { // 2.psd sprintf( pExtPut, ".psd" ); if ( g_pFullFileSystem->FileExists( szFileName ) ) { char chCommand[MAX_PATH]; char szTxtFileName[MAX_PATH] = {0}; GetModSubdirectory( "tmp_lod_psdinfo.txt", szTxtFileName, sizeof( szTxtFileName ) ); sprintf( chCommand, "/C psdinfo \"%s\" > \"%s\"", szFileName, szTxtFileName); vgui::system()->ShellExecuteEx( "open", "cmd.exe", chCommand ); Sys_Sleep( 200 ); CUtlBuffer bufTxtFileBuffer( 0, 0, CUtlBuffer::TEXT_BUFFER ); g_pFullFileSystem->ReadFile( szTxtFileName, 0, bufTxtFileBuffer ); for ( int k = 0; k < 1024; ++ k ) bufTxtFileBuffer.PutChar( 0 ); // Now fix maxwidth/maxheight settings SetBufferValue( ( char * ) bufTxtFileBuffer.Base(), "nomip", bNewNoMip ? "1" : "0" ); bufTxtFileBuffer.SeekPut( CUtlBuffer::SEEK_HEAD, strlen( ( char * ) bufTxtFileBuffer.Base() ) ); // Check out or add the file // Save the file contents if ( g_pFullFileSystem->WriteFile( szTxtFileName, 0, bufTxtFileBuffer ) ) { g_p4factory->SetOpenFileChangeList( "Texture LOD Autocheckout" ); CP4AutoEditFile autop4_edit( szFileName ); sprintf( chCommand, "/C psdinfo -write \"%s\" < \"%s\"", szFileName, szTxtFileName ); Sys_Sleep( 200 ); vgui::system()->ShellExecuteEx( "open", "cmd.exe", chCommand ); Sys_Sleep( 200 ); Msg(" '%s' : saved.\n", szFileName ); CP4AutoAddFile autop4_add( szFileName ); } else { Warning( " '%s' : failed to save - toggle \"nomip\" manually.\n", szFileName ); } } else { // 3. - error sprintf( pExtPut, "" ); Warning( " '%s' : doesn't specify a valid TGA or PSD file!\n", szFileName ); return; } } // Now reload the texture OnCommand( "RebuildVTF" ); #endif // #ifdef IS_WINDOWS_PC } if ( !stricmp( command, "RebuildVTF" ) ) { #ifdef IS_WINDOWS_PC CP4Requirement p4req; if ( !p4 ) g_p4factory->SetDummyMode( true ); CSysModule *pModule; IVTex *pIVTex = VTex_Load( &pModule ); if ( !pIVTex ) return; char const *szTextureFile = m_pInfo->GetString( KEYNAME_NAME ); char szContentFilename[MAX_PATH]; if ( !GetTextureContentPath( szTextureFile, szContentFilename, sizeof(szContentFilename) ) ) return; char pGameDir[MAX_OSPATH]; COM_GetGameDir( pGameDir, sizeof( pGameDir ) ); // Check out the VTF char szVTFFilename[MAX_PATH]; Q_snprintf( szVTFFilename, sizeof( szVTFFilename ) - 1, "%s/materials/%s.vtf", pGameDir, szTextureFile ); g_p4factory->SetOpenFileChangeList( "Texture LOD Autocheckout" ); CP4AutoEditFile autop4_edit( szVTFFilename ); // Get the directory for the outdir StripDirName( szVTFFilename ); // Now we've modified the source, rebuild the texture char *argv[64]; int iArg = 0; argv[iArg++] = ""; argv[iArg++] = "-quiet"; argv[iArg++] = "-UseStandardError"; // These are only here for the -currently released- version of vtex.dll. argv[iArg++] = "-WarningsAsErrors"; argv[iArg++] = "-outdir"; argv[iArg++] = szVTFFilename; argv[iArg++] = szContentFilename; pIVTex->VTex( CubemapsFSFactory, pGameDir, iArg, argv ); VTex_Unload( pModule ); Sys_Sleep( 200 ); // Now reload the texture OnCommand( "Reload" ); #endif // #ifdef IS_WINDOWS_PC } } void CRenderTextureEditor::ApplySchemeSettings( vgui::IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); SetFont( pScheme->GetFont( "Default", IsProportional() ) ); } void CRenderTextureEditor::OnMousePressed( vgui::MouseCode code ) { Close(); } void CRenderTextureEditor::Paint() { CAutoMatSysDebugMode auto_matsysdebugmode; DisableFadeEffect(); SetBgColor( Color( 10, 50, 10, 255 ) ); BaseClass::Paint(); KeyValues *kv = m_pInfo; if ( !kv ) return; char const *szTextureFile = kv->GetString( KEYNAME_NAME ); Q_strncpy( s_chLastViewedTextureBuffer, szTextureFile, sizeof( s_chLastViewedTextureBuffer ) ); char const *szTextureGroup = kv->GetString( KEYNAME_TEXTURE_GROUP ); ITexture *pMatTexture = NULL; if ( *szTextureFile ) pMatTexture = materials->FindTexture( szTextureFile, szTextureGroup, false ); if ( !pMatTexture ) pMatTexture = materials->FindTexture( "debugempty", "", false ); // Determine the texture size int iTxWidth = kv->GetInt( KEYNAME_WIDTH ); int iTxHeight = kv->GetInt( KEYNAME_HEIGHT ); int iTxSize = kv->GetInt( KEYNAME_SIZE ); char const *szTxFormat = kv->GetString( KEYNAME_FORMAT ); int x = TILE_BORDER, y = TILE_BORDER; // Dimensions to draw int iDrawWidth = iTxWidth; int iDrawHeight = iTxHeight; if ( pMatTexture && pMatTexture->IsCubeMap() ) { iDrawWidth = 1024; iDrawHeight = 1024; } if ( iDrawHeight >= iDrawWidth ) { if ( iDrawHeight > TILE_TEXTURE_SIZE ) { iDrawWidth = iDrawWidth * ( float( TILE_TEXTURE_SIZE ) / iDrawHeight ); iDrawHeight = TILE_TEXTURE_SIZE; } if ( iDrawHeight < 64 ) { iDrawWidth = iDrawWidth * ( float( 64 ) / iDrawHeight ); iDrawHeight = 64; } } else { if ( iDrawWidth > TILE_TEXTURE_SIZE ) { iDrawHeight = iDrawHeight * ( float( TILE_TEXTURE_SIZE ) / iDrawWidth ); iDrawWidth = TILE_TEXTURE_SIZE; } if ( iDrawWidth < 64 ) { iDrawHeight = iDrawHeight * ( float( 64 ) / iDrawWidth ); iDrawWidth = 64; } } iDrawHeight = iDrawHeight / ( float( TILE_TEXTURE_SIZE ) / float( TILE_SIZE ) ); iDrawWidth = iDrawWidth / ( float( TILE_TEXTURE_SIZE ) / float( TILE_SIZE ) ); iDrawHeight = max( iDrawHeight, 4 ); iDrawWidth = max( iDrawWidth, 4 ); // // Draw frame // { int tileWidth = 2 * TILE_BORDER + TILE_SIZE; int tileHeight = 3 * TILE_BORDER + TILE_SIZE + TILE_TEXT; g_pMatSystemSurface->DrawSetColor( 255, 255, 255, 255 ); g_pMatSystemSurface->DrawOutlinedRect( x + 1, y + 1, x + tileWidth - 2 , y + tileHeight - 2 ); } // // Draw all // x += TILE_BORDER; y += TILE_BORDER; char chResolveName[ 256 ] = {0}, chResolveNameArg[ 256 ] = {0}; Q_snprintf( chResolveNameArg, sizeof( chResolveNameArg ) - 1, "materials/%s.vtf", szTextureFile ); char const *szResolvedName = g_pFileSystem->RelativePathToFullPath( chResolveNameArg, "game", chResolveName, sizeof( chResolveName ) - 1 ); char const *szPrintFilePrefix = szResolvedName ? "" : "[?]/"; char const *szPrintFileName = szResolvedName ? szResolvedName : szTextureFile; char chSizeBuf[20] = { 0 }; if ( iTxSize >= 0 ) FmtCommaNumber( chSizeBuf, iTxSize ); else chSizeBuf[0] = '-'; g_pMatSystemSurface->DrawColoredTextRect( GetFont(), x, y, TILE_SIZE, TILE_TEXT / 2, 255, 255, 255, 255, "%s%s\n" "%s Kb %dx%d %s", szPrintFilePrefix, szPrintFileName, chSizeBuf, iTxWidth, iTxHeight, szTxFormat ); if ( !m_bufInfoText.TellPut() ) { m_bufInfoText.Printf( "%s%s\r\n" "%s Kb %dx%d %s", szPrintFilePrefix, szPrintFileName, chSizeBuf, iTxWidth, iTxHeight, szTxFormat ); } if ( !kv->GetInt( "SpecialTx" ) ) { char chLine1[256] = {0}; char chLine2[256] = {0}; // // Line 1 // if ( iTxSize > g_warn_texkbytes ) sprintf( chLine1 + strlen( chLine1 ), " Size(%s Kb)", chSizeBuf ); if ( ( iTxWidth > g_warn_texdimensions ) || ( iTxHeight > g_warn_texdimensions ) ) sprintf( chLine1 + strlen( chLine1 ), " Dimensions(%dx%d)", iTxWidth, iTxHeight ); if ( stricmp( szTxFormat, "DXT1" ) && stricmp( szTxFormat, "DXT5" ) ) sprintf( chLine1 + strlen( chLine1 ), " Format(%s)", szTxFormat ); if ( pMatTexture->GetFlags() & TEXTUREFLAGS_NOLOD ) sprintf( chLine1 + strlen( chLine1 ), " NoLod" ); if ( pMatTexture->GetFlags() & TEXTUREFLAGS_NOMIP ) sprintf( chLine1 + strlen( chLine1 ), " NoMip" ); if ( pMatTexture->GetFlags() & TEXTUREFLAGS_ONEBITALPHA ) sprintf( chLine1 + strlen( chLine1 ), " OneBitAlpha" ); // // Line 2 // // Always show stats for higher/lower mips { int wmap = pMatTexture->GetMappingWidth(), hmap = pMatTexture->GetMappingHeight(), dmap = pMatTexture->GetMappingDepth(); int wact = pMatTexture->GetActualWidth(), hact = pMatTexture->GetActualHeight(), dact = pMatTexture->GetActualDepth(); ImageFormat fmt = pMatTexture->GetImageFormat(); if ( wact > 4 || hact > 4 ) { char chbuf[50]; int mem = ImageLoader::GetMemRequired( max( min( wact, 4 ), wact / 2 ), max( min( hact, 4 ), hact / 2 ), max( 1, dact / 2 ), fmt, true ); mem = ( mem + 511 ) / 1024; FmtCommaNumber( chbuf, mem ); sprintf ( chLine2 + strlen( chLine2 ), " %s Kb @ lower mip", chbuf ); } if ( wmap > wact || hmap > hact || dmap > dact ) { char chbuf[ 50 ]; int mem = ImageLoader::GetMemRequired( min( wmap, wact * 2 ), min( hmap, hact * 2 ), min( dmap, dact * 2 ), fmt, true ); mem = ( mem + 511 ) / 1024; FmtCommaNumber( chbuf, mem ); sprintf ( chLine2 + strlen( chLine2 ), " %s Kb @ higher mip", chbuf ); } } if ( chLine1[0] ) { g_pMatSystemSurface->DrawSetColor( 200, 0, 0, 255 ); g_pMatSystemSurface->DrawFilledRect( x - TILE_BORDER/2, y + TILE_TEXT/2, x + TILE_BORDER/2 + (TILE_SIZE * 0.5), y + TILE_TEXT/2 + TILE_TEXT/4 ); g_pMatSystemSurface->DrawColoredTextRect( GetFont(), x, y + TILE_TEXT/2, TILE_SIZE, TILE_TEXT / 4, 255, 255, 255, 255, "%s", chLine1 ); } if ( chLine2[0] ) { // g_pMatSystemSurface->DrawSetColor( 200, 0, 0, 255 ); // g_pMatSystemSurface->DrawFilledRect( x - TILE_BORDER/2, y + TILE_TEXT/2 + TILE_TEXT/4, x + TILE_BORDER/2 + TILE_SIZE, y + TILE_TEXT ); g_pMatSystemSurface->DrawColoredTextRect( GetFont(), x, y + TILE_TEXT/2 + TILE_TEXT/4, TILE_SIZE, TILE_TEXT / 4, 255, 255, 255, 255, "%s", chLine2 ); } } y += TILE_TEXT + TILE_BORDER; // Images placement bool bHasAlpha = !!stricmp( szTxFormat, "DXT1" ); int extTxWidth = TILE_SIZE; int extTxHeight = TILE_SIZE; int orgTxX = 0, orgTxXA = 0; int orgTxY = 0, orgTxYA = 0; if ( bHasAlpha ) { if ( iTxWidth >= iTxHeight * 2 ) { extTxHeight /= 2; orgTxYA = extTxHeight + TILE_BORDER/2; extTxHeight -= 1; } else if ( iTxHeight >= iTxWidth * 2 ) { extTxWidth /= 2; orgTxXA = extTxWidth + TILE_BORDER/2; extTxWidth -= 3; } else { extTxHeight /= 2; orgTxYA = extTxHeight + TILE_BORDER/2; orgTxX = extTxWidth / 4; extTxWidth /= 2; if ( iDrawWidth > extTxWidth ) { iDrawWidth /= 2; iDrawHeight /= 2; } extTxWidth -= 1; extTxHeight -= 1; } } enum { IMG_FRAME_OFF = 2 }; if ( IMaterial *pMaterial = UseDebugMaterial( "debug/debugtexturecolor", pMatTexture, &auto_matsysdebugmode ) ) { g_pMatSystemSurface->DrawSetColor( 255, 255, 255, 255 ); g_pMatSystemSurface->DrawOutlinedRect( x + orgTxX + ( extTxWidth - iDrawWidth ) / 2 - IMG_FRAME_OFF, y + orgTxY + ( extTxHeight - iDrawHeight ) / 2 - IMG_FRAME_OFF, x + orgTxX + ( extTxWidth + iDrawWidth ) / 2 + IMG_FRAME_OFF, y + orgTxY + ( extTxHeight + iDrawHeight ) / 2 + IMG_FRAME_OFF ); RenderTexturedRect( this, pMaterial, x + orgTxX + ( extTxWidth - iDrawWidth ) / 2, y + orgTxY + ( extTxHeight - iDrawHeight ) / 2, x + orgTxX + ( extTxWidth + iDrawWidth ) / 2, y + orgTxY + ( extTxHeight + iDrawHeight ) / 2 ); if ( bHasAlpha ) { orgTxX += orgTxXA; orgTxY += orgTxYA; if ( IMaterial *pMaterialDebug = UseDebugMaterial( "debug/debugtexturealpha", pMatTexture, &auto_matsysdebugmode ) ) { g_pMatSystemSurface->DrawOutlinedRect( x + orgTxX + ( extTxWidth - iDrawWidth ) / 2 - IMG_FRAME_OFF, y + orgTxY + ( extTxHeight - iDrawHeight ) / 2 - IMG_FRAME_OFF, x + orgTxX + ( extTxWidth + iDrawWidth ) / 2 + IMG_FRAME_OFF, y + orgTxY + ( extTxHeight + iDrawHeight ) / 2 + IMG_FRAME_OFF ); RenderTexturedRect( this, pMaterialDebug, x + orgTxX + ( extTxWidth - iDrawWidth ) / 2, y + orgTxY + ( extTxHeight - iDrawHeight ) / 2, x + orgTxX + ( extTxWidth + iDrawWidth ) / 2, y + orgTxY + ( extTxHeight + iDrawHeight ) / 2 ); } } } else { g_pMatSystemSurface->DrawSetColor( 255, 0, 255, 100 ); g_pMatSystemSurface->DrawFilledRect( x + orgTxX + ( extTxWidth - iDrawWidth ) / 2, y + orgTxY + ( extTxWidth - iDrawHeight ) / 2, x + orgTxX + ( extTxWidth + iDrawWidth ) / 2, y + orgTxY + ( extTxWidth + iDrawHeight ) / 2 ); } y += TILE_SIZE + TILE_BORDER; } ////////////////////////////////////////////////////////////////////////// // // Render textures view panel // ////////////////////////////////////////////////////////////////////////// class CRenderTexturesListViewPanel : public vgui::TileViewPanelEx { DECLARE_CLASS_SIMPLE( CRenderTexturesListViewPanel, vgui::TileViewPanelEx ); public: CRenderTexturesListViewPanel( vgui::Panel *parent, char const *szName ); ~CRenderTexturesListViewPanel(); protected: virtual int GetNumTiles(); virtual void GetTileSize( int &wide, int &tall ); virtual void RenderTile( int iTile, int x, int y ); protected: virtual void PerformLayout(); virtual void OnMousePressed( vgui::MouseCode code ); virtual void OnMouseDoublePressed( vgui::MouseCode code ) { OnMousePressed( code ); } public: void SetDataListPanel( vgui::ListPanel *pPanel ); void SetPaintAlpha( bool bPaintAlpha ); CRenderTextureEditor * GetRenderTxEditor() const { return m_pRenderTxEditor; } protected: vgui::ListPanel *m_pListPanel; KeyValues * GetTileData( int iTile ); protected: CRenderTextureEditor *m_pRenderTxEditor; protected: enum { TILE_BORDER = 20, TILE_SIZE = 192, TILE_TEXTURE_SIZE = 256, TILE_TEXT = 35 }; bool m_bPaintAlpha; }; CRenderTexturesListViewPanel::CRenderTexturesListViewPanel( vgui::Panel *parent, char const *szName ) : vgui::TileViewPanelEx( parent, szName ), m_pListPanel( NULL ), m_bPaintAlpha( false ) { m_pRenderTxEditor = new CRenderTextureEditor( this, "TxEdt" ); m_pRenderTxEditor->SetPos( 10, 10 ); m_pRenderTxEditor->PerformLayout(); m_pRenderTxEditor->SetMoveable( true ); m_pRenderTxEditor->SetSizeable( false ); m_pRenderTxEditor->SetClipToParent( true ); m_pRenderTxEditor->SetTitle( "", true ); m_pRenderTxEditor->SetCloseButtonVisible( false ); m_pRenderTxEditor->SetVisible( false ); } CRenderTexturesListViewPanel::~CRenderTexturesListViewPanel() { NULL; } void CRenderTexturesListViewPanel::PerformLayout() { BaseClass::PerformLayout(); } void CRenderTexturesListViewPanel::OnMousePressed( vgui::MouseCode code ) { BaseClass::OnMousePressed( code ); // Figure out which tile got hit and pass its data for preview m_pRenderTxEditor->Close(); if ( !m_pListPanel ) return; int x, y; vgui::input()->GetCursorPos( x, y ); this->ScreenToLocal( x, y ); // Hit test the click int iTile, tileX, tileY; int htResult = HitTest( x, y, iTile ); if ( HT_NOTHING == htResult ) return; if ( !GetTileOrg( iTile, tileX, tileY ) ) return; // Now having the tile retrieve the keyvalues int itemId = m_pListPanel->GetItemIDFromRow( iTile ); if ( itemId < 0 ) return; KeyValues *kv = m_pListPanel->GetItem( itemId ); if ( !kv ) return; // Display the tx editor m_pRenderTxEditor->SetDispInfo( kv, itemId ); if ( tileX + m_pRenderTxEditor->GetWide() > m_li_wide - 2 ) tileX -= tileX + m_pRenderTxEditor->GetWide() - ( m_li_wide - 2 ); if ( tileY + m_pRenderTxEditor->GetTall() > m_li_tall - 2 ) tileY -= tileY + m_pRenderTxEditor->GetTall() - ( m_li_tall - 2 ); int iTopLeftX = 0, iTopLeftY = 0; for ( vgui::Panel *pPanel = this; ( pPanel = pPanel->GetParent() ) != NULL; ) { iTopLeftX = iTopLeftY = 0; pPanel->LocalToScreen( iTopLeftX, iTopLeftY ); } LocalToScreen( tileX, tileY ); if ( tileX < iTopLeftX ) tileX = iTopLeftX; if ( tileY < iTopLeftY ) tileY = iTopLeftY; m_pRenderTxEditor->SetPos( tileX, tileY ); m_pRenderTxEditor->Activate(); } int CRenderTexturesListViewPanel::GetNumTiles() { return m_pListPanel ? m_pListPanel->GetItemCount() : 0; } void CRenderTexturesListViewPanel::GetTileSize( int &wide, int &tall ) { wide = 2 * TILE_BORDER + TILE_SIZE; tall = 2 * TILE_BORDER + TILE_SIZE + TILE_TEXT; }; KeyValues * CRenderTexturesListViewPanel::GetTileData( int iTile ) { int iData = m_pListPanel->GetItemIDFromRow( iTile ); if ( iData < 0 ) return NULL; return m_pListPanel->GetItem( iData ); } void CRenderTexturesListViewPanel::RenderTile( int iTile, int x, int y ) { CAutoMatSysDebugMode auto_matsysdebugmode; KeyValues *kv = GetTileData( iTile ); if ( !kv ) return; char const *szTextureFile = kv->GetString( KEYNAME_NAME ); char const *szTextureGroup = kv->GetString( KEYNAME_TEXTURE_GROUP ); ITexture *pMatTexture = NULL; if ( *szTextureFile ) pMatTexture = materials->FindTexture( szTextureFile, szTextureGroup, false ); if ( !pMatTexture ) pMatTexture = materials->FindTexture( "debugempty", "", false ); // Determine the texture size int iTxWidth = kv->GetInt( KEYNAME_WIDTH ); int iTxHeight = kv->GetInt( KEYNAME_HEIGHT ); int iTxSize = kv->GetInt( KEYNAME_SIZE ); char const *szTxFormat = kv->GetString( KEYNAME_FORMAT ); int iTxFormatLen = strlen( szTxFormat ); char *szTxFormatSuffix = ""; if ( iTxFormatLen > 4 ) { fmtlenreduce: switch ( szTxFormat[ iTxFormatLen - 1 ] ) { case '8': { while ( ( iTxFormatLen > 4 ) && ( szTxFormat[ iTxFormatLen - 2 ] == '8' ) ) -- iTxFormatLen; } break; case '6': { while ( ( iTxFormatLen > 4 ) && ( szTxFormat[ iTxFormatLen - 2 ] == '1' ) && ( szTxFormat[ iTxFormatLen - 3 ] == '6' ) ) iTxFormatLen -= 2; } break; case 'F': if ( !*szTxFormatSuffix ) { iTxFormatLen --; szTxFormatSuffix = "F"; goto fmtlenreduce; } break; } } // Dimensions to draw int iDrawWidth = iTxWidth; int iDrawHeight = iTxHeight; if ( pMatTexture && pMatTexture->IsCubeMap() ) { iDrawWidth = 1024; iDrawHeight = 1024; } if ( iDrawHeight >= iDrawWidth ) { if ( iDrawHeight > TILE_TEXTURE_SIZE ) { iDrawWidth = iDrawWidth * ( float( TILE_TEXTURE_SIZE ) / iDrawHeight ); iDrawHeight = TILE_TEXTURE_SIZE; } if ( iDrawHeight < 64 ) { iDrawWidth = iDrawWidth * ( float( 64 ) / iDrawHeight ); iDrawHeight = 64; } } else { if ( iDrawWidth > TILE_TEXTURE_SIZE ) { iDrawHeight = iDrawHeight * ( float( TILE_TEXTURE_SIZE ) / iDrawWidth ); iDrawWidth = TILE_TEXTURE_SIZE; } if ( iDrawWidth < 64 ) { iDrawHeight = iDrawHeight * ( float( 64 ) / iDrawWidth ); iDrawWidth = 64; } } iDrawHeight = iDrawHeight / ( float( TILE_TEXTURE_SIZE ) / float( TILE_SIZE ) ); iDrawWidth = iDrawWidth / ( float( TILE_TEXTURE_SIZE ) / float( TILE_SIZE ) ); iDrawHeight = max( iDrawHeight, 4 ); iDrawWidth = max( iDrawWidth, 4 ); // // Draw frame // { int tileWidth, tileHeight; GetTileSize( tileWidth, tileHeight ); g_pMatSystemSurface->DrawSetColor( 255, 255, 255, 255 ); g_pMatSystemSurface->DrawOutlinedRect( x + 1, y + 1, x + tileWidth - 2 , y + tileHeight - 2 ); } // // Draw all // x += TILE_BORDER; y += TILE_BORDER/2; int iLenFile = strlen( szTextureFile ); char const *szPrintFilePrefix = ( iLenFile > 22 ) ? "..." : ""; char const *szPrintFileName = ( iLenFile > 22 ) ? ( szTextureFile + iLenFile - 22 ) : szTextureFile; char chSizeBuf[20] = {0}; if ( iTxSize >= 0 ) { FmtCommaNumber( chSizeBuf, iTxSize ); } else { chSizeBuf[0] = '-'; } static Color clrLblNormal( 25, 50, 25, 255 ); static Color clrLblWarn( 75, 75, 0, 255 ); static Color clrLblError( 200, 0, 0, 255 ); bool bWarnTile = ( !kv->GetInt( "SpecialTx" ) ) && ( g_warn_enable && ShallWarnTx( kv, pMatTexture ) ); g_pMatSystemSurface->DrawSetColor( bWarnTile ? clrLblWarn : clrLblNormal ); g_pMatSystemSurface->DrawFilledRect( x - TILE_BORDER/2, y, x + TILE_BORDER/2 + TILE_SIZE, y + TILE_TEXT ); char chInfoText[256] = { 0 }; sprintf( chInfoText, "%s Kb %dx%d %.*s%s %s", chSizeBuf, iTxWidth, iTxHeight, iTxFormatLen, szTxFormat, szTxFormatSuffix, ( pMatTexture->GetFlags() & ( TEXTUREFLAGS_NOLOD | TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_ONEBITALPHA ) ) ? "***" : "" ); int iTextMargins[4] = { 0 }; int iTextHeight = g_pMatSystemSurface->GetFontTall( GetFont() ); { // Compute text extents int iTextLen[4] = { 0 }; iTextLen[0] = 5 + strlen( chSizeBuf ); iTextLen[1] = strchr( chInfoText, 'x' ) + 1 - chInfoText; while ( chInfoText[ iTextLen[1] ] != ' ' ) ++ iTextLen[1]; ++ iTextLen[1]; iTextLen[2] = 2 + iTextLen[1] + iTxFormatLen + strlen( szTxFormatSuffix ); iTextLen[3] = strlen( chInfoText ); for ( int k = 0; k < 4; ++ k ) iTextMargins[k] = g_pMatSystemSurface->DrawTextLen( GetFont(), "%.*s", iTextLen[k], chInfoText ); } // Highlights if ( bWarnTile ) { g_pMatSystemSurface->DrawSetColor( clrLblError ); if ( iTxSize > g_warn_texkbytes ) g_pMatSystemSurface->DrawFilledRect( x - 2, y + iTextHeight + 1, x + iTextMargins[0] - 5, y + TILE_TEXT ); if ( iTxWidth > g_warn_texdimensions || iTxHeight > g_warn_texdimensions ) g_pMatSystemSurface->DrawFilledRect( x + iTextMargins[0] - 2, y + iTextHeight + 1, x + iTextMargins[1] - 1, y + TILE_TEXT ); if ( strcmp( szTxFormat, "DXT1" ) && strcmp( szTxFormat, "DXT5" ) ) g_pMatSystemSurface->DrawFilledRect( x + iTextMargins[1] + 2, y + iTextHeight + 1, x + iTextMargins[2] - 1, y + TILE_TEXT ); if ( pMatTexture->GetFlags() & ( TEXTUREFLAGS_NOLOD | TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_ONEBITALPHA ) ) g_pMatSystemSurface->DrawFilledRect( x + iTextMargins[2] + 3, y + iTextHeight + 1, x + iTextMargins[3] + 2, y + TILE_TEXT ); } g_pMatSystemSurface->DrawColoredTextRect( GetFont(), x, y, TILE_SIZE, TILE_TEXT, 255, 255, 255, 255, "%s%s\n" "%s", szPrintFilePrefix, szPrintFileName, chInfoText ); y += TILE_TEXT + TILE_BORDER/2; // Images placement bool bHasAlpha = m_bPaintAlpha && stricmp( szTxFormat, "DXT1" ); int extTxWidth = TILE_SIZE; int extTxHeight = TILE_SIZE; int orgTxX = 0, orgTxXA = 0; int orgTxY = 0, orgTxYA = 0; if ( bHasAlpha ) { if ( iTxWidth >= iTxHeight * 2 ) { extTxHeight /= 2; orgTxYA = extTxHeight + TILE_BORDER/2; } else if ( iTxHeight >= iTxWidth * 2 ) { extTxWidth /= 2; orgTxXA = extTxWidth + TILE_BORDER/2; x -= TILE_BORDER/4 + 1; } else { extTxHeight /= 2; orgTxYA = extTxHeight + TILE_BORDER/2; orgTxX = extTxWidth / 4; extTxWidth /= 2; x -= TILE_BORDER/4 + 1; if ( iDrawWidth > extTxWidth ) { iDrawWidth /= 2; iDrawHeight /= 2; } } } enum { IMG_FRAME_OFF = 2 }; if ( IMaterial *pMaterial = UseDebugMaterial( "debug/debugtexturecolor", pMatTexture, &auto_matsysdebugmode ) ) { g_pMatSystemSurface->DrawSetColor( 255, 255, 255, 255 ); g_pMatSystemSurface->DrawOutlinedRect( x + orgTxX + ( extTxWidth - iDrawWidth ) / 2 - IMG_FRAME_OFF, y + orgTxY + ( extTxHeight - iDrawHeight ) / 2 - IMG_FRAME_OFF, x + orgTxX + ( extTxWidth + iDrawWidth ) / 2 + IMG_FRAME_OFF, y + orgTxY + ( extTxHeight + iDrawHeight ) / 2 + IMG_FRAME_OFF ); RenderTexturedRect( this, pMaterial, x + orgTxX + ( extTxWidth - iDrawWidth ) / 2, y + orgTxY + ( extTxHeight - iDrawHeight ) / 2, x + orgTxX + ( extTxWidth + iDrawWidth ) / 2, y + orgTxY + ( extTxHeight + iDrawHeight ) / 2, 2, 1 ); if ( bHasAlpha ) { orgTxX += orgTxXA; orgTxY += orgTxYA; if ( IMaterial *pMaterialDebug = UseDebugMaterial( "debug/debugtexturealpha", pMatTexture, &auto_matsysdebugmode ) ) { g_pMatSystemSurface->DrawOutlinedRect( x + orgTxX + ( extTxWidth - iDrawWidth ) / 2 - IMG_FRAME_OFF, y + orgTxY + ( extTxHeight - iDrawHeight ) / 2 - IMG_FRAME_OFF, x + orgTxX + ( extTxWidth + iDrawWidth ) / 2 + IMG_FRAME_OFF, y + orgTxY + ( extTxHeight + iDrawHeight ) / 2 + IMG_FRAME_OFF ); RenderTexturedRect( this, pMaterialDebug, x + orgTxX + ( extTxWidth - iDrawWidth ) / 2, y + orgTxY + ( extTxHeight - iDrawHeight ) / 2, x + orgTxX + ( extTxWidth + iDrawWidth ) / 2, y + orgTxY + ( extTxHeight + iDrawHeight ) / 2, 2, 1 ); } } } else { g_pMatSystemSurface->DrawSetColor( 255, 0, 255, 100 ); g_pMatSystemSurface->DrawFilledRect( x + orgTxX + ( extTxWidth - iDrawWidth ) / 2, y + orgTxY + ( extTxWidth - iDrawHeight ) / 2, x + orgTxX + ( extTxWidth + iDrawWidth ) / 2, y + orgTxY + ( extTxWidth + iDrawHeight ) / 2 ); } y += TILE_SIZE + TILE_BORDER; } void CRenderTexturesListViewPanel::SetDataListPanel( vgui::ListPanel *pPanel ) { m_pListPanel = pPanel; InvalidateLayout(); } void CRenderTexturesListViewPanel::SetPaintAlpha( bool bPaintAlpha ) { m_bPaintAlpha = bPaintAlpha; Repaint(); } //----------------------------------------------------------------------------- // Purpose: Shows entity status report if cl_entityreport cvar is set //----------------------------------------------------------------------------- class CTextureListPanel : public vgui::Frame { DECLARE_CLASS_SIMPLE( CTextureListPanel, vgui::Frame ); public: // Construction CTextureListPanel( vgui::Panel *parent ); virtual ~CTextureListPanel( void ); // Refresh virtual void Paint(); void EndPaint(); // Still inside paint virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); virtual bool ShouldDraw(); virtual void PerformLayout(); virtual void Close(); void OnTurnedOn(); private: void UpdateTotalUsageLabel(); virtual void OnCommand( const char *command ); MESSAGE_FUNC( OnTextChanged, "TextChanged" ); int AddListItem( KeyValues *kv ); bool UpdateDisplayedItem( KeyValues *pDispData, KeyValues *kv ); private: // Font to use for drawing vgui::HFont m_hFont; vgui::ListPanel *m_pListPanel; CRenderTexturesListViewPanel *m_pViewPanel; vgui::CheckButton *m_pSpecialTexs; vgui::CheckButton *m_pResolveTexturePath; CConVarCheckButton *m_pShowTextureMemoryUsageOption; CConVarCheckButton *m_pAllTextures; CConVarCheckButton *m_pViewTextures; vgui::Button *m_pCopyToClipboardButton; vgui::ToggleButton *m_pCollapse; vgui::CheckButton *m_pAlpha; vgui::CheckButton *m_pThumbWarnings; vgui::CheckButton *m_pHideMipped; vgui::CheckButton *m_pFilteringChk; vgui::TextEntry *m_pFilteringText; int m_numDisplayedSizeKB; vgui::Button *m_pReloadAllMaterialsButton; vgui::Button *m_pCommitChangesButton; vgui::Button *m_pDiscardChangesButton; vgui::Label *m_pCVarListLabel; vgui::Label *m_pTotalUsageLabel; }; static int __cdecl KilobytesSortFunc( vgui::ListPanel *pPanel, const vgui::ListPanelItem &item1, const vgui::ListPanelItem &item2 ) { // Reverse sort order so the bigger textures show up first const char *string1 = item1.kv->GetString( KEYNAME_SIZE ); const char *string2 = item2.kv->GetString( KEYNAME_SIZE ); int a = atoi( string1 ); int b = atoi( string2 ); if( a < b ) { return 1; } if( a > b ) { return -1; } return 0; } // // Smart texture list // class CSmartTextureKeyValues { private: CSmartTextureKeyValues( CSmartTextureKeyValues const &x ); CSmartTextureKeyValues& operator = ( CSmartTextureKeyValues const &x ); public: CSmartTextureKeyValues() : m_p( NULL ) { if ( KeyValues *p = g_pMaterialSystemDebugTextureInfo->GetDebugTextureList() ) m_p = p->MakeCopy(); } ~CSmartTextureKeyValues() { if ( m_p ) m_p->deleteThis(); m_p = NULL; } KeyValues * Get() const { return m_p; }; protected: KeyValues *m_p; }; //----------------------------------------------------------------------------- // Purpose: Instances the entity report panel // Input : *parent - //----------------------------------------------------------------------------- CTextureListPanel::CTextureListPanel( vgui::Panel *parent ) : BaseClass( parent, "CTextureListPanel" ), m_numDisplayedSizeKB( 0 ) { // Need parent here, before loading up textures, so getSurfaceBase // will work on this panel ( it's null otherwise ) SetSize( videomode->GetModeStereoWidth() - 20, videomode->GetModeStereoHeight() - 20 ); SetPos( 10, 10 ); SetVisible( true ); SetCursor( null ); SetTitle( "Texture list", false ); SetMenuButtonVisible( false ); m_hFont = vgui::INVALID_FONT; SetFgColor( Color( 0, 0, 0, 255 ) ); SetPaintBackgroundEnabled( true ); // Init the buttons. m_pCVarListLabel = vgui::SETUP_PANEL( new vgui::Label( this, "m_pCVarListLabel", "cvars: mat_texture_limit, mat_texture_list, mat_picmip, mat_texture_list_txlod, mat_texture_list_txlod_sync" ) ); m_pCVarListLabel->SetVisible( false ); // m_pCVarListLabel->SetVisible( true ); m_pTotalUsageLabel = vgui::SETUP_PANEL( new vgui::Label( this, "m_pTotalUsageLabel", "" ) ); m_pTotalUsageLabel->SetVisible( true ); m_pSpecialTexs = vgui::SETUP_PANEL( new vgui::CheckButton( this, "service", "Render Targets and Special Textures" ) ); m_pSpecialTexs->SetVisible( true ); m_pSpecialTexs->AddActionSignalTarget( this ); m_pSpecialTexs->SetCommand( "service" ); m_pResolveTexturePath = vgui::SETUP_PANEL( new vgui::CheckButton( this, "resolvepath", "Resolve Full Texture Path" ) ); m_pResolveTexturePath->SetVisible( true ); m_pResolveTexturePath->AddActionSignalTarget( this ); m_pResolveTexturePath->SetCommand( "resolvepath" ); m_pShowTextureMemoryUsageOption = vgui::SETUP_PANEL( new CConVarCheckButton( this, "m_pShowTextureMemoryUsageOption", "Show Memory Usage on HUD" ) ); m_pShowTextureMemoryUsageOption->SetVisible( true ); m_pShowTextureMemoryUsageOption->SetConVar( &mat_show_texture_memory_usage ); m_pAllTextures = vgui::SETUP_PANEL( new CConVarCheckButton( this, "m_pAllTextures", "Show ALL textures" ) ); m_pAllTextures->SetVisible( true ); m_pAllTextures->SetConVar( &mat_texture_list_all ); m_pAllTextures->AddActionSignalTarget( this ); m_pAllTextures->SetCommand( "AllTextures" ); m_pViewTextures = vgui::SETUP_PANEL( new CConVarCheckButton( this, "m_pViewTextures", "View textures thumbnails" ) ); m_pViewTextures->SetVisible( true ); m_pViewTextures->SetConVar( &mat_texture_list_view ); m_pViewTextures->AddActionSignalTarget( this ); m_pViewTextures->SetCommand( "ViewThumbnails" ); m_pCopyToClipboardButton = vgui::SETUP_PANEL( new vgui::Button( this, "CopyToClipboard", "Copy to Clipboard" ) ); if ( m_pCopyToClipboardButton ) { m_pCopyToClipboardButton->AddActionSignalTarget( this ); m_pCopyToClipboardButton->SetCommand( COPYTOCLIPBOARD_CMDNAME ); } m_pCollapse = vgui::SETUP_PANEL( new vgui::ToggleButton( this, "Collapse", " " ) ); m_pCollapse->AddActionSignalTarget( this ); m_pCollapse->SetCommand( "Collapse" ); m_pCollapse->SetSelected( true ); m_pAlpha = vgui::SETUP_PANEL( new vgui::CheckButton( this, "ShowAlpha", "Alpha" ) ); m_pAlpha->AddActionSignalTarget( this ); m_pAlpha->SetCommand( "ShowAlpha" ); bool bDefaultTxAlphaOn = true; m_pAlpha->SetSelected( bDefaultTxAlphaOn ); m_pThumbWarnings = vgui::SETUP_PANEL( new vgui::CheckButton( this, "ThumbWarnings", "Warns" ) ); m_pThumbWarnings->AddActionSignalTarget( this ); m_pThumbWarnings->SetCommand( "ThumbWarnings" ); m_pThumbWarnings->SetSelected( g_warn_enable ); // Filtering m_pHideMipped = vgui::SETUP_PANEL( new vgui::CheckButton( this, "HideMipped", "Hide Mipped" ) ); m_pHideMipped->AddActionSignalTarget( this ); m_pHideMipped->SetCommand( "HideMipped" ); m_pHideMipped->SetSelected( false ); // Filtering m_pFilteringChk = vgui::SETUP_PANEL( new vgui::CheckButton( this, "FilteringChk", "Filter: " ) ); m_pFilteringChk->AddActionSignalTarget( this ); m_pFilteringChk->SetCommand( "FilteringChk" ); m_pFilteringChk->SetSelected( true ); m_pFilteringText = vgui::SETUP_PANEL( new vgui::TextEntry( this, "FilteringTxt" ) ); m_pFilteringText->AddActionSignalTarget( this ); m_pReloadAllMaterialsButton = vgui::SETUP_PANEL( new vgui::Button( this, "ReloadAllMaterials", "Reload All Materials" ) ); if ( m_pReloadAllMaterialsButton ) { m_pReloadAllMaterialsButton->AddActionSignalTarget( this ); m_pReloadAllMaterialsButton->SetCommand( "ReloadAllMaterials" ); } m_pCommitChangesButton = vgui::SETUP_PANEL( new vgui::Button( this, "CommitChanges", "Commit Changes" ) ); if ( m_pCommitChangesButton ) { m_pCommitChangesButton->AddActionSignalTarget( this ); m_pCommitChangesButton->SetCommand( "CommitChanges" ); } m_pDiscardChangesButton = vgui::SETUP_PANEL( new vgui::Button( this, "DiscardChanges", "Discard Changes" ) ); if ( m_pDiscardChangesButton ) { m_pDiscardChangesButton->AddActionSignalTarget( this ); m_pDiscardChangesButton->SetCommand( "DiscardChanges" ); } // Create the tree control itself. m_pListPanel = vgui::SETUP_PANEL( new vgui::ListPanel( this, "List Panel" ) ); m_pListPanel->SetVisible( !mat_texture_list_view.GetBool() ); int col = -1; m_pListPanel->AddColumnHeader( ++ col, KEYNAME_NAME, "Texture Name", 200, 100, 700, vgui::ListPanel::COLUMN_RESIZEWITHWINDOW ); m_pListPanel->AddColumnHeader( ++ col, KEYNAME_PATH, "Path", 50, 50, 300, 0 ); m_pListPanel->AddColumnHeader( ++ col, KEYNAME_SIZE, "Kilobytes", 50, 50, 50, 0 ); m_pListPanel->SetSortFunc( col, KilobytesSortFunc ); m_pListPanel->SetSortColumnEx( col, 0, true ); // advanced sorting setup m_pListPanel->AddColumnHeader( ++ col, KEYNAME_TEXTURE_GROUP, "Group", 100, 100, 300, 0 ); m_pListPanel->AddColumnHeader( ++ col, KEYNAME_FORMAT, "Format", 250, 50, 300, 0 ); m_pListPanel->AddColumnHeader( ++ col, KEYNAME_WIDTH, "Width", 50, 50, 50, 0 ); m_pListPanel->AddColumnHeader( ++ col, KEYNAME_HEIGHT, "Height", 50, 50, 50, 0 ); m_pListPanel->AddColumnHeader( ++ col, KEYNAME_BINDS_FRAME, "# Binds", 50, 50, 50, 0 ); m_pListPanel->AddColumnHeader( ++ col, KEYNAME_BINDS_MAX, "BindsMax", 50, 50, 50, 0 ); SetBgColor( Color( 0, 0, 0, 100 ) ); m_pListPanel->SetBgColor( Color( 0, 0, 0, 100 ) ); // Create the view control itself m_pViewPanel = vgui::SETUP_PANEL( new CRenderTexturesListViewPanel( this, "View Panel" ) ); m_pViewPanel->SetVisible( mat_texture_list_view.GetBool() ); m_pViewPanel->SetBgColor( Color( 0, 0, 0, 255 ) ); m_pViewPanel->SetDragEnabled( false ); m_pViewPanel->SetDropEnabled( false ); m_pViewPanel->SetPaintAlpha( bDefaultTxAlphaOn ); m_pViewPanel->SetDataListPanel( m_pListPanel ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CTextureListPanel::~CTextureListPanel( void ) { g_pTextureListPanel = NULL; } void CTextureListPanel::ApplySchemeSettings( vgui::IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); // If you change this font, be sure to mark it with // $use_in_fillrate_mode in its .vmt file m_hFont = pScheme->GetFont( "DefaultVerySmall", false ); Assert( m_hFont ); } //----------------------------------------------------------------------------- // Purpose: // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool CTextureListPanel::ShouldDraw( void ) { if ( mat_texture_list.GetInt() ) return true; if ( s_eTxListPanelRequest == TXR_SHOW || s_eTxListPanelRequest == TXR_RUNNING ) return true; return false; } void CTextureListPanel::PerformLayout() { BaseClass::PerformLayout(); // Put the collapse button in the corner m_pCollapse->SetPos( 2, 10 ); m_pCollapse->SetSize( 10, 10 ); m_pCollapse->SetVisible( true ); bool bCollapsed = m_pCollapse->IsSelected(); int x, y, w, t; GetClientArea( x, y, w, t ); int yOffset = y; // The cvar reminder goes on the top. m_pCVarListLabel->SetPos( x, yOffset ); m_pCVarListLabel->SetWide( w ); NULL; // yOffset += m_pCVarListLabel->GetTall(); m_pCVarListLabel->SetVisible( false ); // m_pCVarListLabel->SetVisible( !bCollapsed ); m_pTotalUsageLabel->SetPos( x, yOffset ); m_pTotalUsageLabel->SetWide( w ); yOffset += m_pTotalUsageLabel->GetTall(); m_pTotalUsageLabel->SetVisible( !bCollapsed ); // Align the check boxes. vgui::Panel *buttons[] = { m_pSpecialTexs, m_pShowTextureMemoryUsageOption, m_pAllTextures, m_pViewTextures, m_pFilteringChk, m_pHideMipped, m_pResolveTexturePath, m_pCopyToClipboardButton }; for ( int i=0; i < ARRAYSIZE( buttons ); i++ ) { buttons[i]->SetPos( x, yOffset ); buttons[i]->SetWide( w/2 ); yOffset += buttons[i]->GetTall(); buttons[i]->SetVisible( !bCollapsed ); if ( buttons[i] == m_pViewTextures ) { m_pViewTextures->SetWide( 170 ); int accumw = 170; m_pAlpha->SetPos( x + accumw + 5, yOffset - m_pViewTextures->GetTall() ); m_pAlpha->SetWide( (accumw += 85, 85) ); m_pThumbWarnings->SetPos( x + accumw + 5, yOffset - m_pViewTextures->GetTall() ); m_pThumbWarnings->SetWide( (accumw += 85, 85) ); } if ( buttons[i] == m_pFilteringChk ) { m_pFilteringChk->SetWide( 60 ); int accumw = 60; m_pFilteringText->SetPos( x + accumw + 5, yOffset - m_pFilteringChk->GetTall() ); m_pFilteringText->SetWide( ( accumw += 170, 170 ) ); m_pFilteringText->SetTall( m_pFilteringChk->GetTall() ); m_pFilteringText->SetVisible( !bCollapsed ); } } if ( bCollapsed ) { int xOffset = 85, iWidth; struct LayoutHorz_t { vgui::Panel *pPanel; int iWidth; } layout[] = { { m_pTotalUsageLabel, 290 }, { m_pViewTextures, 170 }, { m_pAlpha, 60 }, { m_pAllTextures, 135 }, { m_pHideMipped, 100 }, { m_pFilteringChk, 60 }, { m_pFilteringText, 130 }, { m_pReloadAllMaterialsButton, 130 }, { m_pCommitChangesButton, 130 }, { m_pDiscardChangesButton, 130 }, }; for ( int k = 0; k < ARRAYSIZE( layout ); ++ k ) { layout[ k ].pPanel->SetPos( xOffset, 2 ); iWidth = layout[ k ].iWidth; iWidth = min( w - xOffset - 30, iWidth ); layout[ k ].pPanel->SetWide( iWidth ); layout[ k ].pPanel->SetVisible( iWidth > 50 ); if ( iWidth > 50 ) xOffset += iWidth + 5; } yOffset = y; } m_pAlpha->SetVisible( m_pViewTextures->IsSelected() ); m_pThumbWarnings->SetVisible( !bCollapsed && m_pViewTextures->IsSelected() ); m_pListPanel->SetBounds( x, yOffset, w, t - (yOffset - y) ); m_pViewPanel->SetBounds( x, yOffset, w, t - (yOffset - y) ); m_pListPanel->SetVisible( !mat_texture_list_view.GetBool() ); m_pViewPanel->SetVisible( mat_texture_list_view.GetBool() ); } bool StripDirName( char *pFilename ) { if ( pFilename[0] == 0 ) return false; char *pLastSlash = pFilename; while ( 1 ) { char *pTestSlash = strchr( pLastSlash, '/' ); if ( !pTestSlash ) { pTestSlash = strchr( pLastSlash, '\\' ); if ( !pTestSlash ) break; } pTestSlash++; // Skip past the slash. pLastSlash = pTestSlash; } if ( pLastSlash == pFilename ) { return false; } else { Assert( pLastSlash[-1] == '/' || pLastSlash[-1] == '\\' ); pLastSlash[-1] = 0; return true; } } static inline void ToLowerInplace( char *chBuffer ) { for ( char *pch = chBuffer; *pch; ++ pch ) { if ( V_isupper( *pch ) ) *pch = tolower( *pch ); } } void KeepSpecialKeys( KeyValues *textureList, bool bServiceKeys ) { KeyValues *pNext; for ( KeyValues *pCur = textureList->GetFirstSubKey(); pCur; pCur=pNext ) { pNext = pCur->GetNextKey(); bool bIsServiceKey = false; char const *szName = pCur->GetString( KEYNAME_NAME ); if ( StringHasPrefix( szName, "_" ) || StringHasPrefix( szName, "[" ) || !stricmp( szName, "backbuffer" ) || StringHasPrefix( szName, "colorcorrection" ) || !stricmp( szName, "depthbuffer" ) || !stricmp( szName, "frontbuffer" ) || !stricmp( szName, "normalize" ) || !*szName ) { bIsServiceKey = true; } if ( bIsServiceKey != bServiceKeys ) { textureList->RemoveSubKey( pCur ); } else if ( bIsServiceKey ) { pCur->SetInt( "SpecialTx", 1 ); } } } void KeepKeysMatchingFilter( KeyValues *textureList, char const *szFilter ) { if ( !szFilter || !*szFilter ) return; char chFilter[MAX_PATH] = {0}, chName[MAX_PATH] = {0}; Q_strncpy( chFilter, szFilter, sizeof( chFilter ) - 1 ); ToLowerInplace( chFilter ); KeyValues *pNext; for ( KeyValues *pCur=textureList->GetFirstSubKey(); pCur; pCur=pNext ) { pNext = pCur->GetNextKey(); char const *szName = pCur->GetString( KEYNAME_NAME ); Q_strncpy( chName, szName, sizeof( chName ) - 1 ); ToLowerInplace( chName ); if ( !strstr( chName, chFilter ) ) { textureList->RemoveSubKey( pCur ); } } } void KeepKeysMarkedNoMip( KeyValues *textureList ) { KeyValues *pNext; for ( KeyValues *pCur=textureList->GetFirstSubKey(); pCur; pCur=pNext ) { pNext = pCur->GetNextKey(); char const *szTextureFile = pCur->GetString( KEYNAME_NAME ); char const *szTextureGroup = pCur->GetString( KEYNAME_TEXTURE_GROUP ); if ( *szTextureFile ) { ITexture *pMatTexture = materials->FindTexture( szTextureFile, szTextureGroup, false ); if ( pMatTexture && !(pMatTexture->GetFlags() & TEXTUREFLAGS_NOMIP) ) { textureList->RemoveSubKey( pCur ); } } } } void CTextureListPanel::UpdateTotalUsageLabel() { char data[1024], kb1[20], kb2[20], kb3[20]; FmtCommaNumber( kb1, (g_pMaterialSystemDebugTextureInfo->GetTextureMemoryUsed( IDebugTextureInfo::MEMORY_BOUND_LAST_FRAME ) + 511) / 1024 ); FmtCommaNumber( kb2, (g_pMaterialSystemDebugTextureInfo->GetTextureMemoryUsed( IDebugTextureInfo::MEMORY_TOTAL_LOADED ) + 511) / 1024 ); FmtCommaNumber( kb3, m_numDisplayedSizeKB ); if ( bool bCollapsed = m_pCollapse->IsSelected() ) { char const *szTitle = ""; Q_snprintf( data, sizeof( data ), "%s[F %s Kb] / [T %s Kb] / [S %s Kb]", szTitle, kb1, kb2, kb3 ); } else { char const *szTitle = "Texture Memory Usage"; char kbMip1[ 20 ], kbMip2[ 20 ]; FmtCommaNumber( kbMip1, (g_pMaterialSystemDebugTextureInfo->GetTextureMemoryUsed( IDebugTextureInfo::MEMORY_ESTIMATE_PICMIP_1 ) + 511) / 1024 ); FmtCommaNumber( kbMip2, (g_pMaterialSystemDebugTextureInfo->GetTextureMemoryUsed( IDebugTextureInfo::MEMORY_ESTIMATE_PICMIP_2 ) + 511) / 1024 ); Q_snprintf( data, sizeof( data ), "%s: frame %s Kb / total %s Kb ( picmip1 = %s Kb, picmip2 = %s Kb ) / shown %s Kb", szTitle, kb1, kb2, kbMip1, kbMip2, kb3 ); } wchar_t unicodeString[1024]; g_pVGuiLocalize->ConvertANSIToUnicode( data, unicodeString, sizeof( unicodeString ) ); m_pTotalUsageLabel->SetText( unicodeString ); } void CTextureListPanel::OnTextChanged( void ) { OnCommand( "FilteringTxt" ); } void CTextureListPanel::OnCommand( const char *command ) { if ( !Q_stricmp( command, "Close" ) ) { vgui::Frame::OnCommand( command ); return; } if ( !Q_stricmp( command, "Collapse" ) ) { InvalidateLayout(); return; } if ( !Q_stricmp( command, "ShowAlpha" ) ) { m_pViewPanel->SetPaintAlpha( m_pAlpha->IsSelected() ); return; } if ( !Q_stricmp( command, "ThumbWarnings" ) ) { g_warn_enable = m_pThumbWarnings->IsSelected(); return; } if ( !Q_stricmp( command, "ViewThumbnails" ) ) { InvalidateLayout(); return; } if ( !Q_stricmp( command, COPYTOCLIPBOARD_CMDNAME ) ) { CopyListPanelToClipboard( m_pListPanel ); return; } if ( !Q_stricmp( command, "ReloadAllMaterials" ) ) { Cbuf_AddText( "mat_reloadallmaterials" ); Cbuf_Execute(); return; } if ( !Q_stricmp( command, "CommitChanges" ) ) { Cbuf_AddText( "mat_texture_list_txlod_sync save" ); Cbuf_Execute(); return; } if ( !Q_stricmp( command, "DiscardChanges" ) ) { Cbuf_AddText( "mat_texture_list_txlod_sync reset" ); Cbuf_Execute(); return; } mat_texture_list_on_f(); InvalidateLayout(); } bool CTextureListPanel::UpdateDisplayedItem( KeyValues *pDispData, KeyValues *kv ) { // Update the item? bool bUpdate = false; // do the stuff that changes often separately. if ( pDispData->GetInt( KEYNAME_BINDS_FRAME ) != kv->GetInt( KEYNAME_BINDS_FRAME ) ) { pDispData->SetInt( KEYNAME_BINDS_FRAME, kv->GetInt( KEYNAME_BINDS_FRAME ) ); bUpdate = true; } if ( pDispData->GetInt( KEYNAME_BINDS_MAX ) != kv->GetInt( KEYNAME_BINDS_MAX ) ) { pDispData->SetInt( KEYNAME_BINDS_MAX, kv->GetInt( KEYNAME_BINDS_MAX ) ); bUpdate = true; } // stuff that changes less frequently if( pDispData->GetInt( KEYNAME_SIZE ) != kv->GetInt( KEYNAME_SIZE ) || pDispData->GetInt( KEYNAME_WIDTH ) != kv->GetInt( KEYNAME_WIDTH ) || pDispData->GetInt( KEYNAME_HEIGHT ) != kv->GetInt( KEYNAME_HEIGHT ) || Q_stricmp( pDispData->GetString( KEYNAME_FORMAT ), kv->GetString( KEYNAME_FORMAT ) ) != 0 || Q_stricmp( pDispData->GetString( KEYNAME_PATH ), kv->GetString( KEYNAME_PATH ) ) != 0 || Q_stricmp( pDispData->GetString( KEYNAME_TEXTURE_GROUP ), kv->GetString( KEYNAME_TEXTURE_GROUP ) ) != 0 ) { pDispData->SetInt( KEYNAME_SIZE, kv->GetInt( KEYNAME_SIZE ) ); pDispData->SetInt( KEYNAME_WIDTH, kv->GetInt( KEYNAME_WIDTH ) ); pDispData->SetInt( KEYNAME_HEIGHT, kv->GetInt( KEYNAME_HEIGHT ) ); pDispData->SetString( KEYNAME_FORMAT, kv->GetString( KEYNAME_FORMAT ) ); pDispData->SetString( KEYNAME_PATH, kv->GetString( KEYNAME_PATH ) ); pDispData->SetString( KEYNAME_TEXTURE_GROUP, kv->GetString( KEYNAME_TEXTURE_GROUP ) ); bUpdate = true; } return bUpdate; } int CTextureListPanel::AddListItem( KeyValues *kv ) { int iItem = m_pListPanel->GetItem( kv->GetString( KEYNAME_NAME ) ); if ( iItem == -1 ) { // Set this so the GetItem() call above can use the key's name (as opposed to the value of its // "Name" subkey) to find this texture again. kv->SetName( kv->GetString( KEYNAME_NAME ) ); // Add this one. iItem = m_pListPanel->AddItem( kv, 0, false, false ); m_pViewPanel->InvalidateLayout(); } else { KeyValues *pValues = m_pListPanel->GetItem( iItem ); bool bNeedUpdate = UpdateDisplayedItem( pValues, kv ); if( bNeedUpdate ) { m_pListPanel->ApplyItemChanges( iItem ); m_pViewPanel->Repaint(); } } return iItem; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTextureListPanel::OnTurnedOn() { if ( g_bRecursiveRequestToShowTextureList ) return; if ( m_pListPanel ) m_pListPanel->DeleteAllItems(); if ( CRenderTextureEditor *pRe = m_pViewPanel->GetRenderTxEditor() ) pRe->Close(); MakePopup( false, false ); MoveToFront(); } void CTextureListPanel::Close() { mat_texture_list_off_f(); } void CTextureListPanel::EndPaint() { UpdateTotalUsageLabel(); } void CTextureListPanel::Paint() { VPROF( "CTextureListPanel::Paint" ); if ( !m_hFont ) return; struct EndPaint_t { CTextureListPanel *m_p; EndPaint_t( CTextureListPanel *p ) : m_p( p ) {} ~EndPaint_t() { m_p->EndPaint(); } } endpaint( this ); if ( !mat_texture_list.GetBool() ) return; if ( !g_pMaterialSystemDebugTextureInfo->IsDebugTextureListFresh( 1 ) ) return; CSmartTextureKeyValues textureList; if ( !textureList.Get() ) return; CRenderTextureEditor *pRte = m_pViewPanel->GetRenderTxEditor(); if ( ( s_eTxListPanelRequest == TXR_RUNNING ) && pRte->IsVisible() ) { KeyValues *kv = NULL; int iHint = 0; pRte->GetDispInfo( kv, iHint ); if ( kv && iHint ) { KeyValues *plv = ( m_pListPanel->IsValidItemID( iHint ) ? m_pListPanel->GetItem( iHint ) : NULL ); if ( plv && !strcmp( plv->GetString( KEYNAME_NAME ), kv->GetString( KEYNAME_NAME ) ) ) { KeyValues *pValData = plv->GetFirstValue(), *pValRendered = kv->GetFirstValue(); for ( ; pValData && pValRendered; pValData = pValData->GetNextValue(), pValRendered = pValRendered->GetNextValue() ) { if ( strcmp( pValData->GetString(), pValRendered->GetString() ) ) break; } if ( pValData || pValRendered ) // Difference found pRte->SetDispInfo( plv, iHint ); } else kv = NULL; } if ( 0 ) // if ( !kv ) { pRte->Close(); pRte->SetDispInfo( NULL, 0 ); } } // If we are fetching all textures, then stop loading material system: if ( mat_texture_list_all.GetBool() ) { if ( s_eTxListPanelRequest == TXR_RUNNING ) { mat_texture_list.SetValue( 0 ); s_eTxListPanelRequest = TXR_SHOW; // Keep displaying our panel } else { s_eTxListPanelRequest = TXR_RUNNING; } } else { if ( s_eTxListPanelRequest == TXR_SHOW ) { // Either first show or turned off "all textures" m_pListPanel->RemoveAll(); m_pViewPanel->InvalidateLayout(); s_eTxListPanelRequest = TXR_RUNNING; return; } } CBitVec<4096 * 8> itemsTouched; // Remove the textures we don't care for KeepSpecialKeys( textureList.Get(), m_pSpecialTexs->IsSelected() ); // If filtering is enabled, then do filtering if ( m_pFilteringChk->IsSelected() && m_pFilteringText->GetTextLength() ) { char chFilterString[ MAX_PATH ]; m_pFilteringText->GetText( chFilterString, sizeof( chFilterString ) - 1 ); chFilterString[ sizeof( chFilterString ) - 1 ] = 0; KeepKeysMatchingFilter( textureList.Get(), chFilterString ); } // If we're to hide mipped, remove anything that isn't marked nomip if ( m_pHideMipped->IsSelected() ) { KeepKeysMarkedNoMip( textureList.Get() ); } // Compute the total size of the displayed textures int cbTotalDisplayedSizeInBytes = 0; for ( KeyValues *pCur = textureList.Get()->GetFirstSubKey(); pCur; pCur=pCur->GetNextKey() ) { int sizeInBytes = pCur->GetInt( KEYNAME_SIZE ); // Accumulate cbTotalDisplayedSizeInBytes += sizeInBytes; // Factor in frames int numCount = pCur->GetInt( "Count" ); if ( numCount > 1 ) sizeInBytes *= numCount; // Size is in kilobytes. int sizeInKilo = ( sizeInBytes + 511 ) / 1024; pCur->SetInt( KEYNAME_SIZE, sizeInKilo ); if ( m_pResolveTexturePath->IsSelected() ) { char chResolveName[ 256 ] = {0}, chResolveNameArg[ 256 ] = {0}; Q_snprintf( chResolveNameArg, sizeof( chResolveNameArg ) - 1, "materials/%s.vtf", pCur->GetString( KEYNAME_NAME ) ); char const *szResolvedName = g_pFileSystem->RelativePathToFullPath( chResolveNameArg, "game", chResolveName, sizeof( chResolveName ) - 1 ); if ( szResolvedName ) { pCur->SetString( KEYNAME_PATH, szResolvedName ); } } int iItem = AddListItem( pCur ); if ( iItem < itemsTouched.GetNumBits() ) itemsTouched.Set( iItem ); } // Set the displayed size m_numDisplayedSizeKB = ( cbTotalDisplayedSizeInBytes + 511 ) / 1024; // Now remove from view items that weren't used. int iNext, numRemoved = 0; for ( int iCur=m_pListPanel->FirstItem(); iCur != m_pListPanel->InvalidItemID(); iCur=iNext ) { iNext = m_pListPanel->NextItem( iCur ); if ( iCur >= itemsTouched.GetNumBits() || !itemsTouched.Get( iCur ) ) { m_pListPanel->RemoveItem( iCur ); ++ numRemoved; } } // Sorting in list panel { int iPri, iSec; bool bAsc; m_pListPanel->GetSortColumnEx( iPri, iSec, bAsc ); iSec = 0; // always secondary sort by name m_pListPanel->SetSortColumnEx( iPri, iSec, bAsc ); m_pListPanel->SortList(); } if ( numRemoved ) { m_pViewPanel->InvalidateLayout(); } } // ------------------------------------------------------------------------------ // // Global functions. // ------------------------------------------------------------------------------ // void VGui_UpdateTextureListPanel() { if ( mat_show_texture_memory_usage.GetInt() ) { con_nprint_t info; info.index = 4; info.time_to_live = 0.2; info.color[0] = 1; info.color[1] = 0.5; info.color[2] = 0; info.fixed_width_font = true; char kb1[20], kb2[20]; FmtCommaNumber( kb1, (g_pMaterialSystemDebugTextureInfo->GetTextureMemoryUsed( IDebugTextureInfo::MEMORY_BOUND_LAST_FRAME ) + 511) / 1024 ); FmtCommaNumber( kb2, (g_pMaterialSystemDebugTextureInfo->GetTextureMemoryUsed( IDebugTextureInfo::MEMORY_TOTAL_LOADED ) + 511) / 1024 ); Con_NXPrintf( &info, "Texture Memory Usage: %s Kb / %s Kb", kb1, kb2 ); } MatViewOverride::DisplaySelectedTextures(); if ( IsX360() ) return; g_pMaterialSystemDebugTextureInfo->EnableGetAllTextures( mat_texture_list_all.GetBool() ); g_pMaterialSystemDebugTextureInfo->EnableDebugTextureList( ( mat_texture_list.GetInt() <= 0 ) ? false : true ); bool bShouldDrawTxListPanel = g_pTextureListPanel->ShouldDraw(); if ( g_pTextureListPanel->IsVisible() != bShouldDrawTxListPanel ) { g_pTextureListPanel->SetVisible( bShouldDrawTxListPanel ); bShouldDrawTxListPanel ? mat_texture_list_on_f() : mat_texture_list_off_f(); } } void CL_CreateTextureListPanel( vgui::Panel *parent ) { g_pTextureListPanel = new CTextureListPanel( parent ); } CON_COMMAND( mat_texture_save_fonts, "Save all font textures" ) { for( int i = 0; i < 8192; i++ ) { char szTextureName[ MAX_PATH ]; Q_snprintf( szTextureName, ARRAYSIZE( szTextureName ), "__font_page_%d.tga", i ); if( !materials->IsTextureLoaded( szTextureName ) ) break; ITexture *pMatTexture = materials->FindTexture( szTextureName, "", false ); if( pMatTexture && !pMatTexture->IsError() ) { bool bRet = SaveTextureImage( szTextureName ); Msg( "SaveTextureImage( '%s' ): %s\n", szTextureName, bRet ? "succeeded" : "failed" ); } } } void mat_texture_list_on_f() { ConVarRef sv_cheats( "sv_cheats" ); if ( sv_cheats.IsValid() && !sv_cheats.GetBool() ) return; ConVarRef mat_queue_mode( "mat_queue_mode" ); if( mat_queue_mode.IsValid() && ( g_nSaveQueueState == INT_MIN ) ) { g_nSaveQueueState = mat_queue_mode.GetInt(); mat_queue_mode.SetValue( 0 ); } mat_texture_list.SetValue( 1 ); s_eTxListPanelRequest = TXR_SHOW; g_pTextureListPanel->OnTurnedOn(); MatViewOverride::RequestSelectNone(); // On Linux, the mouse gets recentered when it's hidden. So if you bring up the texture list // dialog while the game is running, we need to make sure the mouse is shown. Otherwise it's // very tough to use when your mouse keeps getting recentered. if( !g_cursorset && g_pVGuiSurface ) { g_pVGuiSurface->SetCursorAlwaysVisible( true ); g_cursorset = true; } } void mat_texture_list_off_f() { mat_texture_list.SetValue( 0 ); s_eTxListPanelRequest = TXR_HIDE; if( g_cursorset ) { g_pVGuiSurface->SetCursorAlwaysVisible( false ); g_cursorset = false; } if( g_nSaveQueueState != INT_MIN ) { ConVarRef mat_queue_mode( "mat_queue_mode" ); mat_queue_mode.SetValue( g_nSaveQueueState ); g_nSaveQueueState = INT_MIN; } }