summaryrefslogtreecommitdiff
path: root/vgui2/matsys_controls
diff options
context:
space:
mode:
Diffstat (limited to 'vgui2/matsys_controls')
-rw-r--r--vgui2/matsys_controls/QCGenerator.cpp723
-rw-r--r--vgui2/matsys_controls/assetpicker.cpp47
-rw-r--r--vgui2/matsys_controls/baseassetpicker.cpp1625
-rw-r--r--vgui2/matsys_controls/colorpickerpanel.cpp1255
-rw-r--r--vgui2/matsys_controls/curveeditorpanel.cpp200
-rw-r--r--vgui2/matsys_controls/gamefiletreeview.cpp301
-rw-r--r--vgui2/matsys_controls/manipulator.cpp201
-rw-r--r--vgui2/matsys_controls/matsys_controls.vpc66
-rw-r--r--vgui2/matsys_controls/matsyscontrols.cpp100
-rw-r--r--vgui2/matsys_controls/mdlpanel.cpp902
-rw-r--r--vgui2/matsys_controls/mdlpicker.cpp1742
-rw-r--r--vgui2/matsys_controls/mdlsequencepicker.cpp539
-rw-r--r--vgui2/matsys_controls/picker.cpp318
-rw-r--r--vgui2/matsys_controls/potterywheelpanel.cpp1243
-rw-r--r--vgui2/matsys_controls/proceduraltexturepanel.cpp239
-rw-r--r--vgui2/matsys_controls/sequencepicker.cpp485
-rw-r--r--vgui2/matsys_controls/tgapreviewpanel.cpp72
-rw-r--r--vgui2/matsys_controls/vmtpanel.cpp458
-rw-r--r--vgui2/matsys_controls/vmtpicker.cpp78
-rw-r--r--vgui2/matsys_controls/vmtpreviewpanel.cpp625
-rw-r--r--vgui2/matsys_controls/vtfpicker.cpp71
-rw-r--r--vgui2/matsys_controls/vtfpreviewpanel.cpp398
22 files changed, 11688 insertions, 0 deletions
diff --git a/vgui2/matsys_controls/QCGenerator.cpp b/vgui2/matsys_controls/QCGenerator.cpp
new file mode 100644
index 0000000..89f0173
--- /dev/null
+++ b/vgui2/matsys_controls/QCGenerator.cpp
@@ -0,0 +1,723 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#if defined(WIN32) && !defined( _X360 )
+#include <windows.h>
+#endif
+#include "filesystem.h"
+#include "filesystem_init.h"
+#include "appframework/IAppSystemGroup.h"
+#include "appframework/IAppSystem.h"
+#include "appframework/AppFramework.h"
+#include "filesystem_helpers.h"
+
+#include "matsys_controls/QCGenerator.h"
+#include "tier1/KeyValues.h"
+#include "tier2/vconfig.h"
+#include "vgui_controls/ListPanel.h"
+#include "vgui_controls/TextEntry.h"
+#include "vgui_controls/Button.h"
+#include "vgui_controls/FileOpenDialog.h"
+#include "vgui_controls/DirectorySelectDialog.h"
+#include "vgui_controls/ComboBox.h"
+#include "vgui_controls/CheckButton.h"
+#include "vgui_controls/MessageBox.h"
+#include "vgui/ISurface.h"
+#include "vgui/IInput.h"
+#include "vgui/Cursor.h"
+#include "vgui_controls/KeyBoardEditorDialog.h"
+
+#if defined( _X360 )
+#include "xbox/xbox_win32stubs.h"
+#endif
+
+using namespace vgui;
+
+#define MAX_KEYVALUE 1024
+
+
+//-----------------------------------------------------------------------------
+// Purpose: returns a pointer to the 'count' occurence of a character from the end of a string
+// returns 0 of there aren't 'count' number of the character in the string
+//-----------------------------------------------------------------------------
+char *strrchrcount(char *string, int character, int count )
+{
+ int j = count;
+ int numChars = strlen( string );
+ for( int i = numChars; i > 0; i-- )
+ {
+ if( string[i-1] == character )
+ {
+ j--;
+ }
+ if( j == 0 )
+ {
+ return string + i-1;
+ }
+ }
+ return 0;
+}
+
+
+
+class CModalPreserveMessageBox : public vgui::MessageBox
+{
+public:
+ CModalPreserveMessageBox(const char *title, const char *text, vgui::Panel *parent)
+ : vgui::MessageBox( title, text, parent )
+ {
+ m_PrevAppFocusPanel = vgui::input()->GetAppModalSurface();
+ }
+
+ ~CModalPreserveMessageBox()
+ {
+ vgui::input()->SetAppModalSurface( m_PrevAppFocusPanel );
+ }
+
+
+public:
+ vgui::VPANEL m_PrevAppFocusPanel;
+};
+
+void VGUIMessageBox( vgui::Panel *pParent, const char *pTitle, const char *pMsg, ... )
+{
+ char msg[4096];
+ va_list marker;
+ va_start( marker, pMsg );
+ Q_vsnprintf( msg, sizeof( msg ), pMsg, marker );
+ va_end( marker );
+
+ vgui::MessageBox *dlg = new CModalPreserveMessageBox( pTitle, msg, pParent );
+ dlg->DoModal();
+ dlg->Activate();
+ dlg->RequestFocus();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Places all the info from the vgui controls into the QCInfo struct
+//-----------------------------------------------------------------------------
+void QCInfo::SyncFromControls()
+{
+ char tempText[MAX_PATH];
+
+ vgui::Panel *pTargetField = pQCGenerator->FindChildByName( "staticPropCheck" );
+ bStaticProp = ((CheckButton *)pTargetField)->IsSelected();
+
+ pTargetField = pQCGenerator->FindChildByName( "mostlyOpaqueCheck" );
+ bMostlyOpaque = ((CheckButton *)pTargetField)->IsSelected();
+
+ pTargetField = pQCGenerator->FindChildByName( "disableCollisionsCheck" );
+ bDisableCollision = ((CheckButton *)pTargetField)->IsSelected();
+
+ pTargetField = pQCGenerator->FindChildByName( "referencePhysicsCheck" );
+ bReferenceAsPhys = ((CheckButton *)pTargetField)->IsSelected();
+
+ pTargetField = pQCGenerator->FindChildByName( "concaveCheck" );
+ bConcave = ((CheckButton *)pTargetField)->IsSelected();
+
+ pTargetField = pQCGenerator->FindChildByName( "automassCheck" );
+ bAutomass = ((CheckButton *)pTargetField)->IsSelected();
+
+ pTargetField = pQCGenerator->FindChildByName( "massField" );
+ ((TextEntry *)pTargetField)->GetText(tempText, MAX_PATH);
+ fMass = atof(tempText);
+
+ pTargetField = pQCGenerator->FindChildByName( "scaleField" );
+ ((TextEntry *)pTargetField)->GetText(tempText, MAX_PATH);
+ fScale = atof(tempText);
+
+ pTargetField = pQCGenerator->FindChildByName( "collisionSMDField" );
+ ((TextEntry *)pTargetField)->GetText( tempText, MAX_PATH );
+ V_strcpy_safe( pszCollisionPath, tempText );
+
+ pTargetField = pQCGenerator->FindChildByName( "surfacePropertyDropDown" );
+ ((ComboBox *)pTargetField)->GetText( tempText, MAX_PATH );
+ V_strcpy_safe( pszSurfaceProperty, tempText );
+
+ pTargetField = pQCGenerator->FindChildByName( "materialsField" );
+ ((TextEntry *)pTargetField)->GetText( tempText, MAX_PATH );
+ V_strcpy_safe( pszMaterialPath, tempText );
+
+ LODs.RemoveAll();
+ pTargetField = pQCGenerator->FindChildByName( "LODList" );
+ int numLOD = ((ListPanel *)pTargetField)->GetItemCount();
+ for ( int i = 0; i < numLOD; i++ )
+ {
+ KeyValues *key = ((ListPanel *)pTargetField)->GetItem( i );
+ LODInfo newLOD;
+
+ V_strcpy_safe( newLOD.pszFilename, key->GetString( "SMD" ) );
+ newLOD.iLOD = key->GetInt( "LOD" );
+ LODs.AddToTail( newLOD );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called during intialization to setup the initial state of the VGUI controls
+//-----------------------------------------------------------------------------
+void QCInfo::SyncToControls()
+{
+ char tempText[MAX_PATH];
+
+ vgui::Panel *pTargetField = pQCGenerator->FindChildByName( "staticPropCheck" );
+ ((CheckButton *)pTargetField)->SetSelected( bStaticProp );
+
+ pTargetField = pQCGenerator->FindChildByName( "mostlyOpaqueCheck" );
+ ((CheckButton *)pTargetField)->SetSelected( bMostlyOpaque );
+
+ pTargetField = pQCGenerator->FindChildByName( "disableCollisionsCheck" );
+ ((CheckButton *)pTargetField)->SetSelected( bDisableCollision );
+
+ pTargetField = pQCGenerator->FindChildByName( "referencePhysicsCheck" );
+ ((CheckButton *)pTargetField)->SetSelected( bReferenceAsPhys );
+
+ pTargetField = pQCGenerator->FindChildByName( "concaveCheck" );
+ ((CheckButton *)pTargetField)->SetSelected( bConcave );
+
+ pTargetField = pQCGenerator->FindChildByName( "automassCheck" );
+ ((CheckButton *)pTargetField)->SetSelected( bAutomass );
+
+ Q_snprintf( tempText, 10, "%d", (int)fMass );
+ pTargetField = pQCGenerator->FindChildByName( "massField" );
+ ((TextEntry *)pTargetField)->SetText( tempText );
+
+ Q_snprintf( tempText, 10, "%d", (int)fScale );
+ pTargetField = pQCGenerator->FindChildByName( "scaleField" );
+ ((TextEntry *)pTargetField)->SetText( tempText );
+
+ pTargetField = pQCGenerator->FindChildByName( "collisionSMDField" );
+ ((TextEntry *)pTargetField)->SetText( pszCollisionPath );
+
+ pTargetField = pQCGenerator->FindChildByName( "materialsField" );
+ ((TextEntry *)pTargetField)->SetText( pszMaterialPath );
+
+ pTargetField = pQCGenerator->FindChildByName( "surfacePropertyDropDown" );
+ int numItems = ((ComboBox *)pTargetField)->GetItemCount();
+ for( int i = 0; i < numItems; i++ )
+ {
+ ((ComboBox *)pTargetField)->GetItemText( i, tempText, MAX_PATH );
+ if ( !Q_strcmp( tempText, pszSurfaceProperty ) )
+ {
+ ((ComboBox *)pTargetField)->SetItemEnabled( i, true );
+ ((ComboBox *)pTargetField)->SetText( tempText );
+ break;
+ }
+ }
+}
+
+CBrowseButton::CBrowseButton( vgui::Panel *pParent ) : BaseClass( pParent, "Browse Button", "...", pParent, "browse" )
+{
+ SetParent( pParent );
+ pszStartingDirectory = NULL;
+ pszFileFilter = NULL;
+ pszTargetField = NULL;
+}
+
+CBrowseButton::~CBrowseButton()
+{
+}
+
+void CBrowseButton::SetCharVar( char **pVar, const char *pszNewText )
+{
+ if ( *pVar && pszNewText && !Q_strcmp( *pVar, pszNewText ) )
+ {
+ return;
+ }
+
+ if ( *pVar )
+ {
+ delete [] *pVar;
+ *pVar = NULL;
+ }
+
+ if ( pszNewText )
+ {
+ int len = Q_strlen( pszNewText ) + 1;
+ *pVar = new char[ len ];
+ Q_strncpy( *pVar, pszNewText, len );
+ }
+}
+
+void CBrowseButton::InitBrowseInfo( int x, int y, const char *pszName, const char *pszDir, const char *pszFilter, const char *pszField )
+{
+ SetSize( 24, 24 );
+ SetPos( x, y );
+ SetName( pszName );
+ SetCharVar( GetStartingDirectory(), pszDir );
+ SetCharVar( GetFileFilter(), pszFilter );
+ SetCharVar( GetTargetField(), pszField );
+ SetActionMessage();
+}
+
+void CBrowseButton::SetActionMessage()
+{
+ KeyValues *newActionMessage = new KeyValues( "browse", "directory", pszStartingDirectory, "filter", pszFileFilter);
+ newActionMessage->SetString( "targetField", pszTargetField );
+ SetCommand( newActionMessage );
+}
+
+const char *ParseKeyvalue( const char *pBuffer, char *key, char *value )
+{
+ char com_token[1024];
+
+ pBuffer = (const char *)ParseFile( pBuffer, com_token, NULL );
+ if ( Q_strlen( com_token ) < MAX_KEYVALUE )
+ {
+ Q_strncpy( key, com_token, MAX_KEYVALUE );
+ Q_strlower( key );
+ }
+ // no value on a close brace
+ if ( !Q_strcmp( key, "}" ) )
+ {
+ value[0] = 0;
+ return pBuffer;
+ }
+
+ pBuffer = (const char *)ParseFile( pBuffer, com_token, NULL );
+ if ( Q_strlen( com_token ) < MAX_KEYVALUE )
+ {
+ Q_strncpy( value, com_token, MAX_KEYVALUE );
+ Q_strlower( value );
+ }
+
+ return pBuffer;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CQCGenerator::CQCGenerator( vgui::Panel *pParent, const char *pszPath, const char *pszScene ) : BaseClass( pParent, "QCGenerator" )
+{
+ m_QCInfo_t.Init( this );
+
+ SetMinimumSize(846, 770);
+
+ m_pLODPanel = new ListPanel(this, "LODList");
+ m_pLODPanel->SetSelectIndividualCells( true );
+ m_pLODPanel->AddColumnHeader(0, "SMD", "LOD SMD", 450, 0);
+ m_pLODPanel->AddColumnHeader(1, "LOD", "LOD Distance", 50, 0);
+ m_pLODPanel->AddActionSignalTarget( this );
+ m_pLODPanel->SetMouseInputEnabled( true );
+
+ LoadControlSettings( "QCGenerator.res" );
+
+ m_pCollisionBrowseButton = new CBrowseButton( this );
+ m_pCollisionBrowseButton->InitBrowseInfo( 808, 158, "collisionBrowseButton", pszPath, "*.smd", "collisionSMDField" );
+
+ char szTerminatedPath[1024] = "\0";
+ sprintf( szTerminatedPath, "%s\\", pszPath );
+
+ InitializeSMDPaths( szTerminatedPath, pszScene );
+
+ char *pszMaterialsStart = strrchrcount( szTerminatedPath, '\\', 3 ) + 1;
+ char *pszMaterialsEnd = strrchr( szTerminatedPath, '\\');
+ Q_strncpy( m_QCInfo_t.pszMaterialPath, pszMaterialsStart, pszMaterialsEnd - pszMaterialsStart + 1 );
+
+ SetParent( pParent );
+
+ char szGamePath[1024] = "\0";
+ char szSearchPath[1024] = "\0";
+
+ // Get the currently set game configuration
+ GetVConfigRegistrySetting( GAMEDIR_TOKEN, szGamePath, sizeof( szGamePath ) );
+ static const char *pSurfacePropFilename = "\\scripts\\surfaceproperties.txt";
+
+ sprintf( szSearchPath, "%s%s", szGamePath, pSurfacePropFilename );
+
+ FileHandle_t fp = g_pFullFileSystem->Open( szSearchPath, "rb" );
+
+ if ( !fp )
+ {
+ //the set game configuration didn't have a surfaceproperties file; we are grabbing it from hl2
+ //TODO: This only works if they are in a subdirectory that is a peer to an hl2 directory
+ // that contains the file. It potentially needs to search the entire drive or prompt for the location
+ char *pszEndGamePath = Q_strrchr( szGamePath, '\\' );
+ pszEndGamePath[0] = 0;
+ V_strcat_safe( szGamePath, "\\hl2" );
+ sprintf( szSearchPath, "%s%s", szGamePath, pSurfacePropFilename );
+ fp = g_pFullFileSystem->Open( szSearchPath, "rb" );
+ }
+
+ int len = g_pFullFileSystem->Size( fp );
+
+ const char *szSurfacePropContents = new char[len+1];
+ g_pFullFileSystem->Read( (void *)szSurfacePropContents, len, fp );
+
+ char key[MAX_KEYVALUE], value[MAX_KEYVALUE];
+
+ vgui::Panel *pSurfacePropDropDown = FindChildByName( "surfacePropertyDropDown" );
+
+ //filling up the surface property dropdown
+ while ( szSurfacePropContents )
+ {
+ szSurfacePropContents = ParseKeyvalue( szSurfacePropContents, key, value );
+ ((ComboBox *)pSurfacePropDropDown)->AddItem( key, NULL );
+ while ( szSurfacePropContents )
+ {
+ szSurfacePropContents = ParseKeyvalue( szSurfacePropContents, key, value );
+ if (!stricmp( key, "}" ) )
+ {
+ break;
+ }
+ }
+ }
+ m_QCInfo_t.SyncToControls();
+
+ m_pLODEdit = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CQCGenerator::~CQCGenerator()
+{
+}
+
+void CQCGenerator::OnCommand( const char *command )
+{
+ if ( Q_stricmp( command, "createQC" ) == 0 )
+ {
+ m_QCInfo_t.SyncFromControls();
+ GenerateQCFile();
+ }
+ if ( Q_stricmp( command, "deleteSeq" ) == 0 )
+ {
+ //delete it
+ DeleteLOD();
+ }
+ if ( Q_stricmp( command, "editSeq" ) == 0 )
+ {
+ //edit
+ EditLOD();
+ }
+
+ BaseClass::OnCommand( command );
+}
+
+
+void CQCGenerator::OnKeyCodeTyped( KeyCode code )
+{
+ switch ( code )
+ {
+ case KEY_ENTER:
+ EditLOD();
+ }
+}
+
+
+void CQCGenerator::OnBrowse( KeyValues *data )
+{
+ V_strcpy_safe( m_szTargetField, data->GetString( "targetField" ) );
+ const char *filter = data->GetString( "filter" );
+
+ if ( Q_strlen( filter ) == 0 )
+ {
+// BrowseDirectory( data );
+ }
+ else
+ {
+ BrowseFile( data );
+ }
+}
+
+/*
+//This function is no longer used in the current version of the program.
+void CQCGenerator::BrowseDirectory( KeyValues *data )
+{
+ DirectorySelectDialog *pDialog = new DirectorySelectDialog( this, "Select Directory" );
+ pDialog->AddActionSignalTarget( this );
+ pDialog->DoModal();
+ pDialog->SetStartDirectory( data->GetString( "directory" ) );
+}
+*/
+
+void CQCGenerator::BrowseFile( KeyValues *data )
+{
+ const char *filter = data->GetString( "filter" );
+
+ FileOpenDialog *pDialog = new FileOpenDialog( this, "Select File", true );
+ pDialog->AddFilter( filter, filter, true );
+ pDialog->AddActionSignalTarget(this);
+ pDialog->SetStartDirectory( data->GetString( "directory" ) );
+ pDialog->DoModal( true );
+}
+
+void CQCGenerator::OnFileSelected( KeyValues *data )
+{
+ if ( m_szTargetField )
+ {
+ vgui::Panel *pTargetField = FindChildByName( m_szTargetField );
+ ((TextEntry *)pTargetField)->SetText( data->GetString( "fullpath" ) );
+ Repaint();
+ }
+}
+
+void CQCGenerator::OnDirectorySelected( KeyValues *data )
+{
+ if ( m_szTargetField )
+ {
+ vgui::Panel *pTargetField = FindChildByName( m_szTargetField );
+ ((TextEntry *)pTargetField)->SetText( data->GetString( "dir" ) );
+ Repaint();
+ }
+}
+
+bool CQCGenerator::GenerateQCFile()
+{
+ //TODO: clean this up. Consider creating a datatype that includes the string to write out when the QC file is created
+ char *nameBegin = strrchr( m_QCInfo_t.pszSMDPath, '\\' );
+
+ char szPath[MAX_PATH];
+ char szName[MAX_PATH];
+ Q_strncpy( szPath, m_QCInfo_t.pszSMDPath, nameBegin - m_QCInfo_t.pszSMDPath + 2 );
+ V_strcpy_safe( szName, szPath);
+ V_strcat_safe( szName, m_QCInfo_t.pszSceneName);
+ V_strcat_safe( szName, ".qc" );
+ FileHandle_t pSaveFile = g_pFullFileSystem->Open( szName, "wt" );
+ if (!pSaveFile)
+ {
+ char szSaveError[1024] = "";
+ Q_snprintf( szSaveError, 1024, "Save failed: invalid file name '%s'\n\nDirectory '%s' must exist.", szName, szPath );
+ VGUIMessageBox( this, "QC Generator error", szSaveError );
+ return 0;
+ }
+
+ //write qc header
+ g_pFullFileSystem->FPrintf( pSaveFile, "//\n// .qc file version 1.0\n\n");
+ //write out modelname info
+ char szModelName[MAX_PATH];
+ char *modelStart = strrchrcount( szName, '\\', 2) + 1;
+ char *modelEnd = strrchr( szName, '.' );
+ Q_strncpy( szModelName, modelStart, modelEnd - modelStart + 1 );
+ V_strcat_safe( szModelName, ".mdl" );
+ g_pFullFileSystem->FPrintf( pSaveFile, "$modelname %s\n\n", szModelName );
+ //write out scale info
+ g_pFullFileSystem->FPrintf( pSaveFile, "$scale %f\n", m_QCInfo_t.fScale );
+ //write out body info
+ g_pFullFileSystem->FPrintf( pSaveFile, "$body \"Body\" \"%s\"\n", strrchr( m_QCInfo_t.pszSMDPath, '\\' ) + 1 );
+
+ if ( m_QCInfo_t.bStaticProp == true )
+ {
+ g_pFullFileSystem->FPrintf( pSaveFile, "$staticprop\n" );
+ }
+
+ if ( m_QCInfo_t.bMostlyOpaque == true )
+ {
+ g_pFullFileSystem->FPrintf( pSaveFile, "$mostlyopaque\n" );
+ }
+
+ //write out surfaceprop info
+ g_pFullFileSystem->FPrintf( pSaveFile, "$surfaceprop \"%s\"\n\n", m_QCInfo_t.pszSurfaceProperty );
+ //write materials
+
+ g_pFullFileSystem->FPrintf( pSaveFile, "$cdmaterials %s\n\n", m_QCInfo_t.pszMaterialPath);
+
+ if ( m_QCInfo_t.bStaticProp || m_QCInfo_t.bNoAnimation )
+ {
+ g_pFullFileSystem->FPrintf( pSaveFile, "// --------- Animation sequences -------\n");
+ g_pFullFileSystem->FPrintf( pSaveFile, "$sequence \"idle\" \"%s\" fps 30\n\n", strrchr(m_QCInfo_t.pszSMDPath, '\\')+1);
+ }
+
+ //write out lod info
+ for( int i = 0; i < m_QCInfo_t.LODs.Count(); i++ )
+ {
+ LODInfo thisLOD = m_QCInfo_t.LODs.Element( i );
+ g_pFullFileSystem->FPrintf( pSaveFile, "$lod %d\n{\n\treplacemodel \"%s\" \"%s\"\n}\n\n", thisLOD.iLOD, strrchr(m_QCInfo_t.pszSMDPath, '\\')+1, thisLOD.pszFilename );
+ }
+
+ if ( m_QCInfo_t.bDisableCollision != true )
+ {
+ //write out collision header
+ g_pFullFileSystem->FPrintf( pSaveFile, "\n" );
+ //write out collision info
+ if ( m_QCInfo_t.bReferenceAsPhys == true )
+ {
+ g_pFullFileSystem->FPrintf( pSaveFile, "$collisionmodel \"%s\"", strrchr( m_QCInfo_t.pszSMDPath, '\\' ) + 1 );
+ }
+ else
+ {
+ if( Q_strcmp( m_QCInfo_t.pszCollisionPath, "" ) )
+ {
+ g_pFullFileSystem->FPrintf( pSaveFile, "$collisionmodel \"%s\"", strrchr( m_QCInfo_t.pszCollisionPath, '\\' ) + 1 );
+ }
+ }
+
+ g_pFullFileSystem->FPrintf( pSaveFile, " {\n\t// Mass in kilograms\n ");
+
+ if ( m_QCInfo_t.bAutomass == true )
+ {
+ g_pFullFileSystem->FPrintf( pSaveFile, "\t$automass\n" );
+ }
+ else
+ {
+ g_pFullFileSystem->FPrintf( pSaveFile, "\t$mass %f\n", m_QCInfo_t.fMass );
+ }
+
+ if ( m_QCInfo_t.bConcave == true )
+ {
+ g_pFullFileSystem->FPrintf( pSaveFile, "\t$concave\n" );
+ }
+ g_pFullFileSystem->FPrintf( pSaveFile, "}\n\n");
+ }
+
+ g_pFullFileSystem->Close( pSaveFile );
+
+ char szCommand[MAX_PATH];
+ char szGamePath[MAX_PATH];
+
+ char studiomdlPath[512];
+ g_pFullFileSystem->RelativePathToFullPath( "studiomdl.bat", NULL, studiomdlPath, sizeof( studiomdlPath ));
+
+ GetVConfigRegistrySetting( GAMEDIR_TOKEN, szGamePath, sizeof( szGamePath ) );
+
+#ifdef WIN32
+ STARTUPINFO startup;
+ PROCESS_INFORMATION process;
+
+
+ memset(&startup, 0, sizeof(startup));
+ startup.cb = sizeof(startup);
+
+
+ sprintf( szCommand, "%s -game %s %s", studiomdlPath, szGamePath, szName);
+ bool bReturn = CreateProcess( NULL, szCommand, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &startup, &process);
+#else
+ Assert( !"Implement me, why aren't we using a thread tool abstraction?" );
+ bool bReturn = false;
+#endif
+ return bReturn;
+}
+
+void CQCGenerator::InitializeSMDPaths( const char *pszPath, const char *pszScene )
+{
+ V_strcpy_safe( m_QCInfo_t.pszSceneName, pszScene );
+
+ FileFindHandle_t *pFileHandle = new FileFindHandle_t();
+
+ g_pFullFileSystem->AddSearchPath( pszPath, "SMD_DIR" );
+
+ const char *filename = g_pFullFileSystem->FindFirst( "*.smd", pFileHandle );
+
+ bool bFoundReference = false;
+ bool bFoundCollision = false;
+ bool bFoundLOD = false;
+
+ //iterate through .smd files
+ const char *startName = pszScene;
+
+ int nSearchLength = Q_strlen( pszScene );
+
+ int currentLOD = 1;
+
+ while( filename )
+ {
+ if ( !strncmp( startName, filename, nSearchLength ) )
+ {
+ const char *filenameEnd = filename + nSearchLength;
+ if ( !strncmp( filenameEnd, "_ref", 4 ) || !strncmp( filenameEnd, ".smd", 4 ) )
+ {
+ bFoundReference = true;
+ //we have found the reference smd.
+ V_strcpy_safe( m_QCInfo_t.pszSMDPath, pszPath );
+ V_strcat_safe( m_QCInfo_t.pszSMDPath, filename );
+ }
+ if ( !strncmp( filenameEnd, "_phy", 4) || !strncmp( filenameEnd, "_col", 4 ) )
+ {
+ bFoundCollision = true;
+ //we have found the collision smd.
+ V_strcpy_safe( m_QCInfo_t.pszCollisionPath, pszPath );
+ V_strcat_safe( m_QCInfo_t.pszCollisionPath, filename );
+ }
+ if ( !strncmp( filenameEnd, "_lod", 4) )
+ {
+ bFoundLOD = true;
+ //we found an LOD smd.
+ char lodName[255];
+ Q_snprintf( lodName, Q_strlen( lodName ), "lod%d", currentLOD );
+ //we found an LOD
+ KeyValues *newKv = new KeyValues( lodName, "SMD", filename, "LOD", "10" );
+ m_pLODPanel->AddItem( newKv, currentLOD, false, false );
+ currentLOD++;
+ }
+ }
+ filename = g_pFullFileSystem->FindNext( *pFileHandle );
+ }
+ char pszMessage[2048] = "";
+ char pszRefMessage[1024] = "";
+ char pszColMessage[1024] = "";
+ if (!bFoundReference )
+ {
+ V_strcat_safe( m_QCInfo_t.pszSMDPath, pszPath );
+ V_strcat_safe( m_QCInfo_t.pszSMDPath, pszScene );
+ V_strcat_safe( m_QCInfo_t.pszSMDPath, ".smd" );
+ Q_snprintf( pszRefMessage, 1024, "Reference SMD not found.\n\nValid default reference SMDs are %s%s_ref*.smd and %s%s.smd\nUsing default of %s. Model will not compile.\n\n", pszPath, pszScene, pszPath, pszScene, m_QCInfo_t.pszSMDPath );
+ }
+ if ( !bFoundCollision )
+ {
+ Q_snprintf( pszColMessage, 1024, "Collision SMD not found.\n\nThe valid default collision SMD is %s%s_phy*.smd.\nUsing reference SMD as default.\n", pszPath, pszScene );
+ V_strcpy_safe( m_QCInfo_t.pszCollisionPath, m_QCInfo_t.pszSMDPath );
+ m_QCInfo_t.bReferenceAsPhys = true;
+ }
+ if ( !bFoundReference || !bFoundCollision)
+ {
+ V_strcpy_safe( pszMessage, pszRefMessage );
+ V_strcat_safe( pszMessage, pszColMessage );
+ VGUIMessageBox( this, "Error Initializing Paths", pszMessage );
+ }
+}
+
+
+void CQCGenerator::DeleteLOD()
+{
+ int numSelected = m_pLODPanel->GetSelectedItemsCount();
+ int selected;
+ for ( int i = numSelected-1; i >= 0; i-- )
+ {
+ selected = m_pLODPanel->GetSelectedItem( i );
+ m_pLODPanel->RemoveItem( selected );
+ }
+}
+
+void CQCGenerator::EditLOD()
+{
+ int numSelected = m_pLODPanel->GetSelectedItemsCount();
+ if ( numSelected == 1 && !m_pLODPanel->IsInEditMode() )
+ {
+ if ( m_pLODEdit )
+ {
+ m_pLODEdit->MarkForDeletion();
+ m_pLODEdit = NULL;
+ }
+ m_pLODEdit = new vgui::TextEntry( this, "Edit" );
+ m_pLODEdit->SendNewLine( true );
+ m_nSelectedSequence = m_pLODPanel->GetSelectedItem( 0 );
+ m_nSelectedColumn = m_pLODPanel->GetSelectedColumn();
+ m_pLODPanel->EnterEditMode( m_nSelectedSequence, m_nSelectedColumn, m_pLODEdit );
+ }
+}
+
+void CQCGenerator::OnNewLODText()
+{
+ KeyValues *pEditItem = m_pLODPanel->GetItem( m_nSelectedSequence );
+ KeyValues *pListItem = pEditItem;
+ wchar_t szEditText[MAX_PATH];
+
+ pEditItem = pEditItem->GetFirstValue();
+ const char *name = pEditItem->GetName();
+ for( int i = 0; i < m_nSelectedColumn; i++ )
+ {
+ pEditItem = pEditItem->GetNextValue();
+ name = pEditItem->GetName();
+ }
+ m_pLODEdit->GetText( szEditText, MAX_PATH );
+
+ pListItem->SetWString( name, szEditText );
+
+ m_pLODPanel->LeaveEditMode();
+ m_pLODPanel->InvalidateLayout();
+ return;
+} \ No newline at end of file
diff --git a/vgui2/matsys_controls/assetpicker.cpp b/vgui2/matsys_controls/assetpicker.cpp
new file mode 100644
index 0000000..4d30b7f
--- /dev/null
+++ b/vgui2/matsys_controls/assetpicker.cpp
@@ -0,0 +1,47 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "matsys_controls/assetpicker.h"
+
+
+using namespace vgui;
+
+
+//-----------------------------------------------------------------------------
+//
+// Asset Picker with no preview
+//
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CAssetPicker::CAssetPicker( vgui::Panel *pParent, const char *pAssetType,
+ const char *pExt, const char *pSubDir, const char *pTextType ) :
+ BaseClass( pParent, pAssetType, pExt, pSubDir, pTextType )
+{
+ CreateStandardControls( this );
+ LoadControlSettingsAndUserConfig( "resource/assetpicker.res" );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Purpose: Modal picker frame
+//
+//-----------------------------------------------------------------------------
+CAssetPickerFrame::CAssetPickerFrame( vgui::Panel *pParent, const char *pTitle,
+ const char *pAssetType, const char *pExt, const char *pSubDir, const char *pTextType ) :
+ BaseClass( pParent )
+{
+ SetAssetPicker( new CAssetPicker( this, pAssetType, pExt, pSubDir, pTextType ) );
+ LoadControlSettingsAndUserConfig( "resource/assetpickerframe.res" );
+ SetTitle( pTitle, false );
+}
+
+
+
diff --git a/vgui2/matsys_controls/baseassetpicker.cpp b/vgui2/matsys_controls/baseassetpicker.cpp
new file mode 100644
index 0000000..99e6eca
--- /dev/null
+++ b/vgui2/matsys_controls/baseassetpicker.cpp
@@ -0,0 +1,1625 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "filesystem.h"
+#include "matsys_controls/baseassetpicker.h"
+#include "tier1/KeyValues.h"
+#include "tier1/utlntree.h"
+#include "tier1/utlrbtree.h"
+#include "vgui_controls/ListPanel.h"
+#include "vgui_controls/TextEntry.h"
+#include "vgui_controls/ComboBox.h"
+#include "vgui_controls/Button.h"
+#include "vgui_controls/Splitter.h"
+#include "vgui_controls/TreeView.h"
+#include "vgui_controls/ImageList.h"
+#include "vgui_controls/CheckButton.h"
+#include "vgui/ISurface.h"
+#include "vgui/IInput.h"
+#include "vgui/IVGui.h"
+#include "vgui/Cursor.h"
+
+
+using namespace vgui;
+
+
+#define ASSET_LIST_DIRECTORY_INITIAL_SEARCH_TIME 0.25f
+#define ASSET_LIST_DIRECTORY_SEARCH_TIME 0.025f
+
+
+//-----------------------------------------------------------------------------
+// sorting function, should return true if node1 should be displayed before node2
+//-----------------------------------------------------------------------------
+bool AssetTreeViewSortFunc( KeyValues *node1, KeyValues *node2 )
+{
+ const char *pDir1 = node1->GetString( "text", NULL );
+ const char *pDir2 = node2->GetString( "text", NULL );
+ return Q_stricmp( pDir1, pDir2 ) < 0;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Tree view for assets
+//
+//-----------------------------------------------------------------------------
+class CAssetTreeView : public vgui::TreeView
+{
+ DECLARE_CLASS_SIMPLE( CAssetTreeView, vgui::TreeView );
+
+public:
+ CAssetTreeView( vgui::Panel *parent, const char *name, const char *pRootFolderName, const char *pRootDir );
+
+ // Inherited from base classes
+ virtual void GenerateChildrenOfNode( int itemIndex );
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
+
+ // Opens and selects the root folder
+ void OpenRoot();
+
+ // Purpose: Refreshes the active file list
+ void RefreshFileList();
+
+ // Adds a subdirectory
+ DirHandle_t GetRootDirectory( );
+ DirHandle_t AddSubDirectory( DirHandle_t hParent, const char *pDirName );
+ void ClearDirectories();
+
+ // Selects a folder
+ void SelectFolder( const char *pSubDir, const char *pPath );
+
+private:
+ // Allocates the root node
+ void AllocateRootNode( );
+
+ // Purpose: Refreshes the active file list
+ DirHandle_t RefreshTreeViewItem( int nItemIndex );
+
+ // Sets an item to be colored as if its a menu
+ void SetItemColorForDirectories( int nItemID );
+
+ // Add a directory into the treeview
+ void AddDirectoryToTreeView( int nParentItemIndex, const char *pFullParentPath, DirHandle_t hPath );
+
+ // Selects an item in the tree
+ bool SelectFolder_R( int nItemID, const char *pPath );
+
+ CUtlString m_RootFolderName;
+ CUtlString m_RootDirectory;
+ vgui::ImageList m_Images;
+ CUtlNTree< CUtlString, DirHandle_t > m_DirectoryStructure;
+};
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CAssetTreeView::CAssetTreeView( Panel *pParent, const char *pName, const char *pRootFolderName, const char *pRootDir ) : BaseClass(pParent, pName), m_Images( false )
+{
+ SetSortFunc( AssetTreeViewSortFunc );
+
+ m_RootFolderName = pRootFolderName;
+ m_RootDirectory = pRootDir;
+ AllocateRootNode();
+
+ // build our list of images
+ m_Images.AddImage( scheme()->GetImage( "resource/icon_folder", false ) );
+ SetImageList( &m_Images, false );
+
+ SETUP_PANEL( this );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Refreshes the active file list
+//-----------------------------------------------------------------------------
+void CAssetTreeView::OpenRoot()
+{
+ RemoveAll();
+
+ // add the base node
+ const char *pRootDir = m_DirectoryStructure[ m_DirectoryStructure.Root() ];
+ KeyValues *pkv = new KeyValues( "root" );
+ pkv->SetString( "text", m_RootFolderName.Get() );
+ pkv->SetInt( "root", 1 );
+ pkv->SetInt( "expand", 1 );
+ pkv->SetInt( "dirHandle", m_DirectoryStructure.Root() );
+ pkv->SetString( "path", pRootDir );
+ int iRoot = AddItem( pkv, GetRootItemIndex() );
+ pkv->deleteThis();
+ ExpandItem( iRoot, true );
+}
+
+
+//-----------------------------------------------------------------------------
+// Allocates the root node
+//-----------------------------------------------------------------------------
+void CAssetTreeView::AllocateRootNode( )
+{
+ DirHandle_t hRoot = m_DirectoryStructure.Alloc();
+ m_DirectoryStructure.SetRoot( hRoot );
+ m_DirectoryStructure[hRoot] = m_RootDirectory;
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds a subdirectory (maintains sorted order)
+//-----------------------------------------------------------------------------
+DirHandle_t CAssetTreeView::GetRootDirectory( )
+{
+ return m_DirectoryStructure.Root();
+}
+
+DirHandle_t CAssetTreeView::AddSubDirectory( DirHandle_t hParent, const char *pDirName )
+{
+ DirHandle_t hSubdir = m_DirectoryStructure.Alloc();
+ m_DirectoryStructure[hSubdir] = pDirName;
+ m_DirectoryStructure[hSubdir].ToLower();
+
+ DirHandle_t hChild = m_DirectoryStructure.FirstChild( hParent );
+ m_DirectoryStructure.LinkChildBefore( hParent, hChild, hSubdir );
+
+ return hSubdir;
+}
+
+void CAssetTreeView::ClearDirectories()
+{
+ m_DirectoryStructure.RemoveAll();
+ AllocateRootNode();
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets an item to be colored as if its a menu
+//-----------------------------------------------------------------------------
+void CAssetTreeView::SetItemColorForDirectories( int nItemID )
+{
+ // mark directories in orange
+ SetItemFgColor( nItemID, Color(224, 192, 0, 255) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Add a directory into the treeview
+//-----------------------------------------------------------------------------
+void CAssetTreeView::AddDirectoryToTreeView( int nParentItemIndex, const char *pFullParentPath, DirHandle_t hPath )
+{
+ const char *pDirName = m_DirectoryStructure[hPath].Get();
+ KeyValues *kv = new KeyValues( "node", "text", pDirName );
+
+ char pFullPath[MAX_PATH];
+ Q_snprintf( pFullPath, sizeof( pFullPath ), "%s/%s", pFullParentPath, pDirName );
+ Q_FixSlashes( pFullPath );
+ Q_strlower( pFullPath );
+ bool bHasSubdirectories = m_DirectoryStructure.FirstChild( hPath ) != m_DirectoryStructure.InvalidIndex();
+ kv->SetString( "path", pFullPath );
+ kv->SetInt( "expand", bHasSubdirectories );
+ kv->SetInt( "image", 0 );
+ kv->SetInt( "dirHandle", hPath );
+
+ int nItemID = AddItem( kv, nParentItemIndex );
+ kv->deleteThis();
+
+ // mark directories in orange
+ SetItemColorForDirectories( nItemID );
+}
+
+
+//-----------------------------------------------------------------------------
+// override to incremental request and show p4 directories
+//-----------------------------------------------------------------------------
+void CAssetTreeView::GenerateChildrenOfNode( int nItemIndex )
+{
+ KeyValues *pkv = GetItemData( nItemIndex );
+
+ const char *pFullParentPath = pkv->GetString( "path", NULL );
+ if ( !pFullParentPath )
+ return;
+
+ DirHandle_t hPath = (DirHandle_t)pkv->GetInt( "dirHandle", m_DirectoryStructure.InvalidIndex() );
+ if ( hPath == m_DirectoryStructure.InvalidIndex() )
+ return;
+
+ DirHandle_t hChild = m_DirectoryStructure.FirstChild( hPath );
+ while ( hChild != m_DirectoryStructure.InvalidIndex() )
+ {
+ AddDirectoryToTreeView( nItemIndex, pFullParentPath, hChild );
+ hChild = m_DirectoryStructure.NextSibling( hChild );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Refreshes the active file list
+//-----------------------------------------------------------------------------
+DirHandle_t CAssetTreeView::RefreshTreeViewItem( int nItemIndex )
+{
+ if ( nItemIndex < 0 )
+ return m_DirectoryStructure.InvalidIndex();
+
+ // Make sure the expand icons are set correctly
+ KeyValues *pkv = GetItemData( nItemIndex );
+ DirHandle_t hPath = (DirHandle_t)pkv->GetInt( "dirHandle", m_DirectoryStructure.InvalidIndex() );
+ const char *pFullParentPath = pkv->GetString( "path", NULL );
+ bool bHasSubdirectories = m_DirectoryStructure.FirstChild( hPath ) != m_DirectoryStructure.InvalidIndex();
+ if ( bHasSubdirectories != ( pkv->GetInt( "expand" ) != 0 ) )
+ {
+ pkv->SetInt( "expand", bHasSubdirectories );
+ ModifyItem( nItemIndex, pkv );
+ }
+ bool bIsExpanded = IsItemExpanded( nItemIndex );
+ if ( !bIsExpanded )
+ return hPath;
+
+ // Check all children + build a list of children we've already got
+ int nChildCount = GetNumChildren( nItemIndex );
+ DirHandle_t *pFoundHandles = (DirHandle_t*)_alloca( nChildCount * sizeof(DirHandle_t) );
+ memset( pFoundHandles, 0xFF, nChildCount * sizeof(DirHandle_t) );
+ for ( int i = 0; i < nChildCount; ++i )
+ {
+ int nChildItemIndex = GetChild( nItemIndex, i );
+ pFoundHandles[i] = RefreshTreeViewItem( nChildItemIndex );
+ }
+
+ // Check directory structure to see if other directories were added
+ DirHandle_t hChild = m_DirectoryStructure.FirstChild( hPath );
+ for ( ; hChild != m_DirectoryStructure.InvalidIndex(); hChild = m_DirectoryStructure.NextSibling( hChild ) )
+ {
+ // Search for existence of this child already
+ bool bFound = false;
+ for ( int j = 0; j < nChildCount; ++j )
+ {
+ if ( pFoundHandles[j] == hChild )
+ {
+ pFoundHandles[j] = pFoundHandles[nChildCount-1];
+ --nChildCount;
+ bFound = true;
+ break;
+ }
+ }
+
+ if ( bFound )
+ continue;
+
+ // Child is new, add it
+ AddDirectoryToTreeView( nItemIndex, pFullParentPath, hChild );
+ }
+
+ return hPath;
+}
+
+void CAssetTreeView::RefreshFileList()
+{
+ // Make sure the expand icons are set correctly
+ RefreshTreeViewItem( GetRootItemIndex() );
+ InvalidateLayout();
+}
+
+
+//-----------------------------------------------------------------------------
+// Selects a folder
+//-----------------------------------------------------------------------------
+bool CAssetTreeView::SelectFolder_R( int nItemID, const char *pPath )
+{
+ if ( nItemID < 0 )
+ return false;
+
+ KeyValues *kv = GetItemData( nItemID );
+ const char *pTestPath = kv->GetString( "path" );
+ if ( !Q_stricmp( pTestPath, pPath ) )
+ {
+ AddSelectedItem( nItemID, true, false, true );
+ return true;
+ }
+
+ // Substring match..
+ CUtlString str = pTestPath;
+ str += '\\';
+ if ( Q_strnicmp( str, pPath, str.Length() ) )
+ return false;
+
+ ExpandItem( nItemID, true );
+
+ int nChildCount = GetNumChildren( nItemID );
+ for ( int i = 0; i < nChildCount; ++i )
+ {
+ int nChildItemID = GetChild( nItemID, i );
+ if ( SelectFolder_R( nChildItemID, pPath ) )
+ return true;
+ }
+ return false;
+}
+
+void CAssetTreeView::SelectFolder( const char *pSubDir, const char *pPath )
+{
+ char pTemp[MAX_PATH];
+ Q_snprintf( pTemp, sizeof(pTemp), "%s\\%s", pSubDir, pPath );
+ Q_StripTrailingSlash( pTemp );
+
+ int nItem = GetRootItemIndex();
+ SelectFolder_R( nItem, pTemp );
+}
+
+
+
+//-----------------------------------------------------------------------------
+// setup a smaller font
+//-----------------------------------------------------------------------------
+void CAssetTreeView::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+ SetFont( pScheme->GetFont("DefaultSmall") );
+ SetFgColor( Color(216, 222, 211, 255) );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Cache of asset data so we don't need to rebuild all the time
+//
+//-----------------------------------------------------------------------------
+DECLARE_POINTER_HANDLE( AssetList_t );
+#define ASSET_LIST_INVALID ((AssetList_t)(0xFFFF))
+
+
+class CAssetCache
+{
+public:
+ struct CachedAssetInfo_t
+ {
+ CUtlString m_AssetName;
+ int m_nModIndex;
+ };
+
+ struct ModInfo_t
+ {
+ CUtlString m_ModName;
+ CUtlString m_Path;
+ };
+
+ CAssetCache();
+
+ // Mod iteration
+ int ModCount() const;
+ const ModInfo_t& ModInfo( int nIndex ) const;
+
+ // Building the mod list
+ void BuildModList();
+
+ AssetList_t FindAssetList( const char *pAssetType, const char *pSubDir, int nExtCount, const char **ppExt );
+ bool BeginAssetScan( AssetList_t hList, bool bForceRescan = false );
+ CAssetTreeView* GetFileTree( AssetList_t hList );
+ int GetAssetCount( AssetList_t hList ) const;
+ const CachedAssetInfo_t& GetAsset( AssetList_t hList, int nIndex ) const;
+
+ void AddAsset( AssetList_t hList, const CachedAssetInfo_t& info );
+
+ bool ContinueSearchForAssets( AssetList_t hList, float flDuration );
+
+private:
+ struct DirToCheck_t
+ {
+ CUtlString m_DirName;
+ DirHandle_t m_hDirHandle;
+ };
+
+ struct CachedAssetList_t
+ {
+ CachedAssetList_t() {}
+ CachedAssetList_t( const char *pSearchSubDir, int nExtCount, const char **ppSearchExt ) :
+ m_pSubDir( pSearchSubDir, Q_strlen( pSearchSubDir ) + 1 )
+ {
+ m_Ext.AddMultipleToTail( nExtCount, ppSearchExt );
+ }
+ CachedAssetList_t( const CachedAssetList_t& )
+ {
+ // Only used during insertion; do nothing
+ }
+
+ CUtlVector< CachedAssetInfo_t > m_AssetList;
+ CAssetTreeView *m_pFileTree;
+
+ CUtlString m_pSubDir;
+ CUtlVector< const char * > m_Ext;
+
+ CUtlLinkedList< DirToCheck_t > m_DirectoriesToCheck;
+ FileFindHandle_t m_hFind;
+ bool m_bAssetScanComplete;
+ };
+
+private:
+ bool AddFilesInDirectory( CachedAssetList_t& list, const char *pStartingFile, const char *pFilePath, DirHandle_t hDirHandle, float flStartTime, float flDuration );
+ bool DoesExtensionMatch( CachedAssetList_t& list, const char *pFileName );
+ void AddAssetToList( CachedAssetList_t& list, const char *pAssetName, int nModIndex );
+
+private:
+ // List of known mods
+ CUtlVector< ModInfo_t > m_ModList;
+
+ // List of cached assets
+ CUtlRBTree< CachedAssetList_t > m_CachedAssets;
+
+ // Have we built the mod list?
+ bool m_bBuiltModList;
+
+ static bool CachedAssetLessFunc( const CachedAssetList_t& src1, const CachedAssetList_t& src2 );
+};
+
+
+//-----------------------------------------------------------------------------
+// Static instance of the asset cache
+//-----------------------------------------------------------------------------
+static CAssetCache s_AssetCache;
+
+
+//-----------------------------------------------------------------------------
+// Map sort func
+//-----------------------------------------------------------------------------
+bool CAssetCache::CachedAssetLessFunc( const CAssetCache::CachedAssetList_t& src1, const CAssetCache::CachedAssetList_t& src2 )
+{
+ int nRetVal = Q_stricmp( src1.m_pSubDir, src2.m_pSubDir ) > 0;
+ if ( nRetVal != 0 )
+ return nRetVal > 0;
+
+ int nCount = src1.m_Ext.Count();
+ int nDiff = nCount - src2.m_Ext.Count();
+ if ( nDiff != 0 )
+ return nDiff > 0;
+
+ for ( int i = 0; i < nCount; ++i )
+ {
+ nRetVal = Q_stricmp( src1.m_Ext[i], src2.m_Ext[i] );
+ if ( nRetVal != 0 )
+ return nRetVal > 0;
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CAssetCache::CAssetCache() : m_CachedAssets( 0, 0, CachedAssetLessFunc )
+{
+ m_bBuiltModList = false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Mod iteration
+//-----------------------------------------------------------------------------
+int CAssetCache::ModCount() const
+{
+ return m_ModList.Count();
+}
+
+const CAssetCache::ModInfo_t& CAssetCache::ModInfo( int nIndex ) const
+{
+ return m_ModList[nIndex];
+}
+
+
+//-----------------------------------------------------------------------------
+// Building the mod list
+//-----------------------------------------------------------------------------
+void CAssetCache::BuildModList()
+{
+ if ( m_bBuiltModList )
+ return;
+
+ m_bBuiltModList = true;
+
+ m_ModList.RemoveAll();
+
+ // Add all mods
+ int nLen = g_pFullFileSystem->GetSearchPath( "GAME", false, NULL, 0 );
+ char *pSearchPath = (char*)stackalloc( nLen * sizeof(char) );
+ g_pFullFileSystem->GetSearchPath( "GAME", false, pSearchPath, nLen );
+ char *pPath = pSearchPath;
+ while( pPath )
+ {
+ char *pSemiColon = strchr( pPath, ';' );
+ if ( pSemiColon )
+ {
+ *pSemiColon = 0;
+ }
+
+ Q_StripTrailingSlash( pPath );
+ Q_FixSlashes( pPath );
+
+ char pModName[ MAX_PATH ];
+ Q_FileBase( pPath, pModName, sizeof( pModName ) );
+
+ // Always start in an asset-specific directory
+// char pAssetPath[MAX_PATH];
+// Q_snprintf( pAssetPath, MAX_PATH, "%s\\%s", pPath, m_pAssetSubDir );
+// Q_FixSlashes( pPath );
+
+ int i = m_ModList.AddToTail( );
+ m_ModList[i].m_ModName.Set( pModName );
+ m_ModList[i].m_Path.Set( pPath );
+
+ pPath = pSemiColon ? pSemiColon + 1 : NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds an asset to the list of assets of this type
+//-----------------------------------------------------------------------------
+void CAssetCache::AddAssetToList( CachedAssetList_t& list, const char *pAssetName, int nModIndex )
+{
+ int i = list.m_AssetList.AddToTail( );
+ CachedAssetInfo_t& info = list.m_AssetList[i];
+ info.m_AssetName.Set( pAssetName );
+ info.m_nModIndex = nModIndex;
+}
+
+
+//-----------------------------------------------------------------------------
+// Extension matches?
+//-----------------------------------------------------------------------------
+bool CAssetCache::DoesExtensionMatch( CachedAssetList_t& info, const char *pFileName )
+{
+ char pChildExt[MAX_PATH];
+ Q_ExtractFileExtension( pFileName, pChildExt, sizeof(pChildExt) );
+
+ // Check the extension matches
+ int nCount = info.m_Ext.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( !Q_stricmp( info.m_Ext[i], pChildExt ) )
+ return true;
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Recursively add all files matching the wildcard under this directory
+//-----------------------------------------------------------------------------
+bool CAssetCache::AddFilesInDirectory( CachedAssetList_t& list, const char *pStartingFile, const char *pFilePath, DirHandle_t hCurrentDir, float flStartTime, float flDuration )
+{
+ // Indicates no files found
+ if ( list.m_hFind == FILESYSTEM_INVALID_FIND_HANDLE )
+ return true;
+
+ // generate children
+ // add all the items
+ int nModCount = m_ModList.Count();
+ int nSubDirLen = list.m_pSubDir ? Q_strlen(list.m_pSubDir) : 0;
+ const char *pszFileName = pStartingFile;
+ while ( pszFileName )
+ {
+ char pRelativeChildPath[MAX_PATH];
+ Q_snprintf( pRelativeChildPath, MAX_PATH, "%s\\%s", pFilePath, pszFileName );
+
+ if ( g_pFullFileSystem->FindIsDirectory( list.m_hFind ) )
+ {
+ // If .svn is in the name, don't add this directory!!
+ if ( strstr (pszFileName, ".svn") )
+ {
+ pszFileName = g_pFullFileSystem->FindNext( list.m_hFind );
+ continue;
+ }
+
+ if ( Q_strnicmp( pszFileName, ".", 2 ) && Q_strnicmp( pszFileName, "..", 3 ) )
+ {
+ DirHandle_t hDirHandle = list.m_pFileTree->AddSubDirectory( hCurrentDir, pszFileName );
+ int i = list.m_DirectoriesToCheck.AddToTail();
+ list.m_DirectoriesToCheck[i].m_DirName = pRelativeChildPath;
+ list.m_DirectoriesToCheck[i].m_hDirHandle = hDirHandle;
+ }
+ }
+ else
+ {
+ // Check the extension matches
+ if ( DoesExtensionMatch( list, pszFileName ) )
+ {
+ char pFullAssetPath[MAX_PATH];
+ g_pFullFileSystem->RelativePathToFullPath( pRelativeChildPath, "GAME", pFullAssetPath, sizeof(pFullAssetPath) );
+
+ int nModIndex = -1;
+ for ( int i = 0; i < nModCount; ++i )
+ {
+ if ( !Q_strnicmp( pFullAssetPath, m_ModList[i].m_Path, m_ModList[i].m_Path.Length() ) )
+ {
+ nModIndex = i;
+ break;
+ }
+ }
+
+ if ( nModIndex >= 0 )
+ {
+ // Strip 'subdir/' prefix
+ char *pAssetName = pRelativeChildPath;
+ if ( list.m_pSubDir )
+ {
+ if ( !Q_strnicmp( list.m_pSubDir, pAssetName, nSubDirLen ) )
+ {
+ if ( pAssetName[nSubDirLen] == '\\' )
+ {
+ pAssetName += nSubDirLen + 1;
+ }
+ }
+ }
+ strlwr( pAssetName );
+
+ AddAssetToList( list, pAssetName, nModIndex );
+ }
+ }
+ }
+
+ // Don't let the search go for too long at a time
+ if ( Plat_FloatTime() - flStartTime >= flDuration )
+ return false;
+
+ pszFileName = g_pFullFileSystem->FindNext( list.m_hFind );
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Recursively add all files matching the wildcard under this directory
+//-----------------------------------------------------------------------------
+bool CAssetCache::ContinueSearchForAssets( AssetList_t hList, float flDuration )
+{
+ CachedAssetList_t& list = m_CachedAssets[ (int)hList ];
+
+ float flStartTime = Plat_FloatTime();
+ while ( list.m_DirectoriesToCheck.Count() )
+ {
+ const char *pFilePath = list.m_DirectoriesToCheck[ list.m_DirectoriesToCheck.Head() ].m_DirName;
+ DirHandle_t hCurrentDir = list.m_DirectoriesToCheck[ list.m_DirectoriesToCheck.Head() ].m_hDirHandle;
+
+ const char *pStartingFile;
+ if ( list.m_hFind == FILESYSTEM_INVALID_FIND_HANDLE )
+ {
+ char pSearchString[MAX_PATH];
+ Q_snprintf( pSearchString, MAX_PATH, "%s\\*", pFilePath );
+
+ // get the list of files
+ pStartingFile = g_pFullFileSystem->FindFirstEx( pSearchString, "GAME", &list.m_hFind );
+ }
+ else
+ {
+ pStartingFile = g_pFullFileSystem->FindNext( list.m_hFind );
+ }
+
+ if ( !AddFilesInDirectory( list, pStartingFile, pFilePath, hCurrentDir, flStartTime, flDuration ) )
+ return false;
+
+ g_pFullFileSystem->FindClose( list.m_hFind );
+ list.m_hFind = FILESYSTEM_INVALID_FIND_HANDLE;
+ list.m_DirectoriesToCheck.Remove( list.m_DirectoriesToCheck.Head() );
+ }
+ list.m_bAssetScanComplete = true;
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Asset cache iteration
+//-----------------------------------------------------------------------------
+bool CAssetCache::BeginAssetScan( AssetList_t hList, bool bForceRescan )
+{
+ CachedAssetList_t& list = m_CachedAssets[ (int)hList ];
+ if ( bForceRescan )
+ {
+ list.m_bAssetScanComplete = false;
+ if ( list.m_hFind != FILESYSTEM_INVALID_FIND_HANDLE )
+ {
+ g_pFullFileSystem->FindClose( list.m_hFind );
+ list.m_hFind = FILESYSTEM_INVALID_FIND_HANDLE;
+ }
+ list.m_DirectoriesToCheck.RemoveAll();
+ }
+
+ if ( list.m_bAssetScanComplete )
+ return false;
+
+ // This case occurs if we stopped the picker previously while in the middle of a scan
+ if ( list.m_hFind != FILESYSTEM_INVALID_FIND_HANDLE )
+ return true;
+
+ list.m_AssetList.RemoveAll();
+ list.m_pFileTree->ClearDirectories();
+
+ // Add all files, determine which mod they are in.
+ int i = list.m_DirectoriesToCheck.AddToTail();
+ list.m_DirectoriesToCheck[i].m_DirName = list.m_pSubDir;
+ list.m_DirectoriesToCheck[i].m_hDirHandle = list.m_pFileTree->GetRootDirectory();
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Asset cache iteration
+//-----------------------------------------------------------------------------
+AssetList_t CAssetCache::FindAssetList( const char *pAssetType, const char *pSubDir, int nExtCount, const char **ppExt )
+{
+ CachedAssetList_t search( pSubDir, nExtCount, ppExt );
+ int nIndex = m_CachedAssets.Find( search );
+ if ( nIndex == m_CachedAssets.InvalidIndex() )
+ {
+ nIndex = m_CachedAssets.Insert( search );
+ CachedAssetList_t &list = m_CachedAssets[nIndex];
+ list.m_pSubDir = pSubDir;
+ list.m_Ext.AddMultipleToTail( nExtCount, ppExt );
+ list.m_hFind = FILESYSTEM_INVALID_FIND_HANDLE;
+ list.m_bAssetScanComplete = false;
+ list.m_pFileTree = new CAssetTreeView( NULL, "FolderFilter", pAssetType, pSubDir );
+ }
+
+ return (AssetList_t)nIndex;
+}
+
+CAssetTreeView* CAssetCache::GetFileTree( AssetList_t hList )
+{
+ if ( hList == ASSET_LIST_INVALID )
+ return NULL;
+ return m_CachedAssets[ (int)hList ].m_pFileTree;
+}
+
+int CAssetCache::GetAssetCount( AssetList_t hList ) const
+{
+ if ( hList == ASSET_LIST_INVALID )
+ return 0;
+ return m_CachedAssets[ (int)hList ].m_AssetList.Count();
+}
+
+const CAssetCache::CachedAssetInfo_t& CAssetCache::GetAsset( AssetList_t hList, int nIndex ) const
+{
+ Assert( nIndex < GetAssetCount(hList) );
+ return m_CachedAssets[ (int)hList ].m_AssetList[ nIndex ];
+}
+
+
+
+//-----------------------------------------------------------------------------
+//
+// Base asset Picker
+//
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Sort by asset name
+//-----------------------------------------------------------------------------
+static int __cdecl AssetBrowserSortFunc( vgui::ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
+{
+ bool bRoot1 = item1.kv->GetInt("root") != 0;
+ bool bRoot2 = item2.kv->GetInt("root") != 0;
+ if ( bRoot1 != bRoot2 )
+ return bRoot1 ? -1 : 1;
+ const char *pString1 = item1.kv->GetString("asset");
+ const char *pString2 = item2.kv->GetString("asset");
+ return Q_stricmp( pString1, pString2 );
+}
+
+static int __cdecl AssetBrowserModSortFunc( vgui::ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
+{
+ int nMod1 = item1.kv->GetInt("modIndex", -1);
+ int nMod2 = item2.kv->GetInt("modIndex", -1);
+ if ( nMod1 != nMod2 )
+ return nMod1 - nMod2;
+ return AssetBrowserSortFunc( pPanel, item1, item2 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CBaseAssetPicker::CBaseAssetPicker( vgui::Panel *pParent, const char *pAssetType,
+ const char *pExt, const char *pSubDir, const char *pTextType ) :
+ BaseClass( pParent, "AssetPicker" )
+{
+ m_bBuiltAssetList = false;
+ m_pAssetType = pAssetType;
+ m_pAssetTextType = pTextType;
+ m_pAssetExt = pExt;
+ m_pAssetSubDir = pSubDir;
+ m_bFinishedAssetListScan = false;
+ m_bFirstAssetScan = false;
+ m_nMatchingAssets = 0;
+ m_bSubDirCheck = true;
+ m_hAssetList = ASSET_LIST_INVALID;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CBaseAssetPicker::~CBaseAssetPicker()
+{
+ SaveUserConfig();
+
+ // Detach!
+ m_pFileTree->RemoveActionSignalTarget( this );
+ m_pFileTree->SetParent( (Panel*)NULL );
+ m_pFileTree = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates standard controls
+//-----------------------------------------------------------------------------
+void CBaseAssetPicker::CreateStandardControls( vgui::Panel *pParent, bool bAllowMultiselect )
+{
+ int nExtCount = 1 + m_ExtraAssetExt.Count();
+ const char **ppExt = (const char **)_alloca( nExtCount * sizeof(const char *) );
+ ppExt[0] = m_pAssetExt;
+ if ( nExtCount > 1 )
+ {
+ memcpy( ppExt + 1, m_ExtraAssetExt.Base(), nExtCount - 1 );
+ }
+
+ m_hAssetList = s_AssetCache.FindAssetList( m_pAssetType, m_pAssetSubDir, nExtCount, ppExt );
+
+ m_pAssetSplitter = new vgui::Splitter( pParent, "AssetSplitter", SPLITTER_MODE_HORIZONTAL, 1 );
+ vgui::Panel *pSplitterTopSide = m_pAssetSplitter->GetChild( 0 );
+ vgui::Panel *pSplitterBottomSide = m_pAssetSplitter->GetChild( 1 );
+
+ // Combo box for mods
+ m_pModSelector = new ComboBox( pSplitterTopSide, "ModFilter", 5, false );
+ m_pModSelector->AddActionSignalTarget( this );
+
+ // Rescan button
+ m_pRescanButton = new Button( pSplitterTopSide, "RescanButton", "Rescan", this, "AssetRescan" );
+
+ // file browser tree controls
+ m_pFileTree = s_AssetCache.GetFileTree( m_hAssetList );
+ m_pFileTree->SetParent( pSplitterTopSide );
+ m_pFileTree->AddActionSignalTarget( this );
+
+ m_pSubDirCheck = new CheckButton( pSplitterTopSide, "SubDirCheck", "Check subfolders for files?" );
+ m_pSubDirCheck->SetSelected( true );
+ m_pSubDirCheck->SetEnabled( false );
+ m_pSubDirCheck->SetVisible( false );
+ m_pSubDirCheck->AddActionSignalTarget( this );
+
+ char pTemp[512];
+ Q_snprintf( pTemp, sizeof(pTemp), "No .%s files", m_pAssetExt );
+ m_pAssetBrowser = new vgui::ListPanel( pSplitterBottomSide, "AssetBrowser" );
+ m_pAssetBrowser->AddColumnHeader( 0, "mod", "Mod", 52, 0 );
+ m_pAssetBrowser->AddColumnHeader( 1, "asset", m_pAssetType, 128, ListPanel::COLUMN_RESIZEWITHWINDOW );
+ m_pAssetBrowser->SetSelectIndividualCells( false );
+ m_pAssetBrowser->SetMultiselectEnabled( bAllowMultiselect );
+ m_pAssetBrowser->SetEmptyListText( pTemp );
+ m_pAssetBrowser->SetDragEnabled( true );
+ m_pAssetBrowser->AddActionSignalTarget( this );
+ m_pAssetBrowser->SetSortFunc( 0, AssetBrowserModSortFunc );
+ m_pAssetBrowser->SetSortFunc( 1, AssetBrowserSortFunc );
+ m_pAssetBrowser->SetSortColumn( 1 );
+
+ // filter selection
+ m_pFilter = new TextEntry( pSplitterBottomSide, "FilterList" );
+ m_pFilter->AddActionSignalTarget( this );
+
+ // full path
+ m_pFullPath = new TextEntry( pSplitterBottomSide, "FullPath" );
+ m_pFullPath->SetEnabled( false );
+ m_pFullPath->SetEditable( false );
+
+ m_nCurrentModFilter = -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Reads user config settings
+//-----------------------------------------------------------------------------
+void CBaseAssetPicker::ApplyUserConfigSettings( KeyValues *pUserConfig )
+{
+ BaseClass::ApplyUserConfigSettings( pUserConfig );
+
+ // Populates the mod list names
+ RefreshAssetList();
+
+ const char *pFilter = pUserConfig->GetString( "filter", "" );
+ m_FolderFilter = pUserConfig->GetString( "folderfilter", "" );
+ const char *pMod = pUserConfig->GetString( "mod", "" );
+ SetFilter( pFilter );
+ m_nCurrentModFilter = -1;
+ if ( pMod && pMod[0] )
+ {
+ int nCount = s_AssetCache.ModCount();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ const CAssetCache::ModInfo_t& modInfo = s_AssetCache.ModInfo( i );
+ if ( Q_stricmp( pMod, modInfo.m_ModName ) )
+ continue;
+
+ int nItemCount = m_pModSelector->GetItemCount();
+ for ( int j = 0; j < nItemCount; ++j )
+ {
+ int nItemID = m_pModSelector->GetItemIDFromRow( j );
+ KeyValues *kv = m_pModSelector->GetItemUserData( nItemID );
+ int nModIndex = kv->GetInt( "mod" );
+ if ( nModIndex == i )
+ {
+ m_nCurrentModFilter = i;
+ m_pModSelector->ActivateItem( nItemID );
+ break;
+ }
+ }
+ break;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: returns user config settings for this control
+//-----------------------------------------------------------------------------
+void CBaseAssetPicker::GetUserConfigSettings( KeyValues *pUserConfig )
+{
+ BaseClass::GetUserConfigSettings( pUserConfig );
+ pUserConfig->SetString( "filter", m_Filter );
+ pUserConfig->SetString( "folderfilter", m_FolderFilter );
+ pUserConfig->SetString( "mod", ( m_nCurrentModFilter >= 0 ) ?
+ s_AssetCache.ModInfo( m_nCurrentModFilter ).m_ModName : "" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: optimization, return true if this control has any user config settings
+//-----------------------------------------------------------------------------
+bool CBaseAssetPicker::HasUserConfigSettings()
+{
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Allows the picker to browse multiple asset types
+//-----------------------------------------------------------------------------
+void CBaseAssetPicker::AddExtension( const char *pExtension )
+{
+ m_ExtraAssetExt.AddToTail( pExtension );
+}
+
+
+//-----------------------------------------------------------------------------
+// Is multiselect enabled?
+//-----------------------------------------------------------------------------
+bool CBaseAssetPicker::IsMultiselectEnabled() const
+{
+ return m_pAssetBrowser->IsMultiselectEnabled();
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the initial selected asset
+//-----------------------------------------------------------------------------
+void CBaseAssetPicker::SetInitialSelection( const char *pAssetName )
+{
+ // This makes it so the background list filling code will automatically select this asset when it gets to it.
+ m_SelectedAsset = pAssetName;
+
+ if ( pAssetName )
+ {
+ // Sometimes we've already refreshed our list with a bunch of cached resources and the item is already in the list,
+ // so in that case just select it here.
+ int cnt = m_pAssetBrowser->GetItemCount();
+ for ( int i=0; i < cnt; i++ )
+ {
+ KeyValues *kv = m_pAssetBrowser->GetItem( i );
+ if ( !kv )
+ continue;
+
+ const char *pTestAssetName = kv->GetString( "asset" );
+ if ( !pTestAssetName )
+ continue;
+
+ if ( Q_stricmp( pTestAssetName, pAssetName ) == 0 )
+ {
+ m_pAssetBrowser->SetSelectedCell( i, 0 );
+ break;
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Set/get the filter
+//-----------------------------------------------------------------------------
+void CBaseAssetPicker::SetFilter( const char *pFilter )
+{
+ m_Filter = pFilter;
+ m_pFilter->SetText( pFilter );
+}
+
+const char *CBaseAssetPicker::GetFilter()
+{
+ return m_Filter;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: called to open
+//-----------------------------------------------------------------------------
+void CBaseAssetPicker::Activate()
+{
+ RefreshAssetList();
+ RequestFilterFocus();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseAssetPicker::OnKeyCodePressed( KeyCode code )
+{
+ if (( code == KEY_UP ) || ( code == KEY_DOWN ) || ( code == KEY_PAGEUP ) || ( code == KEY_PAGEDOWN ))
+ {
+ KeyValues *pMsg = new KeyValues("KeyCodePressed", "code", code);
+ vgui::ipanel()->SendMessage( m_pAssetBrowser->GetVPanel(), pMsg, GetVPanel());
+ pMsg->deleteThis();
+ }
+ else
+ {
+ BaseClass::OnKeyCodePressed( code );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Is a particular asset visible?
+//-----------------------------------------------------------------------------
+bool CBaseAssetPicker::IsAssetVisible( int nAssetIndex )
+{
+ const CAssetCache::CachedAssetInfo_t& info = s_AssetCache.GetAsset( m_hAssetList, nAssetIndex );
+
+ // Filter based on active mod
+ int nModIndex = info.m_nModIndex;
+ if ( ( m_nCurrentModFilter >= 0 ) && ( m_nCurrentModFilter != nModIndex ) )
+ return false;
+
+ // Filter based on name
+ const char *pAssetName = info.m_AssetName;
+ if ( !Q_strcmp( pAssetName, m_SelectedAsset ) )
+ return true;
+
+ if ( m_Filter.Length() && !Q_stristr( pAssetName, m_Filter.Get() ) )
+ return false;
+
+ // Filter based on folder
+ if ( m_FolderFilter.Length() && Q_strnicmp( pAssetName, m_FolderFilter.Get(), m_FolderFilter.Length() ) )
+ return false;
+
+ // Filter based on subdirectory check
+ if ( !m_bSubDirCheck && strchr( pAssetName + m_FolderFilter.Length(), '\\' ) )
+ return false;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds an asset from the cache to the list
+//-----------------------------------------------------------------------------
+void CBaseAssetPicker::AddAssetToList( int nAssetIndex )
+{
+ const CAssetCache::CachedAssetInfo_t& info = s_AssetCache.GetAsset( m_hAssetList, nAssetIndex );
+
+ bool bInRootDir = !strchr( info.m_AssetName, '\\' ) && !strchr( info.m_AssetName, '/' );
+
+ KeyValues *kv = new KeyValues( "node", "asset", info.m_AssetName );
+ kv->SetString( "mod", s_AssetCache.ModInfo( info.m_nModIndex ).m_ModName );
+ kv->SetInt( "modIndex", info.m_nModIndex );
+ kv->SetInt( "root", bInRootDir );
+ int nItemID = m_pAssetBrowser->AddItem( kv, 0, false, false );
+ kv->deleteThis();
+
+ if ( m_pAssetBrowser->GetSelectedItemsCount() == 0 && !Q_strcmp( m_SelectedAsset, info.m_AssetName ) )
+ {
+ m_pAssetBrowser->SetSelectedCell( nItemID, 0 );
+ }
+
+ KeyValues *pDrag = new KeyValues( "drag", "text", info.m_AssetName );
+ if ( m_pAssetTextType )
+ {
+ pDrag->SetString( "texttype", m_pAssetTextType );
+ }
+ m_pAssetBrowser->SetItemDragData( nItemID, pDrag );
+
+ int i = m_AssetList.AddToTail( );
+ m_AssetList[i].m_nAssetIndex = nAssetIndex;
+ m_AssetList[i].m_nItemId = nItemID;
+
+ bool bIsVisible = IsAssetVisible( i );
+ m_pAssetBrowser->SetItemVisible( nItemID, bIsVisible );
+ if ( bIsVisible )
+ {
+ ++m_nMatchingAssets;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Continues to build the asset list
+//-----------------------------------------------------------------------------
+void CBaseAssetPicker::OnTick()
+{
+ BaseClass::OnTick();
+
+ int nPreAssetCount = s_AssetCache.GetAssetCount( m_hAssetList );
+
+ // Stop getting called back once all assets have been found
+ float flTime = m_bFirstAssetScan ? ASSET_LIST_DIRECTORY_INITIAL_SEARCH_TIME : ASSET_LIST_DIRECTORY_SEARCH_TIME;
+ bool bFinished = s_AssetCache.ContinueSearchForAssets( m_hAssetList, flTime );
+
+ if ( m_bFirstAssetScan )
+ {
+ m_pFileTree->OpenRoot();
+ }
+ m_bFirstAssetScan = false;
+
+ int nPostAssetCount = s_AssetCache.GetAssetCount( m_hAssetList );
+ for ( int i = nPreAssetCount; i < nPostAssetCount; ++i )
+ {
+ AddAssetToList( i );
+ }
+
+ if ( bFinished )
+ {
+ vgui::ivgui()->RemoveTickSignal( GetVPanel() );
+ m_bFinishedAssetListScan = true;
+
+ // Copy the current folder filter.. this is necessary
+ // to finally select the folder loaded from the user config settings
+ // in the free view (since it's finally populated at this point)
+ // NOTE: if a user has changed the folder filter between startup
+ // and this point, this should still work since m_FolderFilter should be updated
+ m_pFileTree->SelectFolder( m_pAssetSubDir, m_FolderFilter );
+ RefreshAssetList( );
+ return;
+ }
+
+ UpdateAssetColumnHeader();
+}
+
+
+//-----------------------------------------------------------------------------
+// Builds the Bsp name list
+//-----------------------------------------------------------------------------
+void CBaseAssetPicker::BuildAssetNameList( )
+{
+ if ( m_bBuiltAssetList )
+ return;
+
+ m_bBuiltAssetList = true;
+ m_nMatchingAssets = 0;
+ m_nCurrentModFilter = -1;
+
+ // Build the list of known mods if we haven't
+ s_AssetCache.BuildModList();
+
+ m_pModSelector->RemoveAll();
+ m_pModSelector->AddItem( "All Mods", new KeyValues( "Mod", "mod", -1 ) );
+ int nModCount = s_AssetCache.ModCount();
+ for ( int i = 0; i < nModCount; ++i )
+ {
+ const char *pModName = s_AssetCache.ModInfo( i ).m_ModName;
+ m_pModSelector->AddItem( pModName, new KeyValues( "Mod", "mod", i ) );
+ }
+ m_pModSelector->ActivateItemByRow( 0 );
+
+ // If we've already read in
+ if ( s_AssetCache.BeginAssetScan( m_hAssetList ) )
+ {
+ m_bFirstAssetScan = true;
+ m_bFinishedAssetListScan = false;
+ vgui::ivgui()->AddTickSignal( GetVPanel(), 10 );
+ }
+ else
+ {
+ m_bFirstAssetScan = false;
+ m_bFinishedAssetListScan = true;
+ }
+
+ int nAssetCount = s_AssetCache.GetAssetCount( m_hAssetList );
+ for ( int i = 0; i < nAssetCount; ++i )
+ {
+ AddAssetToList( i );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Rescan assets
+//-----------------------------------------------------------------------------
+void CBaseAssetPicker::RescanAssets()
+{
+ m_pAssetBrowser->RemoveAll();
+ m_AssetList.RemoveAll();
+ s_AssetCache.BeginAssetScan( m_hAssetList, true );
+ m_bFirstAssetScan = true;
+ m_nMatchingAssets = 0;
+
+ if ( m_bFinishedAssetListScan )
+ {
+ m_bFinishedAssetListScan = false;
+ vgui::ivgui()->AddTickSignal( GetVPanel(), 10 );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the mod path to the item index
+//-----------------------------------------------------------------------------
+const char *CBaseAssetPicker::GetModPath( int nModIndex )
+{
+ return s_AssetCache.ModInfo( nModIndex ).m_Path.Get();
+}
+
+
+//-----------------------------------------------------------------------------
+// Command handler
+//-----------------------------------------------------------------------------
+void CBaseAssetPicker::OnCommand( const char *pCommand )
+{
+ if ( !Q_stricmp( pCommand, "AssetRescan" ) )
+ {
+ RescanAssets();
+ return;
+ }
+
+ BaseClass::OnCommand( pCommand );
+}
+
+
+//-----------------------------------------------------------------------------
+// Update column headers
+//-----------------------------------------------------------------------------
+void CBaseAssetPicker::UpdateAssetColumnHeader( )
+{
+ char pColumnTitle[512];
+ Q_snprintf( pColumnTitle, sizeof(pColumnTitle), "%s (%d/%d)%s",
+ m_pAssetType, m_nMatchingAssets, m_AssetList.Count(), m_bFinishedAssetListScan ? "" : " ..." );
+ m_pAssetBrowser->SetColumnHeaderText( 1, pColumnTitle );
+}
+
+
+//-----------------------------------------------------------------------------
+// Request focus of the filter box
+//-----------------------------------------------------------------------------
+void CBaseAssetPicker::RequestFilterFocus()
+{
+ if ( m_Filter.Length() )
+ {
+ m_pFilter->SelectAllOnFirstFocus( true );
+ }
+ m_pFilter->RequestFocus();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: refreshes the asset list
+//-----------------------------------------------------------------------------
+void CBaseAssetPicker::RefreshAssetList( )
+{
+ BuildAssetNameList();
+
+ // Check the filter matches
+ int nCount = m_AssetList.Count();
+ m_nMatchingAssets = 0;
+ for ( int i = 0; i < nCount; ++i )
+ {
+ // Filter based on active mod
+ bool bIsVisible = IsAssetVisible( i );
+ m_pAssetBrowser->SetItemVisible( m_AssetList[i].m_nItemId, bIsVisible );
+ if ( bIsVisible )
+ {
+ ++m_nMatchingAssets;
+ }
+ }
+
+ UpdateAssetColumnHeader();
+ m_pAssetBrowser->SortList();
+
+ if ( ( m_pAssetBrowser->GetSelectedItemsCount() == 0 ) && ( m_pAssetBrowser->GetItemCount() > 0 ) )
+ {
+ // Invoke a callback if the next selection will be a 'default' selection
+ OnNextSelectionIsDefault();
+ int nItemID = m_pAssetBrowser->GetItemIDFromRow( 0 );
+ m_pAssetBrowser->SetSelectedCell( nItemID, 0 );
+ }
+
+ m_pFileTree->RefreshFileList();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: refreshes dialog on file folder changing
+//-----------------------------------------------------------------------------
+void CBaseAssetPicker::OnFileSelected()
+{
+ // update list
+ const char *pFolderFilter = "";
+ int iItem = m_pFileTree->GetFirstSelectedItem();
+ if ( iItem >= 0 )
+ {
+ KeyValues *pkv = m_pFileTree->GetItemData( iItem );
+ pFolderFilter = pkv->GetString( "path" );
+
+ // The first keys are always the subdir
+ pFolderFilter += Q_strlen( m_pAssetSubDir );
+ if ( *pFolderFilter )
+ {
+ ++pFolderFilter;
+ }
+ }
+
+ if ( Q_stricmp( pFolderFilter, m_FolderFilter.Get() ) )
+ {
+ int nLen = Q_strlen( pFolderFilter );
+ m_FolderFilter = pFolderFilter;
+ if ( nLen > 0 )
+ {
+ m_FolderFilter += '\\';
+ }
+ RefreshAssetList();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: refreshes dialog on text changing
+//-----------------------------------------------------------------------------
+void CBaseAssetPicker::OnTextChanged( KeyValues *pKeyValues )
+{
+ vgui::Panel *pSource = (vgui::Panel*)pKeyValues->GetPtr( "panel" );
+ if ( pSource == m_pFilter )
+ {
+ int nLength = m_pFilter->GetTextLength();
+ char *pNewFilter = (char*)_alloca( (nLength+1) * sizeof(char) );
+ if ( nLength > 0 )
+ {
+ m_pFilter->GetText( pNewFilter, nLength+1 );
+ }
+ else
+ {
+ pNewFilter[0] = 0;
+ }
+ if ( Q_stricmp( pNewFilter, m_Filter.Get() ) )
+ {
+ m_Filter.SetLength( nLength );
+ m_Filter = pNewFilter;
+ RefreshAssetList();
+ }
+ return;
+ }
+
+ if ( pSource == m_pModSelector )
+ {
+ KeyValues *pKeyValuesActive = m_pModSelector->GetActiveItemUserData();
+ if ( pKeyValuesActive )
+ {
+ m_nCurrentModFilter = pKeyValuesActive->GetInt( "mod", -1 );
+ RefreshAssetList();
+ }
+ return;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Updates preview when an item is selected
+//-----------------------------------------------------------------------------
+void CBaseAssetPicker::OnItemSelected( KeyValues *kv )
+{
+ Panel *pPanel = (Panel *)kv->GetPtr( "panel", NULL );
+ if ( pPanel == m_pAssetBrowser )
+ {
+ int nCount = GetSelectedAssetCount();
+ Assert( nCount > 0 );
+ const char *pSelectedAsset = GetSelectedAsset( nCount - 1 );
+
+ // Fill in the full path
+ int nModIndex = GetSelectedAssetModIndex();
+ char pBuf[MAX_PATH];
+ Q_snprintf( pBuf, sizeof(pBuf), "%s\\%s\\%s",
+ s_AssetCache.ModInfo( nModIndex ).m_Path.Get(), m_pAssetSubDir, pSelectedAsset );
+ Q_FixSlashes( pBuf );
+ m_pFullPath->SetText( pBuf );
+
+ surface()->SetCursor( dc_waitarrow );
+ OnSelectedAssetPicked( pSelectedAsset );
+ return;
+ }
+}
+
+void CBaseAssetPicker::OnCheckButtonChecked( KeyValues *kv )
+{
+ vgui::Panel *pSource = (vgui::Panel*)kv->GetPtr( "panel" );
+ if ( pSource == m_pSubDirCheck )
+ {
+ m_bSubDirCheck = m_pSubDirCheck->IsSelected();
+ RefreshAssetList();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the selceted asset count
+//-----------------------------------------------------------------------------
+int CBaseAssetPicker::GetSelectedAssetCount()
+{
+ return m_pAssetBrowser->GetSelectedItemsCount();
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the selceted asset name
+//-----------------------------------------------------------------------------
+const char *CBaseAssetPicker::GetSelectedAsset( int nAssetIndex )
+{
+ int nSelectedAssetCount = m_pAssetBrowser->GetSelectedItemsCount();
+ if ( nAssetIndex < 0 )
+ {
+ nAssetIndex = nSelectedAssetCount - 1;
+ }
+ if ( nSelectedAssetCount <= nAssetIndex || nAssetIndex < 0 )
+ return NULL;
+
+ int nIndex = m_pAssetBrowser->GetSelectedItem( nAssetIndex );
+ KeyValues *pItemKeyValues = m_pAssetBrowser->GetItem( nIndex );
+ return pItemKeyValues->GetString( "asset" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the selceted asset mod index
+//-----------------------------------------------------------------------------
+int CBaseAssetPicker::GetSelectedAssetModIndex( )
+{
+ if ( m_pAssetBrowser->GetSelectedItemsCount() == 0 )
+ return 0;
+
+ int nIndex = m_pAssetBrowser->GetSelectedItem( 0 );
+ KeyValues *pItemKeyValues = m_pAssetBrowser->GetItem( nIndex );
+ return pItemKeyValues->GetInt( "modIndex" );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Purpose: Modal picker frame
+//
+//-----------------------------------------------------------------------------
+CBaseAssetPickerFrame::CBaseAssetPickerFrame( vgui::Panel *pParent ) :
+ BaseClass( pParent, "AssetPickerFrame" )
+{
+ m_pContextKeyValues = NULL;
+ SetDeleteSelfOnClose( true );
+ m_pOpenButton = new Button( this, "OpenButton", "#FileOpenDialog_Open", this, "Open" );
+ m_pCancelButton = new Button( this, "CancelButton", "#FileOpenDialog_Cancel", this, "Cancel" );
+ SetBlockDragChaining( true );
+}
+
+CBaseAssetPickerFrame::~CBaseAssetPickerFrame()
+{
+ CleanUpMessage();
+}
+
+
+//-----------------------------------------------------------------------------
+// Allows the derived class to create the picker
+//-----------------------------------------------------------------------------
+void CBaseAssetPickerFrame::SetAssetPicker( CBaseAssetPicker* pPicker )
+{
+ m_pPicker = pPicker;
+ m_pPicker->AddActionSignalTarget( this );
+}
+
+
+//-----------------------------------------------------------------------------
+// Deletes the message
+//-----------------------------------------------------------------------------
+void CBaseAssetPickerFrame::CleanUpMessage()
+{
+ if ( m_pContextKeyValues )
+ {
+ m_pContextKeyValues->deleteThis();
+ m_pContextKeyValues = NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the initial selected asset
+//-----------------------------------------------------------------------------
+void CBaseAssetPickerFrame::SetInitialSelection( const char *pAssetName )
+{
+ m_pPicker->SetInitialSelection( pAssetName );
+}
+
+
+//-----------------------------------------------------------------------------
+// Set/get the filter
+//-----------------------------------------------------------------------------
+void CBaseAssetPickerFrame::SetFilter( const char *pFilter )
+{
+ m_pPicker->SetFilter( pFilter );
+}
+
+const char *CBaseAssetPickerFrame::GetFilter()
+{
+ return m_pPicker->GetFilter( );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Activate the dialog
+//-----------------------------------------------------------------------------
+void CBaseAssetPickerFrame::DoModal( KeyValues *pKeyValues )
+{
+ BaseClass::DoModal();
+ CleanUpMessage();
+ m_pContextKeyValues = pKeyValues;
+ m_pPicker->Activate();
+}
+
+
+//-----------------------------------------------------------------------------
+// Posts a message (passing the key values)
+//-----------------------------------------------------------------------------
+void CBaseAssetPickerFrame::PostMessageAndClose( KeyValues *pKeyValues )
+{
+ if ( m_pContextKeyValues )
+ {
+ pKeyValues->AddSubKey( m_pContextKeyValues );
+ m_pContextKeyValues = NULL;
+ }
+ CloseModal();
+ PostActionSignal( pKeyValues );
+}
+
+
+//-----------------------------------------------------------------------------
+// On command
+//-----------------------------------------------------------------------------
+void CBaseAssetPickerFrame::OnCommand( const char *pCommand )
+{
+ if ( !Q_stricmp( pCommand, "Open" ) )
+ {
+ KeyValues *pActionKeys = new KeyValues( "AssetSelected" );
+ if ( !m_pPicker->IsMultiselectEnabled() )
+ {
+ const char *pAssetName = m_pPicker->GetSelectedAsset( );
+ pActionKeys->SetString( "asset", pAssetName );
+ }
+ else
+ {
+ char pBuf[512];
+ KeyValues *pAssetKeys = pActionKeys->FindKey( "assets", true );
+ int nCount = m_pPicker->GetSelectedAssetCount();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ Q_snprintf( pBuf, sizeof(pBuf), "asset%d", i );
+ pAssetKeys->SetString( pBuf, m_pPicker->GetSelectedAsset( i ) );
+ }
+ }
+ PostMessageAndClose( pActionKeys );
+ return;
+ }
+
+ if ( !Q_stricmp( pCommand, "Cancel" ) )
+ {
+ CloseModal();
+ return;
+ }
+
+ BaseClass::OnCommand( pCommand );
+}
+
+
diff --git a/vgui2/matsys_controls/colorpickerpanel.cpp b/vgui2/matsys_controls/colorpickerpanel.cpp
new file mode 100644
index 0000000..848f78a
--- /dev/null
+++ b/vgui2/matsys_controls/colorpickerpanel.cpp
@@ -0,0 +1,1255 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//===========================================================================//
+
+#include "matsys_controls/colorpickerpanel.h"
+#include "matsys_controls/matsyscontrols.h"
+#include "matsys_controls/proceduraltexturepanel.h"
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/itexture.h"
+#include "pixelwriter.h"
+#include "VGuiMatSurface/IMatSystemSurface.h"
+#include "vgui_controls/Button.h"
+#include "vgui_controls/TextEntry.h"
+#include "vgui_controls/RadioButton.h"
+#include "vgui/IInput.h"
+#include "tier1/KeyValues.h"
+#include "bitmap/imageformat.h"
+
+using namespace vgui;
+
+
+//-----------------------------------------------------------------------------
+// Color picker
+//-----------------------------------------------------------------------------
+enum ColorType_t
+{
+ COLOR_TYPE_RGB = 0,
+ COLOR_TYPE_HSV,
+};
+
+enum ColorChannel_t
+{
+ CHANNEL_RED = 0,
+ CHANNEL_GREEN,
+ CHANNEL_BLUE,
+
+ CHANNEL_HUE = 0,
+ CHANNEL_SATURATION,
+ CHANNEL_VALUE,
+};
+
+
+//-----------------------------------------------------------------------------
+// Converts RGB to normalized
+//-----------------------------------------------------------------------------
+static void RGB888ToVector( RGB888_t inColor, Vector *pOutVector )
+{
+ pOutVector->Init( inColor.r / 255.0f, inColor.g / 255.0f, inColor.b / 255.0f );
+}
+
+static void VectorToRGB888( const Vector &inVector, RGB888_t &outColor )
+{
+ int r = (int)((inVector.x * 255.0f) + 0.5f);
+ int g = (int)((inVector.y * 255.0f) + 0.5f);
+ int b = (int)((inVector.z * 255.0f) + 0.5f);
+ outColor.r = clamp( r, 0, 255 );
+ outColor.g = clamp( g, 0, 255 );
+ outColor.b = clamp( b, 0, 255 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Convert RGB to HSV
+//-----------------------------------------------------------------------------
+static inline void RGBtoHSV( const RGB888_t &rgb, Vector &hsv )
+{
+ Vector vecRGB;
+ RGB888ToVector( rgb, &vecRGB );
+ RGBtoHSV( vecRGB, hsv );
+}
+
+
+//-----------------------------------------------------------------------------
+// Convert HSV to RGB
+//-----------------------------------------------------------------------------
+static inline void HSVtoRGB( const Vector &hsv, RGB888_t &rgb )
+{
+ Vector vecRGB;
+ HSVtoRGB( hsv, vecRGB );
+ VectorToRGB888( vecRGB, rgb );
+}
+
+
+//-----------------------------------------------------------------------------
+// This previews the 'xy' color
+//-----------------------------------------------------------------------------
+class CColorXYPreview : public CProceduralTexturePanel
+{
+ DECLARE_CLASS_SIMPLE( CColorXYPreview, CProceduralTexturePanel );
+
+public:
+ // constructor
+ CColorXYPreview( vgui::Panel *pParent, const char *pName );
+
+ virtual void RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect );
+ virtual void Paint( void );
+ virtual void OnMousePressed( vgui::MouseCode code );
+ virtual void OnMouseReleased( vgui::MouseCode code );
+ virtual void OnCursorMoved( int x, int y );
+
+ void SetMode( ColorType_t type, ColorChannel_t channel );
+ void SetColor( const RGB888_t &color, const Vector &hsvColor );
+
+private:
+ // Computes a color given a particular x,y value
+ void ComputeColorForPoint( int x, int y, RGB888_t &color );
+ void ComputeHSVColorForPoint( int x, int y, Vector &vscHSV );
+
+ // Updates the color based on the mouse position
+ void UpdateColorFromMouse( int x, int y );
+
+ static ColorChannel_t s_pHSVRemapX[3];
+ static ColorChannel_t s_pHSVRemapY[3];
+ static ColorChannel_t s_pRGBRemapX[3];
+ static ColorChannel_t s_pRGBRemapY[3];
+
+ ColorType_t m_Type;
+ ColorChannel_t m_Channel;
+ RGB888_t m_CurrentColor;
+ Vector m_CurrentHSVColor;
+ vgui::HCursor m_hPickerCursor;
+ bool m_bDraggingMouse;
+};
+
+
+ColorChannel_t CColorXYPreview::s_pHSVRemapX[3] =
+{
+ CHANNEL_SATURATION, CHANNEL_HUE, CHANNEL_HUE
+};
+
+ColorChannel_t CColorXYPreview::s_pHSVRemapY[3] =
+{
+ CHANNEL_VALUE, CHANNEL_VALUE, CHANNEL_SATURATION
+};
+
+ColorChannel_t CColorXYPreview::s_pRGBRemapX[3] =
+{
+ CHANNEL_BLUE, CHANNEL_BLUE, CHANNEL_RED
+};
+
+ColorChannel_t CColorXYPreview::s_pRGBRemapY[3] =
+{
+ CHANNEL_GREEN, CHANNEL_RED, CHANNEL_GREEN
+};
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CColorXYPreview::CColorXYPreview( vgui::Panel *pParent, const char *pName ) : BaseClass( pParent, pName )
+{
+ Init( 256, 256, false );
+ m_CurrentColor.r = m_CurrentColor.g = m_CurrentColor.b = 255;
+ SetMode( COLOR_TYPE_HSV, CHANNEL_HUE );
+ SetMouseInputEnabled( true );
+ m_hPickerCursor = surface()->CreateCursorFromFile( "resource/colorpicker.cur" );
+ SetCursor( m_hPickerCursor );
+ m_bDraggingMouse = false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the mode for the preview
+//-----------------------------------------------------------------------------
+void CColorXYPreview::SetMode( ColorType_t type, ColorChannel_t channel )
+{
+ if ( m_Type != type || m_Channel != channel )
+ {
+ m_Type = type;
+ m_Channel = channel;
+ DownloadTexture();
+ }
+}
+
+void CColorXYPreview::SetColor( const RGB888_t &color, const Vector &hsvColor )
+{
+ if ( color != m_CurrentColor || m_CurrentHSVColor != hsvColor )
+ {
+ m_CurrentColor = color;
+ m_CurrentHSVColor = hsvColor;
+ DownloadTexture();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes a color given a particular x,y value
+//-----------------------------------------------------------------------------
+void CColorXYPreview::ComputeColorForPoint( int x, int y, RGB888_t &color )
+{
+ color = m_CurrentColor;
+ ((unsigned char*)&color)[ s_pRGBRemapX[m_Channel] ] = x;
+ ((unsigned char*)&color)[ s_pRGBRemapY[m_Channel] ] = GetImageHeight() - y - 1;
+}
+
+void CColorXYPreview::ComputeHSVColorForPoint( int x, int y, Vector &vscHSV )
+{
+ vscHSV = m_CurrentHSVColor;
+ vscHSV[ s_pHSVRemapX[m_Channel] ] = (float)x / 255.0f;
+ vscHSV[ s_pHSVRemapY[m_Channel] ] = (float)(GetImageHeight() - y - 1) / 255.0f;
+ if ( vscHSV.y == 0.0f )
+ {
+ vscHSV.x = -1.0f;
+ }
+
+ if ( m_Channel != CHANNEL_HUE )
+ {
+ if ( vscHSV.x != -1.0f )
+ {
+ vscHSV.x *= 360.0f;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Fills the texture w/ the image buffer
+//-----------------------------------------------------------------------------
+void CColorXYPreview::RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect )
+{
+ Assert( pVTFTexture->FrameCount() == 1 );
+ Assert( pVTFTexture->FaceCount() == 1 );
+ Assert( !pTexture->IsMipmapped() );
+
+ int nWidth, nHeight, nDepth;
+ pVTFTexture->ComputeMipLevelDimensions( 0, &nWidth, &nHeight, &nDepth );
+ Assert( nDepth == 1 );
+ Assert( nWidth == m_nWidth && nHeight == m_nHeight );
+
+ CPixelWriter pixelWriter;
+ pixelWriter.SetPixelMemory( pVTFTexture->Format(),
+ pVTFTexture->ImageData( 0, 0, 0 ), pVTFTexture->RowSizeInBytes( 0 ) );
+
+ for ( int y = 0; y < nHeight; ++y )
+ {
+ pixelWriter.Seek( 0, y );
+
+ for ( int x = 0; x < nWidth; ++x )
+ {
+ RGB888_t color;
+ if ( m_Type != COLOR_TYPE_RGB )
+ {
+ Vector vecHSV;
+ ComputeHSVColorForPoint( x, y, vecHSV );
+ HSVtoRGB( vecHSV, color );
+ }
+ else
+ {
+ ComputeColorForPoint( x, y, color );
+ }
+
+ pixelWriter.WritePixel( color.r, color.g, color.b, 255 );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Paints a circle over the currently selected color
+//-----------------------------------------------------------------------------
+void CColorXYPreview::Paint( void )
+{
+ BaseClass::Paint();
+
+ int x, y;
+ if ( m_Type != COLOR_TYPE_RGB )
+ {
+ Vector vecHSVNormalized = m_CurrentHSVColor;
+ if ( vecHSVNormalized.x != -1.0f )
+ {
+ vecHSVNormalized.x *= 255.0f / 360.0f;
+ }
+ vecHSVNormalized.y *= 255.0f;
+ vecHSVNormalized.z *= 255.0f;
+
+ x = (int)( vecHSVNormalized[ s_pHSVRemapX[m_Channel] ] + 0.5f);
+ y = GetImageHeight() - 1 - (int)( vecHSVNormalized[ s_pHSVRemapY[m_Channel] ] + 0.5f );
+ }
+ else
+ {
+ x = ((unsigned char*)&m_CurrentColor)[ s_pRGBRemapX[m_Channel] ];
+ y = GetImageHeight() - 1 - ((unsigned char*)&m_CurrentColor)[ s_pRGBRemapY[m_Channel] ];
+ }
+
+ // Renormalize x, y to actual size
+ int w, h;
+ GetSize( w, h );
+ x = (int)( w * (float)x / 255.0f + 0.5f );
+ y = (int)( h * (float)y / 255.0f + 0.5f );
+ vgui::surface()->DrawSetColor( 255, 255, 255, 255 );
+ vgui::surface()->DrawOutlinedCircle( x, y, 5, 8 );
+ vgui::surface()->DrawSetColor( 0, 0, 0, 255 );
+ vgui::surface()->DrawOutlinedCircle( x, y, 6, 8 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Updates the color based on the mouse position
+//-----------------------------------------------------------------------------
+void CColorXYPreview::UpdateColorFromMouse( int x, int y )
+{
+ int w, h;
+ GetSize( w, h );
+
+ float flNormalizedX = (float)x / (w-1);
+ float flNormalizedY = (float)y / (h-1);
+ flNormalizedX = clamp( flNormalizedX, 0.0f, 1.0f );
+ flNormalizedY = clamp( flNormalizedY, 0.0f, 1.0f );
+
+ int tx = (int)( (GetImageWidth()-1) * flNormalizedX + 0.5f );
+ int ty = (int)( (GetImageHeight()-1) * flNormalizedY + 0.5f );
+ if ( m_Type != COLOR_TYPE_RGB )
+ {
+ Vector vecHSV;
+ ComputeHSVColorForPoint( tx, ty, vecHSV );
+
+ KeyValues *pKeyValues = new KeyValues( "HSVSelected" );
+ pKeyValues->SetFloat( "hue", vecHSV.x );
+ pKeyValues->SetFloat( "saturation", vecHSV.y );
+ pKeyValues->SetFloat( "value", vecHSV.z );
+ PostActionSignal( pKeyValues );
+
+ // This prevents a 1-frame lag in the current color position
+ RGB888_t color;
+ HSVtoRGB( vecHSV, color );
+ SetColor( color, vecHSV );
+ }
+ else
+ {
+ RGB888_t color;
+ ComputeColorForPoint( tx, ty, color );
+
+ Color c( color.r, color.g, color.b, 255 );
+ KeyValues *pKeyValues = new KeyValues( "ColorSelected" );
+ pKeyValues->SetColor( "color", c );
+ PostActionSignal( pKeyValues );
+
+ // This prevents a 1-frame lag in the current color position
+ Vector vecHSV;
+ RGBtoHSV( color, vecHSV );
+ SetColor( color, vecHSV );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Handle input
+//-----------------------------------------------------------------------------
+void CColorXYPreview::OnMousePressed( vgui::MouseCode code )
+{
+ BaseClass::OnMousePressed( code );
+
+ if ( code == MOUSE_LEFT )
+ {
+ if ( !m_bDraggingMouse )
+ {
+ m_bDraggingMouse = true;
+ input()->SetMouseCapture(GetVPanel());
+
+ int x, y;
+ input()->GetCursorPos( x, y );
+ ScreenToLocal( x, y );
+
+ UpdateColorFromMouse( x, y );
+ }
+ }
+}
+
+void CColorXYPreview::OnMouseReleased( vgui::MouseCode code )
+{
+ BaseClass::OnMouseReleased( code );
+
+ if ( code == MOUSE_LEFT )
+ {
+ if ( m_bDraggingMouse )
+ {
+ m_bDraggingMouse = false;
+ input()->SetMouseCapture( (VPANEL)0 );
+ }
+ }
+}
+
+void CColorXYPreview::OnCursorMoved( int x, int y )
+{
+ BaseClass::OnCursorMoved( x, y );
+
+ if ( m_bDraggingMouse )
+ {
+ UpdateColorFromMouse( x, y );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// This previews the 'z' color
+//-----------------------------------------------------------------------------
+class CColorZPreview : public CProceduralTexturePanel
+{
+ DECLARE_CLASS_SIMPLE( CColorZPreview, CProceduralTexturePanel );
+
+public:
+ // constructor
+ CColorZPreview( vgui::Panel *pParent, const char *pName );
+
+ virtual void RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect );
+ virtual void PerformLayout();
+ virtual void Paint( void );
+ virtual void OnCursorMoved( int x,int y );
+ virtual void OnMousePressed( vgui::MouseCode code );
+ virtual void OnMouseReleased( vgui::MouseCode code );
+
+ void SetMode( ColorType_t type, ColorChannel_t channel );
+ void SetColor( const RGB888_t &color, const Vector &hsvColor );
+
+ // Computes a color given a particular x,y value
+ void ComputeColorForPoint( int y, RGB888_t &color );
+ void ComputeHSVColorForPoint( int y, bool bProceduralTexture, Vector &vecHSV );
+
+private:
+ // Updates the color based on the mouse position
+ void UpdateColorFromMouse( int x, int y );
+
+ ColorType_t m_Type;
+ ColorChannel_t m_Channel;
+ RGB888_t m_CurrentColor;
+ Vector m_CurrentHSVColor;
+ bool m_bDraggingMouse;
+};
+
+#define MARKER_WIDTH 6
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CColorZPreview::CColorZPreview( vgui::Panel *pParent, const char *pName ) : BaseClass( pParent, pName )
+{
+ Init( 8, 256, false );
+ m_CurrentColor.r = m_CurrentColor.g = m_CurrentColor.b = 255;
+
+ Vector vecRGB;
+ RGB888ToVector( m_CurrentColor, &vecRGB );
+ RGBtoHSV( vecRGB, m_CurrentHSVColor );
+ m_bDraggingMouse = false;
+
+ SetMouseInputEnabled( true );
+ SetMode( COLOR_TYPE_HSV, CHANNEL_HUE );
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the mode for the preview
+//-----------------------------------------------------------------------------
+void CColorZPreview::SetMode( ColorType_t type, ColorChannel_t channel )
+{
+ if ( m_Type != type || m_Channel != channel )
+ {
+ m_Type = type;
+ m_Channel = channel;
+ DownloadTexture();
+ }
+}
+
+void CColorZPreview::SetColor( const RGB888_t &color, const Vector &hsvColor )
+{
+ if ( color != m_CurrentColor || m_CurrentHSVColor != hsvColor )
+ {
+ m_CurrentColor = color;
+ m_CurrentHSVColor = hsvColor;
+ DownloadTexture();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Lays out the panel
+//-----------------------------------------------------------------------------
+void CColorZPreview::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ int w, h;
+ GetSize( w, h );
+ Rect_t r;
+ r.x = MARKER_WIDTH;
+ r.y = MARKER_WIDTH;
+ r.width = w - (MARKER_WIDTH*2);
+ r.height = h - (MARKER_WIDTH*2);
+
+ SetPaintRect( &r );
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes a color given a particular x,y value
+//-----------------------------------------------------------------------------
+void CColorZPreview::ComputeColorForPoint( int y, RGB888_t &color )
+{
+ color = m_CurrentColor;
+ ((unsigned char*)&color)[ m_Channel ] = GetImageHeight() - y - 1;
+}
+
+void CColorZPreview::ComputeHSVColorForPoint( int y, bool bProceduralTexture, Vector &vecHSV )
+{
+ vecHSV = m_CurrentHSVColor;
+ vecHSV[ m_Channel ] = (float)(GetImageHeight() - y - 1) / 255.0f;
+ if ( m_Channel == CHANNEL_HUE )
+ {
+ if ( vecHSV.x != -1.0f )
+ {
+ vecHSV.x *= 360.0f;
+ }
+
+ if ( bProceduralTexture )
+ {
+ vecHSV.y = 1.0f;
+ vecHSV.z = 1.0f;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Fills the texture w/ the image buffer
+//-----------------------------------------------------------------------------
+void CColorZPreview::RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect )
+{
+ Assert( pVTFTexture->FrameCount() == 1 );
+ Assert( pVTFTexture->FaceCount() == 1 );
+ Assert( !pTexture->IsMipmapped() );
+
+ int nWidth, nHeight, nDepth;
+ pVTFTexture->ComputeMipLevelDimensions( 0, &nWidth, &nHeight, &nDepth );
+ Assert( nDepth == 1 );
+ Assert( nWidth == m_nWidth && nHeight == m_nHeight );
+
+ CPixelWriter pixelWriter;
+ pixelWriter.SetPixelMemory( pVTFTexture->Format(),
+ pVTFTexture->ImageData( 0, 0, 0 ), pVTFTexture->RowSizeInBytes( 0 ) );
+
+ for ( int y = 0; y < nHeight; ++y )
+ {
+ pixelWriter.Seek( 0, y );
+
+ RGB888_t color;
+ if ( m_Type != COLOR_TYPE_RGB )
+ {
+ Vector vecHSV;
+ ComputeHSVColorForPoint( y, true, vecHSV );
+ HSVtoRGB( vecHSV, color );
+ }
+ else
+ {
+ ComputeColorForPoint( y, color );
+ }
+
+ for ( int x = 0; x < nWidth; ++x )
+ {
+ pixelWriter.WritePixel( color.r, color.g, color.b, 255 );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Updates the color based on the mouse position
+//-----------------------------------------------------------------------------
+void CColorZPreview::UpdateColorFromMouse( int x, int y )
+{
+ int w, h;
+ GetSize( w, h );
+ h -= 2 * MARKER_WIDTH;
+
+ float flNormalizedY = (float)( y - MARKER_WIDTH ) / (h-1);
+ flNormalizedY = clamp( flNormalizedY, 0.0f, 1.0f );
+
+ int ty = (int)( (GetImageHeight() - 1) * flNormalizedY + 0.5f );
+ if ( m_Type != COLOR_TYPE_RGB )
+ {
+ Vector vecHSV;
+ ComputeHSVColorForPoint( ty, false, vecHSV );
+
+ KeyValues *pKeyValues = new KeyValues( "HSVSelected" );
+ pKeyValues->SetFloat( "hue", vecHSV.x );
+ pKeyValues->SetFloat( "saturation", vecHSV.y );
+ pKeyValues->SetFloat( "value", vecHSV.z );
+ PostActionSignal( pKeyValues );
+
+ // This prevents a 1-frame lag in the current color position
+ RGB888_t color;
+ HSVtoRGB( vecHSV, color );
+ SetColor( color, vecHSV );
+ }
+ else
+ {
+ RGB888_t color;
+ ComputeColorForPoint( ty, color );
+
+ Color c( color.r, color.g, color.b, 255 );
+ KeyValues *pKeyValues = new KeyValues( "ColorSelected" );
+ pKeyValues->SetColor( "color", c );
+ PostActionSignal( pKeyValues );
+
+ // This prevents a 1-frame lag in the current color position
+ Vector vecHSV;
+ RGBtoHSV( color, vecHSV );
+ SetColor( color, vecHSV );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Handle input
+//-----------------------------------------------------------------------------
+void CColorZPreview::OnMousePressed( vgui::MouseCode code )
+{
+ BaseClass::OnMousePressed( code );
+
+ if ( code == MOUSE_LEFT )
+ {
+ if ( !m_bDraggingMouse )
+ {
+ m_bDraggingMouse = true;
+ input()->SetMouseCapture(GetVPanel());
+
+ int x, y;
+ input()->GetCursorPos( x, y );
+ ScreenToLocal( x, y );
+
+ UpdateColorFromMouse( x, y );
+ }
+ }
+}
+
+void CColorZPreview::OnMouseReleased( vgui::MouseCode code )
+{
+ BaseClass::OnMouseReleased( code );
+
+ if ( code == MOUSE_LEFT )
+ {
+ if ( m_bDraggingMouse )
+ {
+ m_bDraggingMouse = false;
+ input()->SetMouseCapture( (VPANEL)0 );
+ }
+ }
+}
+
+void CColorZPreview::OnCursorMoved( int x, int y )
+{
+ BaseClass::OnCursorMoved( x, y );
+
+ if ( m_bDraggingMouse )
+ {
+ UpdateColorFromMouse( x, y );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Paints the panel (the two arrows, specifically)
+//-----------------------------------------------------------------------------
+void CColorZPreview::Paint( void )
+{
+ BaseClass::Paint();
+
+ int y;
+ if ( m_Type != COLOR_TYPE_RGB )
+ {
+ Vector vecHSVNormalized = m_CurrentHSVColor;
+ if ( vecHSVNormalized.x != -1.0f )
+ {
+ vecHSVNormalized.x *= 255.0f / 360.0f;
+ }
+ vecHSVNormalized.y *= 255.0f;
+ vecHSVNormalized.z *= 255.0f;
+
+ y = GetImageHeight() - 1 - (int)( vecHSVNormalized[ m_Channel ] + 0.5f );
+ }
+ else
+ {
+ y = GetImageHeight() - 1 - ((unsigned char*)&m_CurrentColor)[ m_Channel ];
+ }
+
+ // Renormalize y to actual size
+ int w, h;
+ GetSize( w, h );
+ h -= 2 * MARKER_WIDTH;
+ y = (int)( h * (float)y / 255.0f + 0.5f );
+
+ vgui::surface()->DrawSetColor( 255, 255, 255, 255 );
+
+ int px[3] = { 0, 0, MARKER_WIDTH };
+ int py[3] = { MARKER_WIDTH + y - MARKER_WIDTH, MARKER_WIDTH + y + MARKER_WIDTH, MARKER_WIDTH + y };
+ vgui::surface()->DrawPolyLine( px, py, 3 );
+
+ px[0] = px[1] = w-1;
+ px[2] = w - 1 - MARKER_WIDTH;
+ vgui::surface()->DrawPolyLine( px, py, 3 );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Color picker panel
+//
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// constructor
+//-----------------------------------------------------------------------------
+CColorPickerPanel::CColorPickerPanel( vgui::Panel *pParent, const char *pName ) :
+ BaseClass( pParent, pName )
+{
+ m_pColorXYPreview = new CColorXYPreview( this, "ColorXYPreview" );
+ m_pColorZPreview = new CColorZPreview( this, "ColorZPreview" );
+ m_pColorXYPreview->AddActionSignalTarget( this );
+
+ m_pHueRadio = new RadioButton( this, "HueRadio", "H" );
+ m_pSaturationRadio = new RadioButton( this, "SaturationRadio", "S" );
+ m_pValueRadio = new RadioButton( this, "ValueRadio", "V" );
+ m_pRedRadio = new RadioButton( this, "RedRadio", "R" );
+ m_pGreenRadio = new RadioButton( this, "GreenRadio", "G" );
+ m_pBlueRadio = new RadioButton( this, "BlueRadio", "B" );
+
+ m_pHueText = new TextEntry( this, "HueText" );
+ m_pSaturationText = new TextEntry( this, "SaturationText");
+ m_pValueText = new TextEntry( this, "ValueText" );
+ m_pRedText = new TextEntry( this, "RedText" );
+ m_pGreenText = new TextEntry( this, "GreenText" );
+ m_pBlueText = new TextEntry( this, "BlueText" );
+ m_pAlphaText= new TextEntry( this, "AlphaText" );
+
+ m_pInitialColor = new Panel( this, "InitialColor" );
+ m_pCurrentColor = new Panel( this, "CurrentColor" );
+ m_pInitialColor->SetVisible( true );
+ m_pCurrentColor->SetVisible( true );
+ m_pInitialColor->SetPaintBackgroundEnabled( true );
+ m_pCurrentColor->SetPaintBackgroundEnabled( true );
+
+ m_pInitialColor->SetMouseInputEnabled( false );
+ SetMouseInputEnabled( true );
+
+ Color c( 255, 255, 255, 255 );
+ SetInitialColor( c );
+
+ LoadControlSettings( "resource/colorpicker.res" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the initial color
+//-----------------------------------------------------------------------------
+void CColorPickerPanel::SetInitialColor( Color initialColor )
+{
+ m_InitialColor.r = initialColor.r();
+ m_InitialColor.g = initialColor.g();
+ m_InitialColor.b = initialColor.b();
+
+ m_CurrentAlpha = initialColor.a();
+ m_InitialAlpha = m_CurrentAlpha;
+ m_CurrentColor = m_InitialColor;
+
+ RGBtoHSV( m_CurrentColor, m_CurrentHSVColor );
+ if ( m_CurrentHSVColor.x == -1 )
+ {
+ m_CurrentHSVColor.x = 0;
+ }
+ OnColorChanged();
+}
+
+
+//-----------------------------------------------------------------------------
+// Handle input
+//-----------------------------------------------------------------------------
+void CColorPickerPanel::OnMousePressed( vgui::MouseCode code )
+{
+ BaseClass::OnMousePressed( code );
+
+ if ( code == MOUSE_LEFT )
+ {
+ // Clicking inside the initial color window
+ // resets the current color to the initial color
+ int x, y;
+ input()->GetCursorPos( x, y );
+ ScreenToLocal( x, y );
+
+ int cx, cy, cw, ch;
+ m_pInitialColor->GetBounds( cx, cy, cw, ch );
+ if ( ( cx <= x ) && ( cx+cw > x ) && ( cy <= y ) && ( cy+ch > y ) )
+ {
+ m_CurrentColor = m_InitialColor;
+ m_CurrentAlpha = m_InitialAlpha;
+ RGBtoHSV( m_CurrentColor, m_CurrentHSVColor );
+ if ( m_CurrentHSVColor.x == -1 )
+ {
+ m_CurrentHSVColor.x = 0;
+ }
+ OnColorChanged();
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets the current/initial color
+//-----------------------------------------------------------------------------
+void CColorPickerPanel::GetCurrentColor( Color *pColor )
+{
+ pColor->SetColor( m_CurrentColor.r, m_CurrentColor.g, m_CurrentColor.b, m_CurrentAlpha );
+}
+
+void CColorPickerPanel::GetInitialColor( Color *pColor )
+{
+ pColor->SetColor( m_InitialColor.r, m_InitialColor.g, m_InitialColor.b, m_InitialAlpha );
+}
+
+
+//-----------------------------------------------------------------------------
+// Updates the preview colors
+//-----------------------------------------------------------------------------
+void CColorPickerPanel::UpdatePreviewColors()
+{
+ Color c;
+ c.SetColor( m_InitialColor.r, m_InitialColor.g, m_InitialColor.b, 255 );
+ m_pInitialColor->SetBgColor( c );
+ c.SetColor( m_CurrentColor.r, m_CurrentColor.g, m_CurrentColor.b, 255 );
+ m_pCurrentColor->SetBgColor( c );
+}
+
+
+//-----------------------------------------------------------------------------
+// Used to make sure we win over the scheme settings
+//-----------------------------------------------------------------------------
+void CColorPickerPanel::ApplySchemeSettings(IScheme *pScheme)
+{
+ // Need to override the scheme settings for this button
+ BaseClass::ApplySchemeSettings( pScheme );
+ UpdatePreviewColors();
+}
+
+
+//-----------------------------------------------------------------------------
+// Callbacks from the color preview dialogs
+//-----------------------------------------------------------------------------
+void CColorPickerPanel::OnHSVSelected( KeyValues *data )
+{
+ m_CurrentHSVColor.x = data->GetFloat( "hue" );
+ m_CurrentHSVColor.y = data->GetFloat( "saturation" );
+ m_CurrentHSVColor.z = data->GetFloat( "value" );
+ HSVtoRGB( m_CurrentHSVColor, m_CurrentColor );
+ OnColorChanged();
+}
+
+void CColorPickerPanel::OnColorSelected( KeyValues *data )
+{
+ Color c = data->GetColor( "color" );
+ m_CurrentColor.r = c.r();
+ m_CurrentColor.g = c.g();
+ m_CurrentColor.b = c.b();
+ RGBtoHSV( m_CurrentColor, m_CurrentHSVColor );
+ OnColorChanged();
+}
+
+
+//-----------------------------------------------------------------------------
+// Radio buttons
+//-----------------------------------------------------------------------------
+void CColorPickerPanel::OnRadioButtonChecked( KeyValues *pKeyValues )
+{
+ // NOTE: The radio button command strings are defined in the colorpicker.res file
+ // in game/platform/resource.
+ vgui::Panel *pPanel = (vgui::Panel *)pKeyValues->GetPtr( "panel" );
+ if ( pPanel == m_pRedRadio )
+ {
+ m_pColorXYPreview->SetMode( COLOR_TYPE_RGB, CHANNEL_RED );
+ m_pColorZPreview->SetMode( COLOR_TYPE_RGB, CHANNEL_RED );
+ }
+ else if ( pPanel == m_pGreenRadio )
+ {
+ m_pColorXYPreview->SetMode( COLOR_TYPE_RGB, CHANNEL_GREEN );
+ m_pColorZPreview->SetMode( COLOR_TYPE_RGB, CHANNEL_GREEN );
+ }
+ else if ( pPanel == m_pBlueRadio )
+ {
+ m_pColorXYPreview->SetMode( COLOR_TYPE_RGB, CHANNEL_BLUE );
+ m_pColorZPreview->SetMode( COLOR_TYPE_RGB, CHANNEL_BLUE );
+ }
+ else if ( pPanel == m_pHueRadio )
+ {
+ m_pColorXYPreview->SetMode( COLOR_TYPE_HSV, CHANNEL_HUE );
+ m_pColorZPreview->SetMode( COLOR_TYPE_HSV, CHANNEL_HUE );
+ }
+ else if ( pPanel == m_pSaturationRadio )
+ {
+ m_pColorXYPreview->SetMode( COLOR_TYPE_HSV, CHANNEL_SATURATION );
+ m_pColorZPreview->SetMode( COLOR_TYPE_HSV, CHANNEL_SATURATION );
+ }
+ else if ( pPanel == m_pValueRadio )
+ {
+ m_pColorXYPreview->SetMode( COLOR_TYPE_HSV, CHANNEL_VALUE );
+ m_pColorZPreview->SetMode( COLOR_TYPE_HSV, CHANNEL_VALUE );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Called when the color changes
+//-----------------------------------------------------------------------------
+void CColorPickerPanel::OnColorChanged( vgui::TextEntry *pChanged )
+{
+ char temp[256];
+
+ if ( pChanged != m_pRedText )
+ {
+ Q_snprintf( temp, sizeof(temp), "%d", m_CurrentColor.r );
+ m_pRedText->SetText( temp );
+ }
+ if ( pChanged != m_pGreenText )
+ {
+ Q_snprintf( temp, sizeof(temp), "%d", m_CurrentColor.g );
+ m_pGreenText->SetText( temp );
+ }
+ if ( pChanged != m_pBlueText )
+ {
+ Q_snprintf( temp, sizeof(temp), "%d", m_CurrentColor.b );
+ m_pBlueText->SetText( temp );
+ }
+ if ( pChanged != m_pAlphaText )
+ {
+ Q_snprintf( temp, sizeof( temp ), "%d", m_CurrentAlpha );
+ m_pAlphaText->SetText( temp );
+ }
+
+ if ( pChanged != m_pHueText )
+ {
+ Q_snprintf( temp, sizeof(temp), "%d", (int)(m_CurrentHSVColor.x + 0.5f) );
+ m_pHueText->SetText( temp );
+ }
+ if ( pChanged != m_pSaturationText )
+ {
+ Q_snprintf( temp, sizeof(temp), "%d", (int)(m_CurrentHSVColor.y * 100 + 0.5f) );
+ m_pSaturationText->SetText( temp );
+ }
+ if ( pChanged != m_pValueText )
+ {
+ Q_snprintf( temp, sizeof(temp), "%d", (int)(m_CurrentHSVColor.z * 100 + 0.5f) );
+ m_pValueText->SetText( temp );
+ }
+
+ m_pColorXYPreview->SetColor( m_CurrentColor, m_CurrentHSVColor );
+ m_pColorZPreview->SetColor( m_CurrentColor, m_CurrentHSVColor );
+ UpdatePreviewColors();
+ PostActionSignal( new KeyValues( "command", "command", "preview" ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Called when the color text entry panels change
+//-----------------------------------------------------------------------------
+void CColorPickerPanel::OnTextChanged( KeyValues *data )
+{
+ Panel *pPanel = (Panel *)data->GetPtr( "panel", NULL );
+
+ float flHue = m_CurrentHSVColor.x;
+
+ char buf[256];
+ if ( pPanel == m_pRedText )
+ {
+ m_pRedText->GetText( buf, sizeof(buf) );
+ int val = atoi( buf );
+ m_CurrentColor.r = clamp( val, 0, 255 );
+ RGBtoHSV( m_CurrentColor, m_CurrentHSVColor );
+ }
+ else if ( pPanel == m_pGreenText )
+ {
+ m_pGreenText->GetText( buf, sizeof(buf) );
+ int val = atoi( buf );
+ m_CurrentColor.g = clamp( val, 0, 255 );
+ RGBtoHSV( m_CurrentColor, m_CurrentHSVColor );
+ }
+ else if ( pPanel == m_pBlueText )
+ {
+ m_pBlueText->GetText( buf, sizeof(buf) );
+ int val = atoi( buf );
+ m_CurrentColor.b = clamp( val, 0, 255 );
+ RGBtoHSV( m_CurrentColor, m_CurrentHSVColor );
+ }
+ else if ( pPanel == m_pAlphaText )
+ {
+ m_pAlphaText->GetText( buf, sizeof( buf ) );
+ int val = atoi( buf );
+ m_CurrentAlpha = clamp( val, 0, 255 );
+ }
+ else if ( pPanel == m_pHueText )
+ {
+ m_pHueText->GetText( buf, sizeof(buf) );
+ int val = atoi( buf );
+ m_CurrentHSVColor.x = clamp( val, 0, 360 );
+ HSVtoRGB( m_CurrentHSVColor, m_CurrentColor );
+ }
+ else if ( pPanel == m_pSaturationText )
+ {
+ m_pSaturationText->GetText( buf, sizeof(buf) );
+ int val = atoi( buf );
+ val = clamp( val, 0, 100 );
+ m_CurrentHSVColor.y = (float)val / 100.0f;
+ HSVtoRGB( m_CurrentHSVColor, m_CurrentColor );
+ }
+ else if ( pPanel == m_pValueText )
+ {
+ m_pValueText->GetText( buf, sizeof(buf) );
+ int val = atoi( buf );
+ val = clamp( val, 0, 100 );
+ m_CurrentHSVColor.z = (float)val / 100.0f;
+ HSVtoRGB( m_CurrentHSVColor, m_CurrentColor );
+ }
+
+ // Preserve hue
+ if ( m_CurrentHSVColor.x == -1 )
+ {
+ m_CurrentHSVColor.x = flHue;
+ }
+ OnColorChanged( static_cast<vgui::TextEntry*>(pPanel) );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Purpose: Modal picker frame
+//
+//-----------------------------------------------------------------------------
+CColorPickerFrame::CColorPickerFrame( vgui::Panel *pParent, const char *pTitle ) :
+ BaseClass( pParent, "ColorPickerFrame" )
+{
+ m_pContextKeys = NULL;
+ SetDeleteSelfOnClose( true );
+ m_pPicker = new CColorPickerPanel( this, "ColorPicker" );
+ m_pPicker->AddActionSignalTarget( this );
+ m_pOpenButton = new Button( this, "OkButton", "Ok", this, "Ok" );
+ m_pCancelButton = new Button( this, "CancelButton", "#FileOpenDialog_Cancel", this, "Cancel" );
+ SetBlockDragChaining( true );
+
+ LoadControlSettings( "resource/colorpickerframe.res" );
+
+ int w, h;
+ GetSize( w, h );
+ SetMinimumSize( w, h );
+
+ SetTitle( pTitle, false );
+}
+
+CColorPickerFrame::~CColorPickerFrame()
+{
+ CleanUpMessage();
+}
+
+
+//-----------------------------------------------------------------------------
+// Deletes the message
+//-----------------------------------------------------------------------------
+void CColorPickerFrame::CleanUpMessage()
+{
+ if ( m_pContextKeys )
+ {
+ m_pContextKeys->deleteThis();
+ m_pContextKeys = NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Activate the dialog
+//-----------------------------------------------------------------------------
+void CColorPickerFrame::DoModal( Color initialColor, KeyValues *pContextKeys )
+{
+ CleanUpMessage();
+ m_pPicker->SetInitialColor( initialColor );
+ m_pContextKeys = pContextKeys;
+
+ BaseClass::DoModal();
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets the initial color
+//-----------------------------------------------------------------------------
+void CColorPickerFrame::GetInitialColor( Color *pColor )
+{
+ m_pPicker->GetInitialColor( pColor );
+}
+
+
+//-----------------------------------------------------------------------------
+// On command
+//-----------------------------------------------------------------------------
+void CColorPickerFrame::OnCommand( const char *pCommand )
+{
+ if ( !Q_stricmp( pCommand, "Ok" ) )
+ {
+ Color c;
+ m_pPicker->GetCurrentColor( &c );
+
+ KeyValues *pActionKeys = new KeyValues( "ColorPickerPicked" );
+ pActionKeys->SetColor( "color", c );
+ if ( m_pContextKeys )
+ {
+ pActionKeys->AddSubKey( m_pContextKeys );
+ m_pContextKeys = NULL;
+ }
+ CloseModal();
+ PostActionSignal( pActionKeys );
+ return;
+ }
+
+ if ( !Q_stricmp( pCommand, "Cancel" ) )
+ {
+ vgui::input()->ReleaseAppModalSurface();
+ KeyValues *pActionKeys = new KeyValues( "ColorPickerCancel" );
+ if ( m_pContextKeys )
+ {
+ pActionKeys->AddSubKey( m_pContextKeys );
+ m_pContextKeys = NULL;
+ }
+ CloseModal();
+ PostActionSignal( pActionKeys );
+ return;
+ }
+
+ if ( !Q_stricmp( pCommand, "Preview" ) )
+ {
+ Color c;
+ m_pPicker->GetCurrentColor( &c );
+
+ KeyValues *pActionKeys = new KeyValues( "ColorPickerPreview" );
+ pActionKeys->SetColor( "color", c );
+ if ( m_pContextKeys )
+ {
+ pActionKeys->AddSubKey( m_pContextKeys->MakeCopy() );
+ }
+ PostActionSignal( pActionKeys );
+ return;
+ }
+
+ BaseClass::OnCommand( pCommand );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Purpose: A button which brings up the color picker
+//
+//-----------------------------------------------------------------------------
+CColorPickerButton::CColorPickerButton( vgui::Panel *pParent, const char *pName, vgui::Panel *pActionSignalTarget ) :
+ BaseClass( pParent, pName, "" )
+{
+ m_CurrentColor.SetColor( 255, 255, 255, 255 );
+ if ( pActionSignalTarget )
+ {
+ AddActionSignalTarget( pActionSignalTarget );
+ }
+}
+
+CColorPickerButton::~CColorPickerButton()
+{
+}
+
+void CColorPickerButton::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+ UpdateButtonColor();
+}
+
+
+//-----------------------------------------------------------------------------
+// Called when the picker gets a new color
+//-----------------------------------------------------------------------------
+void CColorPickerButton::OnPicked( KeyValues *data )
+{
+ SetColor( data->GetColor( "color" ) );
+
+ // Fire normal action signal messages
+ KeyValues *pMessage = new KeyValues( "ColorPickerPicked" );
+ pMessage->SetColor( "color", m_CurrentColor );
+ PostActionSignal( pMessage );
+ PlayButtonReleasedSound();
+ SetSelected( false );
+}
+
+
+//-----------------------------------------------------------------------------
+// Called when a color is previewed
+//-----------------------------------------------------------------------------
+void CColorPickerButton::OnPreview( KeyValues *data )
+{
+ KeyValues *pMessage = new KeyValues( "ColorPickerPreview" );
+ pMessage->SetColor( "color", data->GetColor( "color" ) );
+ PostActionSignal( pMessage );
+}
+
+
+//-----------------------------------------------------------------------------
+// Called when cancel is hit in the picker
+//-----------------------------------------------------------------------------
+void CColorPickerButton::OnCancelled( )
+{
+ SetSelected( false );
+
+ KeyValues *pMessage = new KeyValues( "ColorPickerCancel" );
+ pMessage->SetColor( "startingColor", m_CurrentColor );
+ PostActionSignal( pMessage );
+}
+
+
+//-----------------------------------------------------------------------------
+// Perform the click
+//-----------------------------------------------------------------------------
+void CColorPickerButton::DoClick()
+{
+ SetSelected( true );
+
+ CColorPickerFrame *pColorPickerDialog = new CColorPickerFrame( this, "Select Color" );
+ pColorPickerDialog->AddActionSignalTarget( this );
+ pColorPickerDialog->DoModal( m_CurrentColor );
+}
+
+
+//-----------------------------------------------------------------------------
+// Set current color
+//-----------------------------------------------------------------------------
+void CColorPickerButton::SetColor( const Color& clr )
+{
+ m_CurrentColor = clr;
+ UpdateButtonColor();
+}
+
+void CColorPickerButton::SetColor( int r, int g, int b, int a )
+{
+ m_CurrentColor.SetColor( r, g, b, a );
+ UpdateButtonColor();
+}
+
+
+//-----------------------------------------------------------------------------
+// Update button color
+//-----------------------------------------------------------------------------
+void CColorPickerButton::UpdateButtonColor()
+{
+ SetDefaultColor( m_CurrentColor, m_CurrentColor );
+ SetArmedColor( m_CurrentColor, m_CurrentColor );
+ SetDepressedColor( m_CurrentColor, m_CurrentColor );
+}
diff --git a/vgui2/matsys_controls/curveeditorpanel.cpp b/vgui2/matsys_controls/curveeditorpanel.cpp
new file mode 100644
index 0000000..186444d
--- /dev/null
+++ b/vgui2/matsys_controls/curveeditorpanel.cpp
@@ -0,0 +1,200 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//===========================================================================//
+
+#include "matsys_controls/curveeditorpanel.h"
+#include "matsys_controls/matsyscontrols.h"
+#include "tier1/KeyValues.h"
+#include "vgui/ISurface.h"
+#include "vgui/IInput.h"
+#include "vgui/MouseCode.h"
+
+using namespace vgui;
+
+
+
+//-----------------------------------------------------------------------------
+// constructor, destructor
+//-----------------------------------------------------------------------------
+CCurveEditorPanel::CCurveEditorPanel( vgui::Panel *pParent, const char *pName ) : BaseClass( pParent, pName )
+{
+ m_nSelectedPoint = -1;
+ SetMouseInputEnabled( true );
+ SetKeyBoardInputEnabled( true );
+ m_nHighlightedPoint = -1;
+}
+
+CCurveEditorPanel::~CCurveEditorPanel()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Converts screen location to normalized values
+//-----------------------------------------------------------------------------
+void CCurveEditorPanel::ScreenToValue( int x, int y, float *pIn, float *pOut )
+{
+ int w, h;
+ GetSize( w, h );
+
+ *pIn = (float)x / (w-1);
+ *pOut = 1.0f - ((float)y / (h-1));
+}
+
+void CCurveEditorPanel::ValueToScreen( float flIn, float flOut, int *x, int *y )
+{
+ int w, h;
+ GetSize( w, h );
+
+ *x = (int)(flIn * (w-1) + 0.5f);
+ *y = (h-1) - (int)(flOut * (h-1) + 0.5f);
+}
+
+
+//-----------------------------------------------------------------------------
+// Handle input
+//-----------------------------------------------------------------------------
+void CCurveEditorPanel::OnMousePressed( vgui::MouseCode code )
+{
+ BaseClass::OnMousePressed( code );
+
+ int x, y;
+ input()->GetCursorPos( x, y );
+ ScreenToLocal( x, y );
+
+ if ( code == MOUSE_LEFT )
+ {
+ int w, h;
+ GetSize( w, h );
+
+ float flIn, flOut;
+ ScreenToValue( x, y, &flIn, &flOut );
+ float flTolerance = 5.0f / (w-1); // +/- 3 pixels to select the point
+ m_nSelectedPoint = FindOrAddControlPoint( flIn, flTolerance, flOut );
+ }
+}
+
+void CCurveEditorPanel::OnMouseReleased( vgui::MouseCode code )
+{
+ BaseClass::OnMouseReleased( code );
+
+ if ( code == MOUSE_LEFT )
+ {
+ m_nSelectedPoint = -1;
+ }
+}
+
+void CCurveEditorPanel::OnCursorMoved( int x, int y )
+{
+ BaseClass::OnCursorMoved( x, y );
+
+ float flIn, flOut;
+ ScreenToValue( x, y, &flIn, &flOut );
+
+ int w, h;
+ GetSize( w, h );
+ float flTolerance = 5.0f / (w-1); // +/- 3 pixels to select the point
+ m_nHighlightedPoint = FindControlPoint( flIn, flTolerance );
+
+ if ( m_nSelectedPoint < 0 )
+ return;
+ m_nSelectedPoint = ModifyControlPoint( m_nSelectedPoint, flIn, flOut );
+}
+
+
+//-----------------------------------------------------------------------------
+// Handles keypresses
+//-----------------------------------------------------------------------------
+void CCurveEditorPanel::OnKeyCodePressed( vgui::KeyCode code )
+{
+ BaseClass::OnKeyCodePressed( code );
+
+ if ( m_nSelectedPoint >= 0 )
+ {
+ RemoveControlPoint( m_nSelectedPoint );
+ m_nSelectedPoint = -1;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// This paints the grid behind the curves
+//-----------------------------------------------------------------------------
+void CCurveEditorPanel::PaintBackground( void )
+{
+ int w, h;
+ GetSize( w, h );
+
+ vgui::surface()->DrawSetColor( 255, 255, 255, 255 );
+ vgui::surface()->DrawFilledRect( 0, 0, w, h );
+
+ vgui::surface()->DrawSetColor( 128, 128, 128, 255 );
+ vgui::surface()->DrawLine( 0, h/4, w, h/4 );
+ vgui::surface()->DrawLine( 0, h/2, w, h/2 );
+ vgui::surface()->DrawLine( 0, 3*h/4, w, 3*h/4 );
+
+ vgui::surface()->DrawLine( w/4, 0, w/4, h );
+ vgui::surface()->DrawLine( w/2, 0, w/2, h );
+ vgui::surface()->DrawLine( 3*w/4, 0, 3*w/4, h );
+
+ vgui::surface()->DrawSetColor( 0, 0, 0, 255 );
+ vgui::surface()->DrawLine( 0, 0, w, 0 );
+ vgui::surface()->DrawLine( w, 0, w, h );
+ vgui::surface()->DrawLine( w, h, 0, h );
+ vgui::surface()->DrawLine( 0, h, 0, 0 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the color curves operation to edit
+//-----------------------------------------------------------------------------
+void CCurveEditorPanel::Paint( void )
+{
+ int w, h;
+ GetSize( w, h );
+
+ int x0 = 0, y0 = 0;
+
+ // FIXME: Add method to draw multiple lines DrawPolyLine connects the 1st and last points... bleah
+ vgui::surface()->DrawSetColor( 0, 0, 0, 255 );
+ for ( int i = 0; i < w; ++i )
+ {
+ float flIn = (float)i / (w-1);
+ float flOut = GetValue( flIn );
+ int x = i;
+ int y = (h-1) - (int)(flOut * (h-1) + 0.5f);
+ y = clamp( y, 0, h-1 );
+
+ if ( i != 0 )
+ {
+ vgui::surface()->DrawLine( x0, y0, x, y );
+ }
+
+ x0 = x; y0 = y;
+ }
+
+
+ // This will paint the control points
+ // The currently selected one will be painted red and filled.
+ for ( int i = ControlPointCount(); --i >= 0; )
+ {
+ float flIn, flOut;
+ GetControlPoint( i, &flIn, &flOut );
+
+ int cx, cy;
+ ValueToScreen( flIn, flOut, &cx, &cy );
+
+ if ( (i == m_nSelectedPoint) || ((m_nSelectedPoint == -1) && (i == m_nHighlightedPoint)) )
+ {
+ vgui::surface()->DrawSetColor( 255, 0, 0, 255 );
+ vgui::surface()->DrawFilledRect( cx-3, cy-3, cx+3, cy+3 );
+ }
+ else
+ {
+ vgui::surface()->DrawSetColor( 0, 0, 0, 255 );
+ vgui::surface()->DrawOutlinedRect( cx-3, cy-3, cx+3, cy+3 );
+ }
+ }
+} \ No newline at end of file
diff --git a/vgui2/matsys_controls/gamefiletreeview.cpp b/vgui2/matsys_controls/gamefiletreeview.cpp
new file mode 100644
index 0000000..712d54f
--- /dev/null
+++ b/vgui2/matsys_controls/gamefiletreeview.cpp
@@ -0,0 +1,301 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#if defined(WIN32) && !defined( _X360 )
+#include <windows.h>
+#endif
+#undef PropertySheet
+
+#include "matsys_controls/gamefiletreeview.h"
+#include "filesystem.h"
+#include "tier1/KeyValues.h"
+#include "vgui/ISurface.h"
+#include "vgui/Cursor.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+using namespace vgui;
+
+
+//-----------------------------------------------------------------------------
+// list of all tree view icons
+//-----------------------------------------------------------------------------
+enum
+{
+ IMAGE_FOLDER = 1,
+ IMAGE_OPENFOLDER,
+ IMAGE_FILE,
+};
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CGameFileTreeView::CGameFileTreeView( Panel *parent, const char *name, const char *pRootFolderName, const char *pRootDir, const char *pExtension ) : BaseClass(parent, name), m_Images( false )
+{
+ m_RootDir = pRootDir;
+
+ m_Ext = pExtension;
+ m_bUseExt = ( pExtension != NULL );
+
+ m_RootFolderName = pRootFolderName;
+
+ // build our list of images
+ m_Images.AddImage( scheme()->GetImage( "resource/icon_folder", false ) );
+ m_Images.AddImage( scheme()->GetImage( "resource/icon_folder_selected", false ) );
+ m_Images.AddImage( scheme()->GetImage( "resource/icon_file", false ) );
+ SetImageList( &m_Images, false );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Refreshes the active file list
+//-----------------------------------------------------------------------------
+void CGameFileTreeView::RefreshFileList()
+{
+ RemoveAll();
+ SetFgColor(Color(216, 222, 211, 255));
+
+ // add the base node
+ KeyValues *pkv = new KeyValues( "root" );
+ pkv->SetString( "text", m_RootFolderName );
+ pkv->SetInt( "root", 1 );
+ pkv->SetInt( "expand", 1 );
+ int iRoot = AddItem( pkv, GetRootItemIndex() );
+ pkv->deleteThis();
+ ExpandItem( iRoot, true );
+}
+
+
+//-----------------------------------------------------------------------------
+// Selects the root folder
+//-----------------------------------------------------------------------------
+void CGameFileTreeView::SelectRoot()
+{
+ AddSelectedItem( GetRootItemIndex(), true );
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets the number of root directories
+//-----------------------------------------------------------------------------
+int CGameFileTreeView::GetRootDirectoryCount()
+{
+ return GetNumChildren( GetRootItemIndex() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets the ith root directory
+//-----------------------------------------------------------------------------
+const char *CGameFileTreeView::GetRootDirectory( int nIndex )
+{
+ int nItemIndex = GetChild( GetRootItemIndex(), nIndex );
+ KeyValues *kv = GetItemData( nItemIndex );
+ if ( !kv )
+ return NULL;
+ return kv->GetString( "path", NULL );
+}
+
+
+//-----------------------------------------------------------------------------
+// Populate the root node (necessary since tree view can't have multiple roots)
+//-----------------------------------------------------------------------------
+void CGameFileTreeView::PopulateRootNode( int itemIndex )
+{
+ AddDirectoriesOfNode( itemIndex, m_RootDir );
+
+ if ( m_bUseExt )
+ {
+ AddFilesOfNode( itemIndex, m_RootDir, m_Ext );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Populate the root node with directories
+//-----------------------------------------------------------------------------
+bool CGameFileTreeView::DoesDirectoryHaveSubdirectories( const char *pFilePath )
+{
+ char pSearchString[MAX_PATH];
+ Q_snprintf( pSearchString, MAX_PATH, "%s\\*", pFilePath );
+
+ // get the list of files
+ FileFindHandle_t findHandle;
+
+ // generate children
+ // add all the items
+ const char *pszFileName = g_pFullFileSystem->FindFirstEx( pSearchString, "GAME", &findHandle );
+ while ( pszFileName )
+ {
+ bool bIsDirectory = g_pFullFileSystem->FindIsDirectory( findHandle );
+ if ( bIsDirectory && Q_strnicmp( pszFileName, ".", 2 ) && Q_strnicmp( pszFileName, "..", 3 ) )
+ return true;
+
+ pszFileName = g_pFullFileSystem->FindNext( findHandle );
+ }
+
+ g_pFullFileSystem->FindClose( findHandle );
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Populate the root node with directories
+//-----------------------------------------------------------------------------
+void CGameFileTreeView::AddDirectoriesOfNode( int itemIndex, const char *pFilePath )
+{
+ char pSearchString[MAX_PATH];
+ Q_snprintf( pSearchString, MAX_PATH, "%s\\*", pFilePath );
+
+ // get the list of files
+ FileFindHandle_t findHandle;
+
+ // generate children
+ // add all the items
+ const char *pszFileName = g_pFullFileSystem->FindFirstEx( pSearchString, "GAME", &findHandle );
+ while ( pszFileName )
+ {
+ bool bIsDirectory = g_pFullFileSystem->FindIsDirectory( findHandle );
+ if ( bIsDirectory && Q_strnicmp( pszFileName, ".", 2 ) && Q_strnicmp( pszFileName, "..", 3 ) )
+ {
+ KeyValues *kv = new KeyValues( "node", "text", pszFileName );
+
+ char pFullPath[MAX_PATH];
+ Q_snprintf( pFullPath, sizeof(pFullPath), "%s/%s", pFilePath, pszFileName );
+ Q_FixSlashes( pFullPath );
+ Q_strlower( pFullPath );
+ bool bHasSubdirectories = DoesDirectoryHaveSubdirectories( pFullPath );
+ kv->SetString( "path", pFullPath );
+
+ kv->SetInt( "expand", bHasSubdirectories );
+ kv->SetInt( "dir", 1 );
+ kv->SetInt( "image", IMAGE_FOLDER );
+
+ int itemID = AddItem(kv, itemIndex);
+ kv->deleteThis();
+
+ // mark directories in orange
+ SetItemColorForDirectories( itemID );
+ }
+
+ pszFileName = g_pFullFileSystem->FindNext( findHandle );
+ }
+
+ g_pFullFileSystem->FindClose( findHandle );
+}
+
+
+//-----------------------------------------------------------------------------
+// Populate the root node with files
+//-----------------------------------------------------------------------------
+void CGameFileTreeView::AddFilesOfNode( int itemIndex, const char *pFilePath, const char *pExt )
+{
+ char pSearchString[MAX_PATH];
+ Q_snprintf( pSearchString, MAX_PATH, "%s\\*.%s", pFilePath, pExt );
+
+ // get the list of files
+ FileFindHandle_t findHandle;
+
+ // generate children
+ // add all the items
+ const char *pszFileName = g_pFullFileSystem->FindFirst( pSearchString, &findHandle );
+ while ( pszFileName )
+ {
+ if ( !g_pFullFileSystem->FindIsDirectory( findHandle ) )
+ {
+ KeyValues *kv = new KeyValues( "node", "text", pszFileName );
+
+ char pFullPath[MAX_PATH];
+ Q_snprintf( pFullPath, MAX_PATH, "%s\\%s", pFilePath, pszFileName );
+ kv->SetString( "path", pFullPath );
+ kv->SetInt( "image", IMAGE_FILE );
+
+ AddItem(kv, itemIndex);
+ kv->deleteThis();
+ }
+
+ pszFileName = g_pFullFileSystem->FindNext( findHandle );
+ }
+
+ g_pFullFileSystem->FindClose( findHandle );
+}
+
+
+//-----------------------------------------------------------------------------
+// override to incremental request and show p4 directories
+//-----------------------------------------------------------------------------
+void CGameFileTreeView::GenerateChildrenOfNode(int itemIndex)
+{
+ KeyValues *pkv = GetItemData(itemIndex);
+ if ( pkv->GetInt("root") )
+ {
+ PopulateRootNode( itemIndex );
+ return;
+ }
+
+ if (!pkv->GetInt("dir"))
+ return;
+
+ const char *pFilePath = pkv->GetString("path", "");
+ if (!pFilePath[0])
+ return;
+
+ surface()->SetCursor(dc_waitarrow);
+
+ AddDirectoriesOfNode( itemIndex, pFilePath );
+
+ if ( m_bUseExt )
+ {
+ AddFilesOfNode( itemIndex, pFilePath, m_Ext );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// setup a context menu whenever a directory is clicked on
+//-----------------------------------------------------------------------------
+void CGameFileTreeView::GenerateContextMenu( int itemIndex, int x, int y )
+{
+ return;
+
+ /*
+ KeyValues *pkv = GetItemData(itemIndex);
+ const char *pFilePath = pkv->GetString("path", "");
+ if (!pFilePath[0])
+ return;
+
+ Menu *pContext = new Menu(this, "FileContext");
+ pContext->AddMenuItem("Cloak folder", new KeyValues("CloakFolder", "item", itemIndex), GetParent(), NULL);
+
+ // show the context menu
+ pContext->SetPos(x, y);
+ pContext->SetVisible(true);
+ */
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets an item to be colored as if its a menu
+//-----------------------------------------------------------------------------
+void CGameFileTreeView::SetItemColorForDirectories( int itemID )
+{
+ // mark directories in orange
+ SetItemFgColor( itemID, Color(224, 192, 0, 255) );
+}
+
+
+//-----------------------------------------------------------------------------
+// setup a smaller font
+//-----------------------------------------------------------------------------
+void CGameFileTreeView::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+ SetFont( pScheme->GetFont("DefaultSmall") );
+}
+
diff --git a/vgui2/matsys_controls/manipulator.cpp b/vgui2/matsys_controls/manipulator.cpp
new file mode 100644
index 0000000..b582b11
--- /dev/null
+++ b/vgui2/matsys_controls/manipulator.cpp
@@ -0,0 +1,201 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#include "matsys_controls/manipulator.h"
+
+#include "materialsystem/imaterialsystem.h"
+
+#include "vgui/IVGui.h"
+#include "vgui/IInput.h"
+#include "vgui/ISystem.h"
+#include "vgui/MouseCode.h"
+
+#include "mathlib/vector.h"
+#include "mathlib/vmatrix.h"
+#include "mathlib/mathlib.h"
+
+#include <float.h>
+
+using namespace vgui;
+
+
+//-----------------------------------------------------------------------------
+// local helper functions
+//-----------------------------------------------------------------------------
+static float UpdateTime( float &flLastTime )
+{
+ float flTime = vgui::system()->GetFrameTime();
+ float dt = flTime - flLastTime;
+ flLastTime = flTime;
+ return dt;
+}
+
+
+//-----------------------------------------------------------------------------
+// Base class for manipulators which operate on transforms
+//-----------------------------------------------------------------------------
+CTransformManipulator::CTransformManipulator( matrix3x4_t *pTransform ) :
+ m_pTransform( pTransform )
+{
+}
+
+void CTransformManipulator::SetTransform( matrix3x4_t *pTransform )
+{
+ m_pTransform = pTransform;
+}
+
+matrix3x4_t *CTransformManipulator::GetTransform( void )
+{
+ return m_pTransform;
+}
+
+
+//-----------------------------------------------------------------------------
+// CPotteryWheelManip - nendo-style camera manipulator
+//-----------------------------------------------------------------------------
+CPotteryWheelManip::CPotteryWheelManip( matrix3x4_t *pTransform ) :
+ CTransformManipulator( pTransform ),
+ m_lastx( -1 ), m_lasty( -1 ),
+ m_zoom( 100.0f ), m_altitude( 0.0f ), m_azimuth( 0.0f ),
+ m_prevZoom( 100.0f ), m_prevAltitude( 0.0f ), m_prevAzimuth( 0.0f ),
+ m_flLastMouseTime( 0.0f ), m_flLastTickTime( 0.0f ),
+ m_flSpin( 0.0f ), m_bSpin( false )
+{
+}
+
+void CPotteryWheelManip::OnBeginManipulation( void )
+{
+ m_prevZoom = m_zoom;
+ m_prevAltitude = m_altitude;
+ m_prevAzimuth = m_azimuth;
+ m_flLastMouseTime = m_flLastTickTime = vgui::system()->GetFrameTime();
+ m_flSpin = 0.0f;
+ m_bSpin = false;
+}
+
+// Sets the zoom level
+void CPotteryWheelManip::SetZoom( float flZoom )
+{
+ m_prevZoom = m_zoom = flZoom;
+}
+
+
+void CPotteryWheelManip::OnAcceptManipulation( void )
+{
+ m_flSpin = 0.0f;
+ m_bSpin = false;
+}
+
+void CPotteryWheelManip::OnCancelManipulation( void )
+{
+ m_zoom = m_prevZoom;
+ m_altitude = m_prevAltitude;
+ m_azimuth = m_prevAzimuth;
+ m_flSpin = 0.0f;
+ m_bSpin = false;
+ UpdateTransform();
+}
+
+
+void CPotteryWheelManip::OnTick( void )
+{
+ float dt = UpdateTime( m_flLastTickTime );
+
+ if ( m_bSpin )
+ {
+ m_azimuth += dt * m_flSpin;
+ UpdateTransform();
+ }
+}
+
+void CPotteryWheelManip::OnCursorMoved( int x, int y )
+{
+ float dt = UpdateTime( m_flLastMouseTime );
+
+ if ( m_bSpin )
+ {
+ m_lastx = x;
+ m_lasty = y;
+ return;
+ }
+
+ if ( input()->IsMouseDown( MOUSE_MIDDLE ) )
+ {
+ int dy = y - m_lasty;
+ int dx = x - m_lastx;
+
+ if ( abs( dx ) < 2 * abs( dy ) )
+ {
+ UpdateZoom( 0.2f * dy );
+ }
+ else
+ {
+ m_flSpin = (dt != 0.0f) ? 0.002f * dx / dt : 0.0f;
+ m_azimuth += 0.002f * dx;
+ }
+ }
+ else
+ {
+ m_azimuth += 0.002f * ( x - m_lastx );
+ m_altitude -= 0.002f * ( y - m_lasty );
+ m_altitude = max( (float)-M_PI/2, min( (float)M_PI/2, m_altitude ) );
+ }
+ m_lastx = x;
+ m_lasty = y;
+
+ UpdateTransform();
+}
+
+void CPotteryWheelManip::OnMousePressed( vgui::MouseCode code, int x, int y )
+{
+ UpdateTime( m_flLastMouseTime );
+ m_lastx = x;
+ m_lasty = y;
+ m_bSpin = false;
+ m_flSpin = 0.0f;
+}
+
+void CPotteryWheelManip::OnMouseReleased( vgui::MouseCode code, int x, int y )
+{
+ UpdateTime( m_flLastMouseTime );
+
+ if ( code == MOUSE_MIDDLE )
+ {
+ m_bSpin = ( fabs( m_flSpin ) > 1.0f );
+ }
+
+ m_lastx = x;
+ m_lasty = y;
+}
+
+void CPotteryWheelManip::OnMouseWheeled( int delta )
+{
+ UpdateTime( m_flLastMouseTime );
+
+ UpdateZoom( -10.0f * delta );
+ UpdateTransform();
+}
+
+void CPotteryWheelManip::UpdateTransform()
+{
+ if ( !m_pTransform )
+ return;
+
+ float y = m_zoom * sin( m_altitude );
+ float xz = m_zoom * cos( m_altitude );
+ float x = xz * sin( m_azimuth );
+ float z = xz * cos( m_azimuth );
+
+ Vector position(x, y, z);
+ AngleMatrix( RadianEuler( -m_altitude, m_azimuth, 0 ), position, *m_pTransform );
+}
+
+
+void CPotteryWheelManip::UpdateZoom( float delta )
+{
+ m_zoom *= pow( 1.01f, delta );
+}
diff --git a/vgui2/matsys_controls/matsys_controls.vpc b/vgui2/matsys_controls/matsys_controls.vpc
new file mode 100644
index 0000000..c4b1bc8
--- /dev/null
+++ b/vgui2/matsys_controls/matsys_controls.vpc
@@ -0,0 +1,66 @@
+//-----------------------------------------------------------------------------
+// MATSYS_CONTROLS.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$macro SRCDIR "..\.."
+
+$include "$SRCDIR\vpc_scripts\source_lib_base.vpc"
+
+$Project "matsys_controls"
+{
+ $Folder "Source Files"
+ {
+ $File "assetpicker.cpp"
+ $File "baseassetpicker.cpp"
+ $File "colorpickerpanel.cpp"
+ $File "curveeditorpanel.cpp"
+ $File "gamefiletreeview.cpp"
+ $File "manipulator.cpp"
+ $File "matsyscontrols.cpp"
+ $File "mdlpanel.cpp"
+ $File "mdlpicker.cpp"
+ $File "mdlsequencepicker.cpp"
+ $File "picker.cpp"
+ $File "potterywheelpanel.cpp"
+ $File "proceduraltexturepanel.cpp"
+ $File "QCGenerator.cpp"
+ $File "sequencepicker.cpp"
+ $File "tgapreviewpanel.cpp"
+ $File "vmtpicker.cpp"
+ $File "vmtpreviewpanel.cpp"
+ $File "vtfpicker.cpp"
+ $File "vtfpreviewpanel.cpp"
+ $File "vmtpanel.cpp"
+ }
+
+ $Folder "Header Files"
+ {
+ $File "$SRCDIR\public\matsys_controls\assetpicker.h"
+ $File "$SRCDIR\public\matsys_controls\baseassetpicker.h"
+ $File "$SRCDIR\public\matsys_controls\colorpickerpanel.h"
+ $File "$SRCDIR\public\matsys_controls\gamefiletreeview.h"
+ $File "$SRCDIR\public\matsys_controls\manipulator.h"
+ $File "$SRCDIR\public\matsys_controls\matsyscontrols.h"
+ $File "$SRCDIR\public\matsys_controls\mdlpanel.h"
+ $File "$SRCDIR\public\matsys_controls\mdlpicker.h"
+ $File "$SRCDIR\public\matsys_controls\mdlsequencepicker.h"
+ $File "$SRCDIR\public\matsys_controls\picker.h"
+ $File "$SRCDIR\public\matsys_controls\potterywheelpanel.h"
+ $File "$SRCDIR\public\matsys_controls\proceduraltexturepanel.h"
+ $File "$SRCDIR\public\matsys_controls\QCGenerator.h"
+ $File "$SRCDIR\public\matsys_controls\sequencepicker.h"
+ $File "$SRCDIR\public\matsys_controls\tgapreviewpanel.h"
+ $File "$SRCDIR\public\matsys_controls\vmtpicker.h"
+ $File "$SRCDIR\public\matsys_controls\vmtpreviewpanel.h"
+ $File "$SRCDIR\public\matsys_controls\vtfpicker.h"
+ $File "$SRCDIR\public\matsys_controls\vtfpreviewpanel.h"
+ $File "$SRCDIR\public\matsys_controls\vmtpanel.h"
+ }
+
+ $Folder "Link Libraries"
+ {
+ $Lib bitmap [$WIN32]
+ }
+}
diff --git a/vgui2/matsys_controls/matsyscontrols.cpp b/vgui2/matsys_controls/matsyscontrols.cpp
new file mode 100644
index 0000000..11c415f
--- /dev/null
+++ b/vgui2/matsys_controls/matsyscontrols.cpp
@@ -0,0 +1,100 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//===========================================================================//
+
+#include "matsys_controls/matsyscontrols.h"
+#include <materialsystem/imaterialsystem.h>
+#include <materialsystem/imaterialsystemhardwareconfig.h>
+#include <datacache/imdlcache.h>
+#include <VGuiMatSurface/IMatSystemSurface.h>
+#include <istudiorender.h>
+#include "vgui_controls/Controls.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+namespace vgui
+{
+
+IMaterialSystem *g_pMaterialSystem = NULL;
+IMaterialSystem *MaterialSystem()
+{
+ return g_pMaterialSystem;
+}
+
+IMaterialSystemHardwareConfig *g_pMaterialSystemHardwareConfig = NULL;
+IMaterialSystemHardwareConfig *MaterialSystemHardwareConfig()
+{
+ return g_pMaterialSystemHardwareConfig;
+}
+
+IMDLCache *g_pMDLCache = NULL;
+IMDLCache *MDLCache()
+{
+ return g_pMDLCache;
+}
+
+IMatSystemSurface *g_pMatSystemSurface = NULL;
+IMatSystemSurface *MatSystemSurface()
+{
+ return g_pMatSystemSurface;
+}
+
+IStudioRender *g_pStudioRender = NULL;
+IStudioRender *StudioRender()
+{
+ return g_pStudioRender;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: finds a particular interface in the factory set
+//-----------------------------------------------------------------------------
+static void *InitializeInterface( char const *interfaceName, CreateInterfaceFn *factoryList, int numFactories )
+{
+ void *retval;
+
+ for ( int i = 0; i < numFactories; i++ )
+ {
+ CreateInterfaceFn factory = factoryList[ i ];
+ if ( !factory )
+ continue;
+
+ retval = factory( interfaceName, NULL );
+ if ( retval )
+ return retval;
+ }
+
+ // No provider for requested interface!!!
+ // Assert( !"No provider for requested interface!!!" );
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Initializes the controls
+//-----------------------------------------------------------------------------
+bool VGui_InitMatSysInterfacesList( const char *moduleName, CreateInterfaceFn *factoryList, int numFactories )
+{
+ if ( !vgui::VGui_InitInterfacesList( moduleName, factoryList, numFactories ) )
+ return false;
+
+ g_pMaterialSystem = (IMaterialSystem *)InitializeInterface( MATERIAL_SYSTEM_INTERFACE_VERSION, factoryList, numFactories );
+ g_pMatSystemSurface = (IMatSystemSurface *)InitializeInterface( MAT_SYSTEM_SURFACE_INTERFACE_VERSION, factoryList, numFactories );
+ g_pMDLCache = (IMDLCache *)InitializeInterface( MDLCACHE_INTERFACE_VERSION, factoryList, numFactories );
+ g_pStudioRender = (IStudioRender *)InitializeInterface( STUDIO_RENDER_INTERFACE_VERSION, factoryList, numFactories );
+ g_pMaterialSystemHardwareConfig = (IMaterialSystemHardwareConfig *)InitializeInterface( MATERIALSYSTEM_HARDWARECONFIG_INTERFACE_VERSION, factoryList, numFactories );
+
+ // MDL cache + studiorender are optional
+ return ( g_pMaterialSystem && g_pMatSystemSurface && g_pMaterialSystemHardwareConfig );
+}
+
+
+} // namespace vgui
+
+
+
diff --git a/vgui2/matsys_controls/mdlpanel.cpp b/vgui2/matsys_controls/mdlpanel.cpp
new file mode 100644
index 0000000..b99301d
--- /dev/null
+++ b/vgui2/matsys_controls/mdlpanel.cpp
@@ -0,0 +1,902 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "matsys_controls/mdlpanel.h"
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/imaterialsystemhardwareconfig.h"
+#include "materialsystem/imesh.h"
+#include "vgui/IVGui.h"
+#include "tier1/KeyValues.h"
+#include "vgui_controls/Frame.h"
+#include "tier1/convar.h"
+#include "tier0/dbg.h"
+#include "tier1/fmtstr.h"
+#include "istudiorender.h"
+#include "matsys_controls/matsyscontrols.h"
+#include "vcollide.h"
+#include "vcollide_parse.h"
+#include "bone_setup.h"
+#include "vphysics_interface.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+using namespace vgui;
+
+DECLARE_BUILD_FACTORY( CMDLPanel );
+
+static const int THUMBNAIL_SAFE_ZONE_SIZE = 512;
+static const int THUMBNAIL_SAFE_ZONE_HEIGHT = 92;
+static const float THUMBNAIL_SAFE_ZONE_HEIGHT_SCALE = (float)THUMBNAIL_SAFE_ZONE_HEIGHT / THUMBNAIL_SAFE_ZONE_SIZE;
+
+//-----------------------------------------------------------------------------
+// Purpose: Keeps a global clock to autoplay sequences to run from
+// Also deals with speedScale changes
+//-----------------------------------------------------------------------------
+float GetAutoPlayTime( void )
+{
+ static int g_prevTicks;
+ static float g_time;
+
+ int ticks = Plat_MSTime();
+
+ // limit delta so that float time doesn't overflow
+ if (g_prevTicks == 0)
+ {
+ g_prevTicks = ticks;
+ }
+
+ g_time += ( ticks - g_prevTicks ) / 1000.0f;
+ g_prevTicks = ticks;
+
+ return g_time;
+}
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+CMDLPanel::CMDLPanel( vgui::Panel *pParent, const char *pName ) : BaseClass( pParent, pName )
+{
+ SetVisible( true );
+
+ // Used to poll input
+ vgui::ivgui()->AddTickSignal( GetVPanel() );
+
+ // Deal with the default cubemap
+ ITexture *pCubemapTexture = vgui::MaterialSystem()->FindTexture( "editor/cubemap", NULL, true );
+ m_DefaultEnvCubemap.Init( pCubemapTexture );
+ pCubemapTexture = vgui::MaterialSystem()->FindTexture( "editor/cubemap.hdr", NULL, true );
+ m_DefaultHDREnvCubemap.Init( pCubemapTexture );
+
+ SetIdentityMatrix( m_RootMDL.m_MDLToWorld );
+ m_bDrawCollisionModel = false;
+ m_bWireFrame = false;
+ m_bGroundGrid = false;
+ m_bLockView = false;
+ m_bLookAtCamera = true;
+ m_bThumbnailSafeZone = false;
+ m_nNumSequenceLayers = 0;
+ ResetAnimationEventState( &m_EventState );
+}
+
+CMDLPanel::~CMDLPanel()
+{
+ m_aMergeMDLs.Purge();
+ m_DefaultEnvCubemap.Shutdown( );
+ m_DefaultHDREnvCubemap.Shutdown();
+}
+
+
+//-----------------------------------------------------------------------------
+// Scheme settings
+//-----------------------------------------------------------------------------
+void CMDLPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+ SetBackgroundColor( GetBgColor() );
+ SetBorder( pScheme->GetBorder( "MenuBorder") );
+}
+
+
+//-----------------------------------------------------------------------------
+// Rendering options
+//-----------------------------------------------------------------------------
+void CMDLPanel::SetCollsionModel( bool bVisible )
+{
+ m_bDrawCollisionModel = bVisible;
+}
+
+void CMDLPanel::SetGroundGrid( bool bVisible )
+{
+ m_bGroundGrid = bVisible;
+}
+
+void CMDLPanel::SetWireFrame( bool bVisible )
+{
+ m_bWireFrame = bVisible;
+}
+
+void CMDLPanel::SetLockView( bool bLocked )
+{
+ m_bLockView = bLocked;
+}
+
+void CMDLPanel::SetLookAtCamera( bool bLookAtCamera )
+{
+ m_bLookAtCamera = bLookAtCamera;
+}
+
+void CMDLPanel::SetIgnoreDoubleClick( bool bState )
+{
+ m_bIgnoreDoubleClick = bState;
+}
+
+void CMDLPanel::SetThumbnailSafeZone( bool bVisible )
+{
+ m_bThumbnailSafeZone = bVisible;
+}
+
+//-----------------------------------------------------------------------------
+// Stores the clip
+//-----------------------------------------------------------------------------
+void CMDLPanel::SetMDL( MDLHandle_t handle, void *pProxyData )
+{
+ m_RootMDL.m_MDL.SetMDL( handle );
+ m_RootMDL.m_MDL.m_pProxyData = pProxyData;
+
+ Vector vecMins, vecMaxs;
+ GetMDLBoundingBox( &vecMins, &vecMaxs, handle, m_RootMDL.m_MDL.m_nSequence );
+
+ m_RootMDL.m_MDL.m_bWorldSpaceViewTarget = false;
+ m_RootMDL.m_MDL.m_vecViewTarget.Init( 100.0f, 0.0f, vecMaxs.z );
+
+ m_RootMDL.m_flCycleStartTime = 0.f;
+
+ // Set the pose parameters to the default for the mdl
+ SetPoseParameters( NULL, 0 );
+
+ // Clear any sequence layers
+ SetSequenceLayers( NULL, 0 );
+
+ ResetAnimationEventState( &m_EventState );
+}
+
+//-----------------------------------------------------------------------------
+// An MDL was selected
+//-----------------------------------------------------------------------------
+void CMDLPanel::SetMDL( const char *pMDLName, void *pProxyData )
+{
+ MDLHandle_t hMDLFindResult = vgui::MDLCache()->FindMDL( pMDLName );
+ MDLHandle_t hMDL = pMDLName ? hMDLFindResult : MDLHANDLE_INVALID;
+ if ( vgui::MDLCache()->IsErrorModel( hMDL ) )
+ {
+ hMDL = MDLHANDLE_INVALID;
+ }
+
+ SetMDL( hMDL, pProxyData );
+
+ // FindMDL takes a reference and the the CMDL will also hold a reference for as long as it sticks around. Release the FindMDL reference.
+ int nRef = vgui::MDLCache()->Release( hMDLFindResult );
+ (void)nRef; // Avoid unreferenced variable warning
+ AssertMsg( hMDL == MDLHANDLE_INVALID || nRef > 0, "CMDLPanel::SetMDL referenced a model that has a zero ref count." );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns a model bounding box.
+//-----------------------------------------------------------------------------
+bool CMDLPanel::GetBoundingBox( Vector &vecBoundsMin, Vector &vecBoundsMax )
+{
+ // Check to see if we have a valid model to look at.
+ if ( m_RootMDL.m_MDL.GetMDL() == MDLHANDLE_INVALID )
+ return false;
+
+ GetMDLBoundingBox( &vecBoundsMin, &vecBoundsMax, m_RootMDL.m_MDL.GetMDL(), m_RootMDL.m_MDL.m_nSequence );
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns a more accurate bounding sphere
+//-----------------------------------------------------------------------------
+bool CMDLPanel::GetBoundingSphere( Vector &vecCenter, float &flRadius )
+{
+ // Check to see if we have a valid model to look at.
+ if ( m_RootMDL.m_MDL.GetMDL() == MDLHANDLE_INVALID )
+ return false;
+
+ Vector vecEngineCenter;
+ GetMDLBoundingSphere( &vecEngineCenter, &flRadius, m_RootMDL.m_MDL.GetMDL(), m_RootMDL.m_MDL.m_nSequence );
+ VectorTransform( vecEngineCenter, m_RootMDL.m_MDLToWorld, vecCenter );
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CMDLPanel::SetModelAnglesAndPosition( const QAngle &angRot, const Vector &vecPos )
+{
+ SetIdentityMatrix( m_RootMDL.m_MDLToWorld );
+ AngleMatrix( angRot, vecPos, m_RootMDL.m_MDLToWorld );
+}
+
+//-----------------------------------------------------------------------------
+// Sets the camera to look at the model
+//-----------------------------------------------------------------------------
+void CMDLPanel::LookAtMDL()
+{
+ // Check to see if we have a valid model to look at.
+ if ( m_RootMDL.m_MDL.GetMDL() == MDLHANDLE_INVALID )
+ return;
+
+ if ( m_bLockView )
+ return;
+
+ float flRadius;
+ Vector vecCenter;
+ GetBoundingSphere( vecCenter, flRadius );
+ LookAt( vecCenter, flRadius );
+}
+
+
+//-----------------------------------------------------------------------------
+// FIXME: This should be moved into studiorender
+//-----------------------------------------------------------------------------
+static ConVar r_showenvcubemap( "r_showenvcubemap", "0", FCVAR_CHEAT );
+static ConVar r_eyegloss ( "r_eyegloss", "1", FCVAR_ARCHIVE ); // wet eyes
+static ConVar r_eyemove ( "r_eyemove", "1", FCVAR_ARCHIVE ); // look around
+static ConVar r_eyeshift_x ( "r_eyeshift_x", "0", FCVAR_ARCHIVE ); // eye X position
+static ConVar r_eyeshift_y ( "r_eyeshift_y", "0", FCVAR_ARCHIVE ); // eye Y position
+static ConVar r_eyeshift_z ( "r_eyeshift_z", "0", FCVAR_ARCHIVE ); // eye Z position
+static ConVar r_eyesize ( "r_eyesize", "0", FCVAR_ARCHIVE ); // adjustment to iris textures
+static ConVar mat_softwareskin( "mat_softwareskin", "0", FCVAR_CHEAT );
+static ConVar r_nohw ( "r_nohw", "0", FCVAR_CHEAT );
+static ConVar r_nosw ( "r_nosw", "0", FCVAR_CHEAT );
+static ConVar r_teeth ( "r_teeth", "1" );
+static ConVar r_drawentities ( "r_drawentities", "1", FCVAR_CHEAT );
+static ConVar r_flex ( "r_flex", "1" );
+static ConVar r_eyes ( "r_eyes", "1" );
+static ConVar r_skin ( "r_skin","0", FCVAR_CHEAT );
+static ConVar r_maxmodeldecal ( "r_maxmodeldecal", "50" );
+static ConVar r_modelwireframedecal ( "r_modelwireframedecal", "0", FCVAR_CHEAT );
+static ConVar mat_normals ( "mat_normals", "0", FCVAR_CHEAT );
+static ConVar r_eyeglintlodpixels ( "r_eyeglintlodpixels", "0", FCVAR_CHEAT );
+static ConVar r_rootlod ( "r_rootlod", "0" );
+
+static StudioRenderConfig_t s_StudioRenderConfig;
+
+void CMDLPanel::UpdateStudioRenderConfig( void )
+{
+ memset( &s_StudioRenderConfig, 0, sizeof(s_StudioRenderConfig) );
+
+ s_StudioRenderConfig.bEyeMove = !!r_eyemove.GetInt();
+ s_StudioRenderConfig.fEyeShiftX = r_eyeshift_x.GetFloat();
+ s_StudioRenderConfig.fEyeShiftY = r_eyeshift_y.GetFloat();
+ s_StudioRenderConfig.fEyeShiftZ = r_eyeshift_z.GetFloat();
+ s_StudioRenderConfig.fEyeSize = r_eyesize.GetFloat();
+ if( mat_softwareskin.GetInt() || m_bWireFrame )
+ {
+ s_StudioRenderConfig.bSoftwareSkin = true;
+ }
+ else
+ {
+ s_StudioRenderConfig.bSoftwareSkin = false;
+ }
+ s_StudioRenderConfig.bNoHardware = !!r_nohw.GetInt();
+ s_StudioRenderConfig.bNoSoftware = !!r_nosw.GetInt();
+ s_StudioRenderConfig.bTeeth = !!r_teeth.GetInt();
+ s_StudioRenderConfig.drawEntities = r_drawentities.GetInt();
+ s_StudioRenderConfig.bFlex = !!r_flex.GetInt();
+ s_StudioRenderConfig.bEyes = !!r_eyes.GetInt();
+ s_StudioRenderConfig.bWireframe = m_bWireFrame;
+ s_StudioRenderConfig.bDrawNormals = mat_normals.GetBool();
+ s_StudioRenderConfig.skin = r_skin.GetInt();
+ s_StudioRenderConfig.maxDecalsPerModel = r_maxmodeldecal.GetInt();
+ s_StudioRenderConfig.bWireframeDecals = r_modelwireframedecal.GetInt() != 0;
+
+ s_StudioRenderConfig.fullbright = false;
+ s_StudioRenderConfig.bSoftwareLighting = false;
+
+ s_StudioRenderConfig.bShowEnvCubemapOnly = r_showenvcubemap.GetInt() ? true : false;
+ s_StudioRenderConfig.fEyeGlintPixelWidthLODThreshold = r_eyeglintlodpixels.GetFloat();
+
+ StudioRender()->UpdateConfig( s_StudioRenderConfig );
+}
+
+void CMDLPanel::DrawCollisionModel()
+{
+ vcollide_t *pCollide = MDLCache()->GetVCollide( m_RootMDL.m_MDL.GetMDL() );
+
+ if ( !pCollide || pCollide->solidCount <= 0 )
+ return;
+
+ static color32 color = {255,0,0,0};
+
+ IVPhysicsKeyParser *pParser = g_pPhysicsCollision->VPhysicsKeyParserCreate( pCollide->pKeyValues );
+ CStudioHdr studioHdr( g_pMDLCache->GetStudioHdr( m_RootMDL.m_MDL.GetMDL() ), g_pMDLCache );
+
+ matrix3x4_t pBoneToWorld[MAXSTUDIOBONES];
+ m_RootMDL.m_MDL.SetUpBones( m_RootMDL.m_MDLToWorld, MAXSTUDIOBONES, pBoneToWorld );
+
+ // PERFORMANCE: Just parse the script each frame. It's fast enough for tools. If you need
+ // this to go faster then cache off the bone index mapping in an array like HLMV does
+ while ( !pParser->Finished() )
+ {
+ const char *pBlock = pParser->GetCurrentBlockName();
+ if ( !stricmp( pBlock, "solid" ) )
+ {
+ solid_t solid;
+
+ pParser->ParseSolid( &solid, NULL );
+ int boneIndex = Studio_BoneIndexByName( &studioHdr, solid.name );
+ Vector *outVerts;
+ int vertCount = g_pPhysicsCollision->CreateDebugMesh( pCollide->solids[solid.index], &outVerts );
+
+ if ( vertCount )
+ {
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->CullMode( MATERIAL_CULLMODE_CCW );
+ // NOTE: assumes these have been set up already by the model render code
+ // So this is a little bit of a back door to a cache of the bones
+ // this code wouldn't work unless you draw the model this frame before calling
+ // this routine. CMDLPanel always does this, but it's worth noting.
+ // A better solution would be to move the ragdoll visulization into the CDmeMdl
+ // and either draw it there or make it queryable and query/draw here.
+ matrix3x4_t xform;
+ SetIdentityMatrix( xform );
+ if ( boneIndex >= 0 )
+ {
+ MatrixCopy( pBoneToWorld[ boneIndex ], xform );
+ }
+ IMesh *pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, m_Wireframe );
+
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, vertCount/3 );
+
+ for ( int j = 0; j < vertCount; j++ )
+ {
+ Vector out;
+ VectorTransform( outVerts[j].Base(), xform, out.Base() );
+ meshBuilder.Position3fv( out.Base() );
+ meshBuilder.Color4ub( color.r, color.g, color.b, color.a );
+ meshBuilder.TexCoord2f( 0, 0, 0 );
+ meshBuilder.AdvanceVertex();
+ }
+ meshBuilder.End();
+ pMesh->Draw();
+ }
+
+ g_pPhysicsCollision->DestroyDebugMesh( vertCount, outVerts );
+ }
+ else
+ {
+ pParser->SkipBlock();
+ }
+ }
+ g_pPhysicsCollision->VPhysicsKeyParserDestroy( pParser );
+}
+
+void CMDLPanel::SetupRenderState( int nDisplayWidth, int nDisplayHeight )
+{
+ int iWidth = nDisplayWidth;
+ int iHeight = nDisplayHeight;
+ if ( m_bThumbnailSafeZone )
+ {
+ iWidth = THUMBNAIL_SAFE_ZONE_SIZE;
+ iHeight = THUMBNAIL_SAFE_ZONE_SIZE;
+ }
+ BaseClass::SetupRenderState( iWidth, iHeight );
+}
+
+//-----------------------------------------------------------------------------
+// paint it!
+//-----------------------------------------------------------------------------
+void CMDLPanel::OnPaint3D()
+{
+ if ( m_RootMDL.m_MDL.GetMDL() == MDLHANDLE_INVALID )
+ return;
+
+ // FIXME: Move this call into DrawModel in StudioRender
+ StudioRenderConfig_t oldStudioRenderConfig;
+ StudioRender()->GetCurrentConfig( oldStudioRenderConfig );
+
+ UpdateStudioRenderConfig();
+
+ CMatRenderContextPtr pRenderContext( vgui::MaterialSystem() );
+ if ( vgui::MaterialSystemHardwareConfig()->GetHDRType() == HDR_TYPE_NONE )
+ {
+ ITexture *pMyCube = HasLightProbe() ? GetLightProbeCubemap( false ) : m_DefaultEnvCubemap;
+ pRenderContext->BindLocalCubemap( pMyCube );
+ }
+ else
+ {
+ ITexture *pMyCube = HasLightProbe() ? GetLightProbeCubemap( true ) : m_DefaultHDREnvCubemap;
+ pRenderContext->BindLocalCubemap( pMyCube );
+ }
+
+ PrePaint3D( pRenderContext );
+
+ if ( m_bGroundGrid )
+ {
+ DrawGrid();
+ }
+
+ if ( m_bLookAtCamera )
+ {
+ matrix3x4_t worldToCamera;
+ ComputeCameraTransform( &worldToCamera );
+
+ Vector vecPosition;
+ MatrixGetColumn( worldToCamera, 3, vecPosition );
+ m_RootMDL.m_MDL.m_bWorldSpaceViewTarget = true;
+ m_RootMDL.m_MDL.m_vecViewTarget = vecPosition;
+ }
+
+ // Draw the MDL
+ CStudioHdr studioHdr( g_pMDLCache->GetStudioHdr( m_RootMDL.m_MDL.GetMDL() ), g_pMDLCache );
+
+ SetupFlexWeights();
+
+ matrix3x4_t *pBoneToWorld = g_pStudioRender->LockBoneMatrices( studioHdr.numbones() );
+ m_RootMDL.m_MDL.SetUpBones( m_RootMDL.m_MDLToWorld, studioHdr.numbones(), pBoneToWorld, m_PoseParameters, m_SequenceLayers, m_nNumSequenceLayers );
+ g_pStudioRender->UnlockBoneMatrices();
+
+ IMaterial* pOverrideMaterial = GetOverrideMaterial( m_RootMDL.m_MDL.GetMDL() );
+ if ( pOverrideMaterial != NULL )
+ g_pStudioRender->ForcedMaterialOverride( pOverrideMaterial );
+
+ m_RootMDL.m_MDL.Draw( m_RootMDL.m_MDLToWorld, pBoneToWorld );
+
+ if ( pOverrideMaterial != NULL )
+ g_pStudioRender->ForcedMaterialOverride( NULL );
+
+ pOverrideMaterial = NULL;
+
+ // Draw the merge MDLs.
+ matrix3x4_t matMergeBoneToWorld[MAXSTUDIOBONES];
+ int nMergeCount = m_aMergeMDLs.Count();
+ for ( int iMerge = 0; iMerge < nMergeCount; ++iMerge )
+ {
+ if ( m_aMergeMDLs[iMerge].m_bDisabled )
+ continue;
+
+ // Get the merge studio header.
+ studiohdr_t *pStudioHdr = g_pMDLCache->GetStudioHdr( m_aMergeMDLs[iMerge].m_MDL.GetMDL() );
+ matrix3x4_t *pMergeBoneToWorld = &matMergeBoneToWorld[0];
+
+ // If we have a valid mesh, bonemerge it. If we have an invalid mesh we can't bonemerge because
+ // it'll crash trying to pull data from the missing header.
+ if ( pStudioHdr != NULL )
+ {
+ CStudioHdr mergeHdr( pStudioHdr, g_pMDLCache );
+ m_aMergeMDLs[iMerge].m_MDL.SetupBonesWithBoneMerge( &mergeHdr, pMergeBoneToWorld, &studioHdr, pBoneToWorld, m_RootMDL.m_MDLToWorld );
+
+ pOverrideMaterial = GetOverrideMaterial( m_aMergeMDLs[iMerge].m_MDL.GetMDL() );
+ if ( pOverrideMaterial != NULL )
+ g_pStudioRender->ForcedMaterialOverride( pOverrideMaterial );
+
+ m_aMergeMDLs[iMerge].m_MDL.Draw( m_aMergeMDLs[iMerge].m_MDLToWorld, pMergeBoneToWorld );
+
+ if ( pOverrideMaterial != NULL )
+ g_pStudioRender->ForcedMaterialOverride( NULL );
+
+ // Notify of model render
+ RenderingMergedModel( pRenderContext, &mergeHdr, m_aMergeMDLs[iMerge].m_MDL.GetMDL(), pMergeBoneToWorld );
+ }
+ }
+
+ RenderingRootModel( pRenderContext, &studioHdr, m_RootMDL.m_MDL.GetMDL(), pBoneToWorld );
+
+ PostPaint3D( pRenderContext );
+
+ if ( m_bDrawCollisionModel )
+ {
+ DrawCollisionModel();
+ }
+
+ pRenderContext->Flush();
+ StudioRender()->UpdateConfig( oldStudioRenderConfig );
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the current LOD
+//-----------------------------------------------------------------------------
+void CMDLPanel::SetLOD( int nLOD )
+{
+ m_RootMDL.m_MDL.m_nLOD = nLOD;
+
+ int nMergeCount = m_aMergeMDLs.Count();
+ for ( int iMerge = 0; iMerge < nMergeCount; ++iMerge )
+ {
+ m_aMergeMDLs[iMerge].m_MDL.m_nLOD = nLOD;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the current sequence
+//-----------------------------------------------------------------------------
+void CMDLPanel::SetSequence( int nSequence, bool bResetSequence )
+{
+ m_RootMDL.m_MDL.m_nSequence = nSequence;
+
+ if ( bResetSequence )
+ {
+ m_RootMDL.m_flCycleStartTime = GetAutoPlayTime();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Set the current pose parameters. If NULL the pose parameters will be reset
+// to the default values.
+//-----------------------------------------------------------------------------
+void CMDLPanel::SetPoseParameters( const float *pPoseParameters, int nCount )
+{
+ if ( pPoseParameters )
+ {
+ int nParameters = MIN( MAXSTUDIOPOSEPARAM, nCount );
+ for ( int iParam = 0; iParam < nParameters; ++iParam )
+ {
+ m_PoseParameters[ iParam ] = pPoseParameters[ iParam ];
+ }
+ }
+ else if ( m_RootMDL.m_MDL.GetMDL() != MDLHANDLE_INVALID )
+ {
+ CStudioHdr studioHdr( g_pMDLCache->GetStudioHdr( m_RootMDL.m_MDL.GetMDL() ), g_pMDLCache );
+ Studio_CalcDefaultPoseParameters( &studioHdr, m_PoseParameters, MAXSTUDIOPOSEPARAM );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Set a pose parameter by name
+//-----------------------------------------------------------------------------
+bool CMDLPanel::SetPoseParameterByName( const char *pszName, float fValue )
+{
+ CStudioHdr studioHdr( g_pMDLCache->GetStudioHdr( m_RootMDL.m_MDL.GetMDL() ), g_pMDLCache );
+ int nPoseCount = studioHdr.GetNumPoseParameters();
+
+ for ( int i = 0; i < nPoseCount; ++i )
+ {
+ const mstudioposeparamdesc_t &Pose = studioHdr.pPoseParameter( i );
+ if ( V_strcasecmp( pszName, Pose.pszName() ) == 0 )
+ {
+ m_PoseParameters[ i ] = fValue;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Set the overlay sequence layers
+//-----------------------------------------------------------------------------
+void CMDLPanel::SetSequenceLayers( const MDLSquenceLayer_t *pSequenceLayers, int nCount )
+{
+ if ( pSequenceLayers )
+ {
+ m_nNumSequenceLayers = MIN( MAX_SEQUENCE_LAYERS, nCount );
+ for ( int iLayer = 0; iLayer < m_nNumSequenceLayers; ++iLayer )
+ {
+ m_SequenceLayers[ iLayer ] = pSequenceLayers[ iLayer ];
+ ResetAnimationEventState( &m_SequenceLayerEventState[ iLayer ] );
+ }
+ }
+ else
+ {
+ m_nNumSequenceLayers = 0;
+ V_memset( m_SequenceLayers, 0, sizeof( m_SequenceLayers ) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Set the current skin
+//-----------------------------------------------------------------------------
+void CMDLPanel::SetSkin( int nSkin )
+{
+ m_RootMDL.m_MDL.m_nSkin = nSkin;
+}
+
+//-----------------------------------------------------------------------------
+// called when we're ticked...
+//-----------------------------------------------------------------------------
+void CMDLPanel::OnTick()
+{
+ BaseClass::OnTick();
+ if ( m_RootMDL.m_MDL.GetMDL() != MDLHANDLE_INVALID )
+ {
+ m_RootMDL.m_MDL.m_flTime = ( GetAutoPlayTime() - m_RootMDL.m_flCycleStartTime );
+
+ DoAnimationEvents();
+ }
+}
+
+void CMDLPanel::Paint()
+{
+ BaseClass::Paint();
+
+ if ( m_bThumbnailSafeZone )
+ {
+ int iWidth, iHeight;
+ GetSize( iWidth, iHeight );
+
+ CMatRenderContextPtr pRenderContext( vgui::MaterialSystem() );
+
+ IMaterial *safezone = materials->FindMaterial( "vgui/thumbnails_safezone", TEXTURE_GROUP_VGUI, true );
+ if ( safezone )
+ {
+ safezone->IncrementReferenceCount();
+ }
+
+ int screenposx = 0;
+ int screenposy = 0;
+ LocalToScreen( screenposx, screenposy );
+
+ int iScaledHeight = THUMBNAIL_SAFE_ZONE_HEIGHT_SCALE * iHeight;
+ int iRemappedHeight = RemapVal( iScaledHeight, 0, THUMBNAIL_SAFE_ZONE_HEIGHT, 0, iScaledHeight );
+
+ pRenderContext->DrawScreenSpaceRectangle( safezone, screenposx, screenposy, iWidth, iRemappedHeight,
+ 0, 0,
+ THUMBNAIL_SAFE_ZONE_SIZE, THUMBNAIL_SAFE_ZONE_HEIGHT,
+ THUMBNAIL_SAFE_ZONE_SIZE, THUMBNAIL_SAFE_ZONE_SIZE );
+
+ screenposx = 0;
+ screenposy = iHeight - iRemappedHeight;
+ LocalToScreen( screenposx, screenposy );
+ pRenderContext->DrawScreenSpaceRectangle( safezone, screenposx, screenposy, iWidth, iRemappedHeight,
+ 0, 0,
+ THUMBNAIL_SAFE_ZONE_SIZE, THUMBNAIL_SAFE_ZONE_HEIGHT,
+ THUMBNAIL_SAFE_ZONE_SIZE, THUMBNAIL_SAFE_ZONE_SIZE );
+
+ if ( safezone )
+ {
+ safezone->DecrementReferenceCount();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CMDLPanel::DoAnimationEvents()
+{
+ CStudioHdr studioHdr( g_pMDLCache->GetStudioHdr( m_RootMDL.m_MDL.GetMDL() ), g_pMDLCache );
+
+ // If we don't have any sequences, don't do anything
+ if ( studioHdr.GetNumSeq() < 1 )
+ {
+ Assert( studioHdr.GetNumSeq() >= 1 );
+ return;
+ }
+
+ DoAnimationEvents( &studioHdr, m_RootMDL.m_MDL.m_nSequence, m_RootMDL.m_MDL.m_flTime, false, &m_EventState );
+
+ for ( int i = 0; i < m_nNumSequenceLayers; ++i )
+ {
+ float flTime = m_RootMDL.m_MDL.m_flTime - m_SequenceLayers[ i ].m_flCycleBeganAt;
+ //Plat_DebugString( CFmtStr("Animation: time = %f, started = %f, delta = %f\n",m_RootMDL.m_MDL.m_flTime,m_SequenceLayers[ i ].m_flCycleBeganAt,flTime ) );
+ DoAnimationEvents( &studioHdr, m_SequenceLayers[ i ].m_nSequenceIndex, flTime, m_SequenceLayers[ i ].m_bNoLoop, &m_SequenceLayerEventState[ i ] );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CMDLPanel::DoAnimationEvents( CStudioHdr *pStudioHdr, int nSeqNum, float flTime, bool bNoLoop, MDLAnimEventState_t *pEventState )
+{
+ if ( nSeqNum < 0 || nSeqNum >= pStudioHdr->GetNumSeq() )
+ {
+ return;
+ }
+
+ mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( nSeqNum );
+ if ( seqdesc.numevents == 0 )
+ {
+ return;
+ }
+
+ mstudioevent_t *pevent = seqdesc.pEvent( 0 );
+
+ int nFrameCount = Studio_MaxFrame( pStudioHdr, nSeqNum, m_PoseParameters );
+ if ( nFrameCount == 0 )
+ {
+ nFrameCount = 1;
+ }
+ float flEventCycle = ( flTime * m_RootMDL.m_MDL.m_flPlaybackRate ) / nFrameCount;
+ //Plat_DebugString( CFmtStr("Event cycle: %f, playback rate: %f, frame count: %d\n", flEventCycle, m_RootMDL.m_MDL.m_flPlaybackRate, nFrameCount ) );
+ if ( bNoLoop )
+ {
+ flEventCycle = MIN(flEventCycle, 1.0f);
+ }
+ else
+ {
+ flEventCycle -= (int)(flEventCycle);
+ }
+
+ if ( pEventState->m_nEventSequence != nSeqNum )
+ {
+ pEventState->m_nEventSequence = nSeqNum;
+ flEventCycle = 0.0f;
+ pEventState->m_flPrevEventCycle = -0.01f; // back up to get 0'th frame animations
+ }
+
+ if ( flEventCycle == pEventState->m_flPrevEventCycle )
+ {
+ return;
+ }
+
+ // check for looping
+ BOOL bLooped = (flEventCycle < pEventState->m_flPrevEventCycle);
+
+ // This makes sure events that occur at the end of a sequence occur are
+ // sent before events that occur at the beginning of a sequence.
+ if (bLooped)
+ {
+ for (int i = 0; i < (int)seqdesc.numevents; i++)
+ {
+ if ( pevent[i].cycle <= pEventState->m_flPrevEventCycle )
+ continue;
+
+ FireEvent( pevent[ i ].pszEventName(), pevent[ i ].pszOptions() );
+ }
+
+ // Necessary to get the next loop working
+ pEventState->m_flPrevEventCycle = -0.01f;
+ }
+
+ for (int i = 0; i < (int)seqdesc.numevents; i++)
+ {
+ if ( (pevent[i].cycle > pEventState->m_flPrevEventCycle && pevent[i].cycle <= flEventCycle) )
+ {
+ FireEvent( pevent[ i ].pszEventName(), pevent[ i ].pszOptions() );
+ }
+ }
+
+ pEventState->m_flPrevEventCycle = flEventCycle;
+}
+
+void CMDLPanel::FireEvent( const char *pszEventName, const char *pszEventOptions )
+{
+ KeyValues* pKVEvent = new KeyValues( "AnimEvent" );
+ pKVEvent->SetString( "name", pszEventName );
+ pKVEvent->SetString( "options", pszEventOptions );
+ PostActionSignal( pKVEvent );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CMDLPanel::ResetAnimationEventState( MDLAnimEventState_t *pEventState )
+{
+ pEventState->m_nEventSequence = -1;
+ pEventState->m_flPrevEventCycle = -0.01f;
+}
+
+//-----------------------------------------------------------------------------
+// input
+//-----------------------------------------------------------------------------
+void CMDLPanel::OnMouseDoublePressed( vgui::MouseCode code )
+{
+ if ( m_bIgnoreDoubleClick )
+ return;
+
+ float flRadius;
+ Vector vecCenter;
+ GetBoundingSphere( vecCenter, flRadius );
+ LookAt( vecCenter, flRadius );
+
+ BaseClass::OnMouseDoublePressed( code );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CMDLPanel::SetMergeMDL( MDLHandle_t handle, void *pProxyData, int nSkin /*= -1 */ )
+{
+ // Verify that we have a root model to merge to.
+ if ( m_RootMDL.m_MDL.GetMDL() == MDLHANDLE_INVALID )
+ return;
+
+ int iIndex = m_aMergeMDLs.AddToTail();
+ if ( !m_aMergeMDLs.IsValidIndex( iIndex ) )
+ return;
+
+ m_aMergeMDLs[iIndex].m_MDL.SetMDL( handle );
+
+ if ( nSkin != -1 )
+ {
+ m_aMergeMDLs[iIndex].m_MDL.m_nSkin = nSkin;
+ }
+
+ m_aMergeMDLs[iIndex].m_MDL.m_nLOD = m_RootMDL.m_MDL.m_nLOD;
+
+ m_aMergeMDLs[iIndex].m_MDL.m_pProxyData = pProxyData;
+ SetIdentityMatrix( m_aMergeMDLs[iIndex].m_MDLToWorld );
+
+ m_aMergeMDLs[iIndex].m_bDisabled = false;
+
+ // Need to invalidate the layout so the panel will adjust is LookAt for the new model.
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+MDLHandle_t CMDLPanel::SetMergeMDL( const char *pMDLName, void *pProxyData, int nSkin /*= -1 */ )
+{
+ MDLHandle_t hMDLFindResult = vgui::MDLCache()->FindMDL( pMDLName );
+ MDLHandle_t hMDL = pMDLName ? hMDLFindResult : MDLHANDLE_INVALID;
+ if ( vgui::MDLCache()->IsErrorModel( hMDL ) )
+ {
+ hMDL = MDLHANDLE_INVALID;
+ }
+
+ SetMergeMDL( hMDL, pProxyData, nSkin );
+
+ // FindMDL takes a reference and the the CMDL will also hold a reference for as long as it sticks around. Release the FindMDL reference.
+ int nRef = vgui::MDLCache()->Release( hMDLFindResult );
+ (void)nRef; // Avoid unreferenced variable warning
+ AssertMsg( hMDL == MDLHANDLE_INVALID || nRef > 0, "CMDLPanel::SetMergeMDL referenced a model that has a zero ref count." );
+
+ return hMDL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CMDLPanel::GetMergeMDLIndex( void *pProxyData )
+{
+ int nMergeCount = m_aMergeMDLs.Count();
+ for ( int iMerge = 0; iMerge < nMergeCount; ++iMerge )
+ {
+ if ( m_aMergeMDLs[iMerge].m_MDL.m_pProxyData == pProxyData )
+ return iMerge;
+ }
+
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CMDLPanel::GetMergeMDLIndex( MDLHandle_t handle )
+{
+ int nMergeCount = m_aMergeMDLs.Count();
+ for ( int iMerge = 0; iMerge < nMergeCount; ++iMerge )
+ {
+ if ( m_aMergeMDLs[iMerge].m_MDL.GetMDL() == handle )
+ return iMerge;
+ }
+
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CMDL *CMDLPanel::GetMergeMDL( MDLHandle_t handle )
+{
+ int nMergeCount = m_aMergeMDLs.Count();
+ for ( int iMerge = 0; iMerge < nMergeCount; ++iMerge )
+ {
+ if ( m_aMergeMDLs[iMerge].m_MDL.GetMDL() == handle )
+ return (&m_aMergeMDLs[iMerge].m_MDL);
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CMDLPanel::ClearMergeMDLs( void )
+{
+ m_aMergeMDLs.Purge();
+}
diff --git a/vgui2/matsys_controls/mdlpicker.cpp b/vgui2/matsys_controls/mdlpicker.cpp
new file mode 100644
index 0000000..9098c94
--- /dev/null
+++ b/vgui2/matsys_controls/mdlpicker.cpp
@@ -0,0 +1,1742 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "matsys_controls/mdlpicker.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/PropertyPage.h"
+#include "vgui_controls/CheckButton.h"
+#include "vgui_controls/DirectorySelectDialog.h"
+#include "vgui/IVGui.h"
+#include "vgui/IInput.h"
+#include "vgui/ISurface.h"
+#include "vgui/Cursor.h"
+#include "matsys_controls/assetpicker.h"
+#include "matsys_controls/colorpickerpanel.h"
+#include "dmxloader/dmxloader.h"
+#include "tier1/utlbuffer.h"
+#include "bitmap/tgawriter.h"
+#include "tier3/tier3.h"
+#include "istudiorender.h"
+#include "../vgui2/src/VPanel.h"
+#include "tier2/p4helpers.h"
+#include "ivtex.h"
+#include "bitmap/tgaloader.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+using namespace vgui;
+
+static bool SaveTgaAndAddToP4( unsigned char *pImage, ImageFormat imageFormat, int Width, int Height, const char *szDestFilename )
+{
+
+ // allocate a buffer to write the tga into
+ int iMaxTGASize = 1024 + (Width * Height * 4);
+ void *pTGA = malloc( iMaxTGASize );
+ CUtlBuffer buffer( pTGA, iMaxTGASize );
+
+ if( !TGAWriter::WriteToBuffer( pImage, buffer, Width, Height, imageFormat, IMAGE_FORMAT_BGRA8888 ) )
+ {
+ Error( "Couldn't write bitmap data snapshot.\n" );
+ return false;
+ }
+
+ CP4AutoEditAddFile autop4( szDestFilename );
+
+ // async write to disk (this will take ownership of the memory)
+ char szDirName[ _MAX_PATH ];
+ strcpy( szDirName, szDestFilename );
+ V_StripFilename( szDirName );
+ g_pFullFileSystem->CreateDirHierarchy( szDirName, "" );
+ g_pFullFileSystem->AsyncWrite( szDestFilename, buffer.Base(), buffer.TellPut(), true );
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+//
+// MDL Picker
+//
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Sort by MDL name
+//-----------------------------------------------------------------------------
+static int __cdecl MDLBrowserSortFunc( vgui::ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
+{
+ const char *string1 = item1.kv->GetString("mdl");
+ const char *string2 = item2.kv->GetString("mdl");
+ return stricmp( string1, string2 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CMDLPicker::CMDLPicker( vgui::Panel *pParent, int nFlags ) :
+ BaseClass( pParent, "MDL Files", "mdl", "models", "mdlName" )
+{
+ for( int i = 0; i < MAX_SELECTED_MODELS; i++ )
+ {
+ m_hSelectedMDL[ i ] = MDLHANDLE_INVALID;
+ }
+
+ m_nFlags = nFlags; // remember what we show and what not
+
+ m_pRenderPage = NULL;
+ m_pSequencesPage = NULL;
+ m_pActivitiesPage = NULL;
+ m_pSkinsPage = NULL;
+ m_pInfoPage = NULL;
+ m_pScreenCapsPage = NULL;
+
+ m_pSequencesList = NULL;
+ m_pActivitiesList = NULL;
+
+ m_hDirectorySelectDialog = NULL;
+
+ // Horizontal splitter for mdls
+ 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 );
+
+ // Standard browser controls
+ pSplitterLeftSide->RequestFocus();
+ CreateStandardControls( pSplitterLeftSide, false );
+
+ // property sheet - revisions, changes, etc.
+ m_pPreviewSplitter = new Splitter( pSplitterRightSide, "PreviewSplitter", SPLITTER_MODE_HORIZONTAL, 1 );
+
+ vgui::Panel *pSplitterTopSide = m_pPreviewSplitter->GetChild( 0 );
+ vgui::Panel *pSplitterBottomSide = m_pPreviewSplitter->GetChild( 1 );
+
+ // MDL preview
+ m_pMDLPreview = new CMDLPanel( pSplitterTopSide, "MDLPreview" );
+ SetSkipChildDuringPainting( m_pMDLPreview );
+
+ m_pViewsSheet = new vgui::PropertySheet( pSplitterBottomSide, "ViewsSheet" );
+ m_pViewsSheet->AddActionSignalTarget( this );
+
+ // now add wanted features
+ if ( nFlags & PAGE_RENDER )
+ {
+ m_pRenderPage = new vgui::PropertyPage( m_pViewsSheet, "RenderPage" );
+
+ m_pRenderPage->AddActionSignalTarget( this );
+
+ m_pRenderPage->LoadControlSettingsAndUserConfig( "resource/mdlpickerrender.res" );
+
+ RefreshRenderSettings();
+
+ // ground
+ Button *pSelectProbe = (Button*)m_pRenderPage->FindChildByName( "ChooseLightProbe" );
+ pSelectProbe->AddActionSignalTarget( this );
+ }
+
+ if ( nFlags & PAGE_SEQUENCES )
+ {
+ m_pSequencesPage = new vgui::PropertyPage( m_pViewsSheet, "SequencesPage" );
+
+ m_pSequencesList = new vgui::ListPanel( m_pSequencesPage, "SequencesList" );
+ m_pSequencesList->AddColumnHeader( 0, "sequence", "sequence", 52, 0 );
+ m_pSequencesList->AddActionSignalTarget( this );
+ m_pSequencesList->SetSelectIndividualCells( true );
+ m_pSequencesList->SetEmptyListText("No .MDL file currently selected.");
+ m_pSequencesList->SetDragEnabled( true );
+ m_pSequencesList->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_DOWNANDRIGHT, 6, 6, -6, -6 );
+ }
+
+ if ( nFlags & PAGE_ACTIVITIES )
+ {
+ m_pActivitiesPage = new vgui::PropertyPage( m_pViewsSheet, "ActivitiesPage" );
+
+ m_pActivitiesList = new vgui::ListPanel( m_pActivitiesPage, "ActivitiesList" );
+ m_pActivitiesList->AddColumnHeader( 0, "activity", "activity", 52, 0 );
+ m_pActivitiesList->AddActionSignalTarget( this );
+ m_pActivitiesList->SetSelectIndividualCells( true );
+ m_pActivitiesList->SetEmptyListText( "No .MDL file currently selected." );
+ m_pActivitiesList->SetDragEnabled( true );
+ m_pActivitiesList->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_DOWNANDRIGHT, 6, 6, -6, -6 );
+ }
+
+ if ( nFlags & PAGE_SKINS )
+ {
+ m_pSkinsPage = new vgui::PropertyPage( m_pViewsSheet, "SkinsPage" );
+
+ m_pSkinsList = new vgui::ListPanel( m_pSkinsPage, "SkinsList" );
+ m_pSkinsList->AddColumnHeader( 0, "skin", "skin", 52, 0 );
+ m_pSkinsList->AddActionSignalTarget( this );
+ m_pSkinsList->SetSelectIndividualCells( true );
+ m_pSkinsList->SetEmptyListText( "No .MDL file currently selected." );
+ m_pSkinsList->SetDragEnabled( true );
+ m_pSkinsList->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_DOWNANDRIGHT, 6, 6, -6, -6 );
+ }
+
+ if ( nFlags & PAGE_INFO )
+ {
+ m_pInfoPage = new vgui::PropertyPage( m_pViewsSheet, "InfoPage" );
+
+ m_pInfoPage->AddActionSignalTarget( this );
+
+ m_pInfoPage->LoadControlSettingsAndUserConfig( "resource/mdlpickerinfo.res" );
+
+ CheckButton * pTempCheck = (CheckButton *)m_pInfoPage->FindChildByName( "PhysicsObject" );
+ pTempCheck->SetDisabledFgColor1( pTempCheck->GetFgColor());
+ pTempCheck->SetDisabledFgColor2( pTempCheck->GetFgColor());
+ pTempCheck = (CheckButton *)m_pInfoPage->FindChildByName( "StaticObject" );
+ pTempCheck->SetDisabledFgColor1( pTempCheck->GetFgColor());
+ pTempCheck->SetDisabledFgColor2( pTempCheck->GetFgColor());
+ pTempCheck = (CheckButton *)m_pInfoPage->FindChildByName( "DynamicObject" );
+ pTempCheck->SetDisabledFgColor1( pTempCheck->GetFgColor());
+ pTempCheck->SetDisabledFgColor2( pTempCheck->GetFgColor());
+
+ m_pPropDataList = new vgui::ListPanel( m_pInfoPage, "PropData" );
+ m_pPropDataList->AddColumnHeader( 0, "key", "key", 250, ListPanel::COLUMN_FIXEDSIZE );
+ m_pPropDataList->AddColumnHeader( 1, "value", "value", 52, 0 );
+ m_pPropDataList->AddActionSignalTarget( this );
+ m_pPropDataList->SetSelectIndividualCells( false );
+ m_pPropDataList->SetEmptyListText( "No prop_data available." );
+ m_pPropDataList->SetDragEnabled( true );
+ m_pPropDataList->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_DOWNANDRIGHT, 6, 72, -6, -6 );
+
+ RefreshRenderSettings();
+ }
+
+ if ( nFlags & PAGE_SCREEN_CAPS )
+ {
+ m_pScreenCapsPage = new vgui::PropertyPage( m_pViewsSheet, "ScreenCapsPage" );
+
+// not sure why we have to do this for the color picker
+ CColorPickerButton *m_pBackgroundColor;
+ m_pBackgroundColor = new CColorPickerButton( m_pScreenCapsPage, "BackgroundColor", this );
+
+ m_pScreenCapsPage->LoadControlSettingsAndUserConfig( "resource/mdlpickerscreencaps.res" );
+
+ TextEntry *pTempValue;
+ pTempValue = ( TextEntry * )m_pScreenCapsPage->FindChildByName( "WidthText" );
+ pTempValue->SetText( "256" );
+ pTempValue = ( TextEntry * )m_pScreenCapsPage->FindChildByName( "HeightText" );
+ pTempValue->SetText( "256" );
+
+// not sure why this doesn't work
+// m_pBackgroundColor = ( CColorPickerButton * )m_pScreenCapsPage->FindChildByName( "BackgroundColor" );
+ m_pBackgroundColor->SetColor( 255, 0, 0, 255 );
+
+ Label *m_pOutputDirectory;
+ m_pOutputDirectory = ( Label * )m_pScreenCapsPage->FindChildByName( "OutputDirectory" );
+ m_pOutputDirectory->SetText( "c:\\" );
+
+ Button *pSelectProbe = ( Button * )m_pScreenCapsPage->FindChildByName( "Capture" );
+ pSelectProbe->AddActionSignalTarget( this );
+ pSelectProbe = ( Button * )m_pScreenCapsPage->FindChildByName( "OutputDirectorySelect" );
+ pSelectProbe->AddActionSignalTarget( this );
+ pSelectProbe = ( Button * )m_pScreenCapsPage->FindChildByName( "SaveCaps" );
+ pSelectProbe->AddActionSignalTarget( this );
+ pSelectProbe = ( Button * )m_pScreenCapsPage->FindChildByName( "RestoreCaps" );
+ pSelectProbe->AddActionSignalTarget( this );
+ pSelectProbe = ( Button * )m_pScreenCapsPage->FindChildByName( "GenerateBackpackIcons" );
+ pSelectProbe->AddActionSignalTarget( this );
+ }
+
+ // Load layout settings; has to happen before pinning occurs in code
+ LoadControlSettingsAndUserConfig( "resource/mdlpicker.res" );
+
+ // Pages must be added after control settings are set up
+ if ( m_pRenderPage )
+ {
+ m_pViewsSheet->AddPage( m_pRenderPage, "Render" );
+ }
+ if ( m_pSequencesPage )
+ {
+ m_pViewsSheet->AddPage( m_pSequencesPage, "Sequences" );
+ }
+ if ( m_pActivitiesPage )
+ {
+ m_pViewsSheet->AddPage( m_pActivitiesPage, "Activities" );
+ }
+ if ( m_pSkinsPage )
+ {
+ m_pViewsSheet->AddPage( m_pSkinsPage, "Skins" );
+ }
+ if ( m_pInfoPage )
+ {
+ m_pViewsSheet->AddPage( m_pInfoPage, "Info" );
+ }
+ if ( m_pScreenCapsPage )
+ {
+ m_pViewsSheet->AddPage( m_pScreenCapsPage, "Screen Caps" );
+ }
+}
+
+void CMDLPicker::RefreshRenderSettings()
+{
+ vgui::CheckButton *pToggle;
+
+ if ( !m_pRenderPage )
+ return;
+
+ // ground
+ pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName("NoGround");
+ pToggle->AddActionSignalTarget( this );
+ m_pMDLPreview->SetGroundGrid( !pToggle->IsSelected() );
+
+ // collision
+ pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName("Collision");
+ pToggle->AddActionSignalTarget( this );
+ m_pMDLPreview->SetCollsionModel( pToggle->IsSelected() );
+
+ // wireframe
+ pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName("Wireframe");
+ pToggle->AddActionSignalTarget( this );
+ m_pMDLPreview->SetWireFrame( pToggle->IsSelected() );
+
+ // lockview
+ pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName("LockView");
+ pToggle->AddActionSignalTarget( this );
+ m_pMDLPreview->SetLockView( pToggle->IsSelected() );
+
+ // look at camera
+ pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName("LookAtCamera");
+ pToggle->AddActionSignalTarget( this );
+ m_pMDLPreview->SetLookAtCamera( pToggle->IsSelected() );
+
+ // thumbnail safe zone
+ pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName("ThumbnailSafeZone");
+ pToggle->AddActionSignalTarget( this );
+ m_pMDLPreview->SetThumbnailSafeZone( pToggle->IsSelected() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CMDLPicker::~CMDLPicker()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Performs layout
+//-----------------------------------------------------------------------------
+void CMDLPicker::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 CMDLPicker::OnAssetSelected( KeyValues *pParams )
+{
+ const char *pAsset = pParams->GetString( "asset" );
+
+ char pProbeBuf[MAX_PATH];
+ Q_snprintf( pProbeBuf, sizeof(pProbeBuf), "materials/lightprobes/%s", pAsset );
+
+ BeginDMXContext();
+ CDmxElement *pLightProbe = NULL;
+ bool bOk = UnserializeDMX( pProbeBuf, "GAME", true, &pLightProbe );
+ if ( !pLightProbe || !bOk )
+ {
+ char pBuf[1024];
+ Q_snprintf( pBuf, sizeof(pBuf), "Error loading lightprobe file '%s'!\n", pProbeBuf );
+ vgui::MessageBox *pMessageBox = new vgui::MessageBox( "Error Loading File!\n", pBuf, GetParent() );
+ pMessageBox->DoModal( );
+
+ EndDMXContext( true );
+ return;
+ }
+
+ m_pMDLPreview->SetLightProbe( pLightProbe );
+ EndDMXContext( true );
+}
+
+
+//-----------------------------------------------------------------------------
+// Buttons on various pages
+//-----------------------------------------------------------------------------
+void CMDLPicker::OnCommand( const char *pCommand )
+{
+ if ( !Q_stricmp( pCommand, "ChooseLightProbe" ) )
+ {
+ CAssetPickerFrame *pPicker = new CAssetPickerFrame( this, "Select Light Probe (.prb) File",
+ "Light Probe", "prb", "materials/lightprobes", "lightprobe" );
+ pPicker->DoModal();
+ return;
+ }
+ else if ( !Q_stricmp( pCommand, "OutputDirectorySelect" ) )
+ {
+ if ( !m_hDirectorySelectDialog.Get() )
+ {
+ m_hDirectorySelectDialog = new DirectorySelectDialog( this, "Choose Screen Caps output folder" );
+ }
+
+ Label *m_pOutputDirectory;
+ char temp[ MAX_PATH ];
+ m_pOutputDirectory = ( Label * )m_pScreenCapsPage->FindChildByName( "OutputDirectory" );
+ m_pOutputDirectory->GetText( temp, sizeof( temp ) );
+
+ m_hDirectorySelectDialog->MakeReadyForUse();
+ m_hDirectorySelectDialog->SetStartDirectory( temp );
+ m_hDirectorySelectDialog->DoModal();
+ return;
+ }
+ else if ( !Q_stricmp( pCommand, "Capture" ) )
+ {
+ CaptureScreenCaps();
+ return;
+ }
+ else if ( !Q_stricmp( pCommand, "GenerateBackpackIcons" ) )
+ {
+ // shut off the ground grid
+ vgui::CheckButton *pGroundToggle = (vgui::CheckButton *)m_pRenderPage->FindChildByName( "NoGround" );
+ bool bOriginalGridState = pGroundToggle->IsSelected();
+
+ vgui::CheckButton *pSafeZoneToggle = (vgui::CheckButton *)m_pRenderPage->FindChildByName( "ThumbnailSafeZone" );
+ bool bOriginalSafeZoneState = pSafeZoneToggle->IsSelected();
+
+ m_pMDLPreview->SetGroundGrid( false );
+ m_pMDLPreview->SetThumbnailSafeZone( false );
+
+ // make the icons
+ GenerateBackpackIcons();
+
+ // return the ground grid to its original state
+ m_pMDLPreview->SetGroundGrid( !bOriginalGridState );
+ m_pMDLPreview->SetThumbnailSafeZone( bOriginalSafeZoneState );
+
+ return;
+ }
+ else if ( !Q_stricmp( pCommand, "SaveCaps" ) )
+ {
+ SaveCaps( NULL );
+ return;
+ }
+ else if ( !Q_stricmp( pCommand, "RestoreCaps" ) )
+ {
+ if ( input()->IsKeyDown( KEY_RCONTROL ) || input()->IsKeyDown( KEY_LCONTROL ) )
+ {
+ int nCount = m_AssetList.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( m_pAssetBrowser->IsItemVisible( m_AssetList[ i ].m_nItemId ) &&
+ m_pAssetBrowser->IsItemSelected( m_AssetList[ i ].m_nItemId ) )
+ {
+ KeyValues *pItemKeyValues = m_pAssetBrowser->GetItem( m_AssetList[ i ].m_nItemId );
+ const char *pSelectedAsset = pItemKeyValues->GetString( "asset" );
+
+ char szBathPath[ _MAX_PATH ];
+ Label *m_pOutputDirectory;
+
+ m_pOutputDirectory = ( Label * )m_pScreenCapsPage->FindChildByName( "OutputDirectory" );
+ m_pOutputDirectory->GetText( szBathPath, sizeof( szBathPath ) );
+
+ char szPathedFileName[ _MAX_PATH ];
+ sprintf( szPathedFileName, "%s%s", szBathPath, pSelectedAsset );
+
+ Label *m_pResults = ( Label * )m_pScreenCapsPage->FindChildByName( "CaptureResults" );
+ if ( RestoreCaps( szPathedFileName ) )
+ {
+ m_pResults->SetText( "Prefs Restored" );
+ }
+ else
+ {
+ m_pResults->SetText( "Prefs NOT FOUND" );
+ }
+ break;
+ }
+ }
+ }
+ else
+ {
+ RestoreCaps( NULL );
+ }
+ return;
+ }
+
+ BaseClass::OnCommand( pCommand );
+}
+
+
+//-----------------------------------------------------------------------------
+// Handles the directory selection for screen caps
+//-----------------------------------------------------------------------------
+void CMDLPicker::OnDirectorySelected( char const *dir )
+{
+ if ( m_hDirectorySelectDialog != 0 )
+ {
+ m_hDirectorySelectDialog->MarkForDeletion();
+ }
+
+ Label *m_pOutputDirectory;
+ m_pOutputDirectory = ( Label * )m_pScreenCapsPage->FindChildByName( "OutputDirectory" );
+ m_pOutputDirectory->SetText( dir );
+}
+
+
+//-----------------------------------------------------------------------------
+// Screen captures the specific model and writes out a .tga. Assumes the MDLPreview
+// panel has been properly adjusted to 0,0 in screen space and that width / height
+// have been set.
+//-----------------------------------------------------------------------------
+const char *CMDLPicker::CaptureModel( int nModIndex, const char *AssetName, const char *OutputPath, int Width, int Height, Color BackgroundColor, bool bSelectedOnly )
+{
+ char pBuf[ MAX_PATH ];
+ Q_snprintf( pBuf, sizeof( pBuf ), "%s\\%s\\%s", GetModPath( nModIndex ), m_pAssetSubDir, AssetName );
+ Q_FixSlashes( pBuf );
+
+ if ( !bSelectedOnly )
+ {
+ SelectMDL( pBuf, false, -1 );
+ }
+
+ CMatRenderContextPtr pRenderContext( materials );
+
+ g_pMaterialSystem->BeginFrame( 0 );
+ g_pStudioRender->BeginFrame();
+
+// pRenderContext->ClearColor4ub( 0, 0, 0, 0 );
+// pRenderContext->ClearBuffers( true, true );
+
+ Color NewPanelColor;
+
+ NewPanelColor.SetColor( 0, 0, 0, 0 );
+ m_pMDLPreview->SetBackgroundColor( NewPanelColor );
+
+ g_pVGuiSurface->PaintTraverseEx( m_pMDLPreview->GetVPanel(), false );
+
+ g_pStudioRender->EndFrame();
+ g_pMaterialSystem->EndFrame( );
+
+ // get the data from the backbuffer and save to disk
+ // bitmap bits
+ unsigned char *pImageBlack = ( unsigned char * )malloc( Width * 4 * Height );
+
+ // Get Bits from the material system
+ pRenderContext->ReadPixels( 0, 0, Width, Height, pImageBlack, IMAGE_FORMAT_BGRA8888 );
+
+
+ g_pMaterialSystem->BeginFrame( 0 );
+ g_pStudioRender->BeginFrame();
+
+// pRenderContext->ClearColor4ub( 255, 255, 255, 0 );
+// pRenderContext->ClearBuffers( true, true );
+
+ NewPanelColor.SetColor( 255, 255, 255, 0 );
+ m_pMDLPreview->SetBackgroundColor( NewPanelColor );
+
+ g_pVGuiSurface->PaintTraverseEx( m_pMDLPreview->GetVPanel(), false );
+
+ g_pStudioRender->EndFrame();
+ g_pMaterialSystem->EndFrame( );
+
+ // get the data from the backbuffer and save to disk
+ // bitmap bits
+ unsigned char *pImageWhite = ( unsigned char * )malloc( Width * 4 * Height );
+
+ // Get Bits from the material system
+ pRenderContext->ReadPixels( 0, 0, Width, Height, pImageWhite, IMAGE_FORMAT_BGRA8888 );
+
+ unsigned char *pBlackPos = pImageBlack;
+ unsigned char *pWhitePos = pImageWhite;
+ for( int y = 0; y < Height; y++ )
+ {
+ for( int x = 0; x < Width; x++, pBlackPos += 4, pWhitePos += 4 )
+ {
+ if ( ( *( pBlackPos + 0 ) ) != ( *( pWhitePos + 0 ) ) || // blue
+ ( *( pBlackPos + 1 ) ) != ( *( pWhitePos + 1 ) ) || // green
+ ( *( pBlackPos + 2 ) ) != ( *( pWhitePos + 2 ) ) ) // red
+ {
+ unsigned char nBlueDiff = ( *( pBlackPos + 0 ) );
+ unsigned char nGreenDiff = ( *( pBlackPos + 1 ) );
+ unsigned char nRedDiff = ( *( pBlackPos + 2 ) );
+
+ unsigned char nMax = nBlueDiff;
+ if ( nGreenDiff > nMax )
+ {
+ nMax = nGreenDiff;
+ }
+ if ( nRedDiff > nMax )
+ {
+ nMax = nRedDiff;
+ }
+
+ *( pBlackPos + 3 ) = nMax;
+ }
+ else
+ {
+ *( pBlackPos + 3 ) = 0xff;
+ }
+ }
+ }
+
+ static char szPathedFileName[ _MAX_PATH ];
+ sprintf( szPathedFileName, "%s%s", OutputPath, AssetName );
+ V_SetExtension( szPathedFileName, ".tga", sizeof( szPathedFileName ) );
+
+ bool bResult = SaveTgaAndAddToP4( pImageBlack, IMAGE_FORMAT_BGRA8888, Width, Height, szPathedFileName );
+
+ free( pImageBlack );
+ free( pImageWhite );
+
+ if ( !bResult) return NULL;
+
+ if ( bSelectedOnly )
+ {
+ SaveCaps( szPathedFileName );
+ }
+
+ return szPathedFileName;
+}
+
+
+//-----------------------------------------------------------------------------
+// Will go through the asset browser and capture each visible item.
+//-----------------------------------------------------------------------------
+void CMDLPicker::CaptureScreenCaps( void )
+{
+ char temp[ 256 ];
+ TextEntry *pTempValue;
+ int width;
+ int height;
+ char szBathPath[ _MAX_PATH ];
+ Label *m_pOutputDirectory;
+
+ m_pOutputDirectory = ( Label * )m_pScreenCapsPage->FindChildByName( "OutputDirectory" );
+ m_pOutputDirectory->GetText( szBathPath, sizeof( szBathPath ) );
+
+ pTempValue = ( TextEntry * )m_pScreenCapsPage->FindChildByName( "WidthText" );
+ pTempValue->GetText( temp, sizeof( temp ) );
+ width = atoi( temp );
+ pTempValue = ( TextEntry * )m_pScreenCapsPage->FindChildByName( "HeightText" );
+ pTempValue->GetText( temp, sizeof( temp ) );
+ height = atoi( temp );
+
+ int PanelX, PanelY, PanelWidth, PanelHeight;
+ Color PanelColor;
+
+ Panel *pParent = m_pMDLPreview->GetParent();
+ m_pMDLPreview->GetPos( PanelX, PanelY );
+ m_pMDLPreview->GetSize( PanelWidth, PanelHeight );
+ PanelColor = m_pMDLPreview->GetBackgroundColor();
+
+ m_pMDLPreview->SetParent( ( vgui::Panel * )NULL );
+ m_pMDLPreview->SetPos( 0, 0 );
+ m_pMDLPreview->SetSize( width, height );
+
+ CColorPickerButton *m_pBackgroundColor;
+ m_pBackgroundColor = ( CColorPickerButton * )m_pScreenCapsPage->FindChildByName( "BackgroundColor" );
+
+ Color NewPanelColor = m_pBackgroundColor->GetColor();
+ NewPanelColor[3] = 0;
+ m_pMDLPreview->SetBackgroundColor( NewPanelColor );
+ ((VPanel *)m_pMDLPreview->GetVPanel())->Solve();
+
+ bool bSelectedOnly = false;
+ if ( input()->IsKeyDown( KEY_RCONTROL ) || input()->IsKeyDown( KEY_LCONTROL ) )
+ {
+ bSelectedOnly = true;
+ }
+
+ int nCount = m_AssetList.Count();
+ int nNumItems = 0;
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( m_pAssetBrowser->IsItemVisible( m_AssetList[ i ].m_nItemId ) &&
+ ( !bSelectedOnly || m_pAssetBrowser->IsItemSelected( m_AssetList[ i ].m_nItemId ) ) )
+ {
+ KeyValues *pItemKeyValues = m_pAssetBrowser->GetItem( m_AssetList[ i ].m_nItemId );
+ const char *pSelectedAsset = pItemKeyValues->GetString( "asset" );
+ int nModIndex = pItemKeyValues->GetInt( "modIndex" );
+
+ CaptureModel( nModIndex, pSelectedAsset, szBathPath, width, height, NewPanelColor, bSelectedOnly );
+ nNumItems++;
+ }
+ }
+
+ m_pMDLPreview->SetParent( pParent );
+ m_pMDLPreview->SetPos( PanelX, PanelY );
+ m_pMDLPreview->SetSize( PanelWidth, PanelHeight );
+ m_pMDLPreview->SetBackgroundColor( PanelColor );
+ ((VPanel *)m_pMDLPreview->GetVPanel())->Solve();
+
+ Label *m_pResults;
+
+ sprintf( temp, "Captured %d items", nNumItems );
+ m_pResults = ( Label * )m_pScreenCapsPage->FindChildByName( "CaptureResults" );
+ m_pResults->SetText( temp );
+}
+
+
+//-----------------------------------------------------------------------------
+// Stub for XBox360 compiles
+//-----------------------------------------------------------------------------
+#if defined( _X360 )
+const char *getenv( const char *varname )
+{
+ return NULL;
+}
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Writes two very simple .vmt file, one for the passed in asset,
+// and the other for <asset>_large.
+//-----------------------------------------------------------------------------
+void CMDLPicker::WriteBackbackVMTFiles( const char *pAssetName )
+{
+ const char *pVProject = getenv( "VPROJECT" );
+ if ( !pVProject )
+ return;
+
+ char pStrippedAssetName[ MAX_PATH ];
+ V_StripExtension( pAssetName, pStrippedAssetName, sizeof( pStrippedAssetName ) );
+ V_strcat_safe( pStrippedAssetName, GetOutputFileSuffix().Get() );
+
+ char pVMTFilename[ MAX_PATH ];
+ Q_snprintf( pVMTFilename, sizeof( pVMTFilename ), "%s\\materials\\backpack\\%s.vmt", pVProject, pStrippedAssetName );
+ Q_FixSlashes( pVMTFilename );
+
+ char pBaseTextureName[ MAX_PATH ];
+ Q_snprintf( pBaseTextureName, sizeof( pBaseTextureName ), "backpack\\%s", pStrippedAssetName );
+ Q_FixSlashes( pBaseTextureName );
+
+ {
+ CP4AutoEditAddFile autop4( pVMTFilename );
+ FileHandle_t fileHandle = g_pFullFileSystem->Open( pVMTFilename, "w" );
+ if ( fileHandle )
+ {
+
+ g_pFullFileSystem->FPrintf( fileHandle, "\"UnlitGeneric\"\n" );
+ g_pFullFileSystem->FPrintf( fileHandle, "{\n" );
+ g_pFullFileSystem->FPrintf( fileHandle, " \"$baseTexture\" \"%s\"\n", pBaseTextureName );
+ g_pFullFileSystem->FPrintf( fileHandle, " $translucent 1\n" );
+ g_pFullFileSystem->FPrintf( fileHandle, " $vertexcolor 1\n" );
+ g_pFullFileSystem->FPrintf( fileHandle, "}\n" );
+
+ g_pFullFileSystem->Close( fileHandle );
+ }
+ }
+
+ // now write the _large version
+ Q_snprintf( pVMTFilename, sizeof( pVMTFilename ), "%s\\materials\\backpack\\%s_large", pVProject, pStrippedAssetName );
+ V_SetExtension( pVMTFilename, ".vmt", sizeof( pVMTFilename ) );
+ Q_FixSlashes( pVMTFilename );
+
+ Q_snprintf( pBaseTextureName, sizeof( pBaseTextureName ), "backpack\\%s_large", pStrippedAssetName );
+ Q_FixSlashes( pBaseTextureName );
+
+ {
+ CP4AutoEditAddFile autop4( pVMTFilename );
+ FileHandle_t fileHandle = g_pFullFileSystem->Open( pVMTFilename, "w" );
+ if ( fileHandle )
+ {
+ g_pFullFileSystem->FPrintf( fileHandle, "\"UnlitGeneric\"\n" );
+ g_pFullFileSystem->FPrintf( fileHandle, "{\n" );
+ g_pFullFileSystem->FPrintf( fileHandle, " \"$baseTexture\" \"%s\"\n", pBaseTextureName );
+ g_pFullFileSystem->FPrintf( fileHandle, " $translucent 1\n" );
+ g_pFullFileSystem->FPrintf( fileHandle, "}\n" );
+
+ g_pFullFileSystem->Close( fileHandle );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+void *VTexFilesystemFactory( const char *pName, int *pReturnCode )
+{
+ return g_pFullFileSystem;
+}
+
+
+void* MdlPickerFSFactory( const char *pName, int *pReturnCode )
+{
+ if ( IsX360() )
+ return NULL;
+
+ if ( Q_stricmp( pName, FILESYSTEM_INTERFACE_VERSION ) == 0 )
+ return g_pFullFileSystem;
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates the two required icons for the TF2 store/backpack system
+//-----------------------------------------------------------------------------
+void CMDLPicker::GenerateBackpackIcons( void )
+{
+ if ( !g_pVTex )
+ return;
+
+ int width;
+ int height;
+
+ // find the index of the item we are currently viewing
+ int selectedItemIndex;
+ for ( selectedItemIndex = 0; selectedItemIndex < m_AssetList.Count(); ++selectedItemIndex )
+ {
+ if ( m_pAssetBrowser->IsItemVisible( m_AssetList[ selectedItemIndex ].m_nItemId ) &&
+ m_pAssetBrowser->IsItemSelected( m_AssetList[ selectedItemIndex ].m_nItemId ) )
+ {
+ break;
+ }
+ }
+
+ if ( selectedItemIndex >= m_AssetList.Count() )
+ return;
+
+ //
+ // Fetch and check environment variables
+ //
+ const char *pVContent = getenv( "VCONTENT" );
+ if ( !pVContent )
+ {
+ Error( "VCONTENT environment variable not set" );
+ return;
+ }
+
+ const char *pVMod = getenv( "VMOD" );
+ if ( !pVMod )
+ {
+ Error( "VMOD environment variable not set" );
+ return;
+ }
+
+ const char *pVProject = getenv( "VPROJECT" );
+ if ( !pVProject )
+ {
+ Error( "VPROJECT environment variable not set" );
+ return;
+ }
+
+ // extract the filename of the model
+ KeyValues *pItemKeyValues = m_pAssetBrowser->GetItem( m_AssetList[ selectedItemIndex ].m_nItemId );
+ const char *pSelectedAsset = pItemKeyValues->GetString( "asset" );
+
+ // set the P4 changelist label to refer to this set of icons
+ char pChangelistLabel[ MAX_PATH ];
+ V_strcpy_safe( pChangelistLabel, pSelectedAsset );
+ V_FileBase( pChangelistLabel, pChangelistLabel, sizeof( pChangelistLabel ) );
+ V_strcat_safe( pChangelistLabel, " Auto Checkout", sizeof( pChangelistLabel ) );
+ g_p4factory->SetOpenFileChangeList( pChangelistLabel );
+
+ // generate .VMT files for normal and _large icons
+ WriteBackbackVMTFiles( pSelectedAsset );
+
+ // store original state of model preview panel
+ int PanelX, PanelY, PanelWidth, PanelHeight;
+ Color PanelColor;
+
+ Panel *pParent = m_pMDLPreview->GetParent();
+ m_pMDLPreview->GetPos( PanelX, PanelY );
+ m_pMDLPreview->GetSize( PanelWidth, PanelHeight );
+ PanelColor = m_pMDLPreview->GetBackgroundColor();
+
+ // slam preview panel to desired TGA size for large icon, and write it out
+ width = 512;
+ height = 512;
+ m_pMDLPreview->SetParent( ( vgui::Panel * )NULL );
+ m_pMDLPreview->SetPos( 0, 0 );
+ m_pMDLPreview->SetSize( width, height );
+
+ CColorPickerButton *m_pBackgroundColor;
+ m_pBackgroundColor = ( CColorPickerButton * )m_pScreenCapsPage->FindChildByName( "BackgroundColor" );
+
+ Color NewPanelColor = m_pBackgroundColor->GetColor();
+ NewPanelColor[3] = 0;
+ m_pMDLPreview->SetBackgroundColor( NewPanelColor );
+ ((VPanel *)m_pMDLPreview->GetVPanel())->Solve();
+
+ char pLargeAssetName[ MAX_PATH ];
+ V_strcpy_safe( pLargeAssetName, pSelectedAsset );
+ V_StripExtension( pLargeAssetName, pLargeAssetName, ARRAYSIZE( pLargeAssetName ) );
+
+ CUtlString strExtention = GetOutputFileSuffix();
+ strExtention += "_large.mdl";
+
+ V_strcat_safe( pLargeAssetName, strExtention.String() );
+
+ char pOutputPath[ MAX_PATH ];
+ Q_snprintf( pOutputPath, sizeof( pOutputPath ), "%s\\%s\\materialsrc\\backpack\\", pVContent, pVMod );
+ Q_FixSlashes( pOutputPath );
+
+ int nModIndex = pItemKeyValues->GetInt( "modIndex" );
+ const char *pLargeTGAName = CaptureModel( nModIndex, pLargeAssetName, pOutputPath, width, height, NewPanelColor, true );
+ if ( !pLargeTGAName )
+ return;
+
+ // write corresponding .txt file with vtex options
+ char pVTexOptionsFileName[ MAX_PATH ];
+ V_strcpy_safe( pVTexOptionsFileName, pLargeTGAName );
+ V_SetExtension( pVTexOptionsFileName, ".txt", sizeof( pVTexOptionsFileName ) );
+
+ {
+ CP4AutoEditAddFile autop4( pVTexOptionsFileName );
+ FileHandle_t hVTexOptionsFile = g_pFullFileSystem->Open( pVTexOptionsFileName, "w" );
+ if ( hVTexOptionsFile )
+ {
+ g_pFullFileSystem->FPrintf( hVTexOptionsFile, "nomip 1\n" );
+ g_pFullFileSystem->FPrintf( hVTexOptionsFile, "nolod 1\n" );
+ g_pFullFileSystem->Close( hVTexOptionsFile );
+ }
+ }
+
+ // !KLUDGE! Everybody I've talked to says that vtex is *supposed* to
+ // use VPROJECT. But it doesn't. I don't think I can safely change vtex
+ // without breaking tons of stuff. So just force the output directory.
+ // Determine the proper output directory based on VPROJECT
+ char pOutputPathGame[ MAX_PATH ];
+ Q_strncpy( pOutputPathGame, pVProject, sizeof( pOutputPathGame ) );
+ Q_StripTrailingSlash( pOutputPathGame );
+ Q_strncat( pOutputPathGame, "/materials/", sizeof( pOutputPathGame ) );
+ const char *pBackpack = Q_stristr( pLargeTGAName, "backpack" );
+ if ( pBackpack )
+ {
+ Q_strncat( pOutputPathGame, pBackpack, sizeof( pOutputPathGame ) );
+ Q_StripFilename( pOutputPathGame );
+ }
+ Q_FixSlashes( pOutputPathGame );
+
+ // run vtex on the TGA and .txt file to create .VTF and add it to our Perforce changelist
+ char *vTexArgv[64];
+ int vTexArgc = 0;
+ vTexArgv[ vTexArgc++ ] = "";
+ vTexArgv[ vTexArgc++ ] = "-quiet";
+ vTexArgv[ vTexArgc++ ] = "-UseStandardError";
+ vTexArgv[ vTexArgc++ ] = "-WarningsAsErrors";
+ vTexArgv[ vTexArgc++ ] = "-p4skip";
+ vTexArgv[ vTexArgc++ ] = "-outdir";
+ vTexArgv[ vTexArgc++ ] = pOutputPathGame;
+ vTexArgv[ vTexArgc++ ] = (char *)pLargeTGAName;
+
+ g_pVTex->VTex( MdlPickerFSFactory, pOutputPathGame, vTexArgc, vTexArgv );
+
+ // Generale small TGA name, by removing the "large" part
+ char pSmallTGAName[ MAX_PATH ];
+ strcpy( pSmallTGAName, pLargeTGAName );
+ char *_large = Q_stristr( pSmallTGAName, "_large");
+ Assert(_large);
+ strcpy(_large, _large+6);
+
+ // Load up the large icon
+ int nCheckWidth, nCheckHeight;
+ ImageFormat largeFmt;
+ float gamma;
+ CUtlBuffer largeTGAFileData;
+ CUtlMemory<unsigned char> largeTGAImageData;
+
+ if ( !g_pFullFileSystem->ReadFile( pLargeTGAName, NULL, largeTGAFileData )
+ || !TGALoader::GetInfo( largeTGAFileData, &nCheckWidth, &nCheckHeight, &largeFmt, &gamma )
+ || nCheckWidth != width || nCheckHeight != height )
+ {
+ Error( "Failed to reload image header %s", pLargeTGAName );
+ Assert( false );
+ return;
+ }
+
+ largeTGAFileData.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
+ if ( !TGALoader::LoadRGBA8888( largeTGAFileData, largeTGAImageData, nCheckWidth, nCheckHeight )
+ || nCheckWidth != width || nCheckHeight != height )
+ {
+ Error( "Failed to reload image data %s", pLargeTGAName );
+ Assert( false );
+ return;
+ }
+
+ //
+ // Perform a downsample. This is better than just re-rendering at the smaller size,
+ // which essentially just point-samples the image.
+ //
+ CUtlMemory<unsigned char> smallTGAImageData;
+ const int kSmallSize = 128;
+ smallTGAImageData.EnsureCapacity(kSmallSize*kSmallSize*4);
+ ImageLoader::ResampleInfo_t resampleInfo;
+ resampleInfo.m_nSrcWidth = width;
+ resampleInfo.m_nSrcHeight = height;
+ resampleInfo.m_flSrcGamma = gamma;
+ resampleInfo.m_pSrc = (unsigned char *)largeTGAImageData.Base();
+
+ resampleInfo.m_nDestWidth = kSmallSize;
+ resampleInfo.m_nDestHeight = kSmallSize;
+ resampleInfo.m_flDestGamma = gamma;
+ resampleInfo.m_pDest = (unsigned char *)smallTGAImageData.Base();
+
+ resampleInfo.m_nFlags = ImageLoader::RESAMPLE_CLAMPS | ImageLoader::RESAMPLE_CLAMPT;
+ //resampleInfo.m_nFlags |= ImageLoader::RESAMPLE_NICE_FILTER; // Turn this off. It has some sort of edge enhancement or something. Causes edges to ring.
+
+ if ( !ImageLoader::ResampleRGBA8888( resampleInfo ) )
+ {
+ Error( "Failed to resample %s", pLargeTGAName );
+ Assert( false );
+ return;
+ }
+
+ // Save it
+ if ( !SaveTgaAndAddToP4( resampleInfo.m_pDest, IMAGE_FORMAT_RGBA8888, resampleInfo.m_nDestWidth, resampleInfo.m_nDestHeight, pSmallTGAName ) )
+ {
+ return;
+ }
+
+ // Save the .cfg file.
+ SaveCaps( pSmallTGAName );
+
+ // write corresponding .txt file with vtex options
+ V_strcpy_safe( pVTexOptionsFileName, pSmallTGAName );
+ V_SetExtension( pVTexOptionsFileName, ".txt", sizeof( pVTexOptionsFileName ) );
+
+ {
+ CP4AutoEditAddFile autop4( pVTexOptionsFileName );
+ FileHandle_t hVTexOptionsFile = g_pFullFileSystem->Open( pVTexOptionsFileName, "w" );
+ if ( hVTexOptionsFile )
+ {
+ g_pFullFileSystem->FPrintf( hVTexOptionsFile, "nomip 1\n" );
+ g_pFullFileSystem->FPrintf( hVTexOptionsFile, "nolod 1\n" );
+ g_pFullFileSystem->Close( hVTexOptionsFile );
+ }
+ }
+
+ // run vtex on the TGA and .txt file to create .VTF and add it to our Perforce changelist
+ vTexArgc = 0;
+ vTexArgv[ vTexArgc++ ] = "";
+ vTexArgv[ vTexArgc++ ] = "-quiet";
+ vTexArgv[ vTexArgc++ ] = "-UseStandardError";
+ vTexArgv[ vTexArgc++ ] = "-WarningsAsErrors";
+ vTexArgv[ vTexArgc++ ] = "-p4skip";
+ vTexArgv[ vTexArgc++ ] = "-outdir";
+ vTexArgv[ vTexArgc++ ] = pOutputPathGame;
+ vTexArgv[ vTexArgc++ ] = (char *)pSmallTGAName;
+ g_pVTex->VTex( MdlPickerFSFactory, pOutputPathGame, vTexArgc, vTexArgv );
+
+
+ // restore the preview panel to its original state
+ m_pMDLPreview->SetParent( pParent );
+ m_pMDLPreview->SetPos( PanelX, PanelY );
+ m_pMDLPreview->SetSize( PanelWidth, PanelHeight );
+ m_pMDLPreview->SetBackgroundColor( PanelColor );
+ ((VPanel *)m_pMDLPreview->GetVPanel())->Solve();
+}
+
+
+CUtlString CMDLPicker::GetOutputFileSuffix()
+{
+ char temp[256];
+ TextEntry *pTempValue = ( TextEntry * )m_pScreenCapsPage->FindChildByName( "SuffixText" );
+ if ( pTempValue )
+ {
+ pTempValue->GetText( temp, sizeof( temp ) );
+ }
+ return temp;
+}
+
+
+//-----------------------------------------------------------------------------
+// Saves the screen cap information and camera position
+//-----------------------------------------------------------------------------
+void CMDLPicker::SaveCaps( const char *szFileName )
+{
+ char temp[ _MAX_PATH ];
+
+ KeyValues *CaptureData = new KeyValues( "ScreenCaps" );
+
+ Vector vecPos;
+ QAngle angDir;
+ m_pMDLPreview->GetCameraPositionAndAngles( vecPos, angDir );
+ sprintf( temp, "%g %g %g", vecPos.x, vecPos.y, vecPos.z );
+ CaptureData->SetString( "CameraPosition", temp );
+ sprintf( temp, "%g %g %g", angDir.x, angDir.y, angDir.z );
+ CaptureData->SetString( "CameraAngles", temp );
+
+ Vector vecOffset;
+ m_pMDLPreview->GetCameraOffset( vecOffset );
+ sprintf( temp, "%g %g %g", vecOffset.x, vecOffset.y, vecOffset.z );
+ CaptureData->SetString( "CameraOffset", temp );
+
+ CColorPickerButton *m_pBackgroundColor;
+ m_pBackgroundColor = ( CColorPickerButton * )m_pScreenCapsPage->FindChildByName( "BackgroundColor" );
+ Color color = m_pBackgroundColor->GetColor();
+
+ sprintf( temp, "%d %d %d %d", color.r(), color.g(), color.b(), color.a() );
+ CaptureData->SetString( "BackgroundColor", temp );
+
+ TextEntry *pTempValue;
+ pTempValue = ( TextEntry * )m_pScreenCapsPage->FindChildByName( "WidthText" );
+ pTempValue->GetText( temp, sizeof( temp ) );
+ CaptureData->SetString( "Width", temp );
+
+ pTempValue = ( TextEntry * )m_pScreenCapsPage->FindChildByName( "HeightText" );
+ pTempValue->GetText( temp, sizeof( temp ) );
+ CaptureData->SetString( "Height", temp );
+
+ vgui::CheckButton *pToggle;
+ pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName( "NoGround" );
+ CaptureData->SetInt( "NoGround", pToggle->IsSelected() ? 1 : 0 );
+
+ pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName( "Collision" );
+ CaptureData->SetInt( "Collision", pToggle->IsSelected() ? 1 : 0 );
+
+ pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName( "Wireframe" );
+ CaptureData->SetInt( "Wifeframe", pToggle->IsSelected() ? 1 : 0 );
+
+ pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName( "LockView" );
+ CaptureData->SetInt( "LockView", pToggle->IsSelected() ? 1 : 0 );
+
+ pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName( "LookAtCamera" );
+ CaptureData->SetInt( "LookAtCamera", pToggle->IsSelected() ? 1 : 0 );
+
+ for( int i = 1; i < MAX_SELECTED_MODELS; i++ )
+ {
+ if ( m_hSelectedMDL[ i ] != MDLHANDLE_INVALID )
+ {
+ const char *MergedModelName = vgui::MDLCache()->GetModelName( m_hSelectedMDL[ i ] );
+ sprintf( temp, "Merged_%d", i );
+ CaptureData->SetString( temp, MergedModelName );
+ }
+ }
+
+ if ( szFileName != NULL )
+ {
+ strcpy( temp, szFileName );
+ V_SetExtension( temp, ".cfg", sizeof( temp ) );
+ }
+ else
+ {
+ Label *m_pOutputDirectory = ( Label * )m_pScreenCapsPage->FindChildByName( "OutputDirectory" );
+ m_pOutputDirectory->GetText( temp, sizeof( temp ) );
+
+ strcat( temp, "ScreenCaps.cfg" );
+ }
+
+ CaptureData->SaveToFile( g_pFullFileSystem, temp );
+ CP4AutoAddFile autop4( temp );
+}
+
+
+//-----------------------------------------------------------------------------
+// Restores the screen cap information and camera position
+//-----------------------------------------------------------------------------
+bool CMDLPicker::RestoreCaps( const char *szFileName )
+{
+ char temp[ _MAX_PATH ];
+
+ if ( szFileName != NULL )
+ {
+ strcpy( temp, szFileName );
+ V_SetExtension( temp, ".cfg", sizeof( temp ) );
+ }
+ else
+ {
+ Label *m_pOutputDirectory = ( Label * )m_pScreenCapsPage->FindChildByName( "OutputDirectory" );
+ m_pOutputDirectory->GetText( temp, sizeof( temp ) );
+ strcat( temp, "ScreenCaps.cfg" );
+ }
+
+ KeyValues *CaptureData = new KeyValues( "ScreenCaps" );
+
+ if ( !CaptureData->LoadFromFile( g_pFullFileSystem, temp ) )
+ {
+ return false;
+ }
+
+ Vector vecPos;
+ QAngle angDir;
+ Vector vecOffset;
+ sscanf( CaptureData->GetString( "CameraPosition" ), "%g %g %g", &vecPos.x, &vecPos.y, &vecPos.z );
+ sscanf( CaptureData->GetString( "CameraAngles" ), "%g %g %g", &angDir.x, &angDir.y, &angDir.z );
+ sscanf( CaptureData->GetString( "CameraOffset" ), "%g %g %g", &vecOffset.x, &vecOffset.y, &vecOffset.z );
+
+ m_pMDLPreview->SetCameraOffset( vecOffset );
+ m_pMDLPreview->SetCameraPositionAndAngles( vecPos, angDir );
+
+ CColorPickerButton *m_pBackgroundColor;
+ int r, g, b, a;
+ m_pBackgroundColor = ( CColorPickerButton * )m_pScreenCapsPage->FindChildByName( "BackgroundColor" );
+ sscanf( CaptureData->GetString( "BackgroundColor" ), "%d %d %d %d", &r, &g, &b, &a );
+ m_pBackgroundColor->SetColor( r, g, b, a );
+
+ TextEntry *pTempValue;
+ pTempValue = ( TextEntry * )m_pScreenCapsPage->FindChildByName( "WidthText" );
+ pTempValue->SetText( CaptureData->GetString( "Width" ) );
+
+ pTempValue = ( TextEntry * )m_pScreenCapsPage->FindChildByName( "HeightText" );
+ pTempValue->SetText( CaptureData->GetString( "Height" ) );
+
+
+ vgui::CheckButton *pToggle;
+ pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName( "NoGround" );
+ pToggle->SetSelected( ( CaptureData->GetInt( "NoGround" ) == 1 ) );
+
+ pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName( "Collision" );
+ pToggle->SetSelected( ( CaptureData->GetInt( "Collision" ) == 1 ) );
+
+ pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName( "Wireframe" );
+ pToggle->SetSelected( ( CaptureData->GetInt( "Wireframe" ) == 1 ) );
+
+ pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName( "LockView" );
+ pToggle->SetSelected( ( CaptureData->GetInt( "LockView" ) == 1 ) );
+
+ pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName( "LookAtCamera" );
+ pToggle->SetSelected( ( CaptureData->GetInt( "LookAtCamera" ) == 1 ) );
+
+ for( int i = 1; i < MAX_SELECTED_MODELS; i++ )
+ {
+ sprintf( temp, "Merged_%d", i );
+ const char *MergedModelName = CaptureData->GetString( temp, NULL );
+ if ( MergedModelName )
+ {
+ SelectMDL( MergedModelName, false, i );
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: rebuilds the list of activities
+//-----------------------------------------------------------------------------
+void CMDLPicker::RefreshActivitiesAndSequencesList()
+{
+ m_pActivitiesList->RemoveAll();
+ m_pSequencesList->RemoveAll();
+ m_pMDLPreview->SetSequence( 0 );
+
+ if ( m_hSelectedMDL[ 0 ] == MDLHANDLE_INVALID )
+ {
+ m_pActivitiesList->SetEmptyListText("No .MDL file currently selected");
+ m_pSequencesList->SetEmptyListText("No .MDL file currently selected");
+ return;
+ }
+
+ m_pActivitiesList->SetEmptyListText(".MDL file contains no activities");
+ m_pSequencesList->SetEmptyListText(".MDL file contains no sequences");
+
+ studiohdr_t *hdr = vgui::MDLCache()->GetStudioHdr( m_hSelectedMDL[ 0 ] );
+
+ CUtlDict<int, unsigned short> activityNames( true, 0, hdr->GetNumSeq() );
+
+ for (int j = 0; j < hdr->GetNumSeq(); j++)
+ {
+ if ( /*g_viewerSettings.showHidden ||*/ !(hdr->pSeqdesc(j).flags & STUDIO_HIDDEN))
+ {
+ const char *pActivityName = hdr->pSeqdesc(j).pszActivityName();
+ if ( pActivityName && pActivityName[0] )
+ {
+ // Multiple sequences can have the same activity name; only add unique activity names
+ if ( activityNames.Find( pActivityName ) == activityNames.InvalidIndex() )
+ {
+ KeyValues *pkv = new KeyValues("node", "activity", pActivityName );
+ int nItemID = m_pActivitiesList->AddItem( pkv, 0, false, false );
+
+ KeyValues *pDrag = new KeyValues( "drag", "text", pActivityName );
+ pDrag->SetString( "texttype", "activityName" );
+ pDrag->SetString( "mdl", vgui::MDLCache()->GetModelName( m_hSelectedMDL[ 0 ] ) );
+ m_pActivitiesList->SetItemDragData( nItemID, pDrag );
+
+ activityNames.Insert( pActivityName, j );
+ }
+ }
+
+ const char *pSequenceName = hdr->pSeqdesc(j).pszLabel();
+ if ( pSequenceName && pSequenceName[0] )
+ {
+ KeyValues *pkv = new KeyValues("node", "sequence", pSequenceName);
+ int nItemID = m_pSequencesList->AddItem( pkv, 0, false, false );
+
+ KeyValues *pDrag = new KeyValues( "drag", "text", pSequenceName );
+ pDrag->SetString( "texttype", "sequenceName" );
+ pDrag->SetString( "mdl", vgui::MDLCache()->GetModelName( m_hSelectedMDL[ 0 ] ) );
+ m_pSequencesList->SetItemDragData( nItemID, pDrag );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// A MDL was selected
+//-----------------------------------------------------------------------------
+void CMDLPicker::OnSelectedAssetPicked( const char *pMDLName )
+{
+ char pRelativePath[MAX_PATH];
+
+ int nSelectSecondary = -1;
+ if ( input()->IsKeyDown( KEY_RCONTROL ) || input()->IsKeyDown( KEY_LCONTROL ) )
+ {
+ nSelectSecondary = 0;
+ }
+ else if ( input()->IsMouseDown(MOUSE_RIGHT) )
+ {
+ nSelectSecondary = 1;
+ }
+
+ if ( pMDLName )
+ {
+ Q_snprintf( pRelativePath, sizeof(pRelativePath), "models\\%s", pMDLName );
+ SelectMDL( pRelativePath, true, nSelectSecondary );
+ }
+ else
+ {
+ SelectMDL( NULL, true, nSelectSecondary );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Allows external apps to select a MDL
+//-----------------------------------------------------------------------------
+void CMDLPicker::SelectMDL( const char *pRelativePath, bool bDoLookAt, int nSelectSecondary )
+{
+ MDLHandle_t hSelectedMDL = pRelativePath ? vgui::MDLCache()->FindMDL( pRelativePath ) : MDLHANDLE_INVALID;
+ int index = ( nSelectSecondary > 0 ? nSelectSecondary : 0 );
+
+ // We didn't change models after all...
+ if ( hSelectedMDL == m_hSelectedMDL[ index ] )
+ {
+ // vgui::MDLCache()->FindMDL adds a reference by default we don't use, release it again
+ if ( hSelectedMDL != MDLHANDLE_INVALID )
+ {
+ vgui::MDLCache()->Release( hSelectedMDL );
+ }
+ return;
+ }
+
+ m_hSelectedMDL[ index ] = hSelectedMDL;
+
+ if ( vgui::MDLCache()->IsErrorModel( m_hSelectedMDL[ index ] ) )
+ {
+ m_hSelectedMDL[ index ] = MDLHANDLE_INVALID;
+ }
+ if ( nSelectSecondary != -1 )
+ {
+ m_pMDLPreview->ClearMergeMDLs();
+ for( int i = 1; i < MAX_SELECTED_MODELS; i++ )
+ {
+ if ( i != index )
+ {
+ m_hSelectedMDL[ i ] = MDLHANDLE_INVALID;
+ }
+ }
+ }
+
+ if ( index > 0 )
+ {
+ m_pMDLPreview->SetMergeMDL( m_hSelectedMDL[ index ] );
+ }
+ else
+ {
+ m_pMDLPreview->SetMDL( m_hSelectedMDL[ index ] );
+
+ if ( bDoLookAt )
+ {
+ m_pMDLPreview->LookAtMDL();
+ }
+
+ if ( m_nFlags & ( PAGE_SKINS ) )
+ {
+ UpdateSkinsList();
+ }
+
+ if ( m_nFlags & ( PAGE_INFO ) )
+ {
+ UpdateInfoTab();
+ }
+
+ if ( m_nFlags & (PAGE_ACTIVITIES|PAGE_SEQUENCES) )
+ {
+ RefreshActivitiesAndSequencesList();
+ }
+ }
+
+ // vgui::MDLCache()->FindMDL adds a reference by default we don't use, release it again
+ if ( hSelectedMDL != MDLHANDLE_INVALID )
+ {
+ vgui::MDLCache()->Release( hSelectedMDL );
+ }
+
+ PostActionSignal( new KeyValues( "MDLPreviewChanged", "mdl", pRelativePath ? pRelativePath : "" ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: updates revision view on a file being selected
+//-----------------------------------------------------------------------------
+void CMDLPicker::OnCheckButtonChecked(KeyValues *kv)
+{
+// RefreshMDLList();
+ BaseClass::OnCheckButtonChecked( kv );
+ RefreshRenderSettings();
+}
+
+
+void CMDLPicker::GetSelectedMDLName( char *pBuffer, int nMaxLen )
+{
+ Assert( nMaxLen > 0 );
+ if ( GetSelectedAssetCount() > 0 )
+ {
+ Q_snprintf( pBuffer, nMaxLen, "models\\%s", GetSelectedAsset( ) );
+ }
+ else
+ {
+ pBuffer[0] = 0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Gets the selected activity/sequence
+//-----------------------------------------------------------------------------
+int CMDLPicker::GetSelectedPage( )
+{
+ if ( m_pSequencesPage && ( m_pViewsSheet->GetActivePage() == m_pSequencesPage ) )
+ return PAGE_SEQUENCES;
+
+ if ( m_pActivitiesPage && ( m_pViewsSheet->GetActivePage() == m_pActivitiesPage ) )
+ return PAGE_ACTIVITIES;
+
+ return PAGE_NONE;
+}
+
+const char *CMDLPicker::GetSelectedSequenceName()
+{
+ if ( !m_pSequencesPage )
+ return NULL;
+
+ int nIndex = m_pSequencesList->GetSelectedItem( 0 );
+ if ( nIndex >= 0 )
+ {
+ KeyValues *pkv = m_pSequencesList->GetItem( nIndex );
+ return pkv->GetString( "sequence", NULL );
+ }
+
+ return NULL;
+}
+
+const char *CMDLPicker::GetSelectedActivityName()
+{
+ if ( !m_pActivitiesPage )
+ return NULL;
+
+ int nIndex = m_pActivitiesList->GetSelectedItem( 0 );
+ if ( nIndex >= 0 )
+ {
+ KeyValues *pkv = m_pActivitiesList->GetItem( nIndex );
+ return pkv->GetString( "activity", NULL );
+ }
+ return NULL;
+}
+
+int CMDLPicker::GetSelectedSkin()
+{
+ if ( !m_pSkinsPage )
+ return 0;
+
+ int nIndex = m_pSkinsList->GetSelectedItem( 0 );
+ if ( nIndex >= 0 )
+ {
+ return nIndex;
+ }
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Plays the selected activity
+//-----------------------------------------------------------------------------
+void CMDLPicker::SelectActivity( const char *pActivityName )
+{
+ studiohdr_t *pstudiohdr = vgui::MDLCache()->GetStudioHdr( m_hSelectedMDL[ 0 ] );
+ for ( int i = 0; i < pstudiohdr->GetNumSeq(); i++ )
+ {
+ mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( i );
+ if ( stricmp( seqdesc.pszActivityName(), pActivityName ) == 0 )
+ {
+ // FIXME: Add weighted sequence selection logic?
+ m_pMDLPreview->SetSequence( i );
+ break;
+ }
+ }
+
+ PostActionSignal( new KeyValues( "SequenceSelectionChanged", "activity", pActivityName ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Plays the selected sequence
+//-----------------------------------------------------------------------------
+void CMDLPicker::SelectSequence( const char *pSequenceName )
+{
+ studiohdr_t *pstudiohdr = vgui::MDLCache()->GetStudioHdr( m_hSelectedMDL[ 0 ] );
+ for (int i = 0; i < pstudiohdr->GetNumSeq(); i++)
+ {
+ mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( i );
+ if ( !Q_stricmp( seqdesc.pszLabel(), pSequenceName ) )
+ {
+ m_pMDLPreview->SetSequence( i );
+ break;
+ }
+ }
+
+ PostActionSignal( new KeyValues( "SequenceSelectionChanged", "sequence", pSequenceName ) );
+}
+
+void CMDLPicker::SelectSkin( int nSkin )
+{
+ m_pMDLPreview->SetSkin( nSkin );
+ PostActionSignal( new KeyValues( "SkinSelectionChanged", "skin", nSkin));
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Updates preview when an item is selected
+//-----------------------------------------------------------------------------
+void CMDLPicker::OnItemSelected( KeyValues *kv )
+{
+ Panel *pPanel = (Panel *)kv->GetPtr("panel", NULL);
+ if ( m_pSequencesList && (pPanel == m_pSequencesList ) )
+ {
+ const char *pSequenceName = GetSelectedSequenceName();
+ if ( pSequenceName )
+ {
+ SelectSequence( pSequenceName );
+ }
+ return;
+ }
+
+ if ( m_pActivitiesList && ( pPanel == m_pActivitiesList ) )
+ {
+ const char *pActivityName = GetSelectedActivityName();
+ if ( pActivityName )
+ {
+ SelectActivity( pActivityName );
+ }
+ return;
+ }
+
+ if ( m_pSkinsList && ( pPanel == m_pSkinsList ) )
+ {
+ int nSelectedSkin = GetSelectedSkin();
+ SelectSkin( nSelectedSkin );
+
+ return;
+ }
+
+ BaseClass::OnItemSelected( kv );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when a page is shown
+//-----------------------------------------------------------------------------
+void CMDLPicker::OnPageChanged( )
+{
+ if ( m_pSequencesPage && ( m_pViewsSheet->GetActivePage() == m_pSequencesPage ) )
+ {
+ m_pSequencesList->RequestFocus();
+
+ const char *pSequenceName = GetSelectedSequenceName();
+
+ if ( pSequenceName )
+ {
+ SelectSequence( pSequenceName );
+ }
+ return;
+ }
+
+ if ( m_pActivitiesPage && ( m_pViewsSheet->GetActivePage() == m_pActivitiesPage ) )
+ {
+ m_pActivitiesList->RequestFocus();
+
+ const char *pActivityName = GetSelectedActivityName();
+
+ if ( pActivityName )
+ {
+ SelectActivity( pActivityName );
+ }
+ return;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Purpose: Modal picker frame
+//
+//-----------------------------------------------------------------------------
+CMDLPickerFrame::CMDLPickerFrame( vgui::Panel *pParent, const char *pTitle, int nFlags ) :
+ BaseClass( pParent )
+{
+ SetAssetPicker( new CMDLPicker( this, nFlags ) );
+ LoadControlSettingsAndUserConfig( "resource/mdlpickerframe.res" );
+ SetTitle( pTitle, false );
+}
+
+CMDLPickerFrame::~CMDLPickerFrame()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Allows external apps to select a MDL
+//-----------------------------------------------------------------------------
+void CMDLPickerFrame::SelectMDL( const char *pRelativePath )
+{
+ static_cast<CMDLPicker*>( GetAssetPicker() )->SelectMDL( pRelativePath );
+}
+
+int CMDLPicker::UpdateSkinsList()
+{
+ int nNumSkins = 0;
+
+ if ( m_pSkinsList )
+ {
+ m_pSkinsList->RemoveAll();
+
+ studiohdr_t *hdr = vgui::MDLCache()->GetStudioHdr( m_hSelectedMDL[ 0 ] );
+ if ( hdr )
+ {
+ nNumSkins = hdr->numskinfamilies;
+ for ( int i = 0; i < nNumSkins; i++ )
+ {
+ char skinText[25] = "";
+ sprintf( skinText, "skin%i", i );
+ KeyValues *pkv = new KeyValues("node", "skin", skinText );
+ m_pSkinsList->AddItem( pkv, 0, false, false );
+ }
+ }
+ }
+
+ return nNumSkins;
+}
+
+void CMDLPicker::UpdateInfoTab()
+{
+ studiohdr_t *hdr = vgui::MDLCache()->GetStudioHdr( m_hSelectedMDL[ 0 ] );
+ if ( !hdr )
+ return;
+
+ int nMass = hdr->mass;
+ Panel *pTempPanel = m_pInfoPage->FindChildByName("MassValue");
+ char massBuff[10];
+ Q_snprintf( massBuff, 10, "%d", nMass );
+ ((vgui::Label *)pTempPanel)->SetText( massBuff );
+ bool bIsStatic = hdr->flags & STUDIOHDR_FLAGS_STATIC_PROP;
+ bool bIsPhysics = false;
+ const char* buf = hdr->KeyValueText();
+ Label * pTempLabel = (Label *)m_pInfoPage->FindChildByName("StaticText");
+ pTempLabel->SetVisible( false );
+ if( buf )
+ {
+ buf = Q_strstr( buf, "prop_data" );
+ if ( buf )
+ {
+ int iPropDataCount = UpdatePropDataList( buf, bIsStatic );
+ if( iPropDataCount )
+ {
+ bIsPhysics = true;
+ }
+ }
+ else
+ {
+ m_pPropDataList->RemoveAll();
+ }
+ }
+ else
+ {
+ m_pPropDataList->RemoveAll();
+ }
+
+ CheckButton * pTempCheck = (CheckButton *)m_pInfoPage->FindChildByName("StaticObject");
+ pTempCheck->SetCheckButtonCheckable( true );
+ pTempCheck->SetSelected( bIsStatic );
+ pTempCheck->SetCheckButtonCheckable( false );
+ pTempCheck = (CheckButton *)m_pInfoPage->FindChildByName("PhysicsObject");
+ pTempCheck->SetCheckButtonCheckable( true );
+ pTempCheck->SetSelected( bIsPhysics );
+ pTempCheck->SetCheckButtonCheckable( false );
+ pTempCheck = (CheckButton *)m_pInfoPage->FindChildByName("DynamicObject");
+ pTempCheck->SetCheckButtonCheckable( true );
+ pTempCheck->SetSelected( !bIsPhysics );
+ pTempCheck->SetCheckButtonCheckable( false );
+
+
+}
+
+int CMDLPicker::UpdatePropDataList( const char* pszPropData, bool &bIsStatic )
+{
+ int iCount = 0;
+
+ if ( m_pPropDataList )
+ {
+ m_pPropDataList->RemoveAll();
+
+ const char * endPropData = strchr( pszPropData, '}' );
+ char keyText[255] = "";
+ char valueText[255] = "";
+ const char *beginChunk = strchr( pszPropData, '\"' );
+ if ( !beginChunk )
+ {
+ return 0;
+ }
+ beginChunk++;
+ const char *endChunk = strchr( beginChunk, '\"' );
+ while( endChunk )
+ {
+ Q_memcpy( keyText, beginChunk, endChunk - beginChunk );
+ beginChunk = endChunk + 1;
+ beginChunk = strchr( beginChunk, '\"' ) + 1;
+ endChunk = strchr( beginChunk, '\"' );
+ Q_memcpy( valueText, beginChunk, endChunk - beginChunk );
+ if( !Q_strcmp( keyText, "allowstatic" ) && !Q_strcmp( valueText , "1" ) )
+ {
+ if ( !bIsStatic )
+ {
+ Label * pTempLabel = (Label *)m_pInfoPage->FindChildByName("StaticText");
+ pTempLabel->SetVisible( true );
+ }
+ bIsStatic &= true;
+ }
+ KeyValues *pkv = new KeyValues("node", "key", keyText, "value", valueText );
+ m_pPropDataList->AddItem( pkv, 0, false, false );
+ Q_memset( keyText, 0, 255 );
+ Q_memset( valueText, 0, 255 );
+ iCount++;
+ beginChunk = endChunk + 1;
+ beginChunk = strchr( beginChunk, '\"' );
+ if ( !beginChunk || beginChunk > endPropData )
+ {
+ return iCount;
+ }
+ beginChunk++;
+ endChunk = strchr( beginChunk, '\"' );
+ }
+ }
+ return iCount;
+}
diff --git a/vgui2/matsys_controls/mdlsequencepicker.cpp b/vgui2/matsys_controls/mdlsequencepicker.cpp
new file mode 100644
index 0000000..8e4443b
--- /dev/null
+++ b/vgui2/matsys_controls/mdlsequencepicker.cpp
@@ -0,0 +1,539 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "matsys_controls/mdlsequencepicker.h"
+#include "tier1/KeyValues.h"
+#include "tier1/utldict.h"
+#include "datacache/imdlcache.h"
+#include "filesystem.h"
+#include "studio.h"
+#include "vgui/IVGui.h"
+#include "vgui/Cursor.h"
+#include "vgui/ISurface.h"
+#include "vgui_controls/Splitter.h"
+#include "vgui_controls/ComboBox.h"
+#include "vgui_controls/PropertySheet.h"
+#include "vgui_controls/PropertyPage.h"
+#include "vgui_controls/ToolWindow.h"
+#include "vgui_controls/Button.h"
+#include "matsys_controls/gamefiletreeview.h"
+#include "matsys_controls/matsyscontrols.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+using namespace vgui;
+
+
+//-----------------------------------------------------------------------------
+//
+// MDL Sequence Picker
+//
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CMDLSequencePicker::CMDLSequencePicker( vgui::Panel *pParent ) : BaseClass(pParent, "MDLSequencePicker"), m_Images(false)
+{
+ vgui::ivgui()->AddTickSignal( GetVPanel() );
+
+ m_hSelectedMDL = MDLHANDLE_INVALID;
+
+ // Horizontal splitter for mdls
+ m_pMDLSplitter = new Splitter( this, "MDLSplitter", SPLITTER_MODE_VERTICAL, 1 );
+
+ vgui::Panel *pSplitterLeftSide = m_pMDLSplitter->GetChild( 0 );
+ vgui::Panel *pSplitterRightSide = m_pMDLSplitter->GetChild( 1 );
+
+ // filter selection
+ m_pFilterList = new ComboBox( pSplitterLeftSide, "FilterList", 16, true );
+ m_pFilterList->AddActionSignalTarget( this );
+
+ // file browser tree controls
+ m_pFileTree = new CGameFileTreeView( pSplitterLeftSide, "FileTree", "All .MDLs", "models", "mdl" );
+
+ // build our list of images
+ m_Images.AddImage( scheme()->GetImage( "resource/icon_folder", false ) );
+ m_Images.AddImage( scheme()->GetImage( "resource/icon_folder_selected", false ) );
+ m_Images.AddImage( scheme()->GetImage( "resource/icon_file", false ) );
+ m_pFileTree->SetImageList( &m_Images, false );
+ m_pFileTree->AddActionSignalTarget( this );
+
+ // property sheet - revisions, changes, etc.
+ m_pSequenceSplitter = new Splitter( pSplitterRightSide, "SequenceSplitter", SPLITTER_MODE_HORIZONTAL, 1 );
+
+ vgui::Panel *pSplitterTopSide = m_pSequenceSplitter->GetChild( 0 );
+ vgui::Panel *pSplitterBottomSide = m_pSequenceSplitter->GetChild( 1 );
+
+ // MDL preview
+ m_pMDLPreview = new CMDLPanel( pSplitterTopSide, "MDLPreview" );
+ SetSkipChildDuringPainting( m_pMDLPreview );
+
+ m_pViewsSheet = new vgui::PropertySheet( pSplitterBottomSide, "ViewsSheet" );
+ m_pViewsSheet->AddActionSignalTarget( this );
+
+ // sequences
+ m_pSequencesPage = new PropertyPage( m_pViewsSheet, "SequencesPage" );
+ m_pViewsSheet->AddPage( m_pSequencesPage, "Sequences" );
+ m_pSequencesList = new ListPanel( m_pSequencesPage, "SequencesList" );
+ m_pSequencesList->AddColumnHeader( 0, "sequence", "sequence", 52, 0 );
+ m_pSequencesList->AddActionSignalTarget( this );
+ m_pSequencesList->SetSelectIndividualCells( true );
+ m_pSequencesList->SetEmptyListText("No .MDL file currently selected.");
+ m_pSequencesList->SetDragEnabled( true );
+ m_pSequencesList->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_DOWNANDRIGHT, 0, 0, 0, 0 );
+
+ // Activities
+ m_pActivitiesPage = new PropertyPage( m_pViewsSheet, "ActivitiesPage" );
+ m_pViewsSheet->AddPage( m_pActivitiesPage, "Activities" );
+ m_pActivitiesList = new ListPanel( m_pActivitiesPage, "ActivitiesList" );
+ m_pActivitiesList->AddColumnHeader( 0, "activity", "activity", 52, 0 );
+ m_pActivitiesList->AddActionSignalTarget( this );
+ m_pActivitiesList->SetSelectIndividualCells( true );
+ m_pActivitiesList->SetEmptyListText( "No .MDL file currently selected." );
+ m_pActivitiesList->SetDragEnabled( true );
+ m_pActivitiesList->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_DOWNANDRIGHT, 0, 0, 0, 0 );
+
+ // Load layout settings; has to happen before pinning occurs in code
+ LoadControlSettingsAndUserConfig( "resource/mdlsequencepicker.res" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CMDLSequencePicker::~CMDLSequencePicker()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: This is a bit of a hack to make sure that the ToolWindow containing this picker punches
+// a hold for the rendering viewport, too
+// Input : -
+//-----------------------------------------------------------------------------
+void CMDLSequencePicker::OnTick()
+{
+ BaseClass::OnTick();
+ if ( GetParent() )
+ {
+ ToolWindow *tw = dynamic_cast< ToolWindow * >( GetParent()->GetParent() );
+
+ if ( tw )
+ {
+ tw->SetSkipChildDuringPainting( IsVisible() ? m_pMDLPreview : NULL );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: stops app on close
+//-----------------------------------------------------------------------------
+void CMDLSequencePicker::OnClose()
+{
+ BaseClass::OnClose();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: called to open
+//-----------------------------------------------------------------------------
+void CMDLSequencePicker::Activate()
+{
+ RefreshFileList();
+ RefreshActivitiesAndSequencesList();
+}
+
+
+//-----------------------------------------------------------------------------
+// Performs layout
+//-----------------------------------------------------------------------------
+void CMDLSequencePicker::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_pMDLSplitter->SetBounds( 0, 0, w, h );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Refreshes the active file list
+//-----------------------------------------------------------------------------
+void CMDLSequencePicker::RefreshFileList()
+{
+ m_pFileTree->RefreshFileList();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: rebuilds the list of activities
+//-----------------------------------------------------------------------------
+void CMDLSequencePicker::RefreshActivitiesAndSequencesList()
+{
+ m_pActivitiesList->RemoveAll();
+ m_pSequencesList->RemoveAll();
+ m_pMDLPreview->SetSequence( 0 );
+
+ if ( m_hSelectedMDL == MDLHANDLE_INVALID )
+ {
+ m_pActivitiesList->SetEmptyListText("No .MDL file currently selected");
+ m_pSequencesList->SetEmptyListText("No .MDL file currently selected");
+ return;
+ }
+
+ m_pActivitiesList->SetEmptyListText(".MDL file contains no activities");
+ m_pSequencesList->SetEmptyListText(".MDL file contains no sequences");
+
+ studiohdr_t *hdr = vgui::MDLCache()->GetStudioHdr( m_hSelectedMDL );
+
+ CUtlDict<int, unsigned short> activityNames( true, 0, hdr->GetNumSeq() );
+
+ for (int j = 0; j < hdr->GetNumSeq(); j++)
+ {
+ if ( /*g_viewerSettings.showHidden ||*/ !(hdr->pSeqdesc(j).flags & STUDIO_HIDDEN))
+ {
+ const char *pActivityName = hdr->pSeqdesc(j).pszActivityName();
+ if ( pActivityName && pActivityName[0] )
+ {
+ // Multiple sequences can have the same activity name; only add unique activity names
+ if ( activityNames.Find( pActivityName ) == activityNames.InvalidIndex() )
+ {
+ KeyValues *pkv = new KeyValues("node", "activity", pActivityName );
+ int nItemID = m_pActivitiesList->AddItem( pkv, 0, false, false );
+
+ KeyValues *pDrag = new KeyValues( "drag", "text", pActivityName );
+ pDrag->SetString( "texttype", "activityName" );
+ pDrag->SetString( "mdl", vgui::MDLCache()->GetModelName( m_hSelectedMDL ) );
+ m_pActivitiesList->SetItemDragData( nItemID, pDrag );
+
+ activityNames.Insert( pActivityName, j );
+ }
+ }
+
+ const char *pSequenceName = hdr->pSeqdesc(j).pszLabel();
+ if ( pSequenceName && pSequenceName[0] )
+ {
+ KeyValues *pkv = new KeyValues("node", "sequence", pSequenceName);
+ pkv->SetInt( "seqindex", j );
+
+ int nItemID = m_pSequencesList->AddItem( pkv, 0, false, false );
+
+ KeyValues *pDrag = new KeyValues( "drag", "text", pSequenceName );
+ pDrag->SetString( "texttype", "sequenceName" );
+ pDrag->SetString( "mdl", vgui::MDLCache()->GetModelName( m_hSelectedMDL ) );
+ m_pSequencesList->SetItemDragData( nItemID, pDrag );
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: refreshes dialog on text changing
+//-----------------------------------------------------------------------------
+void CMDLSequencePicker::OnTextChanged( vgui::Panel *pPanel, const char *pText )
+{
+// m_pFileTree->SetFilter( pText );
+ RefreshFileList();
+}
+
+
+/*
+//-----------------------------------------------------------------------------
+// Purpose: Selects an sequence based on an activity
+//-----------------------------------------------------------------------------
+int SelectWeightedSequence( studiohdr_t *pstudiohdr, int activity, int curSequence )
+{
+ if (! pstudiohdr)
+ return 0;
+
+ VerifySequenceIndex( pstudiohdr );
+
+ int weighttotal = 0;
+ int seq = ACTIVITY_NOT_AVAILABLE;
+ int weight = 0;
+ for (int i = 0; i < pstudiohdr->GetNumSeq(); i++)
+ {
+ int curActivity = GetSequenceActivity( pstudiohdr, i, &weight );
+ if (curActivity == activity)
+ {
+ if ( curSequence == i && weight < 0 )
+ {
+ seq = i;
+ break;
+ }
+ weighttotal += iabs(weight);
+
+ int randomValue;
+ if ( IsInPrediction() )
+ randomValue = SharedRandomInt( "SelectWeightedSequence", 0, weighttotal - 1, i );
+ else
+ randomValue = RandomInt( 0, weighttotal - 1 );
+
+ if (!weighttotal || randomValue < iabs(weight))
+ seq = i;
+ }
+ }
+
+ return seq;
+}
+*/
+
+//-----------------------------------------------------------------------------
+// Plays the selected activity
+//-----------------------------------------------------------------------------
+void CMDLSequencePicker::PlaySelectedActivity( )
+{
+ int nIndex = m_pActivitiesList->GetSelectedItem( 0 );
+ if ( nIndex < 0 )
+ return;
+
+ KeyValues *pkv = m_pActivitiesList->GetItem( nIndex );
+ const char *pActivityName = pkv->GetString( "activity", NULL );
+ if ( !pActivityName )
+ return;
+
+ studiohdr_t *pstudiohdr = vgui::MDLCache()->GetStudioHdr( m_hSelectedMDL );
+ for ( int i = 0; i < pstudiohdr->GetNumSeq(); i++ )
+ {
+ mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( i );
+ if ( stricmp( seqdesc.pszActivityName(), pActivityName ) == 0 )
+ {
+ // FIXME: Add weighted sequence selection logic?
+ m_pMDLPreview->SetSequence( i );
+ break;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Plays the selected sequence
+//-----------------------------------------------------------------------------
+void CMDLSequencePicker::PlaySelectedSequence( )
+{
+ int nIndex = m_pSequencesList->GetSelectedItem( 0 );
+ if ( nIndex < 0 )
+ return;
+
+ KeyValues *pkv = m_pSequencesList->GetItem( nIndex );
+ const char *pSequenceName = pkv->GetString( "sequence", NULL );
+ if ( !pSequenceName )
+ return;
+
+ studiohdr_t *pstudiohdr = vgui::MDLCache()->GetStudioHdr( m_hSelectedMDL );
+ for (int i = 0; i < pstudiohdr->GetNumSeq(); i++)
+ {
+ mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( i );
+ if ( !Q_stricmp( seqdesc.pszLabel(), pSequenceName ) )
+ {
+ m_pMDLPreview->SetSequence( i );
+ break;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when a page is shown
+//-----------------------------------------------------------------------------
+void CMDLSequencePicker::OnPageChanged( )
+{
+ if ( m_pViewsSheet->GetActivePage() == m_pSequencesPage )
+ {
+ PlaySelectedSequence();
+ return;
+ }
+
+ if ( m_pViewsSheet->GetActivePage() == m_pActivitiesPage )
+ {
+ PlaySelectedActivity();
+ return;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: refreshes dialog on text changing
+//-----------------------------------------------------------------------------
+void CMDLSequencePicker::OnItemSelected( KeyValues *kv )
+{
+ Panel *pPanel = (Panel *)kv->GetPtr("panel", NULL);
+ if ( pPanel == m_pSequencesList )
+ {
+ PlaySelectedSequence();
+ return;
+ }
+
+ if ( pPanel == m_pActivitiesList )
+ {
+ PlaySelectedActivity();
+ return;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// An MDL was selected
+//-----------------------------------------------------------------------------
+void CMDLSequencePicker::SelectMDL( const char *pMDLName )
+{
+ m_hSelectedMDL = pMDLName ? vgui::MDLCache()->FindMDL( pMDLName ) : MDLHANDLE_INVALID;
+ if ( vgui::MDLCache()->IsErrorModel( m_hSelectedMDL ) )
+ {
+ m_hSelectedMDL = MDLHANDLE_INVALID;
+ }
+ m_pMDLPreview->SetMDL( m_hSelectedMDL );
+ m_pMDLPreview->LookAtMDL();
+ RefreshActivitiesAndSequencesList();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: updates revision view on a file being selected
+//-----------------------------------------------------------------------------
+void CMDLSequencePicker::OnFileSelected()
+{
+ // update list
+ int iItem = m_pFileTree->GetFirstSelectedItem();
+ if ( iItem < 0 )
+ return;
+
+ // Don't bother to change if a directory was selected
+ KeyValues *pkv = m_pFileTree->GetItemData(iItem);
+ if ( pkv->GetInt("dir") || pkv->GetInt("root") )
+ return;
+
+ surface()->SetCursor(dc_waitarrow);
+
+ const char *pFullPathName = pkv->GetString( "path" );
+
+ char pRelativePathName[MAX_PATH];
+ g_pFullFileSystem->FullPathToRelativePath( pFullPathName, pRelativePathName, sizeof(pRelativePathName) );
+
+ // FIXME: Check that we're not actually opening the wrong file!!
+ SelectMDL( pRelativePathName );
+}
+
+char const *CMDLSequencePicker::GetModelName()
+{
+ if ( MDLHANDLE_INVALID == m_hSelectedMDL )
+ {
+ return "";
+ }
+
+ return vgui::MDLCache()->GetModelName( m_hSelectedMDL );
+}
+
+char const *CMDLSequencePicker::GetSequenceName()
+{
+ int nIndex = m_pSequencesList->GetSelectedItem( 0 );
+ if ( nIndex < 0 )
+ return "";
+
+ KeyValues *pkv = m_pSequencesList->GetItem( nIndex );
+ const char *pSequenceName = pkv->GetString( "sequence", NULL );
+ if ( !pSequenceName )
+ return "";
+
+ return pSequenceName;
+}
+
+int CMDLSequencePicker::GetSequenceNumber()
+{
+ int nIndex = m_pSequencesList->GetSelectedItem( 0 );
+ if ( nIndex < 0 )
+ return -1;
+ KeyValues *pkv = m_pSequencesList->GetItem( nIndex );
+ return pkv->GetInt( "seqindex", -1 );
+}
+
+//-----------------------------------------------------------------------------
+// Sequence picker frame
+//-----------------------------------------------------------------------------
+CMDLSequencePickerFrame::CMDLSequencePickerFrame( vgui::Panel *parent, char const *title ) :
+ BaseClass( parent, "MDLSequencePickerFrame" )
+{
+ m_pMDLSequencePicker = new CMDLSequencePicker( this );
+ SetTitle( title, true );
+ SetSizeable( false );
+ SetCloseButtonVisible( false );
+ SetMoveable( true );
+ SetMinimumSize( 640, 480 );
+ Activate();
+ m_pMDLSequencePicker->Activate();
+
+ m_pOK = new Button( this, "OK", "#vgui_ok", this );
+ m_pOK->SetCommand( new KeyValues( "OnOK" ) );
+ m_pCancel= new Button( this, "Cancel", "#vgui_cancel", this );
+ m_pOK->SetCommand( new KeyValues( "OnCancel" ) );
+ m_pOK->SetEnabled( false );
+
+ vgui::ivgui()->AddTickSignal( GetVPanel(), 0 );
+}
+
+CMDLSequencePickerFrame::~CMDLSequencePickerFrame()
+{
+}
+
+void CMDLSequencePickerFrame::OnTick()
+{
+ BaseClass::OnTick();
+
+ bool bHasModel = m_pMDLSequencePicker->GetModelName()[ 0 ] != 0 ? true : false;
+ bool bHasSequence = m_pMDLSequencePicker->GetSequenceNumber() != -1 ? true : false;
+
+ m_pOK->SetEnabled( bHasModel && bHasSequence );
+}
+
+void CMDLSequencePickerFrame::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ int x, y, w, h;
+ GetClientArea( x, y, w, h );
+ h -= 24;
+ m_pMDLSequencePicker->SetBounds( x, y, w, h );
+
+ h += 5;
+
+
+ int bw = 120;
+ int bwwithGap = 2 * bw + 10;
+
+ x = ( w - bwwithGap ) / 2;
+ m_pOK->SetBounds( x, y + h, bw, 16 );
+ x += bw + 10;
+ m_pCancel->SetBounds( x, y + h, bw, 16 );
+}
+
+void CMDLSequencePickerFrame::OnCancel()
+{
+ KeyValues *pActionKeys = new KeyValues( "AssetSelected" );
+ pActionKeys->SetString( "ModelName", m_pMDLSequencePicker->GetModelName() );
+ pActionKeys->SetString( "SequenceName", m_pMDLSequencePicker->GetSequenceName() );
+ pActionKeys->SetInt( "SequenceNumber", m_pMDLSequencePicker->GetSequenceNumber() );
+
+ PostActionSignal( pActionKeys );
+
+ CloseModal();
+}
+
+void CMDLSequencePickerFrame::OnOK()
+{
+ CloseModal();
+}
+
+
diff --git a/vgui2/matsys_controls/picker.cpp b/vgui2/matsys_controls/picker.cpp
new file mode 100644
index 0000000..6362213
--- /dev/null
+++ b/vgui2/matsys_controls/picker.cpp
@@ -0,0 +1,318 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "filesystem.h"
+#include "matsys_controls/picker.h"
+#include "tier1/KeyValues.h"
+#include "vgui_controls/ListPanel.h"
+#include "vgui_controls/TextEntry.h"
+#include "vgui_controls/Button.h"
+#include "vgui/ISurface.h"
+#include "vgui/IInput.h"
+
+
+using namespace vgui;
+
+
+//-----------------------------------------------------------------------------
+//
+// Base asset Picker
+//
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Sort by asset name
+//-----------------------------------------------------------------------------
+static int __cdecl PickerBrowserSortFunc( vgui::ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
+{
+ const char *string1 = item1.kv->GetString("choice");
+ const char *string2 = item2.kv->GetString("choice");
+ return stricmp( string1, string2 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CPicker::CPicker( vgui::Panel *pParent, const char *pColumnHeader, const char *pTextType ) :
+ BaseClass( pParent, "Picker" )
+{
+ m_pPickerType = pColumnHeader;
+ m_pPickerTextType = pTextType;
+
+ // FIXME: Make this an image browser
+ m_pPickerBrowser = new vgui::ListPanel( this, "Browser" );
+ m_pPickerBrowser->AddColumnHeader( 0, "choice", m_pPickerType, 52, 0 );
+ m_pPickerBrowser->SetSelectIndividualCells( true );
+ m_pPickerBrowser->SetEmptyListText( "Nothing to pick" );
+ m_pPickerBrowser->SetDragEnabled( true );
+ m_pPickerBrowser->AddActionSignalTarget( this );
+ m_pPickerBrowser->SetSortFunc( 0, PickerBrowserSortFunc );
+ m_pPickerBrowser->SetSortColumn( 0 );
+
+ // filter selection
+ m_pFilterList = new TextEntry( this, "FilterList" );
+ m_pFilterList->AddActionSignalTarget( this );
+ m_pFilterList->RequestFocus();
+
+ LoadControlSettingsAndUserConfig( "resource/picker.res" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CPicker::~CPicker()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPicker::OnKeyCodePressed( KeyCode code )
+{
+ if (( code == KEY_UP ) || ( code == KEY_DOWN ) || ( code == KEY_PAGEUP ) || ( code == KEY_PAGEDOWN ))
+ {
+ KeyValues *pMsg = new KeyValues("KeyCodePressed", "code", code);
+ vgui::ipanel()->SendMessage( m_pPickerBrowser->GetVPanel(), pMsg, GetVPanel());
+ pMsg->deleteThis();
+ }
+ else
+ {
+ BaseClass::OnKeyCodePressed( code );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: refreshes the asset list
+//-----------------------------------------------------------------------------
+void CPicker::SetStringList( const PickerList_t &list )
+{
+ m_Type = list.m_Type;
+ m_pPickerBrowser->RemoveAll();
+
+ int nCount = list.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ const char *pPickerName = list[i].m_pChoiceString;
+ KeyValues *kv = new KeyValues( "node", "choice", pPickerName );
+ if ( m_Type == PICKER_CHOICE_STRING )
+ {
+ kv->SetString( "value", list[i].m_pChoiceValue );
+ }
+ else
+ {
+ kv->SetPtr( "value", list[i].m_pChoiceValuePtr );
+ }
+ int nItemID = m_pPickerBrowser->AddItem( kv, 0, false, false );
+
+ if ( m_Type == PICKER_CHOICE_STRING )
+ {
+ KeyValues *pDrag = new KeyValues( "drag", "text", list[i].m_pChoiceValue );
+ if ( m_pPickerTextType )
+ {
+ pDrag->SetString( "texttype", m_pPickerTextType );
+ }
+ m_pPickerBrowser->SetItemDragData( nItemID, pDrag );
+ }
+ }
+ RefreshChoiceList();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: refreshes the choice list
+//-----------------------------------------------------------------------------
+void CPicker::RefreshChoiceList( )
+{
+ // Check the filter matches
+ int nMatchingCount = 0;
+ int nTotalCount = 0;
+ for ( int nItemID = m_pPickerBrowser->FirstItem(); nItemID != m_pPickerBrowser->InvalidItemID(); nItemID = m_pPickerBrowser->NextItem( nItemID ) )
+ {
+ KeyValues *kv = m_pPickerBrowser->GetItem( nItemID );
+ const char *pPickerName = kv->GetString( "choice" );
+ bool bVisible = !m_Filter.Length() || Q_stristr( pPickerName, m_Filter.Get() );
+ m_pPickerBrowser->SetItemVisible( nItemID, bVisible );
+ if ( bVisible )
+ {
+ ++nMatchingCount;
+ }
+ ++nTotalCount;
+ }
+
+ char pColumnTitle[512];
+ Q_snprintf( pColumnTitle, sizeof(pColumnTitle), "%s (%d/%d)",
+ m_pPickerType, nMatchingCount, nTotalCount );
+ m_pPickerBrowser->SetColumnHeaderText( 0, pColumnTitle );
+
+ m_pPickerBrowser->SortList();
+ if ( ( m_pPickerBrowser->GetSelectedItemsCount() == 0 ) && ( m_pPickerBrowser->GetItemCount() > 0 ) )
+ {
+ int nItemID = m_pPickerBrowser->GetItemIDFromRow( 0 );
+ m_pPickerBrowser->SetSelectedCell( nItemID, 0 );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: refreshes dialog on text changing
+//-----------------------------------------------------------------------------
+void CPicker::OnTextChanged( )
+{
+ int nLength = m_pFilterList->GetTextLength();
+ m_Filter.SetLength( nLength );
+ if ( nLength > 0 )
+ {
+ m_pFilterList->GetText( m_Filter.GetForModify(), nLength+1 );
+ }
+ RefreshChoiceList();
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the selected string
+//-----------------------------------------------------------------------------
+PickerChoiceType_t CPicker::GetSelectionType() const
+{
+ return m_Type;
+}
+
+const char *CPicker::GetSelectedString( ) const
+{
+ if ( m_pPickerBrowser->GetSelectedItemsCount() == 0 )
+ return NULL;
+
+ if ( m_Type != PICKER_CHOICE_STRING )
+ return NULL;
+
+ int nIndex = m_pPickerBrowser->GetSelectedItem( 0 );
+ KeyValues *pItemKeyValues = m_pPickerBrowser->GetItem( nIndex );
+ return pItemKeyValues->GetString( "value" );
+}
+
+void *CPicker::GetSelectedPtr( ) const
+{
+ if ( m_pPickerBrowser->GetSelectedItemsCount() == 0 )
+ return NULL;
+
+ if ( m_Type != PICKER_CHOICE_PTR )
+ return NULL;
+
+ int nIndex = m_pPickerBrowser->GetSelectedItem( 0 );
+ KeyValues *pItemKeyValues = m_pPickerBrowser->GetItem( nIndex );
+ return pItemKeyValues->GetPtr( "value" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the index of the selected string
+//-----------------------------------------------------------------------------
+int CPicker::GetSelectedIndex()
+{
+ if ( m_pPickerBrowser->GetSelectedItemsCount() == 0 )
+ return -1;
+
+ return m_pPickerBrowser->GetSelectedItem( 0 );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Purpose: Modal picker frame
+//
+//-----------------------------------------------------------------------------
+CPickerFrame::CPickerFrame( vgui::Panel *pParent, const char *pTitle, const char *pPickerType, const char *pTextType ) :
+ BaseClass( pParent, "PickerFrame" )
+{
+ m_pContextKeyValues = NULL;
+ SetDeleteSelfOnClose( true );
+ m_pPicker = new CPicker( this, pPickerType, pTextType );
+ m_pPicker->AddActionSignalTarget( this );
+ m_pOpenButton = new Button( this, "OpenButton", "#FileOpenDialog_Open", this, "Open" );
+ m_pCancelButton = new Button( this, "CancelButton", "#FileOpenDialog_Cancel", this, "Cancel" );
+ SetBlockDragChaining( true );
+
+ LoadControlSettingsAndUserConfig( "resource/pickerframe.res" );
+
+ SetTitle( pTitle, false );
+}
+
+CPickerFrame::~CPickerFrame()
+{
+ CleanUpMessage();
+}
+
+
+//-----------------------------------------------------------------------------
+// Deletes the message
+//-----------------------------------------------------------------------------
+void CPickerFrame::CleanUpMessage()
+{
+ if ( m_pContextKeyValues )
+ {
+ m_pContextKeyValues->deleteThis();
+ m_pContextKeyValues = NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Activate the dialog
+//-----------------------------------------------------------------------------
+void CPickerFrame::DoModal( const PickerList_t &list, KeyValues *pContextKeyValues )
+{
+ CleanUpMessage();
+ m_pContextKeyValues = pContextKeyValues;
+ m_pPicker->SetStringList( list );
+ BaseClass::DoModal();
+}
+
+
+//-----------------------------------------------------------------------------
+// On command
+//-----------------------------------------------------------------------------
+void CPickerFrame::OnCommand( const char *pCommand )
+{
+ if ( !Q_stricmp( pCommand, "Open" ) )
+ {
+ KeyValues *pActionKeys = new KeyValues( "Picked" );
+ pActionKeys->SetInt( "choiceIndex", m_pPicker->GetSelectedIndex( ) );
+
+ if ( m_pPicker->GetSelectionType() == PICKER_CHOICE_STRING )
+ {
+ const char *pPickerName = m_pPicker->GetSelectedString( );
+ pActionKeys->SetString( "choice", pPickerName );
+ }
+ else
+ {
+ void *pPickerPtr = m_pPicker->GetSelectedPtr( );
+ pActionKeys->SetPtr( "choice", pPickerPtr );
+ }
+
+ if ( m_pContextKeyValues )
+ {
+ pActionKeys->AddSubKey( m_pContextKeyValues );
+ m_pContextKeyValues = NULL;
+ }
+ PostActionSignal( pActionKeys );
+ CloseModal();
+ return;
+ }
+
+ if ( !Q_stricmp( pCommand, "Cancel" ) )
+ {
+ CloseModal();
+ return;
+ }
+
+ BaseClass::OnCommand( pCommand );
+}
+
+
diff --git a/vgui2/matsys_controls/potterywheelpanel.cpp b/vgui2/matsys_controls/potterywheelpanel.cpp
new file mode 100644
index 0000000..60b3e10
--- /dev/null
+++ b/vgui2/matsys_controls/potterywheelpanel.cpp
@@ -0,0 +1,1243 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "matsys_controls/potterywheelpanel.h"
+#include "matsys_controls/manipulator.h"
+#include "vgui/ISystem.h"
+#include "vgui/Cursor.h"
+#include "vgui/IVGui.h"
+#include "vgui/ISurface.h"
+#include "vgui/IInput.h"
+#include "VGuiMatSurface/IMatSystemSurface.h"
+#include "dmxloader/dmxelement.h"
+#include "vgui_controls/Frame.h"
+#include "convar.h"
+#include "tier0/dbg.h"
+#include "matsys_controls/matsyscontrols.h"
+#include "materialsystem/imaterial.h"
+#include "materialsystem/imaterialsystem.h"
+#include "istudiorender.h"
+#include "materialsystem/imaterialsystemhardwareconfig.h"
+#include "tier2/renderutils.h"
+#include "tier1/KeyValues.h"
+#include "materialsystem/imesh.h"
+
+#include "inputsystem/iinputsystem.h"
+
+#include "renderparm.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+using namespace vgui;
+
+
+//-----------------------------------------------------------------------------
+// Translation manipulator
+//-----------------------------------------------------------------------------
+class CTranslationManipulator : public CTransformManipulator
+{
+public:
+ CTranslationManipulator( matrix3x4_t *pTransform );
+
+ // Methods of IManipulator
+ virtual void OnMousePressed( vgui::MouseCode code, int x, int y );
+ virtual void OnCursorMoved( int x, int y );
+
+protected:
+ int m_lastx, m_lasty;
+};
+
+
+CTranslationManipulator::CTranslationManipulator( matrix3x4_t *pTransform ) : CTransformManipulator( pTransform )
+{
+ m_lastx = m_lasty = 0;
+}
+
+void CTranslationManipulator::OnMousePressed( vgui::MouseCode code, int x, int y )
+{
+ m_lasty = y;
+ m_lastx = x;
+}
+
+void CTranslationManipulator::OnCursorMoved( int x, int y )
+{
+ if ( !m_pTransform )
+ return;
+
+ Vector vPosition;
+ QAngle quakeEuler;
+ MatrixAngles( *m_pTransform, quakeEuler, vPosition );
+
+ Vector forward, right, up;
+ AngleVectors( quakeEuler, &forward, &right, &up );
+
+ int dy = y - m_lasty;
+ int dx = x - m_lastx;
+
+ right *= -0.2f * dx;
+ up *= 0.2f * dy;
+ vPosition += up + right;
+
+ m_lastx = x;
+ m_lasty = y;
+
+ PositionMatrix( vPosition, *m_pTransform );
+}
+
+
+//-----------------------------------------------------------------------------
+// Zoom manipulator
+//-----------------------------------------------------------------------------
+class CZoomManipulator : public CBaseManipulator
+{
+public:
+ CZoomManipulator( float *pDistance );
+
+ // Methods of IManipulator
+ virtual void OnMousePressed( vgui::MouseCode code, int x, int y );
+ virtual void OnCursorMoved( int x, int y );
+
+protected:
+ int m_lasty;
+ float *m_pDistance;
+};
+
+CZoomManipulator::CZoomManipulator( float *pDistance )
+{
+ m_lasty = 0;
+ m_pDistance = pDistance;
+}
+
+void CZoomManipulator::OnMousePressed( vgui::MouseCode code, int x, int y )
+{
+ m_lasty = y;
+}
+
+void CZoomManipulator::OnCursorMoved( int x, int y )
+{
+ float delta = 0.2f * ( y - m_lasty );
+ m_lasty = y;
+ *m_pDistance *= pow( 1.01f, delta );
+}
+
+
+//-----------------------------------------------------------------------------
+// Rotation manipulator
+//-----------------------------------------------------------------------------
+class CRotationManipulator : public CTransformManipulator
+{
+public:
+ CRotationManipulator( matrix3x4_t *pTransform );
+
+ // Inherited from IManipulator
+ virtual void OnMousePressed( vgui::MouseCode code, int x, int y );
+ virtual void OnCursorMoved( int x, int y );
+ virtual void UpdateTransform();
+
+ void UpdateFromMatrix( void );
+
+private:
+ int m_lastx, m_lasty;
+ float m_altitude, m_azimuth, m_roll;
+ bool m_bDoRoll;
+};
+
+
+CRotationManipulator::CRotationManipulator( matrix3x4_t *pTransform ) : CTransformManipulator( pTransform )
+{
+ m_lastx = m_lasty = 0;
+ m_altitude = M_PI/6;
+ m_azimuth = -3*M_PI/4;
+ m_roll = 0.0f;
+ m_bDoRoll = false;
+ UpdateTransform();
+}
+
+void CRotationManipulator::OnMousePressed( vgui::MouseCode code, int x, int y )
+{
+ if ( input()->IsKeyDown( KEY_LALT ) || input()->IsKeyDown( KEY_RALT ) )
+ {
+ m_bDoRoll = true;
+ }
+ else
+ {
+ m_bDoRoll = false;
+ }
+ m_lasty = y;
+ m_lastx = x;
+}
+
+void CRotationManipulator::OnCursorMoved( int x, int y )
+{
+ if ( m_bDoRoll )
+ {
+ m_roll += 0.002f * ( m_lastx - x );
+ }
+ else
+ {
+ m_azimuth += 0.002f * ( m_lastx - x );
+ m_altitude -= 0.002f * ( m_lasty - y );
+ m_altitude = max( (float)-M_PI/2, min( (float)M_PI/2, m_altitude ) );
+ }
+
+ m_lastx = x;
+ m_lasty = y;
+
+ UpdateTransform();
+}
+
+void CRotationManipulator::UpdateTransform()
+{
+ if ( !m_pTransform )
+ return;
+
+ QAngle angles( RAD2DEG( m_altitude ), RAD2DEG( m_azimuth ), RAD2DEG( m_roll ) );
+ Vector vecPosition;
+ MatrixGetColumn( *m_pTransform, 3, vecPosition );
+ AngleMatrix( angles, vecPosition, *m_pTransform );
+}
+
+void CRotationManipulator::UpdateFromMatrix( void )
+{
+ if ( !m_pTransform )
+ return;
+
+ QAngle angDir;
+ Vector vecPos;
+
+ MatrixAngles( *m_pTransform, angDir, vecPos );
+
+ m_altitude = DEG2RAD( angDir.x );
+ m_azimuth = DEG2RAD( angDir.y );
+ m_roll = DEG2RAD( angDir.z );
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+CPotteryWheelPanel::CPotteryWheelPanel( vgui::Panel *pParent, const char *pName ) :
+ BaseClass( pParent, pName ),
+ m_pCameraRotate( NULL ),
+ m_pCameraTranslate( NULL ),
+ m_pCameraZoom( NULL ),
+ m_pLightManip( NULL ),
+ m_pCurrentManip( NULL ),
+ m_nCaptureMouseCode( vgui::MouseCode( -1 ) ),
+ m_xoffset( 0 ), m_yoffset( 0 ),
+ m_bRenderToTexture( true )
+{
+ m_bHasLightProbe = false;
+
+ SetPaintBackgroundEnabled( false );
+ SetPaintBorderEnabled( false );
+ m_ClearColor.SetColor( 76, 88, 68, 255 );
+
+ SetIdentityMatrix( m_CameraPivot );
+
+ CreateDefaultLights();
+
+ m_nManipStartX = m_nManipStartY = 0;
+
+ m_vecCameraOffset.Init( 100.0f, 0.0f, 0.0f );
+
+ m_Camera.m_flZNear = 3.0f;
+ m_Camera.m_flZFar = 16384.0f * 1.73205080757f;
+ m_Camera.m_flFOV = 30.0f;
+
+ m_pCameraRotate = new CRotationManipulator( &m_CameraPivot );
+ m_pCameraTranslate = new CTranslationManipulator( &m_CameraPivot );
+ m_pCameraZoom = new CZoomManipulator( &m_vecCameraOffset.x );
+
+ KeyValues *pMaterialKeys = new KeyValues( "Wireframe", "$model", "1" );
+
+ pMaterialKeys->SetString( "$vertexcolor", "1" );
+ m_Wireframe.Init( "potterywheelpanelwireframe", pMaterialKeys );
+
+ SetKeyBoardInputEnabled( true );
+ UpdateCameraTransform();
+}
+
+void CPotteryWheelPanel::ApplySettings( KeyValues *inResourceData )
+{
+ BaseClass::ApplySettings( inResourceData );
+
+ KeyValues *pLights = inResourceData->FindKey( "lights" );
+ if ( pLights )
+ {
+ ParseLightsFromKV( pLights );
+ }
+}
+
+void CPotteryWheelPanel::Init( int x, int y, int wide, int tall )
+{
+ BaseClass::Init( x, y, wide, tall );
+
+ // Used to poll input
+ vgui::ivgui()->AddTickSignal( GetVPanel() );
+}
+
+
+CPotteryWheelPanel::~CPotteryWheelPanel()
+{
+ m_Wireframe.Shutdown();
+ m_LightProbeBackground.Shutdown();
+ m_LightProbeHDRBackground.Shutdown();
+ m_LightProbeCubemap.Shutdown();
+ m_LightProbeHDRCubemap.Shutdown();
+
+ if ( m_pCameraRotate )
+ {
+ delete m_pCameraRotate;
+ m_pCameraRotate = NULL;
+ }
+
+ if ( m_pCameraZoom )
+ {
+ delete m_pCameraZoom;
+ m_pCameraZoom = NULL;
+ }
+
+ if ( m_pCameraTranslate )
+ {
+ delete m_pCameraTranslate;
+ m_pCameraTranslate = NULL;
+ }
+
+ DestroyLights();
+}
+
+void CPotteryWheelPanel::CreateDefaultLights()
+{
+ for ( int i = 0; i < 6; ++i )
+ {
+ m_vecAmbientCube[i].Init( 0.4f, 0.4f, 0.4f, 1.0f );
+ }
+
+ memset( &m_Lights[0].m_Desc, 0, sizeof(LightDesc_t) );
+ SetIdentityMatrix( m_Lights[0].m_LightToWorld );
+ m_Lights[0].m_Desc.m_Type = MATERIAL_LIGHT_DIRECTIONAL;
+ m_Lights[0].m_Desc.m_Color.Init( 1.0f, 1.0f, 1.0f );
+ m_Lights[0].m_Desc.m_Direction.Init( 0.0f, 0.0f, -1.0f );
+ m_Lights[0].m_Desc.m_Range=0.0;
+ m_Lights[0].m_Desc.m_Attenuation0 = 1.0;
+ m_Lights[0].m_Desc.m_Attenuation1 = 0;
+ m_Lights[0].m_Desc.m_Attenuation2 = 0;
+ m_Lights[0].m_Desc.RecalculateDerivedValues();
+ m_nLightCount = 1;
+
+ m_pLightManip = new CPotteryWheelManip( &m_Lights[0].m_LightToWorld );
+}
+
+
+void CPotteryWheelPanel::DestroyLights()
+{
+ if ( m_pLightManip )
+ {
+ delete m_pLightManip;
+ m_pLightManip = NULL;
+ }
+
+ m_nLightCount = 0;
+}
+
+
+void StringToFloatArray( float *pVector, int count, const char *pString )
+{
+ char *pstr, *pfront, tempString[128];
+ int j;
+
+ Q_strncpy( tempString, pString, sizeof(tempString) );
+ pstr = pfront = tempString;
+
+ for ( j = 0; j < count; j++ ) // lifted from pr_edict.c
+ {
+ pVector[j] = atof( pfront );
+
+ // skip any leading whitespace
+ while ( *pstr && *pstr <= ' ' )
+ pstr++;
+
+ // skip to next whitespace
+ while ( *pstr && *pstr > ' ' )
+ pstr++;
+
+ if (!*pstr)
+ break;
+
+ pstr++;
+ pfront = pstr;
+ }
+ for ( j++; j < count; j++ )
+ {
+ pVector[j] = 0;
+ }
+}
+
+void StringToVector( float *pVector, const char *pString )
+{
+ StringToFloatArray( pVector, 3, pString );
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets initialize lights from KeyValues
+//-----------------------------------------------------------------------------
+void CPotteryWheelPanel::ParseLightsFromKV( KeyValues *pLightsKV )
+{
+ int nLightCount = 0;
+ FOR_EACH_SUBKEY( pLightsKV, pLocalLight )
+ {
+ Assert( nLightCount < MAX_LIGHT_COUNT );
+ if ( nLightCount >= MAX_LIGHT_COUNT )
+ break;
+
+ LightDesc_t *pDesc = &m_Lights[nLightCount].m_Desc;
+ const char *pType = pLocalLight->GetString( "name" );
+ Vector vecColor;
+ StringToVector( vecColor.Base(), pLocalLight->GetString( "color" ) );
+
+ if ( !Q_stricmp( pType, "directional" ) )
+ {
+ Vector vecDirection;
+ StringToVector( vecDirection.Base(), pLocalLight->GetString( "direction" ) );
+ pDesc->InitDirectional( vecDirection.Normalized(), vecColor );
+ ++nLightCount;
+ continue;
+ }
+
+ if ( !Q_stricmp( pType, "point" ) )
+ {
+ Vector vecAtten;
+ StringToVector( vecAtten.Base(), pLocalLight->GetString( "attenuation" ) );
+ Vector vecOrigin;
+ StringToVector( vecOrigin.Base(), pLocalLight->GetString( "origin" ) );
+ pDesc->InitPoint( vecOrigin, vecColor );
+ pDesc->m_Attenuation0 = vecAtten.x;
+ pDesc->m_Attenuation1 = vecAtten.y;
+ pDesc->m_Attenuation2 = vecAtten.z;
+ pDesc->m_Range = pLocalLight->GetFloat( "maxDistance" );
+ pDesc->RecalculateDerivedValues();
+ ++nLightCount;
+ continue;
+ }
+
+ if ( !Q_stricmp( pType, "spot" ) )
+ {
+ Vector vecAtten;
+ StringToVector( vecAtten.Base(), pLocalLight->GetString( "attenuation" ) );
+ Vector vecOrigin;
+ StringToVector( vecOrigin.Base(), pLocalLight->GetString( "origin" ) );
+ pDesc->InitSpot( vecOrigin, vecColor, vec3_origin,
+ pLocalLight->GetFloat( "inner_cone_angle" ),
+ pLocalLight->GetFloat( "outer_cone_angle" ) );
+
+ Vector vecDirection;
+ StringToVector( vecDirection.Base(), pLocalLight->GetString( "direction" ) );
+ pDesc->m_Direction = vecDirection.Normalized();
+ pDesc->m_Attenuation0 = vecAtten.x;
+ pDesc->m_Attenuation1 = vecAtten.y;
+ pDesc->m_Attenuation2 = vecAtten.z;
+ pDesc->m_Range = pLocalLight->GetFloat( "maxDistance" );
+ pDesc->m_Falloff = pLocalLight->GetFloat( "exponent" );
+ pDesc->RecalculateDerivedValues();
+ ++nLightCount;
+ continue;
+ }
+
+ AssertMsg1( 0, "Failed to initialize light with type '%s'", pType );
+ }
+
+ AssertMsg( nLightCount > 0, "Must specify at least one valid light" );
+
+ m_nLightCount = nLightCount;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the background color
+//-----------------------------------------------------------------------------
+void CPotteryWheelPanel::SetBackgroundColor( int r, int g, int b )
+{
+ m_ClearColor.SetColor( r, g, b, 255 );
+}
+
+void CPotteryWheelPanel::SetBackgroundColor( const Color& c )
+{
+ m_ClearColor = c;
+}
+
+const Color& CPotteryWheelPanel::GetBackgroundColor() const
+{
+ return m_ClearColor;
+}
+
+
+//-----------------------------------------------------------------------------
+// Light probe
+//-----------------------------------------------------------------------------
+void CPotteryWheelPanel::SetLightProbe( CDmxElement *pLightProbe )
+{
+ m_LightProbeBackground.Shutdown();
+ m_LightProbeHDRBackground.Shutdown();
+ m_LightProbeCubemap.Shutdown();
+ m_LightProbeHDRCubemap.Shutdown();
+
+ DestroyLights();
+
+ m_bHasLightProbe = ( pLightProbe != NULL );
+ if ( !m_bHasLightProbe )
+ {
+ CreateDefaultLights();
+ return;
+ }
+
+ const char *pCubemap = pLightProbe->GetValueString( "cubemap" );
+ m_LightProbeCubemap.Init( pCubemap, TEXTURE_GROUP_OTHER );
+
+ const char *pCubemapHDR = pLightProbe->HasAttribute( "cubemapHdr" ) ? pLightProbe->GetValueString( "cubemapHdr" ) : pCubemap;
+ m_LightProbeHDRCubemap.Init( pCubemapHDR, TEXTURE_GROUP_OTHER );
+
+ KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" );
+ pVMTKeyValues->SetInt( "$ignorez", 1 );
+ pVMTKeyValues->SetString( "$envmap", pCubemap );
+ pVMTKeyValues->SetInt( "$no_fullbright", 1 );
+ pVMTKeyValues->SetInt( "$nocull", 1 );
+ m_LightProbeBackground.Init( "SPWP_LightProbeBackground", pVMTKeyValues );
+ m_LightProbeBackground->Refresh();
+
+ pVMTKeyValues = new KeyValues( "UnlitGeneric" );
+ pVMTKeyValues->SetInt( "$ignorez", 1 );
+ pVMTKeyValues->SetString( "$envmap", pCubemapHDR );
+ pVMTKeyValues->SetInt( "$no_fullbright", 1 );
+ pVMTKeyValues->SetInt( "$nocull", 1 );
+ m_LightProbeHDRBackground.Init( "SPWP_LightProbeBackground_HDR", pVMTKeyValues );
+ m_LightProbeHDRBackground->Refresh();
+
+ const CUtlVector< Vector >& ambientCube = pLightProbe->GetArray<Vector>( "ambientCube" );
+ if ( ambientCube.Count() == 6 )
+ {
+ for ( int i = 0; i < 6; ++i )
+ {
+ m_vecAmbientCube[i].Init( ambientCube[i].x, ambientCube[i].y, ambientCube[i].z, 0.0f );
+ }
+ }
+
+ const CUtlVector< CDmxElement* >& localLights = pLightProbe->GetArray< CDmxElement* >( "localLights" );
+ int nLightCount = localLights.Count();
+ for ( int i = 0; i < nLightCount; ++i )
+ {
+ if ( m_nLightCount == MAX_LIGHT_COUNT )
+ break;
+
+ LightDesc_t *pDesc = &m_Lights[m_nLightCount].m_Desc;
+ CDmxElement *pLocalLight = localLights[ i ];
+ const char *pType = pLocalLight->GetValueString( "name" );
+ const Vector& vecColor = pLocalLight->GetValue<Vector>( "color" );
+
+ if ( !Q_stricmp( pType, "directional" ) )
+ {
+ pDesc->InitDirectional( pLocalLight->GetValue<Vector>( "direction" ), vecColor );
+ ++m_nLightCount;
+ continue;
+ }
+
+ if ( !Q_stricmp( pType, "point" ) )
+ {
+ const Vector& vecAtten = pLocalLight->GetValue<Vector>( "attenuation" );
+ pDesc->InitPoint( pLocalLight->GetValue<Vector>( "origin" ), vecColor );
+ pDesc->m_Attenuation0 = vecAtten.x;
+ pDesc->m_Attenuation1 = vecAtten.y;
+ pDesc->m_Attenuation2 = vecAtten.z;
+ pDesc->m_Range = pLocalLight->GetValue<float>( "maxDistance" );
+ pDesc->RecalculateDerivedValues();
+ ++m_nLightCount;
+ continue;
+ }
+
+ if ( !Q_stricmp( pType, "spot" ) )
+ {
+ const Vector& vecAtten = pLocalLight->GetValue<Vector>( "attenuation" );
+ pDesc->InitSpot( pLocalLight->GetValue<Vector>( "origin" ), vecColor, vec3_origin,
+ RAD2DEG ( pLocalLight->GetValue<float>( "theta" ) ),
+ RAD2DEG ( pLocalLight->GetValue<float>( "phi" ) ) );
+
+ pDesc->m_Direction = pLocalLight->GetValue<Vector>( "direction" );
+ pDesc->m_Attenuation0 = vecAtten.x;
+ pDesc->m_Attenuation1 = vecAtten.y;
+ pDesc->m_Attenuation2 = vecAtten.z;
+ pDesc->m_Range = pLocalLight->GetValue<float>( "maxDistance" );
+ pDesc->m_Falloff = pLocalLight->GetValue<float>( "exponent" );
+ pDesc->RecalculateDerivedValues();
+ ++m_nLightCount;
+ continue;
+ }
+ }
+
+ if ( nLightCount > 0 )
+ {
+ m_pLightManip = new CPotteryWheelManip( &m_Lights[0].m_LightToWorld );
+ }
+}
+
+bool CPotteryWheelPanel::HasLightProbe() const
+{
+ return m_bHasLightProbe;
+}
+
+ITexture *CPotteryWheelPanel::GetLightProbeCubemap( bool bHDR )
+{
+ if ( !m_bHasLightProbe )
+ return NULL;
+
+ return bHDR ? m_LightProbeHDRCubemap : m_LightProbeCubemap;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CPotteryWheelPanel::GetCameraFOV( void )
+{
+ return m_Camera.m_flFOV;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPotteryWheelPanel::SetCameraFOV( float flFOV )
+{
+ m_Camera.m_flFOV = flFOV;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPotteryWheelPanel::SetCameraOffset( const Vector &vecOffset )
+{
+ m_vecCameraOffset = vecOffset;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPotteryWheelPanel::GetCameraOffset( Vector &vecOffset )
+{
+ vecOffset = m_vecCameraOffset;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPotteryWheelPanel::SetCameraPositionAndAngles( const Vector &vecPos, const QAngle &angDir, bool syncManipulators )
+{
+ SetIdentityMatrix( m_CameraPivot );
+ AngleMatrix( angDir, vecPos, m_CameraPivot );
+
+ UpdateCameraTransform();
+ if ( syncManipulators )
+ {
+ SyncManipulation();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPotteryWheelPanel::GetCameraPositionAndAngles( Vector &vecPos, QAngle &angDir )
+{
+ MatrixAngles( m_CameraPivot, angDir, vecPos );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPotteryWheelPanel::ResetCameraPivot( void )
+{
+ SetIdentityMatrix( m_CameraPivot );
+}
+
+//-----------------------------------------------------------------------------
+// Sets the camera to look at the the thing we're spinning around
+//-----------------------------------------------------------------------------
+void CPotteryWheelPanel::LookAt( float flRadius )
+{
+ // Compute the distance to the camera for the object based on its
+ // radius and fov.
+
+ // since tan( fov/2 ) = f/d
+ // cos( fov/2 ) = r / r' where r = sphere radius, r' = perp distance from sphere center to max extent of camera
+ // d/f = r'/d' where d' is distance of camera to sphere
+ // d' = r' / tan( fov/2 ) * r' = r / ( cos (fov/2) * tan( fov/2 ) ) = r / sin( fov/2 )
+ float flFOVx = m_Camera.m_flFOV;
+
+ // Compute fov/2 in radians
+ flFOVx *= M_PI / 360.0f;
+
+ // Compute an effective fov based on the aspect ratio
+ // if the height is smaller than the width
+ int w, h;
+ GetSize( w, h );
+ if ( h < w )
+ {
+ flFOVx = atan( h * tan( flFOVx ) / w );
+ }
+
+ m_vecCameraOffset.x = -( flRadius / sin( flFOVx ) );
+ UpdateCameraTransform();
+}
+
+
+void CPotteryWheelPanel::LookAt( const Vector &vecCenter, float flRadius )
+{
+ MatrixSetColumn( vecCenter, 3, m_CameraPivot );
+ LookAt( flRadius );
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets up render state in the material system for rendering
+//-----------------------------------------------------------------------------
+void CPotteryWheelPanel::SetupRenderState( int nDisplayWidth, int nDisplayHeight )
+{
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+
+ VMatrix view, projection;
+ ComputeViewMatrix( &view, m_Camera );
+ ComputeProjectionMatrix( &projection, m_Camera, nDisplayWidth, nDisplayHeight );
+
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->LoadIdentity( );
+
+ pRenderContext->MatrixMode( MATERIAL_VIEW );
+ pRenderContext->LoadMatrix( view );
+
+ pRenderContext->MatrixMode( MATERIAL_PROJECTION );
+ pRenderContext->LoadMatrix( projection );
+
+ LightDesc_t *pDesc = (LightDesc_t*)stackalloc( m_nLightCount * sizeof(LightDesc_t) );
+ for ( int i = 0; i < m_nLightCount; ++i )
+ {
+ pDesc[i] = m_Lights[i].m_Desc;
+ VectorTransform( m_Lights[i].m_Desc.m_Position, m_Lights[i].m_LightToWorld, pDesc->m_Position );
+ VectorRotate( m_Lights[i].m_Desc.m_Direction, m_Lights[i].m_LightToWorld, pDesc->m_Direction );
+ VectorNormalize( pDesc->m_Direction );
+ pRenderContext->SetLight( i, pDesc[i] );
+ }
+
+ LightDesc_t desc;
+ desc.m_Type = MATERIAL_LIGHT_DISABLE;
+ int nMaxLightCount = g_pMaterialSystemHardwareConfig->MaxNumLights();
+ for ( int i = m_nLightCount; i < nMaxLightCount; ++i )
+ {
+ pRenderContext->SetLight( i, desc );
+ }
+
+ pRenderContext->SetAmbientLightCube( m_vecAmbientCube );
+
+ // FIXME: Remove this! This should automatically happen in DrawModel
+ // in studiorender.
+ if ( !g_pStudioRender )
+ return;
+
+ VMatrix worldToCamera;
+ MatrixInverseTR( view, worldToCamera );
+ Vector vecOrigin, vecRight, vecUp, vecForward;
+ MatrixGetColumn( worldToCamera, 0, &vecRight );
+ MatrixGetColumn( worldToCamera, 1, &vecUp );
+ MatrixGetColumn( worldToCamera, 2, &vecForward );
+ MatrixGetColumn( worldToCamera, 3, &vecOrigin );
+ g_pStudioRender->SetViewState( vecOrigin, vecRight, vecUp, vecForward );
+
+ g_pStudioRender->SetLocalLights( m_nLightCount, pDesc );
+ g_pStudioRender->SetAmbientLightColors( m_vecAmbientCube );
+}
+
+
+//-----------------------------------------------------------------------------
+// Compute the camera world position
+//-----------------------------------------------------------------------------
+void CPotteryWheelPanel::UpdateCameraTransform( )
+{
+ // Set up the render state for the camera + light
+ matrix3x4_t offset, worldToCamera;
+ SetIdentityMatrix( offset );
+ MatrixSetColumn( m_vecCameraOffset, 3, offset );
+ ConcatTransforms( m_CameraPivot, offset, worldToCamera );
+ MatrixAngles( worldToCamera, m_Camera.m_angles, m_Camera.m_origin );
+}
+
+void CPotteryWheelPanel::ComputeCameraTransform( matrix3x4_t *pWorldToCamera )
+{
+ AngleMatrix( m_Camera.m_angles, m_Camera.m_origin, *pWorldToCamera );
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes the position in the panel of a particular 3D point
+//-----------------------------------------------------------------------------
+void CPotteryWheelPanel::ComputePanelPosition( const Vector &vecPosition, Vector2D *pPanelPos )
+{
+ int w, h;
+ GetSize( w, h );
+
+ matrix3x4_t worldToCamera;
+ ComputeCameraTransform( &worldToCamera );
+ MatrixAngles( worldToCamera, m_Camera.m_angles, m_Camera.m_origin );
+ ComputeScreenSpacePosition( pPanelPos, vecPosition, m_Camera, w, h );
+}
+
+
+//-----------------------------------------------------------------------------
+// Utility method to draw a grid at the 'ground'
+//-----------------------------------------------------------------------------
+void CPotteryWheelPanel::DrawGrid()
+{
+ matrix3x4_t transform;
+ CMatRenderContextPtr pRenderContext( MaterialSystem() );
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->LoadIdentity( );
+
+ pRenderContext->Bind( m_Wireframe );
+
+ IMesh *pMesh = pRenderContext->GetDynamicMesh();
+
+ int nGridDim = 10;
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_LINES, 2 * nGridDim + 2 );
+
+ float bounds = 100.0f;
+ float delta = 2 * bounds / nGridDim;
+ for ( int i = 0; i < nGridDim + 1; ++i )
+ {
+ float xy = -bounds + delta * i;
+
+ meshBuilder.Position3f( xy, -bounds, 0 );
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.AdvanceVertex();
+ meshBuilder.Position3f( xy, bounds, 0 );
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( -bounds, xy, 0 );
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.AdvanceVertex();
+ meshBuilder.Position3f( bounds, xy, 0 );
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.AdvanceVertex();
+ }
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+
+//-----------------------------------------------------------------------------
+// paint it!
+//-----------------------------------------------------------------------------
+void CPotteryWheelPanel::Paint()
+{
+ int iWidth, iHeight;
+ GetSize( iWidth, iHeight );
+
+ int screenw, screenh;
+ vgui::surface()->GetScreenSize( screenw, screenh );
+
+ int windowposx = 0, windowposy = 0;
+ GetPos( windowposx, windowposy );
+
+ int windowposright = windowposx + iWidth;
+ int windowposbottom = windowposy + iHeight;
+
+ if ( windowposright >= screenw )
+ {
+ iWidth -= ( windowposright - screenw );
+ }
+ if ( windowposbottom >= screenh )
+ {
+ iHeight -= ( windowposbottom - screenh );
+ }
+
+ int startx = 0, starty = 0;
+ if( windowposx < 0 )
+ {
+ startx = -windowposx;
+ iWidth -= startx;
+ }
+ if ( windowposy < 0 )
+ {
+ starty = -windowposy;
+ iHeight -= starty;
+ }
+
+ int w, h;
+ GetSize( w, h );
+ vgui::MatSystemSurface()->Begin3DPaint( 0, 0, w, h, m_bRenderToTexture );
+
+ if ( m_pCurrentManip )
+ {
+ m_pCurrentManip->SetViewportSize( iWidth, iHeight );
+ }
+
+ // Set up the render state for the camera + light
+ SetupRenderState( iWidth, iHeight );
+
+ CMatRenderContextPtr pRenderContext( vgui::MaterialSystem() );
+
+ if ( m_bUseParentBG && GetParent() )
+ {
+ Color bgCol = GetParent()->GetBgColor();
+ pRenderContext->ClearColor4ub( bgCol.r(), bgCol.g(), bgCol.b(), bgCol.a() );
+ }
+ else
+ {
+ pRenderContext->ClearColor4ub( m_ClearColor.r(), m_ClearColor.g(), m_ClearColor.b(), m_ClearColor.a() );
+ }
+ pRenderContext->ClearBuffers( m_bRenderToTexture, true );
+
+ pRenderContext->CullMode( MATERIAL_CULLMODE_CCW );
+ pRenderContext->SetIntRenderingParameter( INT_RENDERPARM_WRITE_DEPTH_TO_DESTALPHA, false );
+
+ if ( HasLightProbe() )
+ {
+ IMaterial *pMaterial = ( vgui::MaterialSystemHardwareConfig()->GetHDRType() == HDR_TYPE_NONE ) ?
+ m_LightProbeBackground : m_LightProbeHDRBackground;
+
+ RenderBox( m_Camera.m_origin, vec3_angle, Vector( -100, -100, -100 ), Vector( 100, 100, 100 ),
+ Color( 255, 255, 255, 255 ), pMaterial, true );
+ }
+
+ OnPaint3D();
+
+ pRenderContext->CullMode( MATERIAL_CULLMODE_CW );
+
+ vgui::MatSystemSurface()->End3DPaint( );
+}
+
+
+//-----------------------------------------------------------------------------
+// called when we're ticked...
+//-----------------------------------------------------------------------------
+void CPotteryWheelPanel::OnTick()
+{
+ BaseClass::OnTick();
+ if ( m_pCurrentManip )
+ {
+ m_pCurrentManip->OnTick();
+ UpdateCameraTransform();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// input
+//-----------------------------------------------------------------------------
+void CPotteryWheelPanel::OnKeyCodePressed(KeyCode code)
+{
+ if ( m_pCurrentManip )
+ {
+ switch( code )
+ {
+ case KEY_RSHIFT:
+ case KEY_LSHIFT:
+ // start translate mode
+ AcceptManipulation( false );
+ EnterManipulationMode( CAMERA_TRANSLATE, false );
+ break;
+
+ case KEY_RCONTROL:
+ case KEY_LCONTROL:
+ // start light mode
+ AcceptManipulation( false );
+ EnterManipulationMode( LIGHT_MODE, false );
+ break;
+ }
+ }
+
+ BaseClass::OnKeyCodePressed( code );
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: soaks up any remaining messages
+//-----------------------------------------------------------------------------
+void CPotteryWheelPanel::OnKeyCodeReleased(KeyCode code)
+{
+ if ( m_pCurrentManip )
+ {
+ switch( code )
+ {
+ case KEY_RSHIFT:
+ case KEY_LSHIFT:
+ case KEY_RCONTROL:
+ case KEY_LCONTROL:
+ {
+ // stop manipulation mode
+ AcceptManipulation( false );
+ switch ( m_nCaptureMouseCode )
+ {
+ default:
+ case MOUSE_LEFT:
+ EnterManipulationMode( CAMERA_ROTATE, false );
+ break;
+
+ case MOUSE_MIDDLE:
+ EnterManipulationMode( CAMERA_TRANSLATE, false );
+ break;
+
+ case MOUSE_RIGHT:
+ EnterManipulationMode( CAMERA_ZOOM, false );
+ break;
+ }
+ }
+ break;
+ }
+ }
+ BaseClass::OnKeyCodeReleased( code );
+}
+
+void CPotteryWheelPanel::OnMousePressed( vgui::MouseCode code )
+{
+ if ( m_pCurrentManip )
+ return;
+
+ RequestFocus();
+
+ if ( input()->IsKeyDown( KEY_RSHIFT ) || input()->IsKeyDown( KEY_LSHIFT ) )
+ {
+ EnterManipulationMode( CAMERA_TRANSLATE, true, code );
+ }
+ else if ( input()->IsKeyDown( KEY_RCONTROL ) || input()->IsKeyDown( KEY_LCONTROL ) )
+ {
+ EnterManipulationMode( LIGHT_MODE, true, code );
+ }
+ else
+ {
+ switch ( code )
+ {
+ case MOUSE_LEFT:
+ EnterManipulationMode( CAMERA_ROTATE, true, code );
+ break;
+
+ case MOUSE_MIDDLE:
+ EnterManipulationMode( CAMERA_TRANSLATE, true, code );
+ break;
+
+ case MOUSE_RIGHT:
+ EnterManipulationMode( CAMERA_ZOOM, true, code );
+ break;
+ }
+ }
+
+ BaseClass::OnMousePressed( code );
+}
+
+void CPotteryWheelPanel::OnMouseReleased( vgui::MouseCode code )
+{
+ int x, y;
+ input()->GetCursorPos( x, y );
+ ScreenToLocal( x, y );
+
+ AcceptManipulation();
+
+ BaseClass::OnMouseReleased( code );
+}
+
+void CPotteryWheelPanel::OnCursorMoved( int x, int y )
+{
+ if ( m_pCurrentManip )
+ {
+ if ( WarpMouse( x, y ) )
+ {
+ m_pCurrentManip->OnCursorMoved( x, y );
+ }
+ }
+
+ BaseClass::OnCursorMoved( x, y );
+}
+
+void CPotteryWheelPanel::OnMouseWheeled( int delta )
+{
+ if ( m_pCurrentManip )
+ {
+ m_pCurrentManip->OnMouseWheeled( delta );
+ }
+
+ BaseClass::OnMouseWheeled( delta );
+}
+
+
+void CPotteryWheelPanel::EnterManipulationMode( ManipulationMode_t manipMode, bool bMouseCapture, vgui::MouseCode mouseCode /* = -1 */ )
+{
+ switch ( manipMode )
+ {
+ case CAMERA_ROTATE:
+ m_pCurrentManip = m_pCameraRotate;
+ break;
+
+ case CAMERA_TRANSLATE:
+ m_pCurrentManip = m_pCameraTranslate;
+ break;
+
+ case CAMERA_ZOOM:
+ m_pCurrentManip = m_pCameraZoom;
+ break;
+
+ case LIGHT_MODE:
+ m_pCurrentManip = m_pLightManip;
+ break;
+ }
+
+ if ( !m_pCurrentManip )
+ return;
+
+ m_pCurrentManip->OnBeginManipulation();
+
+ m_xoffset = m_yoffset = 0;
+
+ // Warp the mouse to the center of the screen
+ int width, height;
+ GetSize( width, height );
+ int x = width / 2;
+ int y = height / 2;
+
+ if ( bMouseCapture )
+ {
+ input()->GetCursorPos( m_nManipStartX, m_nManipStartY );
+ EnableMouseCapture( true, mouseCode );
+
+ int xpos = x;
+ int ypos = y;
+ LocalToScreen( xpos, ypos );
+ input()->SetCursorPos( xpos, ypos );
+ }
+
+ m_pCurrentManip->OnMousePressed( mouseCode, x, y );
+}
+
+void CPotteryWheelPanel::AcceptManipulation( bool bReleaseMouseCapture )
+{
+ if ( m_pCurrentManip )
+ {
+ m_pCurrentManip->OnAcceptManipulation();
+
+ if ( bReleaseMouseCapture )
+ {
+ EnableMouseCapture( false );
+ input()->SetCursorPos( m_nManipStartX, m_nManipStartY );
+ }
+
+ m_pCurrentManip = NULL;
+ }
+}
+
+void CPotteryWheelPanel::CancelManipulation()
+{
+ if ( m_pCurrentManip )
+ {
+ m_pCurrentManip->OnCancelManipulation();
+
+ EnableMouseCapture( false );
+ input()->SetCursorPos( m_nManipStartX, m_nManipStartY );
+
+ m_pCurrentManip = NULL;
+ }
+
+}
+
+void CPotteryWheelPanel::ApplyManipulation()
+{
+ if ( dynamic_cast< CRotationManipulator * >( m_pCameraRotate ) )
+ {
+ dynamic_cast< CRotationManipulator * >( m_pCameraRotate )->UpdateTransform();
+ }
+ UpdateCameraTransform();
+}
+
+void CPotteryWheelPanel::SyncManipulation()
+{
+ if ( dynamic_cast< CRotationManipulator * >( m_pCameraRotate ) )
+ {
+ dynamic_cast< CRotationManipulator * >( m_pCameraRotate )->UpdateFromMatrix();
+ }
+}
+
+void CPotteryWheelPanel::OnMouseCaptureLost()
+{
+ SetCursor( vgui::dc_arrow );
+ m_nCaptureMouseCode = vgui::MouseCode( -1 );
+}
+
+void CPotteryWheelPanel::EnableMouseCapture( bool enable, vgui::MouseCode mouseCode /* = -1 */ )
+{
+ if ( enable )
+ {
+ m_nCaptureMouseCode = mouseCode;
+ SetCursor( vgui::dc_none );
+ input()->SetMouseCaptureEx( GetVPanel(), m_nCaptureMouseCode );
+ }
+ else
+ {
+ m_nCaptureMouseCode = vgui::MouseCode( -1 );
+ input()->SetMouseCapture( (VPANEL)0 );
+ SetCursor( vgui::dc_arrow );
+ }
+}
+
+bool CPotteryWheelPanel::WarpMouse( int &x, int &y )
+{
+ // Re-force capture if it was lost...
+ if ( input()->GetMouseCapture() != GetVPanel() )
+ {
+ input()->GetCursorPos( m_nManipStartX, m_nManipStartY );
+ EnableMouseCapture( true, m_nCaptureMouseCode );
+ }
+
+ int width, height;
+ GetSize( width, height );
+
+ int centerx = width / 2;
+ int centery = height / 2;
+
+ // skip this event
+ if ( x == centerx && y == centery )
+ return false;
+
+ int xpos = centerx;
+ int ypos = centery;
+ LocalToScreen( xpos, ypos );
+
+#if defined( DX_TO_GL_ABSTRACTION )
+ //
+ // Really reset the cursor to the center for the PotteryWheel Control
+ //
+ // In TF2's edit loadout dialog there is a character model that you can rotate
+ // around using the mouse. This control resets the cursor to the center of the window
+ // after each mouse move. Except the input()->SetCursorPos results (after a lot of redirection) to
+ // vgui/matsurface/Cursor.cpp function CursorSetPos but it has a (needed) test to not move the
+ // cursor if it's currently hidden. Rather than change all the levels between here and there
+ // to support a flag, we are just jumping to the chase and directly calling the inputsystem
+ // SetCursorPosition on OpenGL platforms
+ //
+ g_pInputSystem->SetCursorPosition( xpos, ypos );
+#else
+ input()->SetCursorPos( xpos, ypos );
+#endif
+
+ int dx = x - centerx;
+ int dy = y - centery;
+
+ x += m_xoffset;
+ y += m_yoffset;
+
+ m_xoffset += dx;
+ m_yoffset += dy;
+
+ return true;
+}
diff --git a/vgui2/matsys_controls/proceduraltexturepanel.cpp b/vgui2/matsys_controls/proceduraltexturepanel.cpp
new file mode 100644
index 0000000..a198dd5
--- /dev/null
+++ b/vgui2/matsys_controls/proceduraltexturepanel.cpp
@@ -0,0 +1,239 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//===========================================================================//
+
+#include "matsys_controls/proceduraltexturepanel.h"
+#include "matsys_controls/matsyscontrols.h"
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/itexture.h"
+#include "VGuiMatSurface/IMatSystemSurface.h"
+#include "tier1/KeyValues.h"
+#include "pixelwriter.h"
+
+
+using namespace vgui;
+
+
+//-----------------------------------------------------------------------------
+// constructor
+//-----------------------------------------------------------------------------
+CProceduralTexturePanel::CProceduralTexturePanel( vgui::Panel *pParent, const char *pName ) : BaseClass( pParent, pName )
+{
+ m_pImageBuffer = NULL;
+ m_bMaintainProportions = false;
+ m_bUsePaintRect = false;
+ m_PaintRect.x = m_PaintRect.y = 0;
+ m_PaintRect.width = m_PaintRect.height = 0;
+}
+
+CProceduralTexturePanel::~CProceduralTexturePanel()
+{
+ CleanUp();
+}
+
+
+//-----------------------------------------------------------------------------
+// initialization, shutdown
+//-----------------------------------------------------------------------------
+bool CProceduralTexturePanel::Init( int nWidth, int nHeight, bool bAllocateImageBuffer )
+{
+ m_nWidth = nWidth;
+ m_nHeight = nHeight;
+ if ( bAllocateImageBuffer )
+ {
+ m_pImageBuffer = new BGRA8888_t[nWidth * nHeight];
+ }
+
+ m_TextureSubRect.x = m_TextureSubRect.y = 0;
+ m_TextureSubRect.width = nWidth;
+ m_TextureSubRect.height = nHeight;
+
+ char pTemp[512];
+ Q_snprintf( pTemp, 512, "__%s", GetName() );
+
+ ITexture *pTex = MaterialSystem()->CreateProceduralTexture( pTemp, TEXTURE_GROUP_VGUI,
+ m_nWidth, m_nHeight, IMAGE_FORMAT_BGRX8888,
+ TEXTUREFLAGS_CLAMPS | TEXTUREFLAGS_CLAMPT | TEXTUREFLAGS_NOMIP |
+ TEXTUREFLAGS_NOLOD | TEXTUREFLAGS_PROCEDURAL | TEXTUREFLAGS_SINGLECOPY );
+ pTex->SetTextureRegenerator( this );
+ m_ProceduralTexture.Init( pTex );
+
+ KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" );
+ pVMTKeyValues->SetString( "$basetexture", pTemp );
+ pVMTKeyValues->SetInt( "$nocull", 1 );
+ pVMTKeyValues->SetInt( "$nodebug", 1 );
+ m_ProceduralMaterial.Init( MaterialSystem()->CreateMaterial( pTemp, pVMTKeyValues ));
+
+ m_nTextureID = MatSystemSurface()->CreateNewTextureID( false );
+ MatSystemSurface()->DrawSetTextureMaterial( m_nTextureID, m_ProceduralMaterial );
+ return true;
+}
+
+void CProceduralTexturePanel::Shutdown()
+{
+ CleanUp();
+}
+
+
+//-----------------------------------------------------------------------------
+// Maintain proportions when drawing
+//-----------------------------------------------------------------------------
+void CProceduralTexturePanel::MaintainProportions( bool bEnable )
+{
+ m_bMaintainProportions = bEnable;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the image buffer + dimensions
+//-----------------------------------------------------------------------------
+void CProceduralTexturePanel::CleanUp()
+{
+ if ( (ITexture*)m_ProceduralTexture )
+ {
+ m_ProceduralTexture->SetTextureRegenerator( NULL );
+ }
+ m_ProceduralTexture.Shutdown();
+ m_ProceduralMaterial.Shutdown();
+
+ if ( m_pImageBuffer )
+ {
+ delete[] m_pImageBuffer;
+ m_pImageBuffer = NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Default implementation of regenerate texture bits
+//-----------------------------------------------------------------------------
+void CProceduralTexturePanel::RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect )
+{
+ Assert( m_pImageBuffer );
+ Assert( pVTFTexture->FrameCount() == 1 );
+ Assert( pVTFTexture->FaceCount() == 1 );
+ Assert( !pTexture->IsMipmapped() );
+
+ int nWidth, nHeight, nDepth;
+ pVTFTexture->ComputeMipLevelDimensions( 0, &nWidth, &nHeight, &nDepth );
+ Assert( nDepth == 1 );
+ Assert( nWidth == m_nWidth && nHeight == m_nHeight );
+
+ CPixelWriter pixelWriter;
+ pixelWriter.SetPixelMemory( pVTFTexture->Format(),
+ pVTFTexture->ImageData( 0, 0, 0 ), pVTFTexture->RowSizeInBytes( 0 ) );
+
+ for ( int y = 0; y < nHeight; ++y )
+ {
+ pixelWriter.Seek( 0, y );
+ BGRA8888_t *pTexel = &m_pImageBuffer[y * m_nWidth];
+ for ( int x = 0; x < nWidth; ++x, ++pTexel )
+ {
+ pixelWriter.WritePixel( pTexel->r, pTexel->g, pTexel->b, pTexel->a );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the image buffer + dimensions
+//-----------------------------------------------------------------------------
+BGRA8888_t *CProceduralTexturePanel::GetImageBuffer()
+{
+ Assert( m_pImageBuffer );
+ return m_pImageBuffer;
+}
+
+int CProceduralTexturePanel::GetImageWidth() const
+{
+ return m_nWidth;
+}
+
+int CProceduralTexturePanel::GetImageHeight() const
+{
+ return m_nHeight;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the paint rect
+//-----------------------------------------------------------------------------
+void CProceduralTexturePanel::SetPaintRect( const Rect_t *pPaintRect )
+{
+ m_bUsePaintRect = ( pPaintRect != NULL );
+ if ( m_bUsePaintRect )
+ {
+ m_PaintRect = *pPaintRect;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the draw rect
+//-----------------------------------------------------------------------------
+void CProceduralTexturePanel::SetTextureSubRect( const Rect_t &subRect )
+{
+ m_TextureSubRect = subRect;
+}
+
+
+//-----------------------------------------------------------------------------
+// Redownloads the procedural texture
+//-----------------------------------------------------------------------------
+void CProceduralTexturePanel::DownloadTexture()
+{
+ m_ProceduralTexture->Download();
+}
+
+
+//-----------------------------------------------------------------------------
+// Paints the texture
+//-----------------------------------------------------------------------------
+void CProceduralTexturePanel::Paint( void )
+{
+ vgui::surface()->DrawSetTexture( m_nTextureID );
+ vgui::surface()->DrawSetColor( 255, 255, 255, 255 );
+
+ int x = 0;
+ int y = 0;
+ int w, h;
+ GetSize( w, h );
+ if ( m_bUsePaintRect )
+ {
+ x = m_PaintRect.x;
+ y = m_PaintRect.y;
+ w = m_PaintRect.width;
+ h = m_PaintRect.height;
+ }
+
+ if ( m_bMaintainProportions )
+ {
+ if ( m_TextureSubRect.width > m_TextureSubRect.height )
+ {
+ h = w * m_TextureSubRect.height / m_TextureSubRect.width;
+ }
+ else
+ {
+ w = h * m_TextureSubRect.width / m_TextureSubRect.height;
+ }
+ }
+
+ // Rotated version of the bitmap!
+ // Rotate about the center of the bitmap
+ vgui::Vertex_t verts[4];
+ verts[0].m_Position.Init( x, y );
+ verts[0].m_TexCoord.Init( (float)m_TextureSubRect.x / m_nWidth, (float)m_TextureSubRect.y / m_nHeight );
+
+ verts[1].m_Position.Init( w+x, y );
+ verts[1].m_TexCoord.Init( (float)(m_TextureSubRect.x + m_TextureSubRect.width) / m_nWidth, (float)m_TextureSubRect.y / m_nHeight );
+
+ verts[2].m_Position.Init( w+x, h+y );
+ verts[2].m_TexCoord.Init( (float)(m_TextureSubRect.x + m_TextureSubRect.width) / m_nWidth, (float)(m_TextureSubRect.y + m_TextureSubRect.height) / m_nHeight );
+
+ verts[3].m_Position.Init( x, h+y );
+ verts[3].m_TexCoord.Init( (float)m_TextureSubRect.x / m_nWidth, (float)(m_TextureSubRect.y + m_TextureSubRect.height) / m_nHeight );
+
+ vgui::surface()->DrawTexturedPolygon( 4, verts );
+}
diff --git a/vgui2/matsys_controls/sequencepicker.cpp b/vgui2/matsys_controls/sequencepicker.cpp
new file mode 100644
index 0000000..7cc7b03
--- /dev/null
+++ b/vgui2/matsys_controls/sequencepicker.cpp
@@ -0,0 +1,485 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "matsys_controls/sequencepicker.h"
+
+#include "tier1/utldict.h"
+#include "tier1/KeyValues.h"
+#include "studio.h"
+#include "vgui/IInput.h"
+#include "vgui/ISurface.h"
+#include "vgui_controls/Splitter.h"
+#include "vgui_controls/PropertyPage.h"
+#include "vgui_controls/PropertySheet.h"
+#include "vgui_controls/ListPanel.h"
+#include "vgui_controls/Button.h"
+#include "matsys_controls/matsyscontrols.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+using namespace vgui;
+
+
+//-----------------------------------------------------------------------------
+//
+// Sequence Picker
+//
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Sort by sequence name
+//-----------------------------------------------------------------------------
+static int __cdecl SequenceSortFunc( vgui::ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
+{
+ const char *string1 = item1.kv->GetString("sequence");
+ const char *string2 = item2.kv->GetString("sequence");
+ return stricmp( string1, string2 );
+}
+
+static int __cdecl ActivitySortFunc( vgui::ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
+{
+ const char *string1 = item1.kv->GetString("activity");
+ const char *string2 = item2.kv->GetString("activity");
+ return stricmp( string1, string2 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CSequencePicker::CSequencePicker( vgui::Panel *pParent, int nFlags ) : BaseClass( pParent, "SequencePicker" )
+{
+ m_hSelectedMDL = MDLHANDLE_INVALID;
+
+ // property sheet - revisions, changes, etc.
+ m_pPreviewSplitter = new Splitter( this, "PreviewSplitter", SPLITTER_MODE_HORIZONTAL, 1 );
+
+ vgui::Panel *pSplitterTopSide = m_pPreviewSplitter->GetChild( 0 );
+ vgui::Panel *pSplitterBottomSide = m_pPreviewSplitter->GetChild( 1 );
+
+ // MDL preview
+ m_pMDLPreview = new CMDLPanel( pSplitterTopSide, "MDLPreview" );
+ SetSkipChildDuringPainting( m_pMDLPreview );
+
+ m_pViewsSheet = new vgui::PropertySheet( pSplitterBottomSide, "ViewsSheet" );
+ m_pViewsSheet->AddActionSignalTarget( this );
+
+ // sequences
+ m_pSequencesPage = NULL;
+ m_pSequencesList = NULL;
+ if ( nFlags & PICK_SEQUENCES )
+ {
+ m_pSequencesPage = new PropertyPage( m_pViewsSheet, "SequencesPage" );
+ m_pViewsSheet->AddPage( m_pSequencesPage, "Sequences" );
+ m_pSequencesList = new ListPanel( m_pSequencesPage, "SequencesList" );
+ m_pSequencesList->AddColumnHeader( 0, "sequence", "sequence", 52, 0 );
+ m_pSequencesList->AddActionSignalTarget( this );
+ m_pSequencesList->SetSelectIndividualCells( true );
+ m_pSequencesList->SetEmptyListText(".MDL file contains no activities");
+ m_pSequencesList->SetDragEnabled( true );
+ m_pSequencesList->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_DOWNANDRIGHT, 0, 0, 0, 0 );
+ m_pSequencesList->SetSortFunc( 0, SequenceSortFunc );
+ m_pSequencesList->SetSortColumn( 0 );
+ }
+
+ // Activities
+ m_pActivitiesPage = NULL;
+ m_pActivitiesList = NULL;
+ if ( nFlags & PICK_ACTIVITIES )
+ {
+ m_pActivitiesPage = new PropertyPage( m_pViewsSheet, "ActivitiesPage" );
+ m_pViewsSheet->AddPage( m_pActivitiesPage, "Activities" );
+ m_pActivitiesList = new ListPanel( m_pActivitiesPage, "ActivitiesList" );
+ m_pActivitiesList->AddColumnHeader( 0, "activity", "activity", 52, 0 );
+ m_pActivitiesList->AddActionSignalTarget( this );
+ m_pActivitiesList->SetSelectIndividualCells( true );
+ m_pActivitiesList->SetEmptyListText( ".MDL file contains no activities" );
+ m_pActivitiesList->SetDragEnabled( true );
+ m_pActivitiesList->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_DOWNANDRIGHT, 0, 0, 0, 0 );
+ m_pActivitiesList->SetSortFunc( 0, ActivitySortFunc );
+ m_pActivitiesList->SetSortColumn( 0 );
+ }
+
+ // Load layout settings; has to happen before pinning occurs in code
+ LoadControlSettingsAndUserConfig( "resource/sequencepicker.res" );
+
+ SETUP_PANEL( this );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CSequencePicker::~CSequencePicker()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Performs layout
+//-----------------------------------------------------------------------------
+void CSequencePicker::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_pPreviewSplitter->SetBounds( 0, 0, w, h );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: rebuilds the list of activities + sequences
+//-----------------------------------------------------------------------------
+void CSequencePicker::RefreshActivitiesAndSequencesList()
+{
+ if ( m_pActivitiesList )
+ {
+ m_pActivitiesList->RemoveAll();
+ }
+
+ if ( m_pSequencesList )
+ {
+ m_pSequencesList->RemoveAll();
+ }
+
+ m_pMDLPreview->SetSequence( 0 );
+
+ if ( m_hSelectedMDL == MDLHANDLE_INVALID )
+ return;
+
+ studiohdr_t *hdr = vgui::MDLCache()->GetStudioHdr( m_hSelectedMDL );
+
+ CUtlDict<int, unsigned short> activityNames( true, 0, hdr->GetNumSeq() );
+
+ for (int j = 0; j < hdr->GetNumSeq(); j++)
+ {
+ if ( /*g_viewerSettings.showHidden ||*/ !(hdr->pSeqdesc(j).flags & STUDIO_HIDDEN))
+ {
+ const char *pActivityName = hdr->pSeqdesc(j).pszActivityName();
+ if ( m_pActivitiesList && pActivityName && pActivityName[0] )
+ {
+ // Multiple sequences can have the same activity name; only add unique activity names
+ if ( activityNames.Find( pActivityName ) == activityNames.InvalidIndex() )
+ {
+ KeyValues *pkv = new KeyValues("node", "activity", pActivityName );
+ int nItemID = m_pActivitiesList->AddItem( pkv, 0, false, false );
+
+ KeyValues *pDrag = new KeyValues( "drag", "text", pActivityName );
+ pDrag->SetString( "texttype", "activityName" );
+ pDrag->SetString( "mdl", vgui::MDLCache()->GetModelName( m_hSelectedMDL ) );
+ m_pActivitiesList->SetItemDragData( nItemID, pDrag );
+
+ activityNames.Insert( pActivityName, j );
+ }
+ }
+
+ const char *pSequenceName = hdr->pSeqdesc(j).pszLabel();
+ if ( m_pSequencesList && pSequenceName && pSequenceName[0] )
+ {
+ KeyValues *pkv = new KeyValues("node", "sequence", pSequenceName);
+ int nItemID = m_pSequencesList->AddItem( pkv, 0, false, false );
+
+ KeyValues *pDrag = new KeyValues( "drag", "text", pSequenceName );
+ pDrag->SetString( "texttype", "sequenceName" );
+ pDrag->SetString( "mdl", vgui::MDLCache()->GetModelName( m_hSelectedMDL ) );
+ m_pSequencesList->SetItemDragData( nItemID, pDrag );
+ }
+ }
+ }
+
+ if ( m_pSequencesList )
+ {
+ m_pSequencesList->SortList();
+ }
+
+ if ( m_pActivitiesList )
+ {
+ m_pActivitiesList->SortList();
+ }
+}
+
+
+/*
+//-----------------------------------------------------------------------------
+// Purpose: Selects an sequence based on an activity
+//-----------------------------------------------------------------------------
+int SelectWeightedSequence( studiohdr_t *pstudiohdr, int activity, int curSequence )
+{
+ if (! pstudiohdr)
+ return 0;
+
+ VerifySequenceIndex( pstudiohdr );
+
+ int weighttotal = 0;
+ int seq = ACTIVITY_NOT_AVAILABLE;
+ int weight = 0;
+ for (int i = 0; i < pstudiohdr->GetNumSeq(); i++)
+ {
+ int curActivity = GetSequenceActivity( pstudiohdr, i, &weight );
+ if (curActivity == activity)
+ {
+ if ( curSequence == i && weight < 0 )
+ {
+ seq = i;
+ break;
+ }
+ weighttotal += iabs(weight);
+
+ int randomValue;
+ if ( IsInPrediction() )
+ randomValue = SharedRandomInt( "SelectWeightedSequence", 0, weighttotal - 1, i );
+ else
+ randomValue = RandomInt( 0, weighttotal - 1 );
+
+ if (!weighttotal || randomValue < iabs(weight))
+ seq = i;
+ }
+ }
+
+ return seq;
+}
+*/
+
+
+//-----------------------------------------------------------------------------
+// Gets the selected activity/sequence
+//-----------------------------------------------------------------------------
+CSequencePicker::PickType_t CSequencePicker::GetSelectedSequenceType( )
+{
+ if ( m_pSequencesPage && ( m_pViewsSheet->GetActivePage() == m_pSequencesPage ) )
+ return PICK_SEQUENCES;
+ if ( m_pActivitiesPage && ( m_pViewsSheet->GetActivePage() == m_pActivitiesPage ) )
+ return PICK_ACTIVITIES;
+ return PICK_NONE;
+}
+
+const char *CSequencePicker::GetSelectedSequenceName( )
+{
+ if ( m_pSequencesPage && ( m_pViewsSheet->GetActivePage() == m_pSequencesPage ) )
+ {
+ int nIndex = m_pSequencesList->GetSelectedItem( 0 );
+ if ( nIndex >= 0 )
+ {
+ KeyValues *pkv = m_pSequencesList->GetItem( nIndex );
+ return pkv->GetString( "sequence", NULL );
+ }
+ return NULL;
+ }
+
+ if ( m_pActivitiesPage && ( m_pViewsSheet->GetActivePage() == m_pActivitiesPage ) )
+ {
+ int nIndex = m_pActivitiesList->GetSelectedItem( 0 );
+ if ( nIndex >= 0 )
+ {
+ KeyValues *pkv = m_pActivitiesList->GetItem( nIndex );
+ return pkv->GetString( "activity", NULL );
+ }
+ return NULL;
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Plays the selected activity
+//-----------------------------------------------------------------------------
+void CSequencePicker::PlayActivity( const char *pActivityName )
+{
+ studiohdr_t *pstudiohdr = vgui::MDLCache()->GetStudioHdr( m_hSelectedMDL );
+ for ( int i = 0; i < pstudiohdr->GetNumSeq(); i++ )
+ {
+ mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( i );
+ if ( stricmp( seqdesc.pszActivityName(), pActivityName ) == 0 )
+ {
+ // FIXME: Add weighted sequence selection logic?
+ m_pMDLPreview->SetSequence( i );
+ break;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Plays the selected sequence
+//-----------------------------------------------------------------------------
+void CSequencePicker::PlaySequence( const char *pSequenceName )
+{
+ studiohdr_t *pstudiohdr = vgui::MDLCache()->GetStudioHdr( m_hSelectedMDL );
+ for (int i = 0; i < pstudiohdr->GetNumSeq(); i++)
+ {
+ mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( i );
+ if ( !Q_stricmp( seqdesc.pszLabel(), pSequenceName ) )
+ {
+ m_pMDLPreview->SetSequence( i );
+ break;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when a page is shown
+//-----------------------------------------------------------------------------
+void CSequencePicker::OnPageChanged( )
+{
+ if ( m_pSequencesPage && ( m_pViewsSheet->GetActivePage() == m_pSequencesPage ) )
+ {
+ const char *pSequenceName = GetSelectedSequenceName();
+ if ( pSequenceName )
+ {
+ PlaySequence( pSequenceName );
+ PostActionSignal( new KeyValues( "SequencePreviewChanged", "sequence", pSequenceName ) );
+ }
+ return;
+ }
+
+ if ( m_pActivitiesPage && ( m_pViewsSheet->GetActivePage() == m_pActivitiesPage ) )
+ {
+ const char *pActivityName = GetSelectedSequenceName();
+ if ( pActivityName )
+ {
+ PlayActivity( pActivityName );
+ PostActionSignal( new KeyValues( "SequencePreviewChanged", "activity", pActivityName ) );
+ }
+ return;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: refreshes dialog on text changing
+//-----------------------------------------------------------------------------
+void CSequencePicker::OnItemSelected( KeyValues *kv )
+{
+ Panel *pPanel = (Panel *)kv->GetPtr("panel", NULL);
+ if ( m_pSequencesList && (pPanel == m_pSequencesList ) )
+ {
+ const char *pSequenceName = GetSelectedSequenceName();
+ if ( pSequenceName )
+ {
+ PlaySequence( pSequenceName );
+ PostActionSignal( new KeyValues( "SequencePreviewChanged", "sequence", pSequenceName ) );
+ }
+ return;
+ }
+
+ if ( m_pActivitiesList && ( pPanel == m_pActivitiesList ) )
+ {
+ const char *pActivityName = GetSelectedSequenceName();
+ if ( pActivityName )
+ {
+ PlayActivity( pActivityName );
+ PostActionSignal( new KeyValues( "SequencePreviewChanged", "activity", pActivityName ) );
+ }
+ return;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the MDL to select sequences in
+//-----------------------------------------------------------------------------
+void CSequencePicker::SetMDL( const char *pMDLName )
+{
+ m_hSelectedMDL = pMDLName ? vgui::MDLCache()->FindMDL( pMDLName ) : MDLHANDLE_INVALID;
+ if ( vgui::MDLCache()->IsErrorModel( m_hSelectedMDL ) )
+ {
+ m_hSelectedMDL = MDLHANDLE_INVALID;
+ }
+ m_pMDLPreview->SetMDL( m_hSelectedMDL );
+ m_pMDLPreview->LookAtMDL();
+ RefreshActivitiesAndSequencesList();
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Purpose: Modal picker frame
+//
+//-----------------------------------------------------------------------------
+CSequencePickerFrame::CSequencePickerFrame( vgui::Panel *pParent, int nFlags ) : BaseClass( pParent, "SequencePickerFrame" )
+{
+ SetDeleteSelfOnClose( true );
+ m_pPicker = new CSequencePicker( this, nFlags );
+ m_pPicker->AddActionSignalTarget( this );
+ m_pOpenButton = new Button( this, "OpenButton", "#FileOpenDialog_Open", this, "Open" );
+ m_pCancelButton = new Button( this, "CancelButton", "#FileOpenDialog_Cancel", this, "Cancel" );
+ SetBlockDragChaining( true );
+
+ LoadControlSettingsAndUserConfig( "resource/sequencepickerframe.res" );
+
+ m_pOpenButton->SetEnabled( false );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Activate the dialog
+//-----------------------------------------------------------------------------
+void CSequencePickerFrame::DoModal( const char *pMDLName )
+{
+ m_pPicker->SetMDL( pMDLName );
+ BaseClass::DoModal();
+}
+
+
+//-----------------------------------------------------------------------------
+// On mdl preview changed
+//-----------------------------------------------------------------------------
+void CSequencePickerFrame::OnSequencePreviewChanged( KeyValues *pKeyValues )
+{
+ const char *pSequence = pKeyValues->GetString( "sequence", NULL );
+ const char *pActivity = pKeyValues->GetString( "activity", NULL );
+ m_pOpenButton->SetEnabled( pSequence || pActivity );
+}
+
+
+//-----------------------------------------------------------------------------
+// On command
+//-----------------------------------------------------------------------------
+void CSequencePickerFrame::OnCommand( const char *pCommand )
+{
+ if ( !Q_stricmp( pCommand, "Open" ) )
+ {
+ CSequencePicker::PickType_t type = m_pPicker->GetSelectedSequenceType( );
+ if (( type == CSequencePicker::PICK_SEQUENCES ) || ( type == CSequencePicker::PICK_ACTIVITIES ))
+ {
+ const char *pSequenceName = m_pPicker->GetSelectedSequenceName();
+ if ( pSequenceName )
+ {
+ if ( type == CSequencePicker::PICK_SEQUENCES )
+ {
+ PostActionSignal( new KeyValues("SequenceSelected", "sequence", pSequenceName ) );
+ }
+ else
+ {
+ PostActionSignal( new KeyValues("SequenceSelected", "activity", pSequenceName ) );
+ }
+ CloseModal();
+ return;
+ }
+ }
+ return;
+ }
+
+ if ( !Q_stricmp( pCommand, "Cancel" ) )
+ {
+ CloseModal();
+ return;
+ }
+
+ BaseClass::OnCommand( pCommand );
+}
+
+
diff --git a/vgui2/matsys_controls/tgapreviewpanel.cpp b/vgui2/matsys_controls/tgapreviewpanel.cpp
new file mode 100644
index 0000000..c91b6f3
--- /dev/null
+++ b/vgui2/matsys_controls/tgapreviewpanel.cpp
@@ -0,0 +1,72 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//===========================================================================//
+
+#include "matsys_controls/tgapreviewpanel.h"
+#include "bitmap/tgaloader.h"
+#include "tier1/utlbuffer.h"
+#include "filesystem.h"
+
+using namespace vgui;
+
+
+//-----------------------------------------------------------------------------
+//
+// TGA Preview panel
+//
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// constructor
+//-----------------------------------------------------------------------------
+CTGAPreviewPanel::CTGAPreviewPanel( vgui::Panel *pParent, const char *pName ) :
+ BaseClass( pParent, pName )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the current TGA
+//-----------------------------------------------------------------------------
+void CTGAPreviewPanel::SetTGA( const char *pFullPath )
+{
+ int nWidth, nHeight;
+ ImageFormat format;
+ float flGamma;
+
+ CUtlBuffer buf;
+ if ( !g_pFullFileSystem->ReadFile( pFullPath, NULL, buf ) )
+ {
+ Warning( "Can't open TGA file: %s\n", pFullPath );
+ return;
+ }
+
+ TGALoader::GetInfo( buf, &nWidth, &nHeight, &format, &flGamma );
+
+ Shutdown();
+ Init( nWidth, nHeight, true );
+ m_TGAName = pFullPath;
+
+ buf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
+ if ( !TGALoader::Load( (unsigned char*)GetImageBuffer(), buf,
+ nWidth, nHeight, IMAGE_FORMAT_BGRA8888, flGamma, false ) )
+ {
+ Shutdown();
+ }
+ else
+ {
+ DownloadTexture();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets the current TGA
+//-----------------------------------------------------------------------------
+const char *CTGAPreviewPanel::GetTGA() const
+{
+ return m_TGAName;
+}
diff --git a/vgui2/matsys_controls/vmtpanel.cpp b/vgui2/matsys_controls/vmtpanel.cpp
new file mode 100644
index 0000000..2b37f1f
--- /dev/null
+++ b/vgui2/matsys_controls/vmtpanel.cpp
@@ -0,0 +1,458 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//===========================================================================//
+
+#include "matsys_controls/vmtpanel.h"
+#include "materialsystem/imesh.h"
+#include "materialsystem/imaterial.h"
+#include "materialsystem/itexture.h"
+#include "VGuiMatSurface/IMatSystemSurface.h"
+#include "vgui_controls/ScrollBar.h"
+#include "matsys_controls/matsyscontrols.h"
+#include "vgui/IVGui.h"
+#include "vgui_controls/ToolWindow.h"
+#include "tier2/renderutils.h"
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// Enums
+//-----------------------------------------------------------------------------
+enum
+{
+ SCROLLBAR_SIZE=18, // the width of a scrollbar
+ WINDOW_BORDER_WIDTH=2 // the width of the window's border
+};
+
+#define SPHERE_RADIUS 10.0f
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+CVMTPanel::CVMTPanel( vgui::Panel *pParent, const char *pName ) : BaseClass( pParent, pName )
+{
+ m_bUseActualSize = true;
+ m_pMaterial = NULL;
+
+ m_pHorizontalBar = new ScrollBar( this, "HorizScrollBar", false );
+ m_pHorizontalBar->AddActionSignalTarget(this);
+ m_pHorizontalBar->SetVisible(false);
+
+ m_pVerticalBar = new ScrollBar( this, "VertScrollBar", true );
+ m_pVerticalBar->AddActionSignalTarget(this);
+ m_pVerticalBar->SetVisible(false);
+
+ LookAt( SPHERE_RADIUS );
+
+ m_pLightmapTexture.Init( "//platform/materials/debug/defaultlightmap", "editor" );
+ m_DefaultEnvCubemap.Init( "editor/cubemap", "editor", true );
+}
+
+CVMTPanel::~CVMTPanel()
+{
+ m_pLightmapTexture.Shutdown();
+ m_DefaultEnvCubemap.Shutdown();
+ if (m_pMaterial)
+ {
+ m_pMaterial->DecrementReferenceCount();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Scheme
+//-----------------------------------------------------------------------------
+void CVMTPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+ SetBorder( pScheme->GetBorder( "MenuBorder") );
+}
+
+
+//-----------------------------------------------------------------------------
+// Set the material to draw
+//-----------------------------------------------------------------------------
+void CVMTPanel::SetMaterial( IMaterial *pMaterial )
+{
+ if (pMaterial)
+ {
+ pMaterial->IncrementReferenceCount();
+ }
+ if (m_pMaterial)
+ {
+ m_pMaterial->DecrementReferenceCount();
+ }
+ m_pMaterial = pMaterial;
+ InvalidateLayout();
+}
+
+
+//-----------------------------------------------------------------------------
+// Set rendering mode (stretch to full screen, or use actual size)
+//-----------------------------------------------------------------------------
+void CVMTPanel::RenderUsingActualSize( bool bEnable )
+{
+ m_bUseActualSize = bEnable;
+ InvalidateLayout();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: relayouts out the panel after any internal changes
+//-----------------------------------------------------------------------------
+void CVMTPanel::PerformLayout()
+{
+ BaseClass::PerformLayout();
+ return;
+
+ // Get the current size, see if it's big enough to view the entire thing
+ int iWidth, iHeight;
+ GetSize( iWidth, iHeight );
+
+ // In the case of stretching, just stretch to the size and blow off
+ // the scrollbars. Same holds true if there's no material
+ if (!m_bUseActualSize || !m_pMaterial)
+ {
+ m_iViewableWidth = iWidth;
+ m_iViewableHeight = iHeight;
+ m_pHorizontalBar->SetVisible(false);
+ m_pVerticalBar->SetVisible(false);
+ return;
+ }
+
+ // Check the size of the material...
+ int iMaterialWidth = m_pMaterial->GetMappingWidth();
+ int iMaterialHeight = m_pMaterial->GetMappingHeight();
+
+ // Check if the scroll bars are visible
+ bool bHorizScrollVisible = (iMaterialWidth > iWidth);
+ bool bVertScrollVisible = (iMaterialHeight > iHeight);
+
+ m_pHorizontalBar->SetVisible(bHorizScrollVisible);
+ m_pVerticalBar->SetVisible(bVertScrollVisible);
+
+ // Shrink the bars if both are visible
+ m_iViewableWidth = bVertScrollVisible ? iWidth - SCROLLBAR_SIZE - WINDOW_BORDER_WIDTH : iWidth;
+ m_iViewableHeight = bHorizScrollVisible ? iHeight - SCROLLBAR_SIZE - WINDOW_BORDER_WIDTH : iHeight;
+
+ // Set the position of the horizontal bar...
+ if (bHorizScrollVisible)
+ {
+ m_pHorizontalBar->SetPos(0, iHeight - SCROLLBAR_SIZE);
+ m_pHorizontalBar->SetSize( m_iViewableWidth, SCROLLBAR_SIZE );
+
+ m_pHorizontalBar->SetRangeWindow( m_iViewableWidth );
+ m_pHorizontalBar->SetRange( 0, iMaterialWidth );
+
+ // FIXME: Change scroll amount based on how much is not visible?
+ m_pHorizontalBar->SetButtonPressedScrollValue( 5 );
+ }
+
+ // Set the position of the vertical bar...
+ if (bVertScrollVisible)
+ {
+ m_pVerticalBar->SetPos(iWidth - SCROLLBAR_SIZE, 0);
+ m_pVerticalBar->SetSize(SCROLLBAR_SIZE, m_iViewableHeight);
+
+ m_pVerticalBar->SetRangeWindow( m_iViewableHeight );
+ m_pVerticalBar->SetRange( 0, iMaterialHeight);
+ m_pVerticalBar->SetButtonPressedScrollValue( 5 );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// paint it stretched to the window size
+//-----------------------------------------------------------------------------
+void CVMTPanel::DrawStretchedToPanel( CMeshBuilder &meshBuilder )
+{
+ // Draw a polygon the size of the panel
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.Position3f( 0, 0, 0 );
+ meshBuilder.TexCoord2f( 0, 0, 0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.Position3f( 0, m_iViewableHeight, 0 );
+ meshBuilder.TexCoord2f( 0, 0, 1 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.Position3f( m_iViewableWidth, m_iViewableHeight, 0 );
+ meshBuilder.TexCoord2f( 0, 1, 1 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.Position3f( m_iViewableWidth, 0, 0 );
+ meshBuilder.TexCoord2f( 0, 0, 1 );
+ meshBuilder.AdvanceVertex();
+}
+
+
+//-----------------------------------------------------------------------------
+// paint it actual size
+//-----------------------------------------------------------------------------
+void CVMTPanel::DrawActualSize( CMeshBuilder &meshBuilder )
+{
+ // Check the size of the material...
+ int iMaterialWidth = m_pMaterial->GetMappingWidth();
+ int iMaterialHeight = m_pMaterial->GetMappingHeight();
+
+ Vector2D ul;
+ Vector2D lr;
+ Vector2D tul;
+ Vector2D tlr;
+
+ if (m_iViewableWidth >= iMaterialWidth)
+ {
+ // Center the material if we've got enough horizontal space
+ ul.x = (m_iViewableWidth - iMaterialWidth) * 0.5f;
+ lr.x = ul.x + iMaterialWidth;
+ tul.x = 0.0f; tlr.x = 1.0f;
+ }
+ else
+ {
+ // Use the scrollbars here...
+ int val = m_pHorizontalBar->GetValue();
+ tul.x = (float)val / (float)iMaterialWidth;
+ tlr.x = tul.x + (float)m_iViewableWidth / (float)iMaterialWidth;
+
+ ul.x = 0;
+ lr.x = m_iViewableWidth;
+ }
+
+ if (m_iViewableHeight >= iMaterialHeight)
+ {
+ // Center the material if we've got enough vertical space
+ ul.y = (m_iViewableHeight - iMaterialHeight) * 0.5f;
+ lr.y = ul.y + iMaterialHeight;
+ tul.y = 0.0f; tlr.y = 1.0f;
+ }
+ else
+ {
+ // Use the scrollbars here...
+ int val = m_pVerticalBar->GetValue();
+
+ tul.y = (float)val / (float)iMaterialHeight;
+ tlr.y = tul.y + (float)m_iViewableHeight / (float)iMaterialHeight;
+
+ ul.y = 0;
+ lr.y = m_iViewableHeight;
+ }
+
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.Position3f( ul.x, ul.y, 0 );
+ meshBuilder.TexCoord2f( 0, tul.x, tul.y );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.Position3f( lr.x, ul.y, 0 );
+ meshBuilder.TexCoord2f( 0, tlr.x, tul.y );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.Position3f( lr.x, lr.y, 0 );
+ meshBuilder.TexCoord2f( 0, tlr.x, tlr.y );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.Position3f( ul.x, lr.y, 0 );
+ meshBuilder.TexCoord2f( 0, tul.x, tlr.y );
+ meshBuilder.AdvanceVertex();
+}
+
+
+//-----------------------------------------------------------------------------
+// Draw it on a sphere
+//-----------------------------------------------------------------------------
+void CVMTPanel::RenderSphere( const Vector &vCenter, float flRadius, int nTheta, int nPhi )
+{
+ int nVertices = nTheta * nPhi;
+ int nIndices = 2 * ( nTheta + 1 ) * ( nPhi - 1 );
+
+ CMatRenderContextPtr pRenderContext( MaterialSystem() );
+ pRenderContext->FogMode( MATERIAL_FOG_NONE );
+ pRenderContext->SetNumBoneWeights( 0 );
+ pRenderContext->Bind( m_pMaterial );
+ pRenderContext->BindLightmapTexture( m_pLightmapTexture );
+ pRenderContext->BindLocalCubemap( m_DefaultEnvCubemap );
+
+ IMesh* pMesh = pRenderContext->GetDynamicMesh();
+
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, nVertices, nIndices );
+
+ bool bIsUsingLightmap = m_pMaterial->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_LIGHTMAP );
+ bool bIsUsingBumpedLightmap = m_pMaterial->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_BUMPED_LIGHTMAPS );
+
+ int nLightmapWidth = m_pLightmapTexture->GetActualWidth();
+ float flHalfLuxel = 0.5f / nLightmapWidth;
+
+ //
+ // Build the index buffer.
+ //
+ int i, j;
+ for ( i = 0; i < nPhi; ++i )
+ {
+ for ( j = 0; j < nTheta; ++j )
+ {
+ float u = j / ( float )(nTheta - 1);
+ float v = i / ( float )(nPhi - 1);
+ float theta = ( j != nTheta-1 ) ? 2.0f * M_PI * u : 0.0f;
+ float phi = M_PI * v;
+
+ Vector vecPos;
+ vecPos.x = flRadius * sin(phi) * cos(theta);
+ vecPos.y = flRadius * sin(phi) * sin(theta);
+ vecPos.z = flRadius * cos(phi);
+
+ Vector vecNormal = vecPos;
+ VectorNormalize( vecNormal );
+
+ Vector4D vecTangentS;
+ Vector vecTangentT;
+ vecTangentS.Init( -vecPos.y, vecPos.x, 0.0f, 1.0f );
+ if ( VectorNormalize( vecTangentS.AsVector3D() ) == 0.0f )
+ {
+ vecTangentS.Init( 1.0f, 0.0f, 0.0f, 1.0f );
+ }
+
+ CrossProduct( vecNormal, vecTangentS.AsVector3D(), vecTangentT );
+
+ unsigned char red = (int)( u * 255.0f );
+ unsigned char green = (int)( v * 255.0f );
+ unsigned char blue = (int)( v * 255.0f );
+ unsigned char alpha = (int)( v * 255.0f );
+
+ vecPos += vCenter;
+
+ float u1, u2, v1, v2;
+ u1 = u2 = u;
+ v1 = v2 = v;
+
+ if ( bIsUsingLightmap )
+ {
+ u1 = RemapVal( u1, 0.0f, 1.0f, flHalfLuxel, 0.25 - flHalfLuxel );
+
+ if ( bIsUsingBumpedLightmap )
+ {
+ u2 = 0.25f;
+ v2 = 0.0f;
+ }
+ }
+
+ meshBuilder.Position3fv( vecPos.Base() );
+ meshBuilder.Normal3fv( vecNormal.Base() );
+ meshBuilder.Color4ub( red, green, blue, alpha );
+ meshBuilder.TexCoord2f( 0, u, v );
+ meshBuilder.TexCoord2f( 1, u1, v1 );
+ meshBuilder.TexCoord2f( 2, u2, v2 );
+ meshBuilder.TangentS3fv( vecTangentS.Base() );
+ meshBuilder.TangentT3fv( vecTangentT.Base() );
+ meshBuilder.UserData( vecTangentS.Base() );
+ meshBuilder.AdvanceVertex();
+ }
+ }
+
+ //
+ // Emit the triangle strips.
+ //
+ int idx = 0;
+ for ( i = 0; i < nPhi - 1; ++i )
+ {
+ for ( j = 0; j < nTheta; ++j )
+ {
+ idx = nTheta * i + j;
+
+ meshBuilder.FastIndex( idx );
+ meshBuilder.FastIndex( idx + nTheta );
+ }
+
+ //
+ // Emit a degenerate triangle to skip to the next row without
+ // a connecting triangle.
+ //
+ if ( i < nPhi - 2 )
+ {
+ meshBuilder.FastIndex( idx + 1 );
+ meshBuilder.FastIndex( idx + 1 + nTheta );
+ }
+ }
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+
+//-----------------------------------------------------------------------------
+// Power of two FB texture
+//-----------------------------------------------------------------------------
+static CTextureReference s_pPowerOfTwoFrameBufferTexture;
+
+static ITexture *GetPowerOfTwoFrameBufferTexture( void )
+{
+ if( !s_pPowerOfTwoFrameBufferTexture )
+ {
+ s_pPowerOfTwoFrameBufferTexture.Init( materials->FindTexture( "_rt_PowerOfTwoFB", TEXTURE_GROUP_RENDER_TARGET ) );
+ }
+
+ return s_pPowerOfTwoFrameBufferTexture;
+}
+
+
+//-----------------------------------------------------------------------------
+// paint it!
+//-----------------------------------------------------------------------------
+void CVMTPanel::OnPaint3D()
+{
+ if (!m_pMaterial)
+ return;
+
+ // Deal with refraction
+ CMatRenderContextPtr pRenderContext( MaterialSystem() );
+ if ( m_pMaterial->NeedsPowerOfTwoFrameBufferTexture() )
+ {
+ ITexture *pTexture = GetPowerOfTwoFrameBufferTexture();
+ if ( pTexture && !pTexture->IsError() )
+ {
+ pRenderContext->CopyRenderTargetToTexture( pTexture );
+ pRenderContext->SetFrameBufferCopyTexture( pTexture );
+ }
+ }
+
+ // Draw a background (translucent objects will appear that way)
+
+ // FIXME: Draw the outline of this panel?
+
+ pRenderContext->CullMode( MATERIAL_CULLMODE_CW );
+
+ RenderSphere( vec3_origin, SPHERE_RADIUS, 20, 20 );
+ /*
+ pRenderContext->MatrixMode( MATERIAL_PROJECTION );
+ pRenderContext->LoadIdentity();
+ pRenderContext->Ortho( 0, 0, m_iViewableWidth, m_iViewableHeight, 0, 1 );
+
+ pRenderContext->Bind( m_pMaterial );
+ IMesh *pMesh = pRenderContext->GetDynamicMesh();
+ CMeshBuilder meshBuilder;
+
+ meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
+
+ if (!m_bUseActualSize)
+ {
+ DrawStretchedToPanel( meshBuilder );
+ }
+ else
+ {
+ DrawActualSize( meshBuilder );
+ }
+
+ meshBuilder.End();
+ pMesh->Draw();
+ */
+}
+ \ No newline at end of file
diff --git a/vgui2/matsys_controls/vmtpicker.cpp b/vgui2/matsys_controls/vmtpicker.cpp
new file mode 100644
index 0000000..f642067
--- /dev/null
+++ b/vgui2/matsys_controls/vmtpicker.cpp
@@ -0,0 +1,78 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "filesystem.h"
+#include "matsys_controls/vmtpicker.h"
+#include "matsys_controls/vmtpreviewpanel.h"
+#include "vgui_controls/Splitter.h"
+
+
+using namespace vgui;
+
+
+//-----------------------------------------------------------------------------
+//
+// Asset Picker with no preview
+//
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CVMTPicker::CVMTPicker( vgui::Panel *pParent, bool bAllowMultiselect ) :
+ BaseClass( pParent, "VMT Files", "vmt", "materials", "vmtName" )
+{
+ // Horizontal splitter for preview
+ m_pPreviewSplitter = new Splitter( this, "PreviewSplitter", SPLITTER_MODE_VERTICAL, 1 );
+ vgui::Panel *pSplitterLeftSide = m_pPreviewSplitter->GetChild( 0 );
+ vgui::Panel *pSplitterRightSide = m_pPreviewSplitter->GetChild( 1 );
+
+ m_p2D3DSplitter = new Splitter( pSplitterRightSide, "2D3DSplitter", SPLITTER_MODE_HORIZONTAL, 1 );
+ vgui::Panel *pSplitterTopSide = m_p2D3DSplitter->GetChild( 0 );
+ vgui::Panel *pSplitterBottomSide = m_p2D3DSplitter->GetChild( 1 );
+
+ // VMT preview
+ m_pVMTPreview2D = new CVMTPreviewPanel( pSplitterTopSide, "VMTPreview2D" );
+ m_pVMTPreview3D = new CVMTPreviewPanel( pSplitterBottomSide, "VMTPreview3D" );
+ m_pVMTPreview3D->DrawIn3DMode( true );
+
+ // Standard browser controls
+ CreateStandardControls( pSplitterLeftSide, bAllowMultiselect );
+
+ LoadControlSettingsAndUserConfig( "resource/vmtpicker.res" );
+}
+
+CVMTPicker::~CVMTPicker()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Derived classes have this called when the previewed asset changes
+//-----------------------------------------------------------------------------
+void CVMTPicker::OnSelectedAssetPicked( const char *pAssetName )
+{
+ m_pVMTPreview2D->SetVMT( pAssetName );
+ m_pVMTPreview3D->SetVMT( pAssetName );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Purpose: Modal picker frame
+//
+//-----------------------------------------------------------------------------
+CVMTPickerFrame::CVMTPickerFrame( vgui::Panel *pParent, const char *pTitle, bool bAllowMultiselect ) :
+ BaseClass( pParent )
+{
+ SetAssetPicker( new CVMTPicker( this, bAllowMultiselect ) );
+ LoadControlSettingsAndUserConfig( "resource/vmtpickerframe.res" );
+ SetTitle( pTitle, false );
+}
+
+
+
diff --git a/vgui2/matsys_controls/vmtpreviewpanel.cpp b/vgui2/matsys_controls/vmtpreviewpanel.cpp
new file mode 100644
index 0000000..6616df2
--- /dev/null
+++ b/vgui2/matsys_controls/vmtpreviewpanel.cpp
@@ -0,0 +1,625 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//===========================================================================//
+
+#include "matsys_controls/vmtpreviewpanel.h"
+#include "matsys_controls/matsyscontrols.h"
+#include "VGuiMatSurface/IMatSystemSurface.h"
+#include "materialsystem/MaterialSystemUtil.h"
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/imaterial.h"
+#include "materialsystem/itexture.h"
+#include "materialsystem/imesh.h"
+#include "tier1/KeyValues.h"
+
+
+using namespace vgui;
+
+
+#define FOV 90.0f
+#define ZNEAR 0.1f
+#define ZFAR 2000.0f
+#define ROTATION_SPEED 40.0f // degrees/sec
+#define VIEW_DISTANCE 12.0f
+
+//-----------------------------------------------------------------------------
+//
+// VMT Preview panel
+//
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// constructor
+//-----------------------------------------------------------------------------
+CVMTPreviewPanel::CVMTPreviewPanel( vgui::Panel *pParent, const char *pName ) :
+ BaseClass( pParent, pName )
+{
+ SetVMT( "//platform/materials/vgui/vtfnotloaded" );
+
+ m_pLightmapTexture.Init( "//platform/materials/debug/defaultlightmap", "editor" );
+ m_DefaultEnvCubemap.Init( "editor/cubemap", "editor", true );
+ m_LightDirection.Init( 0.0f, 1.0f, -1.0f );
+ m_LightColor.SetColor( 255, 255, 255, 255 );
+ m_flLightIntensity = 2.0f;
+ m_bDrawIn3DMode = false;
+
+ // Reset the camera direction
+ m_vecCameraDirection.Init( 1.0f, 0.0f, 0.0f );
+ m_flLastRotationTime = Plat_FloatTime();
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the current VMT
+//-----------------------------------------------------------------------------
+void CVMTPreviewPanel::SetVMT( const char *pMaterialName )
+{
+ m_Material.Init( pMaterialName, "editor material" );
+ m_VMTName = pMaterialName;
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets the current VMT
+//-----------------------------------------------------------------------------
+const char *CVMTPreviewPanel::GetVMT() const
+{
+ return m_VMTName;
+}
+
+
+//-----------------------------------------------------------------------------
+// View it in 3D or 2D mode
+//-----------------------------------------------------------------------------
+void CVMTPreviewPanel::DrawIn3DMode( bool b3DMode )
+{
+ m_bDrawIn3DMode = b3DMode;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets up lighting state
+//-----------------------------------------------------------------------------
+void CVMTPreviewPanel::SetupLightingState()
+{
+ LightDesc_t desc;
+ memset( &desc, 0, sizeof(desc) );
+
+ desc.m_Type = MATERIAL_LIGHT_DIRECTIONAL;
+
+ desc.m_Color[0] = m_LightColor.r();
+ desc.m_Color[1] = m_LightColor.g();
+ desc.m_Color[2] = m_LightColor.b();
+ desc.m_Color *= m_flLightIntensity / 255.0f;
+
+ desc.m_Attenuation0 = 1.0f;
+ desc.m_Attenuation1 = 0.0f;
+ desc.m_Attenuation2 = 0.0f;
+ desc.m_Flags = LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION0;
+
+ desc.m_Direction = m_LightDirection;
+ VectorNormalize( desc.m_Direction );
+
+ desc.m_Theta = 0.0f;
+ desc.m_Phi = 0.0f;
+ desc.m_Falloff = 1.0f;
+
+ CMatRenderContextPtr pRenderContext( MaterialSystem() );
+
+ pRenderContext->SetLight( 0, desc );
+}
+
+
+//-----------------------------------------------------------------------------
+// Draw a sphere
+//-----------------------------------------------------------------------------
+void CVMTPreviewPanel::RenderSphere( const Vector &vCenter, float flRadius, int nTheta, int nPhi )
+{
+ int nVertices = nTheta * nPhi;
+ int nIndices = 2 * ( nTheta + 1 ) * ( nPhi - 1 );
+
+ CMatRenderContextPtr pRenderContext( MaterialSystem() );
+
+ IMesh* pMesh = pRenderContext->GetDynamicMesh();
+
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, nVertices, nIndices );
+
+ bool bIsUsingLightmap = m_Material->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_LIGHTMAP );
+ bool bIsUsingBumpedLightmap = m_Material->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_BUMPED_LIGHTMAPS );
+
+ int nLightmapWidth = m_pLightmapTexture->GetActualWidth();
+ float flHalfLuxel = 0.5f / nLightmapWidth;
+
+ //
+ // Build the index buffer.
+ //
+ int i, j;
+ for ( i = 0; i < nPhi; ++i )
+ {
+ for ( j = 0; j < nTheta; ++j )
+ {
+ float u = j / ( float )(nTheta - 1);
+ float v = i / ( float )(nPhi - 1);
+ float theta = ( j != nTheta-1 ) ? 2.0f * M_PI * u : 0.0f;
+ float phi = M_PI * v;
+
+ Vector vecPos;
+ vecPos.x = flRadius * sin(phi) * cos(theta);
+ vecPos.y = flRadius * sin(phi) * sin(theta);
+ vecPos.z = flRadius * cos(phi);
+
+ Vector vecNormal = vecPos;
+ VectorNormalize( vecNormal );
+
+ Vector4D vecTangentS;
+ Vector vecTangentT;
+ vecTangentS.Init( vecPos.z, -vecPos.x, 0.0f, 1.0f );
+ if ( VectorNormalize( vecTangentS.AsVector3D() ) == 0.0f )
+ {
+ vecTangentS.Init( 1.0f, 0.0f, 0.0f, 1.0f );
+ }
+
+ CrossProduct( vecNormal, vecTangentS.AsVector3D(), vecTangentT );
+
+ unsigned char red = (int)( u * 255.0f );
+ unsigned char green = (int)( v * 255.0f );
+ unsigned char blue = (int)( v * 255.0f );
+ unsigned char alpha = (int)( v * 255.0f );
+
+ vecPos += vCenter;
+
+ float u1, u2, v1, v2;
+ u1 = u2 = u;
+ v1 = v2 = v;
+
+ if ( bIsUsingLightmap )
+ {
+ u1 = RemapVal( u1, 0.0f, 1.0f, flHalfLuxel, 0.25 - flHalfLuxel );
+
+ if ( bIsUsingBumpedLightmap )
+ {
+ u2 = 0.25f;
+ v2 = 0.0f;
+ }
+ }
+
+ meshBuilder.Position3fv( vecPos.Base() );
+ meshBuilder.Normal3fv( vecNormal.Base() );
+ meshBuilder.Color4ub( red, green, blue, alpha );
+ meshBuilder.TexCoord2f( 0, 2.0f * u, v );
+ meshBuilder.TexCoord2f( 1, u1, v1 );
+ meshBuilder.TexCoord2f( 2, u2, v2 );
+ meshBuilder.TangentS3fv( vecTangentS.Base() );
+ meshBuilder.TangentT3fv( vecTangentT.Base() );
+ meshBuilder.BoneWeight( 0, 1.0f );
+ meshBuilder.BoneMatrix( 0, 0 );
+ meshBuilder.UserData( vecTangentS.Base() );
+ meshBuilder.AdvanceVertex();
+ }
+ }
+
+ //
+ // Emit the triangle strips.
+ //
+ int idx = 0;
+ for ( i = 0; i < nPhi - 1; ++i )
+ {
+ for ( j = 0; j < nTheta; ++j )
+ {
+ idx = nTheta * i + j;
+
+ meshBuilder.FastIndex( idx );
+ meshBuilder.FastIndex( idx + nTheta );
+ }
+
+ //
+ // Emit a degenerate triangle to skip to the next row without
+ // a connecting triangle.
+ //
+ if ( i < nPhi - 2 )
+ {
+ meshBuilder.FastIndex( idx + 1 );
+ meshBuilder.FastIndex( idx + 1 + nTheta );
+ }
+ }
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+
+//-----------------------------------------------------------------------------
+// Draw sprite-card based materials
+//-----------------------------------------------------------------------------
+void CVMTPreviewPanel::RenderSpriteCard( const Vector &vCenter, float flRadius )
+{
+ CMatRenderContextPtr pRenderContext( MaterialSystem() );
+ IMesh *pMesh = pRenderContext->GetDynamicMesh();
+
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
+
+ // Draw a polygon the size of the panel
+ meshBuilder.Position3fv( vCenter.Base() );
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.TexCoord4f( 0, 0.0f, 0.0f, 1.0f, 1.0f );
+ meshBuilder.TexCoord4f( 1, 0.0f, 0.0f, 1.0f, 1.0f );
+ meshBuilder.TexCoord4f( 2, 0.0f, 0.0f, flRadius, 0.0f );
+ meshBuilder.TexCoord2f( 3, 0, 0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( vCenter.Base() );
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.TexCoord4f( 0, 0.0f, 0.0f, 1.0f, 1.0f );
+ meshBuilder.TexCoord4f( 1, 0.0f, 0.0f, 1.0f, 1.0f );
+ meshBuilder.TexCoord4f( 2, 0.0f, 0.0f, flRadius, 0.0f );
+ meshBuilder.TexCoord2f( 3, 0, 1 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( vCenter.Base() );
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.TexCoord4f( 0, 0.0f, 0.0f, 1.0f, 1.0f );
+ meshBuilder.TexCoord4f( 1, 0.0f, 0.0f, 1.0f, 1.0f );
+ meshBuilder.TexCoord4f( 2, 0.0f, 0.0f, flRadius, 0.0f );
+ meshBuilder.TexCoord2f( 3, 1, 1 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( vCenter.Base() );
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.TexCoord4f( 0, 0.0f, 0.0f, 1.0f, 1.0f );
+ meshBuilder.TexCoord4f( 1, 0.0f, 0.0f, 1.0f, 1.0f );
+ meshBuilder.TexCoord4f( 2, 0.0f, 0.0f, flRadius, 0.0f );
+ meshBuilder.TexCoord2f( 3, 1, 0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+
+//-----------------------------------------------------------------------------
+// Paints a regular texture
+//-----------------------------------------------------------------------------
+void CVMTPreviewPanel::DrawRectangle( void )
+{
+ // Get the aspect ratio of the material
+ int tw = m_Material->GetMappingWidth();
+ int th = m_Material->GetMappingHeight();
+
+ if ( tw <= 0 || th <= 0 )
+ return;
+
+ int w, h;
+ GetSize( w, h );
+ if ( w == 0 || h == 0 )
+ return;
+
+ SetupOrthoMatrix( w, h );
+ SetupLightingState();
+
+ CMatRenderContextPtr pRenderContext( MaterialSystem() );
+
+ pRenderContext->MatrixMode( MATERIAL_VIEW );
+ pRenderContext->LoadIdentity();
+
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->LoadIdentity();
+
+ IMesh* pMesh = pRenderContext->GetDynamicMesh();
+
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, 4, 4 );
+
+ bool bIsUsingLightmap = m_Material->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_LIGHTMAP );
+ bool bIsUsingBumpedLightmap = m_Material->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_BUMPED_LIGHTMAPS );
+
+ int nLightmapWidth = m_pLightmapTexture->GetActualWidth();
+ float flHalfLuxel = 0.5f / nLightmapWidth;
+ Vector2D halfTexel( 0.5f / tw, 0.5f / th );
+
+ Vector vecNormal( 0.0f, 0.0f, 1.0f );
+ Vector4D vecTangentS( 1.0f, 0.0f, 0.0f, 1.0f );
+ Vector vecTangentT;
+ CrossProduct( vecNormal, vecTangentS.AsVector3D(), vecTangentT );
+
+ float screenaspect = (float)tw / (float)th;
+ float aspect = (float)w / (float)h;
+
+ float ratio = screenaspect / aspect;
+
+ // Screen is wider, need bars at top and bottom
+ int x2, y2;
+ int x, y;
+ x = y = 0;
+ int nXBorder = w > 15 ? 5 : w / 3;
+ int nYBorder = h > 15 ? 5 : h / 3;
+ w -= 2 * nXBorder;
+ h -= 2 * nYBorder;
+ if ( ratio > 1.0f )
+ {
+ int usetall = (float)w / screenaspect;
+ y = ( h - usetall ) / 2;
+ h = usetall;
+ }
+ // Screen is narrower, need bars at left/right
+ else
+ {
+ int usewide = (float)h * screenaspect;
+ x = ( w - usewide ) / 2;
+ w = usewide;
+ }
+ x += nXBorder;
+ y += nYBorder;
+
+ x2 = x+w; y2 = y+h;
+
+ float u = halfTexel.x;
+ float v = halfTexel.y;
+
+ float u1_l, u1_r, v1_t, v1_b;
+ float u2_l, u2_r, v2_t, v2_b;
+
+ u1_l = u2_l = u;
+ u1_r = u2_r = 1.0f - u;
+ v1_t = v2_t = v;
+ v1_b = v2_b = 1.0f - v;
+
+ if ( bIsUsingLightmap )
+ {
+ u1_l = v1_t = flHalfLuxel;
+ u1_r = v1_b = 0.25 - flHalfLuxel;
+ if ( bIsUsingBumpedLightmap )
+ {
+ u2_l = u2_r = 0.25f;
+ v2_t = v2_b = 0.0f;
+ }
+ }
+
+ meshBuilder.Position3f( x, y2, 0.0f );
+ meshBuilder.Normal3fv( vecNormal.Base() );
+ meshBuilder.Color4ub( 255, 0, 0, 255 );
+ meshBuilder.TexCoord2f( 0, u, v );
+ meshBuilder.TexCoord2f( 1, u1_l, v1_t );
+ meshBuilder.TexCoord2f( 2, u2_l, v2_t );
+ meshBuilder.TangentS3fv( vecTangentS.Base() );
+ meshBuilder.TangentT3fv( vecTangentT.Base() );
+ meshBuilder.BoneWeight( 0, 1.0f );
+ meshBuilder.BoneMatrix( 0, 0 );
+ meshBuilder.UserData( vecTangentS.Base() );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( x, y, 0.0f );
+ meshBuilder.Normal3fv( vecNormal.Base() );
+ meshBuilder.Color4ub( 255, 255, 255, 64 );
+ meshBuilder.TexCoord2f( 0, u, 1.0f - v );
+ meshBuilder.TexCoord2f( 1, u1_l, v1_b );
+ meshBuilder.TexCoord2f( 2, u2_l, v2_b );
+ meshBuilder.TangentS3fv( vecTangentS.Base() );
+ meshBuilder.TangentT3fv( vecTangentT.Base() );
+ meshBuilder.BoneWeight( 0, 1.0f );
+ meshBuilder.BoneMatrix( 0, 0 );
+ meshBuilder.UserData( vecTangentS.Base() );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( x2, y2, 0.0f );
+ meshBuilder.Normal3fv( vecNormal.Base() );
+ meshBuilder.Color4ub( 0, 0, 255, 255 );
+ meshBuilder.TexCoord2f( 0, 1.0f - u, v );
+ meshBuilder.TexCoord2f( 1, u1_r, v1_t );
+ meshBuilder.TexCoord2f( 2, u2_r, v2_t );
+ meshBuilder.TangentS3fv( vecTangentS.Base() );
+ meshBuilder.TangentT3fv( vecTangentT.Base() );
+ meshBuilder.BoneWeight( 0, 1.0f );
+ meshBuilder.BoneMatrix( 0, 0 );
+ meshBuilder.UserData( vecTangentS.Base() );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( x2, y, 0.0f );
+ meshBuilder.Normal3fv( vecNormal.Base() );
+ meshBuilder.Color4ub( 0, 255, 0, 64 );
+ meshBuilder.TexCoord2f( 0, 1.0f - u, 1.0f - v );
+ meshBuilder.TexCoord2f( 1, u1_r, v1_b );
+ meshBuilder.TexCoord2f( 2, u2_r, v2_b );
+ meshBuilder.TangentS3fv( vecTangentS.Base() );
+ meshBuilder.TangentT3fv( vecTangentT.Base() );
+ meshBuilder.BoneWeight( 0, 1.0f );
+ meshBuilder.BoneMatrix( 0, 0 );
+ meshBuilder.UserData( vecTangentS.Base() );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.FastIndex( 0 );
+ meshBuilder.FastIndex( 1 );
+ meshBuilder.FastIndex( 2 );
+ meshBuilder.FastIndex( 3 );
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+
+//-----------------------------------------------------------------------------
+// Paints a cubemap texture
+//-----------------------------------------------------------------------------
+void CVMTPreviewPanel::DrawSphere( void )
+{
+ float flNewTime = Plat_FloatTime();
+
+ // Circle the camera around the origin
+ VMatrix rot;
+ MatrixBuildRotateZ( rot, ROTATION_SPEED * (flNewTime - m_flLastRotationTime ) );
+ Vector vecTemp;
+ Vector3DMultiply( rot, m_vecCameraDirection, vecTemp );
+ m_vecCameraDirection = vecTemp;
+ m_flLastRotationTime = flNewTime;
+
+ int w, h;
+ GetSize( w, h );
+ SetupProjectionMatrix( w, h );
+ SetupLightingState();
+
+ LookAt( vec3_origin, VIEW_DISTANCE );
+
+ // Draw a sphere at the origin
+ if ( !m_Material->IsSpriteCard() )
+ {
+ RenderSphere( vec3_origin, 10.0f, 20, 20 );
+ }
+ else
+ {
+ RenderSpriteCard( vec3_origin, 10.0f );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the camera to look at the the thing we're spinning around
+//-----------------------------------------------------------------------------
+void CVMTPreviewPanel::LookAt( const Vector &vecLookAt, float flRadius )
+{
+ // Compute the distance to the camera for the object based on its
+ // radius and fov.
+
+ // since tan( fov/2 ) = f/d
+ // cos( fov/2 ) = r / r' where r = sphere radius, r' = perp distance from sphere center to max extent of camera
+ // d/f = r'/d' where d' is distance of camera to sphere
+ // d' = r' / tan( fov/2 ) * r' = r / ( cos (fov/2) * tan( fov/2 ) ) = r / sin( fov/2 )
+ float flFOVx = FOV;
+
+ // Compute fov/2 in radians
+ flFOVx *= M_PI / 360.0f;
+
+ // Compute an effective fov based on the aspect ratio
+ // if the height is smaller than the width
+ int w, h;
+ GetSize( w, h );
+ if ( h < w )
+ {
+ flFOVx = atan( h * tan( flFOVx ) / w );
+ }
+
+ float flDistance = flRadius / sin( flFOVx );
+
+ Vector vecMDLOrigin = vecLookAt;
+ Vector vecCameraOrigin;
+ VectorMA( vecMDLOrigin, -flDistance, m_vecCameraDirection, vecCameraOrigin );
+
+ CMatRenderContextPtr pRenderContext( MaterialSystem() );
+ QAngle angles;
+ VectorAngles( m_vecCameraDirection, angles );
+
+ pRenderContext->MatrixMode( MATERIAL_VIEW );
+ pRenderContext->LoadIdentity();
+
+ // convert from a right handed system to a left handed system
+ // since dx for wants it that way.
+// pRenderContext->Scale( 1.0f, 1.0f, -1.0f );
+
+ pRenderContext->Rotate( -90, 1, 0, 0 ); // put Z going up
+ pRenderContext->Rotate( 90, 0, 0, 1 ); // put Z going up
+ pRenderContext->Rotate( -angles[2], 1, 0, 0 );
+ pRenderContext->Rotate( -angles[0], 0, 1, 0 );
+ pRenderContext->Rotate( -angles[1], 0, 0, 1 );
+ pRenderContext->Translate( -vecCameraOrigin[0], -vecCameraOrigin[1], -vecCameraOrigin[2] );
+}
+
+
+//-----------------------------------------------------------------------------
+// Set up a projection matrix for a 90 degree fov
+//-----------------------------------------------------------------------------
+void CVMTPreviewPanel::SetupProjectionMatrix( int nWidth, int nHeight )
+{
+ VMatrix proj;
+ float flFOV = FOV;
+ float flZNear = ZNEAR;
+ float flZFar = ZFAR;
+ float flApsectRatio = (nHeight != 0.0f) ? (float)nWidth / (float)nHeight : 100.0f;
+
+ float halfWidth = tan( flFOV * M_PI / 360.0 );
+ float halfHeight = halfWidth / flApsectRatio;
+
+ memset( proj.Base(), 0, sizeof( proj ) );
+ proj[0][0] = 1.0f / halfWidth;
+ proj[1][1] = 1.0f / halfHeight;
+ proj[2][2] = flZFar / ( flZNear - flZFar );
+ proj[3][2] = -1.0f;
+ proj[2][3] = flZNear * flZFar / ( flZNear - flZFar );
+
+ CMatRenderContextPtr pRenderContext( MaterialSystem() );
+ pRenderContext->MatrixMode( MATERIAL_PROJECTION );
+ pRenderContext->LoadMatrix( proj );
+}
+
+
+//-----------------------------------------------------------------------------
+// Set up a orthographic projection matrix
+//-----------------------------------------------------------------------------
+void CVMTPreviewPanel::SetupOrthoMatrix( int nWidth, int nHeight )
+{
+ CMatRenderContextPtr pRenderContext( MaterialSystem() );
+ pRenderContext->MatrixMode( MATERIAL_PROJECTION );
+ pRenderContext->LoadIdentity();
+ pRenderContext->Ortho( 0, 0, nWidth, nHeight, -1.0f, 1.0f );
+}
+
+
+//-----------------------------------------------------------------------------
+// Power of two FB texture
+//-----------------------------------------------------------------------------
+static CTextureReference s_pPowerOfTwoFrameBufferTexture;
+
+static ITexture *GetPowerOfTwoFrameBufferTexture( void )
+{
+ if( !s_pPowerOfTwoFrameBufferTexture )
+ {
+ s_pPowerOfTwoFrameBufferTexture.Init( vgui::MaterialSystem()->FindTexture( "_rt_PowerOfTwoFB", TEXTURE_GROUP_RENDER_TARGET ) );
+ }
+
+ return s_pPowerOfTwoFrameBufferTexture;
+}
+
+
+//-----------------------------------------------------------------------------
+// Paints the texture
+//-----------------------------------------------------------------------------
+void CVMTPreviewPanel::Paint( void )
+{
+ CMatRenderContextPtr pRenderContext( MaterialSystem() );
+ int w, h;
+ GetSize( w, h );
+ vgui::MatSystemSurface()->Begin3DPaint( 0, 0, w, h );
+
+ // Deal with refraction
+ if ( m_Material->NeedsPowerOfTwoFrameBufferTexture() )
+ {
+ ITexture *pTexture = GetPowerOfTwoFrameBufferTexture();
+ if ( pTexture && !pTexture->IsError() )
+ {
+ pRenderContext->CopyRenderTargetToTexture( pTexture );
+ pRenderContext->SetFrameBufferCopyTexture( pTexture );
+ }
+ }
+
+ pRenderContext->ClearColor4ub( 76, 88, 68, 255 );
+ pRenderContext->ClearBuffers( true, true );
+
+ pRenderContext->FogMode( MATERIAL_FOG_NONE );
+ pRenderContext->SetNumBoneWeights( 0 );
+ pRenderContext->Bind( m_Material );
+ pRenderContext->BindLightmapTexture( m_pLightmapTexture );
+ pRenderContext->BindLocalCubemap( m_DefaultEnvCubemap );
+
+ if ( m_bDrawIn3DMode || m_Material->IsSpriteCard() )
+ {
+ DrawSphere();
+ }
+ else
+ {
+ DrawRectangle();
+ }
+
+ vgui::MatSystemSurface()->End3DPaint( );
+} \ No newline at end of file
diff --git a/vgui2/matsys_controls/vtfpicker.cpp b/vgui2/matsys_controls/vtfpicker.cpp
new file mode 100644
index 0000000..5597376
--- /dev/null
+++ b/vgui2/matsys_controls/vtfpicker.cpp
@@ -0,0 +1,71 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "filesystem.h"
+#include "matsys_controls/vtfpicker.h"
+#include "matsys_controls/vtfpreviewpanel.h"
+#include "vgui_controls/Splitter.h"
+
+
+using namespace vgui;
+
+
+//-----------------------------------------------------------------------------
+//
+// Asset Picker with no preview
+//
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CVTFPicker::CVTFPicker( vgui::Panel *pParent ) :
+ BaseClass( pParent, "VTF Files", "vtf", "materials", "vtfName" )
+{
+ // Horizontal splitter for preview
+ m_pPreviewSplitter = new Splitter( this, "PreviewSplitter", SPLITTER_MODE_VERTICAL, 1 );
+ vgui::Panel *pSplitterLeftSide = m_pPreviewSplitter->GetChild( 0 );
+ vgui::Panel *pSplitterRightSide = m_pPreviewSplitter->GetChild( 1 );
+
+ // VTF preview
+ m_pVTFPreview = new CVTFPreviewPanel( pSplitterRightSide, "VTFPreview" );
+
+ // Standard browser controls
+ CreateStandardControls( pSplitterLeftSide );
+
+ LoadControlSettingsAndUserConfig( "resource/vtfpicker.res" );
+}
+
+CVTFPicker::~CVTFPicker()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Derived classes have this called when the previewed asset changes
+//-----------------------------------------------------------------------------
+void CVTFPicker::OnSelectedAssetPicked( const char *pAssetName )
+{
+ m_pVTFPreview->SetVTF( pAssetName );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Purpose: Modal picker frame
+//
+//-----------------------------------------------------------------------------
+CVTFPickerFrame::CVTFPickerFrame( vgui::Panel *pParent, const char *pTitle ) :
+ BaseClass( pParent )
+{
+ SetAssetPicker( new CVTFPicker( this ) );
+ LoadControlSettingsAndUserConfig( "resource/vtfpickerframe.res" );
+ SetTitle( pTitle, false );
+}
+
+
+
diff --git a/vgui2/matsys_controls/vtfpreviewpanel.cpp b/vgui2/matsys_controls/vtfpreviewpanel.cpp
new file mode 100644
index 0000000..abd7afa
--- /dev/null
+++ b/vgui2/matsys_controls/vtfpreviewpanel.cpp
@@ -0,0 +1,398 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//===========================================================================//
+
+#include "matsys_controls/vtfpreviewpanel.h"
+#include "matsys_controls/matsyscontrols.h"
+#include "VGuiMatSurface/IMatSystemSurface.h"
+#include "materialsystem/MaterialSystemUtil.h"
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/itexture.h"
+#include "materialsystem/imesh.h"
+#include "tier1/KeyValues.h"
+
+
+using namespace vgui;
+
+
+#define FOV 90.0f
+#define ZNEAR 0.1f
+#define ZFAR 2000.0f
+#define ROTATION_SPEED 120.0f // degrees/sec
+
+//-----------------------------------------------------------------------------
+//
+// VTF Preview panel
+//
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// constructor
+//-----------------------------------------------------------------------------
+CVTFPreviewPanel::CVTFPreviewPanel( vgui::Panel *pParent, const char *pName ) :
+ BaseClass( pParent, pName )
+{
+ SetVTF( "//platform/materials/vgui/vtfnotloaded", true );
+ m_nTextureID = MatSystemSurface()->CreateNewTextureID( false );
+}
+
+CVTFPreviewPanel::~CVTFPreviewPanel()
+{
+ if ( vgui::surface() && m_nTextureID != -1 )
+ {
+ vgui::surface()->DestroyTextureID( m_nTextureID );
+ m_nTextureID = -1;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the current VTF
+//-----------------------------------------------------------------------------
+void CVTFPreviewPanel::SetVTF( const char *pFullPath, bool bLoadImmediately )
+{
+ m_PreviewTexture.Init( pFullPath, "editor texture" );
+ m_VTFName = pFullPath;
+
+ KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" );
+ if ( m_PreviewTexture->IsCubeMap() )
+ {
+ pVMTKeyValues->SetString( "$envmap", pFullPath );
+ }
+ else if ( m_PreviewTexture->IsNormalMap() )
+ {
+ pVMTKeyValues->SetString( "$bumpmap", pFullPath );
+ }
+ else
+ {
+ pVMTKeyValues->SetString( "$basetexture", pFullPath );
+ }
+ pVMTKeyValues->SetInt( "$nocull", 1 );
+ pVMTKeyValues->SetInt( "$nodebug", 1 );
+ m_PreviewMaterial.Init( MaterialSystem()->CreateMaterial( pFullPath, pVMTKeyValues ));
+
+ MatSystemSurface()->DrawSetTextureMaterial( m_nTextureID, m_PreviewMaterial );
+
+ // Reset the camera direction
+ m_vecCameraDirection.Init( 1.0f, 0.0f, 0.0f );
+ m_flLastRotationTime = Plat_FloatTime();
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets the current VTF
+//-----------------------------------------------------------------------------
+const char *CVTFPreviewPanel::GetVTF() const
+{
+ return m_VTFName;
+}
+
+
+//-----------------------------------------------------------------------------
+// Draw a sphere
+//-----------------------------------------------------------------------------
+void CVTFPreviewPanel::RenderSphere( const Vector &vCenter, float flRadius, int nTheta, int nPhi )
+{
+ CMatRenderContextPtr pRenderContext( MaterialSystem() );
+
+ int nVertices = nTheta * nPhi;
+ int nIndices = 2 * ( nTheta + 1 ) * ( nPhi - 1 );
+
+ pRenderContext->FogMode( MATERIAL_FOG_NONE );
+ pRenderContext->SetNumBoneWeights( 0 );
+ pRenderContext->Bind( m_PreviewMaterial );
+
+ IMesh* pMesh = pRenderContext->GetDynamicMesh();
+
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, nVertices, nIndices );
+
+ //
+ // Build the index buffer.
+ //
+ int i, j;
+ for ( i = 0; i < nPhi; ++i )
+ {
+ for ( j = 0; j < nTheta; ++j )
+ {
+ float u = j / ( float )(nTheta - 1);
+ float v = i / ( float )(nPhi - 1);
+ float theta = ( j != nTheta-1 ) ? 2.0f * M_PI * u : 0.0f;
+ float phi = M_PI * v;
+
+ Vector vecPos;
+ vecPos.x = flRadius * sin(phi) * cos(theta);
+ vecPos.y = flRadius * cos(phi);
+ vecPos.z = -flRadius * sin(phi) * sin(theta);
+
+ Vector vecNormal = vecPos;
+ VectorNormalize( vecNormal );
+
+ Vector4D vecTangentS;
+ Vector vecTangentT;
+ vecTangentS.Init( vecPos.z, -vecPos.x, 0.0f, 1.0f );
+ if ( VectorNormalize( vecTangentS.AsVector3D() ) == 0.0f )
+ {
+ vecTangentS.Init( 1.0f, 0.0f, 0.0f, 1.0f );
+ }
+
+ CrossProduct( vecNormal, vecTangentS.AsVector3D(), vecTangentT );
+
+ unsigned char red = (int)( u * 255.0f );
+ unsigned char green = (int)( v * 255.0f );
+ unsigned char blue = (int)( v * 255.0f );
+ unsigned char alpha = (int)( v * 255.0f );
+
+ vecPos += vCenter;
+
+ float u1, u2, v1, v2;
+ u1 = u2 = u;
+ v1 = v2 = v;
+
+ meshBuilder.Position3fv( vecPos.Base() );
+ meshBuilder.Normal3fv( vecNormal.Base() );
+ meshBuilder.Color4ub( red, green, blue, alpha );
+ meshBuilder.TexCoord2f( 0, u, v );
+ meshBuilder.TexCoord2f( 1, u1, v1 );
+ meshBuilder.TexCoord2f( 2, u2, v2 );
+ meshBuilder.TangentS3fv( vecTangentS.Base() );
+ meshBuilder.TangentT3fv( vecTangentT.Base() );
+ meshBuilder.BoneWeight( 0, 1.0f );
+ meshBuilder.BoneMatrix( 0, 0 );
+ meshBuilder.UserData( vecTangentS.Base() );
+ meshBuilder.AdvanceVertex();
+ }
+ }
+
+ //
+ // Emit the triangle strips.
+ //
+ int idx = 0;
+ for ( i = 0; i < nPhi - 1; ++i )
+ {
+ for ( j = 0; j < nTheta; ++j )
+ {
+ idx = nTheta * i + j;
+
+ meshBuilder.FastIndex( idx );
+ meshBuilder.FastIndex( idx + nTheta );
+ }
+
+ //
+ // Emit a degenerate triangle to skip to the next row without
+ // a connecting triangle.
+ //
+ if ( i < nPhi - 2 )
+ {
+ meshBuilder.FastIndex( idx + 1 );
+ meshBuilder.FastIndex( idx + 1 + nTheta );
+ }
+ }
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+
+//-----------------------------------------------------------------------------
+// Paints a regular texture
+//-----------------------------------------------------------------------------
+void CVTFPreviewPanel::PaintStandardTexture( void )
+{
+ int x, y, w, h;
+ x = y = 0;
+ GetSize( w, h );
+ vgui::surface()->DrawSetTexture( m_nTextureID );
+ vgui::surface()->DrawSetColor( 255, 255, 255, 255 );
+
+ // Get the aspect ratio of the texture
+ int tw = m_PreviewTexture->GetActualWidth();
+ int th = m_PreviewTexture->GetActualHeight();
+
+ if ( th > 0 && h > 0 )
+ {
+ float screenaspect = (float)tw / (float)th;
+ float aspect = (float)w / (float)h;
+
+ float ratio = screenaspect / aspect;
+
+ // Screen is wider, need bars at top and bottom
+ if ( ratio > 1.0f )
+ {
+ int usetall = (float)w / screenaspect;
+ y = ( h - usetall ) / 2;
+ h = usetall;
+ }
+ // Screen is narrower, need bars at left/right
+ else
+ {
+ int usewide = (float)h * screenaspect;
+ x = ( w - usewide ) / 2;
+ w = usewide;
+ }
+ }
+
+ vgui::surface()->DrawTexturedRect( x, y, x+w, y+h );
+}
+
+
+//-----------------------------------------------------------------------------
+// Paints a normalmap texture
+//-----------------------------------------------------------------------------
+void CVTFPreviewPanel::PaintNormalMapTexture( void )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Paints a volume texture
+//-----------------------------------------------------------------------------
+void CVTFPreviewPanel::PaintVolumeTexture( void )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Paints a cubemap texture
+//-----------------------------------------------------------------------------
+void CVTFPreviewPanel::PaintCubeTexture( void )
+{
+ float flNewTime = Plat_FloatTime();
+
+ // Circle the camera around the origin
+ VMatrix rot;
+ MatrixBuildRotateZ( rot, ROTATION_SPEED * (flNewTime - m_flLastRotationTime ) );
+ Vector vecTemp;
+ Vector3DMultiply( rot, m_vecCameraDirection, vecTemp );
+ m_vecCameraDirection = vecTemp;
+ m_flLastRotationTime = flNewTime;
+
+ LookAt( vec3_origin, 12.0f );
+
+ // Draw a sphere at the origin
+ RenderSphere( vec3_origin, 10.0f, 20, 20 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the camera to look at the the thing we're spinning around
+//-----------------------------------------------------------------------------
+void CVTFPreviewPanel::LookAt( const Vector &vecLookAt, float flRadius )
+{
+ // Compute the distance to the camera for the object based on its
+ // radius and fov.
+
+ // since tan( fov/2 ) = f/d
+ // cos( fov/2 ) = r / r' where r = sphere radius, r' = perp distance from sphere center to max extent of camera
+ // d/f = r'/d' where d' is distance of camera to sphere
+ // d' = r' / tan( fov/2 ) * r' = r / ( cos (fov/2) * tan( fov/2 ) ) = r / sin( fov/2 )
+ float flFOVx = FOV;
+
+ // Compute fov/2 in radians
+ flFOVx *= M_PI / 360.0f;
+
+ // Compute an effective fov based on the aspect ratio
+ // if the height is smaller than the width
+ int w, h;
+ GetSize( w, h );
+ if ( h < w )
+ {
+ flFOVx = atan( h * tan( flFOVx ) / w );
+ }
+
+ float flDistance = flRadius / sin( flFOVx );
+
+ Vector vecMDLOrigin = vecLookAt;
+ Vector vecCameraOrigin;
+ VectorMA( vecMDLOrigin, -flDistance, m_vecCameraDirection, vecCameraOrigin );
+
+ CMatRenderContextPtr pRenderContext( MaterialSystem() );
+ QAngle angles;
+ VectorAngles( m_vecCameraDirection, angles );
+
+ pRenderContext->MatrixMode( MATERIAL_VIEW );
+ pRenderContext->LoadIdentity();
+
+ // convert from a right handed system to a left handed system
+ // since dx for wants it that way.
+// pRenderContext->Scale( 1.0f, 1.0f, -1.0f );
+
+ pRenderContext->Rotate( -90, 1, 0, 0 ); // put Z going up
+ pRenderContext->Rotate( 90, 0, 0, 1 ); // put Z going up
+ pRenderContext->Rotate( -angles[2], 1, 0, 0 );
+ pRenderContext->Rotate( -angles[0], 0, 1, 0 );
+ pRenderContext->Rotate( -angles[1], 0, 0, 1 );
+ pRenderContext->Translate( -vecCameraOrigin[0], -vecCameraOrigin[1], -vecCameraOrigin[2] );
+}
+
+
+//-----------------------------------------------------------------------------
+// Set up a projection matrix for a 90 degree fov
+//-----------------------------------------------------------------------------
+void CVTFPreviewPanel::SetupProjectionMatrix( int nWidth, int nHeight )
+{
+ CMatRenderContextPtr pRenderContext( MaterialSystem() );
+ VMatrix proj;
+ float flFOV = FOV;
+ float flZNear = ZNEAR;
+ float flZFar = ZFAR;
+ float flApsectRatio = (nHeight != 0.0f) ? (float)nWidth / (float)nHeight : 100.0f;
+
+#if 1
+ float halfWidth = tan( flFOV * M_PI / 360.0 );
+ float halfHeight = halfWidth / flApsectRatio;
+#else
+ float halfHeight = tan( flFOV * M_PI / 360.0 );
+ float halfWidth = flApsectRatio * halfHeight;
+#endif
+ memset( proj.Base(), 0, sizeof( proj ) );
+ proj[0][0] = 1.0f / halfWidth;
+ proj[1][1] = 1.0f / halfHeight;
+ proj[2][2] = flZFar / ( flZNear - flZFar );
+ proj[3][2] = -1.0f;
+ proj[2][3] = flZNear * flZFar / ( flZNear - flZFar );
+
+ pRenderContext->MatrixMode( MATERIAL_PROJECTION );
+ pRenderContext->LoadMatrix( proj );
+}
+
+
+//-----------------------------------------------------------------------------
+// Paints the texture
+//-----------------------------------------------------------------------------
+void CVTFPreviewPanel::Paint( void )
+{
+ if ( !m_PreviewTexture->IsCubeMap() && /*!m_PreviewTexture->IsNormalMap() &&*/ !m_PreviewTexture->IsVolumeTexture() )
+ {
+ PaintStandardTexture();
+ return;
+ }
+
+ CMatRenderContextPtr pRenderContext( MaterialSystem() );
+ int w, h;
+ GetSize( w, h );
+ vgui::MatSystemSurface()->Begin3DPaint( 0, 0, w, h );
+
+ pRenderContext->ClearColor4ub( 76, 88, 68, 255 );
+ pRenderContext->ClearBuffers( true, true );
+
+ SetupProjectionMatrix( w, h );
+
+ if ( m_PreviewTexture->IsCubeMap() )
+ {
+ PaintCubeTexture();
+ }
+ else if ( m_PreviewTexture->IsNormalMap() )
+ {
+ PaintNormalMapTexture();
+ }
+ else if ( m_PreviewTexture->IsVolumeTexture() )
+ {
+ PaintVolumeTexture();
+ }
+
+ vgui::MatSystemSurface()->End3DPaint( );
+} \ No newline at end of file