csgo-2018-source/vgui2/matsys_controls/particlepicker.cpp
2021-07-24 21:11:47 -07:00

1312 lines
33 KiB
C++

//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======
//
// Purpose:
//
//=============================================================================
#include "matsys_controls/particlepicker.h"
#include "tier1/keyvalues.h"
#include "tier1/utldict.h"
#include "filesystem.h"
#include "studio.h"
#include "matsys_controls/matsyscontrols.h"
#include "matsys_controls/mdlpanel.h"
#include "vgui_controls/Splitter.h"
#include "vgui_controls/ComboBox.h"
#include "vgui_controls/Button.h"
#include "vgui_controls/PropertySheet.h"
#include "vgui_controls/MessageBox.h"
#include "vgui_controls/MenuItem.h"
#include "vgui_controls/MenuButton.h"
#include "vgui_controls/PropertyPage.h"
#include "vgui_controls/CheckButton.h"
#include "vgui_controls/ScrollableEditablePanel.h"
#include "vgui_controls/ScrollBar.h"
#include "vgui_controls/Tooltip.h"
#include "vgui/IVGui.h"
#include "vgui/IInput.h"
#include "vgui/ISurface.h"
#include "vgui/Cursor.h"
#include "matsys_controls/assetpicker.h"
#include "dmxloader/dmxloader.h"
#include "particles/particles.h"
#include "vstdlib/jobthread.h"
#include "dme_controls/particlesystempanel.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
using namespace vgui;
//-----------------------------------------------------------------------------
struct CachedParticleInfo_t
{
CachedAssetInfo_t m_AssetInfo;
CUtlString m_FileName;
};
struct PCFToLoad_t
{
CUtlString m_FileName;
int m_ModId;
};
static CUtlVector<PCFToLoad_t> sCacheUnloadedPCFs;
static CUtlVector<CachedParticleInfo_t> sCacheParticleList;
//-----------------------------------------------------------------------------
class CParticleSnapshotPanel: public vgui::EditablePanel
{
DECLARE_CLASS_SIMPLE( CParticleSnapshotPanel, vgui::EditablePanel );
public:
CParticleSnapshotPanel( vgui::Panel *pParent, const char *pName );
virtual ~CParticleSnapshotPanel( );
MESSAGE_FUNC( OnSetFocus, "SetFocus" );
MESSAGE_FUNC_CHARPTR( OnParticleSystemSelected, "ParticleSystemSelected", SystemName );
virtual void OnMousePressed(MouseCode code);
virtual void OnMouseDoublePressed(MouseCode code);
virtual void ApplySchemeSettings(IScheme *pScheme);
virtual void Paint();
void SetParticleSystem( const char *szSystemName, int nId );
void Simulate();
void SetSelected( bool bSelected );
const char *GetSystemName() const;
bool IsSelected();
CParticleCollection* GetSystem();
int GetId();
void SetPreviewEnabled( bool bEnabled );
void UpdateRelatives( IImage *pIcon, CUtlVector<CParticleSnapshotGrid::PSysRelativeInfo_t>& sysParents, CUtlVector<CParticleSnapshotGrid::PSysRelativeInfo_t>& sysChildren );
protected:
CParticleSystemPanel *m_pParticlePanel;
MenuButton *m_pParentsButton;
MenuButton *m_pChildrenButton;
vgui::Label *m_pLabel;
int m_nSystemIndex;
CUtlString m_SystemName;
bool m_bSelected;
int m_nSystemId;
bool m_bPreviewEnabled;
Color m_SelectedBgColor;
Color m_SelectedTextColor;
};
CParticleSnapshotPanel::CParticleSnapshotPanel( vgui::Panel *pParent, const char *pName ):
BaseClass(pParent,pName)
{
m_bSelected = false;
m_bPreviewEnabled = true;
m_pParticlePanel = new CParticleSystemPanel( this, pName );
m_pParticlePanel->SetSelfSimulation(false);
m_pParticlePanel->SetGridColor( 128, 128, 128 );
m_pParticlePanel->RenderGrid(true);
m_pParticlePanel->SetParentMouseNotify(true);
m_pParticlePanel->EnableAutoViewing(true);
m_pLabel = new Label( this, "SystemLabel", "Unnamed" );
m_pLabel->SetContentAlignment( Label::a_center );
m_pLabel->SetPaintBackgroundEnabled( false );
m_pLabel->SetMouseInputEnabled( false );
CBoxSizer *pSizer = new CBoxSizer(ESLD_VERTICAL);
pSizer->AddPanel( m_pParticlePanel, SizerAddArgs_t().Expand( 1.0f ).Padding( 5 ) );
{
CBoxSizer *pRow = new CBoxSizer(ESLD_HORIZONTAL);
m_pParentsButton = new MenuButton( this, "ParentsButton", "P" );
pRow->AddPanel( m_pParentsButton, SizerAddArgs_t().Expand( 0.0f ).Padding( 0 ) );
m_pChildrenButton = new MenuButton( this, "ChildrenButton", "C" );
pRow->AddPanel( m_pChildrenButton, SizerAddArgs_t().Expand( 0.0f ).Padding( 0 ) );
pRow->AddPanel( m_pLabel, SizerAddArgs_t().Expand( 0.0f ).Padding( 2 ) );
pSizer->AddSizer( pRow, SizerAddArgs_t().Expand( 0.0f ).Padding( 5 ) );
}
pSizer->AddSpacer( SizerAddArgs_t().Padding( 5 ) );
SetSizer( pSizer );
InvalidateLayout(true);
m_pParticlePanel->ResetView();
m_pParticlePanel->LookAt( 50.f );
}
void CParticleSnapshotPanel::SetPreviewEnabled( bool bEnabled )
{
m_bPreviewEnabled = bEnabled;
m_pParticlePanel->SetVisible(m_bPreviewEnabled);
InvalidateLayout();
}
CParticleSnapshotPanel::~CParticleSnapshotPanel( )
{
}
void CParticleSnapshotPanel::OnParticleSystemSelected( const char *SystemName )
{
// hand the signal up
PostActionSignal( new KeyValues( "ParticleSystemSelected", "SystemName", SystemName ) );
}
int CParticleSnapshotPanel::GetId()
{
return m_nSystemId;
}
CParticleCollection* CParticleSnapshotPanel::GetSystem()
{
return m_pParticlePanel->GetParticleSystem();
}
void CParticleSnapshotPanel::OnSetFocus()
{
BaseClass::OnSetFocus();
}
void CParticleSnapshotPanel::UpdateRelatives( IImage *pIcon, CUtlVector<CParticleSnapshotGrid::PSysRelativeInfo_t>& sysParents, CUtlVector<CParticleSnapshotGrid::PSysRelativeInfo_t>& sysChildren )
{
char countBuf[8];
// parents
if ( sysParents.Count() )
{
Menu *pParentsMenu = new Menu(GetParent()->GetParent(), "ParentsMenu");
for ( int i = 0; i < sysParents.Count(); ++i )
{
int nItemIdx = pParentsMenu->AddMenuItem( sysParents[i].relName, new KeyValues( "ParticleSystemSelected", "SystemName", sysParents[i].relName ), this );
pParentsMenu->GetMenuItem(nItemIdx)->SetEnabled( sysParents[i].bVisibleInCurrentView );
}
m_pParentsButton->SetEnabled( true );
V_snprintf( countBuf, sizeof(countBuf), "%d", sysParents.Count() );
m_pParentsButton->SetText( countBuf );
m_pParentsButton->SetMenu( pParentsMenu );
}
else
{
m_pParentsButton->SetEnabled( false );
m_pParentsButton->SetText( "P" );
m_pParentsButton->SetMenu( NULL );
}
// children
if ( sysChildren.Count() )
{
Menu *pChildrenMenu = new Menu(GetParent()->GetParent(), "ChildrenMenu");
for ( int i = 0; i < sysChildren.Count(); ++i )
{
int nItemIdx = pChildrenMenu->AddMenuItem( sysChildren[i].relName, new KeyValues( "ParticleSystemSelected", "SystemName", sysChildren[i].relName ), this );
pChildrenMenu->GetMenuItem(nItemIdx)->SetEnabled( sysChildren[i].bVisibleInCurrentView );
}
m_pChildrenButton->SetEnabled( true );
V_snprintf( countBuf, sizeof(countBuf), "%d", sysChildren.Count() );
m_pChildrenButton->SetText( countBuf );
m_pChildrenButton->SetMenu( pChildrenMenu );
}
else
{
m_pChildrenButton->SetEnabled( false );
m_pChildrenButton->SetText( "C" );
m_pChildrenButton->SetMenu( NULL );
}
}
void CParticleSnapshotPanel::OnMousePressed( MouseCode code )
{
BaseClass::OnMousePressed( code );
if ( code == MOUSE_LEFT )
{
if ( input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT) )
{
PostActionSignal( new KeyValues( "ParticleSystemShiftSelected", "SystemName", m_SystemName ) );
}
else if ( input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) )
{
PostActionSignal( new KeyValues( "ParticleSystemCtrlSelected", "SystemName", m_SystemName ) );
}
else
{
PostActionSignal( new KeyValues( "ParticleSystemSelected", "SystemName", m_SystemName ) );
}
}
}
void CParticleSnapshotPanel::OnMouseDoublePressed( MouseCode code )
{
BaseClass::OnMouseDoublePressed( code );
if ( code == MOUSE_LEFT )
{
PostActionSignal( new KeyValues( "ParticleSystemPicked", "SystemName", m_SystemName ) );
}
}
void CParticleSnapshotPanel::SetParticleSystem( const char *szSystemName, int nId )
{
bool bSameSystem = !V_strcmp( m_SystemName, szSystemName );
m_SystemName = szSystemName;
m_nSystemId = nId;
if ( !bSameSystem || m_pParticlePanel->GetParticleSystem() == NULL )
{
m_pParticlePanel->SetParticleSystem(szSystemName);
m_pParticlePanel->SetControlPointValue( 0, Vector(0.f,0.f,0.f) );
m_pParticlePanel->SetControlPointValue( 1, Vector(100.f,0.f,0.f) );
m_pParticlePanel->LookAt( 50.f );
m_pLabel->SetText( m_SystemName );
m_pParticlePanel->ResetView();
}
if ( !m_pParticlePanel->GetParticleSystem() )
{
CUtlString errStr = "ERROR: ";
errStr += m_SystemName;
m_pLabel->SetText( errStr );
m_pParticlePanel->SetBackgroundColor( 64,0,0 );
}
else
{
m_pParticlePanel->SetBackgroundColor( 32, 32, 32 );
}
InvalidateLayout();
}
void CParticleSnapshotPanel::ApplySchemeSettings(IScheme *pScheme)
{
BaseClass::ApplySchemeSettings(pScheme);
m_SelectedBgColor = pScheme->GetColor( "ListPanel.SelectedBgColor", Color(255, 255, 255, 0) );
m_SelectedTextColor = pScheme->GetColor( "ListPanel.SelectedTextColor", Color(255, 255, 255, 0) );
// override the colors because alpha = 0 is bad
Color btnFgColor = pScheme->GetColor("Button.TextColor", Color(255, 255, 255, 255) );
Color btnBgColor = pScheme->GetColor("ListPanel.BgColor", Color(128, 128, 128, 255) );
m_pChildrenButton->SetDefaultColor( btnFgColor, btnBgColor );
m_pChildrenButton->SetArmedColor( btnFgColor, btnBgColor );
m_pChildrenButton->SetDepressedColor( btnFgColor, btnBgColor );
m_pParentsButton->SetDefaultColor( btnFgColor, btnBgColor );
m_pParentsButton->SetArmedColor( btnFgColor, btnBgColor );
m_pParentsButton->SetDepressedColor( btnFgColor, btnBgColor );
}
void CParticleSnapshotPanel::Simulate()
{
if( !m_bPreviewEnabled )
{
return;
}
Panel* pParent = GetParent();
int x0,y0,x1, y1;
GetClipRect( x0, y0, x1, y1 );
int nPX0, nPY0, nPX1, nPY1;
pParent->GetClipRect( nPX0, nPY0, nPX1, nPY1 );
if( x1 < nPX0 || x0 > nPX1 ||
y1 < nPY0 || y0 > nPY1 )
{
// out of bounds
return;
}
m_pParticlePanel->Simulate();
}
void CParticleSnapshotPanel::Paint()
{
BaseClass::Paint();
if( m_bSelected )
{
DrawBox( 1, 1, GetWide()-2, GetTall()-2, m_SelectedBgColor, 1.0f );
}
if( m_bPreviewEnabled )
{
// black border around the preview
int x, y, w, t;
m_pParticlePanel->GetBounds( x, y, w, t );
surface()->DrawSetColor( Color(0,0,0,255) );
surface()->DrawOutlinedRect( x-1, y-1, x+w+1, y+t+1 );
}
}
const char *CParticleSnapshotPanel::GetSystemName() const
{
return m_SystemName;
}
void CParticleSnapshotPanel::SetSelected( bool bSelected )
{
m_bSelected = bSelected;
}
bool CParticleSnapshotPanel::IsSelected( )
{
return m_bSelected;
}
//-----------------------------------------------------------------------------
//
// Particle Snapshot Grid
//
//-----------------------------------------------------------------------------
const int MIN_PANEL_SIZE = 200;
const int MAX_PANEL_SIZE = 250;
const int MIN_NUM_COLS = 1;
const int OFFSET = 4;
const int TOOLBAR_HEIGHT = 30;
CParticleSnapshotGrid::CParticleSnapshotGrid( vgui::Panel *pParent, const char *pName ):
BaseClass(pParent,pName)
{
m_pRelativesImgNeither = scheme()->GetImage( "tools/particles/icon_particles_rel_neither", false );
m_pRelativesImgPOnly = scheme()->GetImage( "tools/particles/icon_particles_rel_ponly", false );
m_pRelativesImgCOnly = scheme()->GetImage( "tools/particles/icon_particles_rel_conly", false );
m_pRelativesImgBoth = scheme()->GetImage( "tools/particles/icon_particles_rel_both", false );
m_pScrollBar = new ScrollBar( this, "ScrollBar", true );
m_pScrollBar->SetWide( 16 );
m_pScrollBar->SetAutoResize( PIN_TOPRIGHT, AUTORESIZE_DOWN, 0, TOOLBAR_HEIGHT, -16, 0 );
m_pScrollBar->AddActionSignalTarget( this );
m_pScrollPanel = new Panel( this, "ScrollPanel" );
m_pScrollPanel->SetAutoResize( PIN_TOPLEFT, AUTORESIZE_DOWNANDRIGHT, 0, TOOLBAR_HEIGHT, -16, 0 );
m_pScrollPanel->DisableMouseInputForThisPanel(true);
m_pToolPanel = new Panel( this, "ToolPanel" );
m_pToolPanel->SetTall(TOOLBAR_HEIGHT);
m_pToolPanel->SetAutoResize( PIN_TOPLEFT, AUTORESIZE_RIGHT, 0, 0, -16, 0 );
m_pToolPanel->SetSizer( new CBoxSizer(ESLD_HORIZONTAL) );
m_pPreviewCheckbox = new CheckButton( m_pToolPanel, "PreviewCheckbox", "Show Previews" );
m_pPreviewCheckbox->SetSelected(true);
m_pPreviewCheckbox->AddActionSignalTarget( this );
m_pToolPanel->GetSizer()->AddPanel( m_pPreviewCheckbox, SizerAddArgs_t() );
m_pNoSystemsLabel = new Label( m_pScrollPanel, "NoSystemsLabel", "<No Systems>" );
m_nCurrentColCount = MIN_NUM_COLS;
m_nMostRecentSelectedIndex = -1;
vgui::ivgui()->AddTickSignal( GetVPanel(), 0 );
SetKeyBoardInputEnabled(true);
}
void CParticleSnapshotGrid::MapSystemRelatives( )
{
// somewhat slow, but doesn't get called frequently or with large enough data sets to optimize
m_ParentsMap.RemoveAll();
m_ParentsMap.AddMultipleToTail( m_Panels.Count() );
m_ChildrenMap.RemoveAll();
m_ChildrenMap.AddMultipleToTail( m_Panels.Count() );
for ( int i = 0; i < g_pParticleSystemMgr->GetParticleSystemCount(); i++ )
{
const char *pPotentialParentName = g_pParticleSystemMgr->GetParticleSystemNameFromIndex(i);
CParticleSystemDefinition *pPotentialParent = g_pParticleSystemMgr->FindParticleSystem( pPotentialParentName );
//////////////////////
// see if this system is visible
int nParentVisibileIndex = -1;
for ( int p = 0; p < m_Panels.Count(); ++p )
{
CParticleCollection *pVisible = m_Panels[p]->GetSystem();
if( pVisible == NULL )
continue;
if ( pVisible->m_pDef == pPotentialParent )
{
nParentVisibileIndex = p;
break;
}
}
//////////////////////
// see if this system is a parent of any visible system; check all its children
for ( int c = 0; c < pPotentialParent->m_Children.Count(); ++c )
{
CParticleSystemDefinition *pChild = NULL;
if ( pPotentialParent->m_Children[c].m_bUseNameBasedLookup )
{
pChild = g_pParticleSystemMgr->FindParticleSystem( pPotentialParent->m_Children[c].m_Name );
}
else
{
pChild = g_pParticleSystemMgr->FindParticleSystem( pPotentialParent->m_Children[c].m_Id );
}
if ( pChild == NULL )
continue;
//////////////////////
// is the child visible?
int nChildVisibleIndex = -1;
for ( int p = 0; p < m_Panels.Count(); ++p )
{
CParticleCollection *pVisible = m_Panels[p]->GetSystem();
if( pVisible == NULL )
continue;
if ( pVisible->m_pDef == pChild )
{
// this child is visible; add entry, and mark parent if visible
PSysRelativeInfo_t parentInfo;
parentInfo.relName = pPotentialParent->GetName();
parentInfo.bVisibleInCurrentView = ( nParentVisibileIndex != -1 );
m_ParentsMap[p].AddToTail( parentInfo );
nChildVisibleIndex = p;
break;
}
}
if ( nParentVisibileIndex != -1 )
{
// this parent is visible; add entry, and mark child if visible
PSysRelativeInfo_t childInfo;
childInfo.relName = pChild->GetName();
childInfo.bVisibleInCurrentView = ( nChildVisibleIndex != -1 );
m_ChildrenMap[nParentVisibileIndex].AddToTail( childInfo );
}
}
}
}
void CParticleSnapshotGrid::UpdatePanelRelatives( int nIndex )
{
CParticleSnapshotPanel* pPanel = m_Panels[nIndex];
CUtlVector<PSysRelativeInfo_t>& sysParents = m_ParentsMap[nIndex];
CUtlVector<PSysRelativeInfo_t>& sysChildren = m_ChildrenMap[nIndex];
IImage *pImg = m_pRelativesImgNeither;
if ( sysParents.Count() && sysChildren.Count() )
{
pImg = m_pRelativesImgBoth;
}
else if ( sysParents.Count() )
{
pImg = m_pRelativesImgPOnly;
}
else if ( sysChildren.Count() )
{
pImg = m_pRelativesImgCOnly;
}
pPanel->UpdateRelatives( pImg, sysParents, sysChildren );
}
void CParticleSnapshotGrid::OnScrollBarSliderMoved()
{
LayoutScrolled();
}
void CParticleSnapshotGrid::OnCheckButtonChecked( KeyValues *kv )
{
SetAllPreviewEnabled( m_pPreviewCheckbox->IsSelected() );
}
void CParticleSnapshotGrid::ApplySchemeSettings(IScheme *pScheme)
{
BaseClass::ApplySchemeSettings(pScheme);
}
void CParticleSnapshotGrid::UpdateAllRelatives()
{
MapSystemRelatives();
for ( int i = 0; i < m_Panels.Count(); ++i )
{
UpdatePanelRelatives( i );
}
}
void CParticleSnapshotGrid::OnMousePressed(MouseCode code)
{
BaseClass::OnMousePressed( code );
SelectSystem("",false,false);
PostActionSignal( new KeyValues( "ParticleSystemSelectionChanged" ) );
}
static int PanelSortHelperI( CParticleSnapshotPanel *const *a, CParticleSnapshotPanel * const *b )
{
return V_stricmp((*a)->GetSystemName(),(*b)->GetSystemName());
}
void CParticleSnapshotGrid::SetParticleList( const CUtlVector<const char *>& ParticleNames )
{
bool bPreviewEnabled = m_pPreviewCheckbox->IsSelected();
// might have too many panels
while ( m_Panels.Count() > ParticleNames.Count() )
{
delete m_Panels.Tail();
m_Panels.RemoveMultipleFromTail(1);
}
// might have too few panels
while ( m_Panels.Count() < ParticleNames.Count() )
{
char szPanelName[32];
V_snprintf( szPanelName, sizeof(szPanelName), "ParticlePanel%d", m_Panels.Count() );
CParticleSnapshotPanel *pPanel = new CParticleSnapshotPanel( m_pScrollPanel, szPanelName );
pPanel->SetPreviewEnabled(bPreviewEnabled);
pPanel->AddActionSignalTarget(this);
m_Panels.AddToTail( pPanel );
}
// reinit them all, with the new system names
for ( int i = 0; i < m_Panels.Count(); ++i )
{
if( i < ParticleNames.Count() )
{
m_Panels[i]->SetParticleSystem( ParticleNames[i], i );
}
else
{
m_Panels[i]->SetParticleSystem( "", i );
}
m_Panels[i]->SetSelected(false);
}
// now sort the panels however we want
m_Panels.Sort( PanelSortHelperI );
UpdateAllRelatives();
m_nMostRecentSelectedIndex = -1;
InvalidateLayout();
}
int CParticleSnapshotGrid::GetPanelWide()
{
int nWide = GetWide() - OFFSET*2 - m_pScrollBar->GetWide();
return MIN(MAX_PANEL_SIZE,nWide/m_nCurrentColCount);
}
int CParticleSnapshotGrid::GetPanelTall()
{
if ( m_pPreviewCheckbox->IsSelected() )
{
return GetPanelWide();
}
else
{
return 30; // TODO: This shouldn't be hard-coded :|
}
}
void CParticleSnapshotGrid::PerformLayout()
{
BaseClass::PerformLayout();
int nWide = GetWide() - OFFSET*2 - m_pScrollBar->GetWide();
m_nCurrentColCount = MAX(MIN_NUM_COLS,nWide/MIN_PANEL_SIZE);
LayoutScrolled();
}
void CParticleSnapshotGrid::SetAllPreviewEnabled( bool bEnabled )
{
for ( int i = 0; i < m_Panels.Count(); ++i )
{
m_Panels[i]->SetPreviewEnabled(bEnabled);
}
InvalidateLayout(true);
LayoutScrolled();
}
void CParticleSnapshotGrid::LayoutScrolled()
{
// int nWide = GetWide() - m_pScrollBar->GetWide();
int nPanels = m_Panels.Count();
int nRows = (nPanels+m_nCurrentColCount-1)/m_nCurrentColCount; // panels/cols rounded up
// int nRemainder = (nWide - m_nCurrentColCount*m_nCurrentPanelSize - OFFSET*2);
int nScrollPos = m_pScrollBar->GetValue();
int nPanelW = GetPanelWide();
int nPanelT = GetPanelTall();
m_pScrollBar->SetRange( 0, nRows*nPanelT+OFFSET*2 );
m_pScrollBar->SetRangeWindow( m_pScrollPanel->GetTall() );
for( int r = 0; r < nRows; ++r )
{
for( int c = 0; c < m_nCurrentColCount; ++c )
{
int i = c + r*m_nCurrentColCount;
if( i >= nPanels ) continue;
if( IsSystemVisible( i ) )
{
m_Panels[i]->SetSize(nPanelW,nPanelT);
m_Panels[i]->SetPos(OFFSET+c*nPanelW,OFFSET-nScrollPos+r*nPanelT);
m_Panels[i]->SetVisible(true);
}
else
{
m_Panels[i]->SetSize(1,1);
m_Panels[i]->SetPos(-10,-10);
m_Panels[i]->SetVisible(false);
}
}
}
if ( nPanels == 0 )
{
m_pNoSystemsLabel->SetVisible( true );
int w,t;
m_pNoSystemsLabel->GetContentSize(w,t);
w *= 2;
m_pNoSystemsLabel->SetWide( w );
m_pNoSystemsLabel->SetPos( (m_pScrollPanel->GetWide()-w)/2, (m_pScrollPanel->GetTall()-t)/2 );
}
else
{
m_pNoSystemsLabel->SetVisible( false );
}
}
void CParticleSnapshotGrid::OnMouseWheeled(int delta)
{
int val = m_pScrollBar->GetValue();
val -= (delta * 30);
m_pScrollBar->SetValue(val);
RequestFocus();
}
bool CParticleSnapshotGrid::IsSystemVisible( int nIndex )
{
int nViewTall = m_pScrollPanel->GetTall();
int nRow = (nIndex / m_nCurrentColCount);
int nTopOfPanel = nRow * GetPanelTall();
int nBottomOfPanel = nTopOfPanel + GetPanelTall();
int nCurrentScrollValue = m_pScrollBar->GetValue();
return ( nTopOfPanel < nCurrentScrollValue+nViewTall &&
nBottomOfPanel > nCurrentScrollValue );
}
void CParticleSnapshotGrid::SelectIndex( int nIndex, bool bAddToSelection, bool bToggle )
{
// not the most efficient, but easy
SelectSystem( m_Panels[nIndex]->GetSystemName(), bAddToSelection, bToggle );
}
void CParticleSnapshotGrid::SelectId( int nId, bool bAddToSelection, bool bToggle )
{
// not the most efficient, but easy
SelectSystem( GetSystemName(nId), bAddToSelection, bToggle );
}
void CParticleSnapshotGrid::DeselectAll()
{
for ( int i = 0; i < m_Panels.Count(); ++i )
{
m_Panels[i]->SetSelected(false);
}
m_nMostRecentSelectedIndex = -1;
}
int CParticleSnapshotGrid::GetSelectedSystemCount()
{
int nSelected = 0;
for ( int i = 0; i < m_Panels.Count(); ++i )
{
if( m_Panels[i]->IsSelected() )
{
nSelected++;
}
}
return nSelected;
}
const char *CParticleSnapshotGrid::GetSystemName( int nSystemId )
{
int nInternalIndex = IdToIndex( nSystemId );
if ( nInternalIndex < 0 || nInternalIndex >= m_Panels.Count() )
{
return "";
}
else
{
return m_Panels[nInternalIndex]->GetSystemName();
}
}
int CParticleSnapshotGrid::GetSelectedSystemId( int nSelectionIndex )
{
for ( int i = 0; i < m_Panels.Count(); ++i )
{
if( m_Panels[i]->IsSelected() )
{
nSelectionIndex--;
if( nSelectionIndex < 0 )
{
return m_Panels[i]->GetId();
}
}
}
return -1;
}
void CParticleSnapshotGrid::SelectSystem( const char *pSystemName, bool bAddToSelection, bool bToggle )
{
int nSelectedIndex = -1;
for ( int i = 0; i < m_Panels.Count(); ++i )
{
if ( !V_strcmp(m_Panels[i]->GetSystemName(),pSystemName) )
{
if ( m_Panels[i]->IsSelected() )
{
if ( bAddToSelection && bToggle )
{
m_Panels[i]->SetSelected( false );
}
}
else
{
nSelectedIndex = i;
m_Panels[i]->SetSelected( true );
m_Panels[i]->RequestFocus();
}
m_nMostRecentSelectedIndex = i;
}
else if( !bAddToSelection )
{
m_Panels[i]->SetSelected(false);
}
}
if ( nSelectedIndex != -1 )
{
int nViewTall = m_pScrollPanel->GetTall();
int nRow = (nSelectedIndex / m_nCurrentColCount);
int nTopOfPanel = nRow * GetPanelTall() + OFFSET;
int nBottomOfPanel = nTopOfPanel + GetPanelTall() + OFFSET;
int nCurrentScrollValue = m_pScrollBar->GetValue();
if( nTopOfPanel < nCurrentScrollValue )
{
m_pScrollBar->SetValue(nTopOfPanel);
}
else if( nBottomOfPanel > nCurrentScrollValue+nViewTall )
{
m_pScrollBar->SetValue(nBottomOfPanel-nViewTall);
}
}
Repaint();
}
/*
static void ProcessPSystem( CParticleSnapshotPanel *&pPanel )
{
pPanel->Simulate();
}
*/
void CParticleSnapshotGrid::OnTick()
{
// ParallelProcess( m_Panels.Base(), m_Panels.Count(), ProcessPSystem );
for ( int i = 0; i < m_Panels.Count(); ++i )
{
m_Panels[i]->Simulate();
}
}
void CParticleSnapshotGrid::OnParticleSystemSelected( const char *SystemName )
{
SelectSystem( SystemName, false, false );
PostActionSignal( new KeyValues( "ParticleSystemSelectionChanged" ) );
}
void CParticleSnapshotGrid::OnParticleSystemCtrlSelected( const char *SystemName )
{
SelectSystem( SystemName, true, true );
PostActionSignal( new KeyValues( "ParticleSystemSelectionChanged" ) );
}
void CParticleSnapshotGrid::OnParticleSystemShiftSelected( const char *SystemName )
{
int nIdx = InternalFindSystemIndexByName( SystemName );
if ( nIdx != -1 && m_nMostRecentSelectedIndex != -1 )
{
int nFrom = MIN( nIdx, m_nMostRecentSelectedIndex );
int nTo = MAX( nIdx, m_nMostRecentSelectedIndex );
for ( int i = nFrom; i <= nTo; ++i )
{
SelectIndex( i, true, false );
}
PostActionSignal( new KeyValues( "ParticleSystemSelectionChanged" ) );
}
}
int CParticleSnapshotGrid::InternalFindSystemIndexByName( const char *pSystemName )
{
for ( int i = 0; i < m_Panels.Count(); ++i )
{
if ( !V_strcmp(m_Panels[i]->GetSystemName(),pSystemName) )
{
return i;
}
}
return -1;
}
void CParticleSnapshotGrid::OnParticleSystemPicked( const char *SystemName )
{
PostActionSignal( new KeyValues( "ParticleSystemPicked", "SystemName", SystemName ) );
}
int CParticleSnapshotGrid::IdToIndex( int nId )
{
for ( int i = 0; i < m_Panels.Count(); ++i )
{
if ( m_Panels[i]->GetId() == nId )
{
return i;
}
}
return -1;
}
void CParticleSnapshotGrid::OnKeyCodeTyped( vgui::KeyCode code )
{
if ( code == KEY_UP ||
code == KEY_DOWN ||
code == KEY_LEFT ||
code == KEY_RIGHT )
{
if ( m_nMostRecentSelectedIndex != -1 )
{
int nNextIndex = m_nMostRecentSelectedIndex;
if ( code == KEY_UP )
{
nNextIndex -= m_nCurrentColCount;
}
else if ( code == KEY_DOWN )
{
nNextIndex += m_nCurrentColCount;
}
else if ( code == KEY_LEFT )
{
nNextIndex--;
}
else if ( code == KEY_RIGHT )
{
nNextIndex++;
}
int nPanels = m_Panels.Count();
int nCurrentRowCount = (nPanels+m_nCurrentColCount-1)/m_nCurrentColCount;
if ( nNextIndex/m_nCurrentColCount >= nCurrentRowCount-1 &&
nNextIndex >= nPanels )
{
// if you're not on the last row and hit down,
// but nothing is below you, still pop to the last
// item in the list
nNextIndex = nPanels-1;
}
if( nNextIndex >= 0 && nNextIndex < nPanels )
{
SelectIndex( nNextIndex, false, false );
PostActionSignal( new KeyValues( "ParticleSystemSelectionChanged" ) );
RequestFocus();
}
}
}
else
{
BaseClass::OnKeyCodeTyped( code );
}
}
//-----------------------------------------------------------------------------
//
// Particle Picker
//
//-----------------------------------------------------------------------------
static int StringSortHelperI( const char * const *a, const char * const *b )
{
return V_stricmp(*a,*b);
}
void CParticlePicker::OnAssetListChanged( )
{
CUtlVector<const char*> assetNames;
int nCount = GetAssetCount();
for ( int i = 0; i < nCount; ++i )
{
if ( IsAssetVisible( i ) )
{
assetNames.AddToTail( GetAssetName(i) );
}
}
assetNames.Sort( StringSortHelperI );
m_pSnapshotGrid->SetParticleList( assetNames );
}
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CParticlePicker::CParticlePicker( vgui::Panel *pParent ) :
BaseClass( pParent, "Particle Systems", "pcf", "particles", "pcfName" )
{
m_pFileBrowserSplitter = new Splitter( this, "FileBrowserSplitter", SPLITTER_MODE_VERTICAL, 1 );
float flFractions[] = { 0.33f, 0.67f };
m_pFileBrowserSplitter->RespaceSplitters( flFractions );
vgui::Panel *pSplitterLeftSide = m_pFileBrowserSplitter->GetChild( 0 );
vgui::Panel *pSplitterRightSide = m_pFileBrowserSplitter->GetChild( 1 );
pSplitterLeftSide->RequestFocus();
CreateStandardControls( pSplitterLeftSide, false );
AutoLayoutStandardControls();
m_pSnapshotGrid = new CParticleSnapshotGrid( pSplitterRightSide, "ParticleSystemPanel" );
m_pSnapshotGrid->SetAutoResize( PIN_TOPLEFT, AUTORESIZE_DOWNANDRIGHT, 0, 0, 0, 0 );
m_pSnapshotGrid->AddActionSignalTarget(this);
}
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CParticlePicker::~CParticlePicker()
{
}
//-----------------------------------------------------------------------------
// Performs layout
//-----------------------------------------------------------------------------
void CParticlePicker::PerformLayout()
{
// NOTE: This call should cause auto-resize to occur
// which should fix up the width of the panels
BaseClass::PerformLayout();
int w, h;
GetSize( w, h );
// Layout the mdl splitter
m_pFileBrowserSplitter->SetBounds( 0, 0, w, h );
}
//-----------------------------------------------------------------------------
// Buttons on various pages
//-----------------------------------------------------------------------------
void CParticlePicker::OnAssetSelected( KeyValues *pParams )
{
}
//-----------------------------------------------------------------------------
// A particle system was selected
//-----------------------------------------------------------------------------
void CParticlePicker::OnSelectedAssetPicked( const char *pParticleSysName )
{
SelectParticleSys( pParticleSysName );
m_pSnapshotGrid->SelectSystem( pParticleSysName, false, false );
}
void CParticlePicker::OnMousePressed(MouseCode code)
{
SelectParticleSys( "" );
m_pSnapshotGrid->SelectSystem( "", false, false );
}
//-----------------------------------------------------------------------------
// Allows external apps to select a particle system
//-----------------------------------------------------------------------------
void CParticlePicker::SelectParticleSys( const char *pRelativePath )
{
PostActionSignal( new KeyValues( "SelectedParticleSysChanged", "particle", pRelativePath ? pRelativePath : "" ) );
}
void CParticlePicker::GetSelectedParticleSysName( char *pBuffer, int nMaxLen )
{
Assert( nMaxLen > 0 );
if ( GetSelectedAssetCount() > 0 )
{
Q_snprintf( pBuffer, nMaxLen, "%s", GetSelectedAsset() );
}
else
{
pBuffer[0] = 0;
}
}
void CParticlePicker::OnParticleSystemSelectionChanged()
{
const char *SystemName = m_pSnapshotGrid->GetSystemName( m_pSnapshotGrid->GetSelectedSystemId(0) );
SetSelection( SystemName );
}
void CParticlePicker::OnParticleSystemPicked( const char *SystemName )
{
OnKeyCodeTyped(KEY_ENTER);
}
int CParticlePicker::GetAssetCount()
{
return sCacheParticleList.Count();
}
const char *CParticlePicker::GetAssetName( int nAssetIndex )
{
return sCacheParticleList[nAssetIndex].m_AssetInfo.m_AssetName;
}
const CachedAssetInfo_t& CParticlePicker::GetCachedAsset( int nAssetIndex )
{
return sCacheParticleList[nAssetIndex].m_AssetInfo;
}
int CParticlePicker::GetCachedAssetCount()
{
return sCacheParticleList.Count();
}
void CParticlePicker::CachePCFInfo( int nModIndex, const char *pFileName )
{
const CacheModInfo_t& modInfo = ModInfo(nModIndex);
if ( pFileName[0] == '!' )
{
++pFileName;
}
CUtlBuffer buf;
CUtlString pcfPath = modInfo.m_Path;
pcfPath += "/";
pcfPath += pFileName;
// cache the particles into the manager
g_pParticleSystemMgr->ReadParticleConfigFile( pcfPath, true );
// and read it ourselves to see what came out
if ( !g_pFullFileSystem->ReadFile( pcfPath, modInfo.m_Path, buf ) )
{
return;
}
DECLARE_DMX_CONTEXT_DECOMMIT( true );
CDmxElement *pRoot;
if ( !UnserializeDMX( buf, &pRoot, pFileName ) )
{
return;
}
if ( !Q_stricmp( pRoot->GetTypeString(), "DmeParticleSystemDefinition" ) )
{
CachedParticleInfo_t &info = sCacheParticleList[sCacheParticleList.AddToTail()];
info.m_AssetInfo.m_AssetName = pRoot->GetName();
info.m_AssetInfo.m_nModIndex = nModIndex;
info.m_FileName = pFileName;
return;
}
const CDmxAttribute *pDefinitions = pRoot->GetAttribute( "particleSystemDefinitions" );
if ( !pDefinitions || pDefinitions->GetType() != AT_ELEMENT_ARRAY )
{
CleanupDMX( pRoot );
return;
}
const CUtlVector< CDmxElement* >& definitions = pDefinitions->GetArray<CDmxElement*>( );
int nCount = definitions.Count();
for ( int i = 0; i < nCount; ++i )
{
CachedParticleInfo_t &info = sCacheParticleList[sCacheParticleList.AddToTail()];
info.m_AssetInfo.m_AssetName = definitions[i]->GetName();
info.m_AssetInfo.m_nModIndex = nModIndex;
info.m_FileName = pFileName;
}
CleanupDMX( pRoot );
}
void CParticlePicker::HandleModParticles( int nModIndex )
{
const CacheModInfo_t &modInfo = ModInfo(nModIndex);
CUtlString manifestPath = modInfo.m_Path;
manifestPath += "/particles/particles_manifest.txt" ;
CUtlVector<CUtlString> pcfList;
GetParticleManifest( pcfList, manifestPath );
int nCount = pcfList.Count();
for ( int i = 0; i < nCount; ++i )
{
PCFToLoad_t &p = sCacheUnloadedPCFs[sCacheUnloadedPCFs.AddToTail()];
p.m_FileName = pcfList[i];
p.m_ModId = nModIndex;
}
}
bool CParticlePicker::BeginCacheAssets( bool bForceRecache )
{
if ( bForceRecache )
{
sCacheParticleList.RemoveAll();
sCacheUnloadedPCFs.RemoveAll();
}
if ( sCacheParticleList.Count() )
{
return true;
}
int nCount = ModCount();
for ( int i = 0; i < nCount; ++i )
{
HandleModParticles( i );
}
return false;
}
bool CParticlePicker::IncrementalCacheAssets( float flTimeAllowed )
{
float flStartTime = Plat_FloatTime();
while ( sCacheUnloadedPCFs.Count() )
{
PCFToLoad_t &p = sCacheUnloadedPCFs.Tail();
CachePCFInfo( p.m_ModId, p.m_FileName );
sCacheUnloadedPCFs.RemoveMultipleFromTail(1);
// might have run out of time
if ( Plat_FloatTime() - flStartTime >= flTimeAllowed )
break;
}
return (sCacheUnloadedPCFs.Count() == 0);
}
CUtlString CParticlePicker::GetSelectedAssetFullPath( int nSelectionIndex )
{
int nAssetIndex = GetSelectedAssetIndex( nSelectionIndex );
if( nAssetIndex < 0 || nAssetIndex >= sCacheParticleList.Count() )
{
return CUtlString("ERROR");
}
CachedParticleInfo_t &p = sCacheParticleList[nAssetIndex];
const CacheModInfo_t &m = ModInfo( p.m_AssetInfo.m_nModIndex );
char pBuf[MAX_PATH+128];
Q_snprintf( pBuf, sizeof(pBuf), "%s\\%s::%s",
m.m_Path.Get(), p.m_FileName.Get(), p.m_AssetInfo.m_AssetName.Get() );
Q_FixSlashes( pBuf );
return CUtlString(pBuf);
}
//-----------------------------------------------------------------------------
//
// Purpose: Modal picker frame
//
//-----------------------------------------------------------------------------
CParticlePickerFrame::CParticlePickerFrame( vgui::Panel *pParent, const char *pTitle ) :
BaseClass( pParent )
{
SetAssetPicker( new CParticlePicker( this ) );
LoadControlSettingsAndUserConfig( "resource/mdlpickerframe.res" );
SetTitle( pTitle, false );
}
CParticlePickerFrame::~CParticlePickerFrame()
{
}
void CParticlePickerFrame::SelectParticleSys( const char *pRelativePath )
{
static_cast<CParticlePicker*>( GetAssetPicker() )->SelectParticleSys( pRelativePath );
}