summaryrefslogtreecommitdiff
path: root/utils/hlfaceposer/AnimationBrowser.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /utils/hlfaceposer/AnimationBrowser.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'utils/hlfaceposer/AnimationBrowser.cpp')
-rw-r--r--utils/hlfaceposer/AnimationBrowser.cpp1500
1 files changed, 1500 insertions, 0 deletions
diff --git a/utils/hlfaceposer/AnimationBrowser.cpp b/utils/hlfaceposer/AnimationBrowser.cpp
new file mode 100644
index 0000000..b487250
--- /dev/null
+++ b/utils/hlfaceposer/AnimationBrowser.cpp
@@ -0,0 +1,1500 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include <windows.h>
+#include "AnimationBrowser.h"
+#include "hlfaceposer.h"
+#include "ChoreoView.h"
+#include "StudioModel.h"
+#include "ViewerSettings.h"
+#include "choreowidgetdrawhelper.h"
+#include "faceposer_models.h"
+#include "tabwindow.h"
+#include "inputproperties.h"
+#include "KeyValues.h"
+#include "filesystem.h"
+#include "tier1/KeyValues.h"
+#include "tier1/UtlBuffer.h"
+
+#define MAX_THUMBNAILSIZE 256
+#define MIN_THUMBNAILSIZE 64
+#define THUMBNAIL_SIZE_STEP 4
+#define DEFAULT_THUMBNAIL_SIZE 128
+#define TOP_GAP 70
+
+AnimationBrowser *g_pAnimationBrowserTool = 0;
+extern double realtime;
+
+void CreatePath( const char *pPath );
+
+void CCustomAnim::LoadFromFile()
+{
+ char fn[ 512 ];
+ if ( !filesystem->String( m_Handle, fn, sizeof( fn ) ) )
+ return;
+
+ KeyValues *kv = new KeyValues( "CustomAnimation" );
+ if ( kv->LoadFromFile( filesystem, fn, "MOD" ) )
+ {
+ for ( KeyValues *sub = kv->GetFirstSubKey(); sub ; sub = sub->GetNextKey() )
+ {
+ CUtlSymbol anim;
+ anim = sub->GetString();
+
+ m_Animations.AddToTail( anim );
+ }
+ }
+ kv->deleteThis();
+}
+
+void CCustomAnim::SaveToFile()
+{
+ char fn[ 512 ];
+ if ( !filesystem->String( m_Handle, fn, sizeof( fn ) ) )
+ return;
+
+ CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
+
+ buf.Printf( "\"%s\"\n", m_ShortName.String() );
+ buf.Printf( "{\n" );
+ for ( int i = 0; i < m_Animations.Count(); ++i )
+ {
+ buf.Printf( "\t\"item%d\" \"%s\"\n", i + 1, m_Animations[ i ].String() );
+ }
+ buf.Printf( "}\n" );
+
+ CreatePath( fn );
+ filesystem->WriteFile( fn, "MOD", buf );
+}
+
+bool CCustomAnim::HasAnimation( char const *search )
+{
+ CUtlSymbol searchSym;
+ searchSym = search;
+ if ( m_Animations.Find( searchSym ) != m_Animations.InvalidIndex() )
+ return true;
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CAnimBrowserTab : public CTabWindow
+{
+ typedef CTabWindow BaseClass;
+
+public:
+
+
+ CAnimBrowserTab( AnimationBrowser *parent, int x, int y, int w, int h, int id = 0, int style = 0 ) :
+ CTabWindow( (mxWindow *)parent, x, y, w, h, id, style )
+ {
+ // SetInverted( true );
+ }
+
+ void Init( void )
+ {
+ add( "all" );
+ add( "gestures" );
+ add( "postures" );
+ add( "search results" );
+ }
+
+ virtual void ShowRightClickMenu( int mx, int my )
+ {
+ POINT pt;
+ GetCursorPos( &pt );
+ ScreenToClient( (HWND)getHandle(), &pt );
+
+ // New scene, edit comments
+ mxPopupMenu *pop = new mxPopupMenu();
+
+ pop->add ("&New Group...", IDC_AB_CREATE_CUSTOM );
+
+ mxPopupMenu *sub = NULL;
+ for ( int i = 0; i < m_CustomGroups.Count(); ++i )
+ {
+ if ( !sub )
+ {
+ sub = new mxPopupMenu();
+ }
+ sub->add( va( "%s", m_CustomGroups[ i ].String() ), IDC_AB_DELETEGROUPSTART + i );
+ }
+ if ( sub )
+ {
+ pop->addMenu( "Delete Group", sub );
+ }
+
+ pop->addSeparator();
+
+ sub = new mxPopupMenu();
+ for ( int i = 0; i < m_CustomGroups.Count(); ++i )
+ {
+ sub->add( va( "%s", m_CustomGroups[ i ].String() ), IDC_AB_RENAMEGROUPSTART + i );
+ }
+
+ pop->addMenu( "Rename Group", sub );
+
+ pop->popup( getParent(), pt.x, pt.y );
+ }
+
+ void UpdateCustomTabs( CUtlVector< CCustomAnim * >& list )
+ {
+ m_CustomGroups.Purge();
+
+ while ( getItemCount() > AnimationBrowser::FILTER_FIRST_CUSTOM )
+ {
+ remove( getItemCount() - 1 );
+ }
+
+ for ( int i = 0; i < list.Count(); ++i )
+ {
+ const CCustomAnim *anim = list[ i ];
+ add( anim->m_ShortName.String() );
+ m_CustomGroups.AddToTail( anim->m_ShortName );
+ }
+ }
+
+private:
+
+ CUtlVector< CUtlSymbol > m_CustomGroups;
+};
+
+
+
+AnimationBrowser::AnimationBrowser( mxWindow *parent, int id /*=0*/ )
+: IFacePoserToolWindow( "AnimationBrowser", "Animations" ),
+ mxWindow( parent, 0, 0, 0, 0, "AnimationBrowser", id )
+{
+ setId( id );
+
+ m_nTopOffset = 0;
+ slScrollbar = new mxScrollbar( this, 0, 0, 18, 100, IDC_AB_TRAYSCROLL, mxScrollbar::Vertical );
+
+ m_nLastNumAnimations = -1;
+
+ m_nGranularity = 10;
+
+ m_nCurCell = -1;
+
+ m_nClickedCell = -1;
+
+ m_nGap = 4;
+ m_nDescriptionHeight = 34;
+ m_nSnapshotWidth = g_viewerSettings.thumbnailsizeanim;
+ m_nSnapshotWidth = max( MIN_THUMBNAILSIZE, m_nSnapshotWidth );
+ m_nSnapshotWidth = min( MAX_THUMBNAILSIZE, m_nSnapshotWidth );
+
+ g_viewerSettings.thumbnailsizeanim = m_nSnapshotWidth;
+
+ m_nSnapshotHeight = m_nSnapshotWidth + m_nDescriptionHeight;
+
+ m_bDragging = false;
+ m_nDragCell = -1;
+
+ m_szSearchString[0]=0;
+
+ m_pFilterTab = new CAnimBrowserTab( this, 5, 5, 240, 20, IDC_AB_FILTERTAB );
+ m_pFilterTab->Init();
+
+ m_pSearchEntry = new mxLineEdit( this, 0, 0, 0, 0, "" );
+
+ m_pThumbnailIncreaseButton = new mxButton( this, 0, 0, 18, 18, "+", IDC_AB_THUMBNAIL_INCREASE );
+ m_pThumbnailDecreaseButton = new mxButton( this, 0, 0, 18, 18, "-", IDC_AB_THUMBNAIL_DECREASE );
+ m_nCurFilter = FILTER_NONE;
+
+ m_flDragTime = 0.0f;
+
+ OnFilter();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : AnimationBrowser::~AnimationBrowser
+//-----------------------------------------------------------------------------
+AnimationBrowser::~AnimationBrowser ( void )
+{
+ g_pAnimationBrowserTool = NULL;
+}
+
+void AnimationBrowser::Shutdown()
+{
+ PurgeCustom();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : cellsize -
+//-----------------------------------------------------------------------------
+void AnimationBrowser::SetCellSize( int cellsize )
+{
+ m_nSnapshotWidth = cellsize;
+ m_nSnapshotHeight = cellsize + m_nDescriptionHeight;
+
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void AnimationBrowser::Deselect( void )
+{
+ m_nCurCell = -1;
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : exp -
+//-----------------------------------------------------------------------------
+void AnimationBrowser::Select( int sequence )
+{
+ m_nCurCell = sequence;
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int AnimationBrowser::ComputePixelsNeeded( void )
+{
+ int seqcount = GetSequenceCount();
+
+ if ( !seqcount )
+ return 100;
+
+ // Remove scroll bar
+ int w = this->w2() - 16;
+
+ int colsperrow;
+
+ colsperrow = ( w - m_nGap ) / ( m_nSnapshotWidth + m_nGap );
+ // At least one
+ colsperrow = max( 1, colsperrow );
+
+ int rowsneeded = ( ( seqcount + colsperrow - 1 ) / colsperrow );
+ return rowsneeded * ( m_nSnapshotHeight + m_nGap ) + m_nGap + TOP_GAP + GetCaptionHeight();
+}
+
+bool AnimationBrowser::ComputeRect( int cell, int& rcx, int& rcy, int& rcw, int& rch )
+{
+ // Remove scroll bar
+ int w = this->w2() - 16;
+
+ int colsperrow;
+
+ colsperrow = ( w - m_nGap ) / ( m_nSnapshotWidth + m_nGap );
+ // At least one
+ colsperrow = max( 1, colsperrow );
+
+ int row, col;
+
+ row = cell / colsperrow;
+ col = cell % colsperrow;
+
+ // don't allow partial columns
+
+ rcx = m_nGap + col * ( m_nSnapshotWidth + m_nGap );
+ rcy = GetCaptionHeight() + TOP_GAP + ( -m_nTopOffset * m_nGranularity ) + m_nGap + row * ( m_nSnapshotHeight + m_nGap );
+
+ // Starts off screen
+ if ( rcx < 0 )
+ return false;
+
+ // Ends off screen
+ if ( rcx + m_nSnapshotWidth + m_nGap > this->w2() )
+ return false;
+
+ // Allow partial in y direction
+ if ( rcy > this->h2() )
+ return false;
+
+ if ( rcy + m_nSnapshotHeight + m_nGap < 0 )
+ return false;
+
+ // Some portion is onscreen
+ rcw = m_nSnapshotWidth;
+ rch = m_nSnapshotHeight;
+ return true;
+}
+
+void AnimationBrowser::DrawSequenceFocusRect( CChoreoWidgetDrawHelper& helper, int x, int y, int w, int h, COLORREF clr )
+{
+ helper.DrawOutlinedRect( clr, PS_SOLID, 4, x, y, x + w, y + h );
+}
+
+void AnimationBrowser::DrawSequenceDescription( CChoreoWidgetDrawHelper& helper, int x, int y, int w, int h, int sequence, mstudioseqdesc_t &seqdesc )
+{
+ int textheight = 15;
+
+ RECT textRect;
+ textRect.left = x + 5;
+ textRect.top = y + h - 2 * textheight - 12;
+ textRect.right = x + w - 10;
+ textRect.bottom = y + h - 12;
+
+ helper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 63, 63, 63 ), textRect, "%s", seqdesc.pszLabel() );
+
+ StudioModel *mdl = models->GetActiveStudioModel();
+ if ( !mdl )
+ return;
+
+ OffsetRect( &textRect, 0, textheight );
+
+ helper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 63, 63, 63 ), textRect, "%.2f seconds",
+ mdl->GetDuration( sequence ) );
+
+ textRect.top = y + h - 4 * textheight - 1;
+ textRect.bottom = textRect.top + textheight;
+
+ helper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 50, 200, 255 ), textRect, "frames %i",
+ mdl->GetNumFrames( sequence ) );
+
+ OffsetRect( &textRect, 0, textheight - 4 );
+
+ helper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 50, 200, 255 ), textRect, "fps %.2f",
+ (float)mdl->GetFPS( sequence ) );
+
+}
+
+bool AnimationBrowser::PaintBackground( void )
+{
+ redraw();
+ return false;
+}
+
+void AnimationBrowser::DrawThumbNail( int sequence, CChoreoWidgetDrawHelper& helper, int rcx, int rcy, int rcw, int rch )
+{
+ HDC dc = helper.GrabDC();
+
+ helper.DrawFilledRect( GetSysColor( COLOR_BTNFACE ), rcx, rcy, rcw + rcx, rch + rcy );
+
+ mstudioseqdesc_t *pseqdesc = GetSeqDesc( sequence );
+ if ( !pseqdesc )
+ return;
+
+ mxbitmapdata_t *bm = models->GetBitmapForSequence( models->GetActiveModelIndex(), TranslateSequenceNumber( sequence ) );
+ if ( bm && bm->valid )
+ {
+ DrawBitmapToDC( dc, rcx, rcy, rcw, rch - m_nDescriptionHeight, *bm );
+ helper.DrawOutlinedRect( RGB( 127, 127, 127 ), PS_SOLID, 1, rcx, rcy, rcx + rcw, rcy + rch - m_nDescriptionHeight );
+ }
+
+ DrawSequenceDescription( helper, rcx, rcy, rcw, rch, TranslateSequenceNumber( sequence ), *pseqdesc );
+
+ if ( sequence == m_nCurCell )
+ {
+ DrawSequenceFocusRect( helper, rcx, rcy, rcw, rch - m_nDescriptionHeight, RGB( 255, 100, 63 ) );
+ }
+}
+
+void AnimationBrowser::redraw()
+{
+ if ( !ToolCanDraw() )
+ return;
+
+ bool updateSelection = false;
+
+ int curcount = GetSequenceCount();
+ if ( curcount != m_nLastNumAnimations )
+ {
+ m_nTopOffset = 0;
+ RepositionSlider();
+ m_nLastNumAnimations = curcount;
+ updateSelection = true;
+ }
+
+ CChoreoWidgetDrawHelper helper( this, GetSysColor( COLOR_BTNFACE ) );
+ HandleToolRedraw( helper );
+
+ int w, h;
+ w = w2();
+ h = h2();
+
+ RECT clipRect;
+ helper.GetClientRect( clipRect );
+
+ clipRect.top += TOP_GAP + GetCaptionHeight();
+
+ helper.StartClipping( clipRect );
+
+ int rcx, rcy, rcw, rch;
+
+ EnableStickySnapshotMode( );
+
+ int c = curcount;
+ for ( int i = 0; i < c; i++ )
+ {
+ if ( !ComputeRect( i, rcx, rcy, rcw, rch ) )
+ {
+ // Cache in .bmp no matter what
+ // This was too slow, so turning it back off
+ //models->GetBitmapForSequence( models->GetActiveModelIndex(), TranslateSequenceNumber( i ) );
+ continue;
+ }
+
+ DrawThumbNail( i, helper, rcx, rcy, rcw, rch );
+ }
+
+ DisableStickySnapshotMode( );
+
+ helper.StopClipping();
+
+ RECT rcText;
+ rcText.right = w2();
+ rcText.left = rcText.right - 120;
+ rcText.top = 8;
+ rcText.bottom = rcText.top + 15;
+
+ helper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 63, 63, 63 ), rcText, "%i sequences",
+ curcount );
+
+}
+
+int AnimationBrowser::GetCellUnderPosition( int x, int y )
+{
+ int count = GetSequenceCount();
+ if ( !count )
+ return -1;
+
+ int rcx, rcy, rcw, rch;
+ int c = 0;
+ while ( c < count )
+ {
+ if ( !ComputeRect( c, rcx, rcy, rcw, rch ) )
+ {
+ c++;
+ continue;
+ }
+
+ if ( x >= rcx && x <= rcx + rcw &&
+ y >= rcy && y <= rcy + rch )
+ {
+ return c;
+ }
+
+ c++;
+ }
+ return -1;
+}
+
+void AnimationBrowser::RepositionSlider( void )
+{
+ int trueh = h2() - GetCaptionHeight();
+
+ int heightpixels = trueh / m_nGranularity;
+ int rangepixels = ComputePixelsNeeded() / m_nGranularity;
+
+ if ( rangepixels < heightpixels )
+ {
+ m_nTopOffset = 0;
+ slScrollbar->setVisible( false );
+ }
+ else
+ {
+ slScrollbar->setVisible( true );
+ }
+
+ slScrollbar->setBounds( w2() - 16, GetCaptionHeight() + TOP_GAP, 16, trueh - TOP_GAP );
+
+ m_nTopOffset = max( 0, m_nTopOffset );
+ m_nTopOffset = min( rangepixels, m_nTopOffset );
+
+ slScrollbar->setRange( 0, rangepixels );
+ slScrollbar->setValue( m_nTopOffset );
+ slScrollbar->setPagesize( heightpixels );
+}
+
+void AnimationBrowser::SetClickedCell( int cell )
+{
+ m_nClickedCell = cell;
+ Select( cell );
+}
+
+void AnimationBrowser::ShowRightClickMenu( int mx, int my )
+{
+ mstudioseqdesc_t *pseqdesc = GetSeqDesc( m_nCurCell );
+ if ( !pseqdesc )
+ return;
+
+ mxPopupMenu *pop = new mxPopupMenu();
+ Assert( pop );
+
+ pop->add( va( "New Group..." ), IDC_AB_CREATE_CUSTOM );
+
+ if ( m_CustomAnimationTabs.Count() > 0 )
+ {
+ mxPopupMenu *ca = new mxPopupMenu();
+ Assert( ca );
+
+ for ( int i = 0; i < m_CustomAnimationTabs.Count() ; ++i )
+ {
+ CCustomAnim *anim = m_CustomAnimationTabs[ i ];
+ ca->add( va( "%s", anim->m_ShortName.String() ), IDC_AB_ADDTOGROUPSTART + i );
+ }
+
+ pop->addMenu( "Add to Group", ca );
+
+ ca = new mxPopupMenu();
+
+ bool useMenu = false;
+ for ( int i = 0; i < m_CustomAnimationTabs.Count() ; ++i )
+ {
+ CCustomAnim *anim = m_CustomAnimationTabs[ i ];
+ if ( anim->HasAnimation( pseqdesc->pszLabel() ) )
+ {
+ ca->add( va( "%s", anim->m_ShortName.String() ), IDC_AB_REMOVEFROMGROUPSTART + i );
+ useMenu = true;
+ }
+ }
+
+ if ( useMenu )
+ {
+ pop->addMenu( "Remove from Group", ca );
+ }
+ else
+ {
+ delete ca;
+ }
+ }
+
+ pop->addSeparator();
+
+ pop->add( va( "Re-create thumbnail for '%s'", pseqdesc->pszLabel() ), IDC_AB_CONTEXT_CREATEBITMAP );
+ pop->add( va( "Re-create all thumbnails" ), IDC_AB_CONTEXT_CREATEALLBITMAPS );
+
+ pop->popup( this, mx, my );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void AnimationBrowser::DrawFocusRect( void )
+{
+ HDC dc = GetDC( NULL );
+
+ ::DrawFocusRect( dc, &m_rcFocus );
+
+ ReleaseDC( NULL, dc );
+}
+
+static bool IsWindowOrChild( mxWindow *parent, HWND test )
+{
+ HWND parentHwnd = (HWND)parent->getHandle();
+ if ( test == parentHwnd ||
+ IsChild( parentHwnd, test ) )
+ {
+ return true;
+ }
+ return false;
+}
+
+int AnimationBrowser::handleEvent (mxEvent *event)
+{
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ int iret = 0;
+
+ if ( HandleToolEvent( event ) )
+ {
+ return iret;
+ }
+
+ switch ( event->event )
+ {
+ case mxEvent::Action:
+ {
+ iret = 1;
+ switch ( event->action )
+ {
+ default:
+ if ( event->action >= IDC_AB_ADDTOGROUPSTART && event->action <= IDC_AB_ADDTOGROUPEND )
+ {
+ int index = event->action - IDC_AB_ADDTOGROUPSTART;
+ mstudioseqdesc_t *pseqdesc = GetSeqDesc( m_nCurCell );
+ if ( pseqdesc )
+ {
+ AddAnimationToCustomFile( index, pseqdesc->pszLabel() );
+ }
+ }
+ else if ( event->action >= IDC_AB_REMOVEFROMGROUPSTART && event->action <= IDC_AB_REMOVEFROMGROUPEND )
+ {
+ int index = event->action - IDC_AB_REMOVEFROMGROUPSTART;
+ mstudioseqdesc_t *pseqdesc = GetSeqDesc( m_nCurCell );
+ if ( pseqdesc )
+ {
+ RemoveAnimationFromCustomFile( index, pseqdesc->pszLabel() );
+ }
+ }
+ else if ( event->action >= IDC_AB_DELETEGROUPSTART && event->action <= IDC_AB_DELETEGROUPEND )
+ {
+ int index = event->action - IDC_AB_DELETEGROUPSTART;
+ DeleteCustomFile( index );
+ }
+ else if ( event->action >= IDC_AB_RENAMEGROUPSTART && event->action <= IDC_AB_RENAMEGROUPEND )
+ {
+ int index = event->action - IDC_AB_RENAMEGROUPSTART;
+ RenameCustomFile( index );
+ }
+ else
+ {
+ iret = 0;
+ }
+ break;
+ case IDC_AB_CREATE_CUSTOM:
+ {
+ OnAddCustomAnimationFilter();
+ }
+ break;
+ case IDC_AB_FILTERTAB:
+ {
+ int index = m_pFilterTab->getSelectedIndex();
+ if ( index >= 0 )
+ {
+ m_nCurFilter = index;
+ OnFilter();
+ }
+ }
+ break;
+ case IDC_AB_TRAYSCROLL:
+ {
+ if (event->modifiers == SB_THUMBTRACK)
+ {
+ int offset = event->height;
+
+ slScrollbar->setValue( offset );
+
+ m_nTopOffset = offset;
+
+ redraw();
+ }
+ else if ( event->modifiers == SB_PAGEUP )
+ {
+ int offset = slScrollbar->getValue();
+
+ offset -= m_nGranularity;
+ offset = max( offset, slScrollbar->getMinValue() );
+
+ slScrollbar->setValue( offset );
+ InvalidateRect( (HWND)slScrollbar->getHandle(), NULL, TRUE );
+
+ m_nTopOffset = offset;
+
+ redraw();
+ }
+ else if ( event->modifiers == SB_PAGEDOWN )
+ {
+ int offset = slScrollbar->getValue();
+
+ offset += m_nGranularity;
+ offset = min( offset, slScrollbar->getMaxValue() );
+
+ slScrollbar->setValue( offset );
+ InvalidateRect( (HWND)slScrollbar->getHandle(), NULL, TRUE );
+
+ m_nTopOffset = offset;
+
+ redraw();
+ }
+ }
+ break;
+ case IDC_AB_THUMBNAIL_INCREASE:
+ {
+ ThumbnailIncrease();
+ }
+ break;
+ case IDC_AB_THUMBNAIL_DECREASE:
+ {
+ ThumbnailDecrease();
+ }
+ break;
+ case IDC_AB_CONTEXT_CREATEBITMAP:
+ {
+ int current_model = models->GetActiveModelIndex();
+
+ if ( m_nClickedCell >= 0 )
+ {
+ models->RecreateAnimationBitmap( current_model, TranslateSequenceNumber( m_nClickedCell ) );
+ }
+ redraw();
+ }
+ break;
+ case IDC_AB_CONTEXT_CREATEALLBITMAPS:
+ {
+ int current_model = models->GetActiveModelIndex();
+ models->RecreateAllAnimationBitmaps( current_model );
+ redraw();
+ }
+ break;
+ }
+ break;
+ }
+ case mxEvent::MouseDown:
+ {
+ if ( !( event->buttons & mxEvent::MouseRightButton ) )
+ {
+ // Figure out cell #
+ int cell = GetCellUnderPosition( event->x, event->y );
+ if ( cell >= 0 && cell < GetSequenceCount() )
+ {
+ int cx, cy, cw, ch;
+ if ( ComputeRect( cell, cx, cy, cw, ch ) )
+ {
+ m_flDragTime = realtime;
+ m_bDragging = true;
+ m_nDragCell = cell;
+
+ m_nXStart = (short)event->x;
+ m_nYStart = (short)event->y;
+
+ m_rcFocus.left = cx;
+ m_rcFocus.top = cy;
+ m_rcFocus.right = cx + cw;
+ m_rcFocus.bottom = cy + ch - m_nDescriptionHeight;
+
+ POINT pt;
+ pt.x = pt.y = 0;
+ ClientToScreen( (HWND)getHandle(), &pt );
+
+ OffsetRect( &m_rcFocus, pt.x, pt.y );
+
+ m_rcOrig = m_rcFocus;
+
+ DrawFocusRect();
+
+ Select( cell );
+ m_nClickedCell = cell;
+ }
+ }
+ else
+ {
+ Deselect();
+ redraw();
+ }
+ }
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseDrag:
+ {
+ if ( m_bDragging )
+ {
+ // Draw drag line of some kind
+ DrawFocusRect();
+
+ // update pos
+ m_rcFocus = m_rcOrig;
+ OffsetRect( &m_rcFocus, ( (short)event->x - m_nXStart ),
+ ( (short)event->y - m_nYStart ) );
+
+ DrawFocusRect();
+ }
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseUp:
+ {
+ iret = 1;
+
+ if ( event->buttons & mxEvent::MouseRightButton )
+ {
+ SetClickedCell( GetCellUnderPosition( (short)event->x, (short)event->y ) );
+ ShowRightClickMenu( (short)event->x, (short)event->y );
+ return iret;
+ }
+
+ if ( m_bDragging && m_nClickedCell >= 0 )
+ {
+ mstudioseqdesc_t *pseqdesc = GetSeqDesc( m_nClickedCell );
+
+ DrawFocusRect();
+ m_bDragging = false;
+ // See if we let go on top of the choreo view
+
+ // Convert x, y to screen space
+ POINT pt;
+ pt.x = (short)event->x;
+ pt.y = (short)event->y;
+ ClientToScreen( (HWND)getHandle(), &pt );
+
+ HWND maybeTool = WindowFromPoint( pt );
+
+ // Now tell choreo view
+ if ( maybeTool && pseqdesc )
+ {
+ if ( IsWindowOrChild( g_pChoreoView, maybeTool ) )
+ {
+ if ( g_pChoreoView->CreateAnimationEvent( pt.x, pt.y, pseqdesc->pszLabel() ) )
+ {
+ return iret;
+ }
+ }
+ }
+ }
+ }
+ break;
+ case mxEvent::Size:
+ {
+ int width = w2();
+ // int height = h2();
+
+ int ch = GetCaptionHeight() + 10;
+
+ m_pSearchEntry->setBounds( 5, ch, width - 10 - 170, 18 );
+
+ m_pThumbnailIncreaseButton->setBounds( width - 40, 4 + ch, 16, 16 );
+ m_pThumbnailDecreaseButton->setBounds( width - 20, 4 + ch, 16, 16 );
+
+ m_pFilterTab->setBounds( 5, ch + 20, width - 10, 20 );
+
+ m_nTopOffset = 0;
+ RepositionSlider();
+
+ redraw();
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseWheeled:
+ {
+ // Figure out cell #
+ POINT pt;
+
+ pt.x = event->x;
+ pt.y = event->y;
+
+ ScreenToClient( (HWND)getHandle(), &pt );
+
+ if ( event->height < 0 )
+ {
+ m_nTopOffset = min( m_nTopOffset + 10, slScrollbar->getMaxValue() );
+ }
+ else
+ {
+ m_nTopOffset = max( m_nTopOffset - 10, 0 );
+ }
+ RepositionSlider();
+ redraw();
+ iret = 1;
+ }
+ break;
+ case mxEvent::KeyDown:
+ case mxEvent::KeyUp:
+ {
+ bool search = false;
+ // int n = 3;
+ if ( event->key == VK_ESCAPE && m_szSearchString[ 0 ] )
+ {
+ m_pSearchEntry->setLabel( "" );
+ m_szSearchString[ 0 ] = 0;
+ m_pFilterTab->select( FILTER_NONE );
+ m_nCurFilter = FILTER_NONE;
+ OnFilter();
+ }
+ else
+ {
+ // Text changed?
+ char sz[ 512 ];
+ m_pSearchEntry->getText( sz, sizeof( sz ) );
+ if ( Q_stricmp( sz, m_szSearchString ) )
+ {
+ Q_strncpy( m_szSearchString, sz, sizeof( m_szSearchString ) );
+ search = true;
+ }
+ }
+
+ if ( search )
+ {
+ if ( Q_strlen( m_szSearchString ) > 0 )
+ {
+ m_pFilterTab->select( FILTER_STRING );
+ m_nCurFilter = FILTER_STRING;
+ }
+ else
+ {
+ m_pFilterTab->select( FILTER_NONE );
+ m_nCurFilter = FILTER_NONE;
+ }
+ OnFilter();
+ }
+ }
+ break;
+ };
+
+ if ( iret )
+ {
+ SetActiveTool( this );
+ }
+ return iret;
+}
+
+// HACK HACK: VS2005 is generating bogus code for this little operation in the function below...
+#pragma optimize( "g", off )
+float roundcycle( float cycle )
+{
+ int rounded = (int)(cycle);
+ float cy2 = cycle - rounded;
+ return cy2;
+}
+#pragma optimize( "", on )
+
+void AnimationBrowser::Think( float dt )
+{
+ if ( !m_bDragging )
+ return;
+
+ if ( m_nClickedCell < 0 )
+ return;
+
+ StudioModel *model = models->GetActiveStudioModel();
+ if ( model )
+ {
+ int iSequence = TranslateSequenceNumber( m_nClickedCell );
+ float dur = model->GetDuration( iSequence );
+ if ( dur > 0.0f )
+ {
+ float elapsed = (float)realtime - m_flDragTime;
+
+ float flFrameRate = 0.0f;
+ float flGroundSpeed = 0.0f;
+ model->GetSequenceInfo( iSequence, &flFrameRate, &flGroundSpeed );
+
+ float cycle = roundcycle( elapsed * flFrameRate );
+
+ // This should be the only thing on the model!!!
+ model->ClearAnimationLayers();
+
+ // FIXME: shouldn't sequences always be lower priority than gestures?
+ int iLayer = model->GetNewAnimationLayer( 0 );
+ model->SetOverlaySequence( iLayer, iSequence, 1.0f );
+ model->SetOverlayRate( iLayer, cycle, 0.0f );
+ }
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void AnimationBrowser::ThumbnailIncrease( void )
+{
+ if ( m_nSnapshotWidth + THUMBNAIL_SIZE_STEP <= MAX_THUMBNAILSIZE )
+ {
+ m_nSnapshotWidth += THUMBNAIL_SIZE_STEP;
+ g_viewerSettings.thumbnailsizeanim = m_nSnapshotWidth;
+ m_nSnapshotHeight = m_nSnapshotWidth + m_nDescriptionHeight;
+
+ Con_Printf( "Thumbnail size %i x %i\n", m_nSnapshotWidth, m_nSnapshotWidth );
+
+ redraw();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void AnimationBrowser::ThumbnailDecrease( void )
+{
+ if ( m_nSnapshotWidth - THUMBNAIL_SIZE_STEP >= MIN_THUMBNAILSIZE )
+ {
+ m_nSnapshotWidth -= THUMBNAIL_SIZE_STEP;
+ g_viewerSettings.thumbnailsizeanim = m_nSnapshotWidth;
+ m_nSnapshotHeight = m_nSnapshotWidth + m_nDescriptionHeight;
+
+ Con_Printf( "Thumbnail size %i x %i\n", m_nSnapshotWidth, m_nSnapshotWidth );
+
+ redraw();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void AnimationBrowser::RestoreThumbnailSize( void )
+{
+ m_nSnapshotWidth = g_viewerSettings.thumbnailsizeanim;
+ m_nSnapshotWidth = max( MIN_THUMBNAILSIZE, m_nSnapshotWidth );
+ m_nSnapshotWidth = min( MAX_THUMBNAILSIZE, m_nSnapshotWidth );
+
+ g_viewerSettings.thumbnailsizeanim = m_nSnapshotWidth;
+
+ m_nSnapshotHeight = m_nSnapshotWidth + m_nDescriptionHeight;
+
+ redraw();
+}
+
+void AnimationBrowser::ReloadBitmaps( void )
+{
+ Assert( 0 );
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *model -
+// sequence -
+// Output : static bool
+//-----------------------------------------------------------------------------
+static bool IsTypeOfSequence( StudioModel *model, int sequence, char const *typestring )
+{
+ bool match = false;
+
+ if ( !model->GetStudioHdr() )
+ return match;
+
+ KeyValues *seqKeyValues = new KeyValues("");
+ if ( seqKeyValues->LoadFromBuffer( model->GetFileName( ), model->GetKeyValueText( sequence ) ) )
+ {
+ // Do we have a build point section?
+ KeyValues *pkvAllFaceposer = seqKeyValues->FindKey("faceposer");
+ if ( pkvAllFaceposer )
+ {
+ char const *t = pkvAllFaceposer->GetString( "type", "" );
+ if ( t && !Q_stricmp( t, typestring ) )
+ {
+ match = true;
+ }
+ }
+ }
+
+ seqKeyValues->deleteThis();
+
+ return match;
+}
+
+bool AnimationBrowser::SequencePassesFilter( StudioModel *model, int sequence, mstudioseqdesc_t &seqdesc )
+{
+ if (model->IsHidden( sequence ))
+ return false;
+
+ switch ( m_nCurFilter )
+ {
+ default:
+ {
+
+ int offset = m_nCurFilter - FILTER_FIRST_CUSTOM;
+ if ( offset >= 0 && offset < m_CustomAnimationTabs.Count() )
+ {
+ // Find the name
+ CCustomAnim *anim = m_CustomAnimationTabs[ offset ];
+ return anim->HasAnimation( seqdesc.pszLabel() );
+ }
+ return true;
+ }
+ break;
+ case FILTER_NONE:
+ {
+ return true;
+ }
+ break;
+ case FILTER_GESTURES:
+ if ( IsTypeOfSequence( model, sequence, "gesture" ) )
+ {
+ return true;
+ }
+ break;
+ case FILTER_POSTURES:
+ if ( IsTypeOfSequence( model, sequence, "posture" ) )
+ {
+ return true;
+ }
+ break;
+ case FILTER_STRING:
+ if ( Q_stristr( seqdesc.pszLabel(), m_szSearchString ) )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void AnimationBrowser::OnFilter()
+{
+ m_Filtered.RemoveAll();
+
+ StudioModel *model = models->GetActiveStudioModel();
+ if ( !model )
+ return;
+
+ CStudioHdr *hdr = model->GetStudioHdr();
+ if ( !hdr )
+ return;
+
+ int count = hdr->GetNumSeq();
+
+ for ( int i = 0; i < count; i++ )
+ {
+ mstudioseqdesc_t &seqdesc = hdr->pSeqdesc( i );
+
+ // if it passes the filter, add it
+ if ( SequencePassesFilter( model, i, seqdesc ) )
+ {
+ m_Filtered.AddToTail( i );
+ }
+ }
+
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int AnimationBrowser::GetSequenceCount()
+{
+ return m_Filtered.Count();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : index -
+// Output : mstudioseqdesc_t
+//-----------------------------------------------------------------------------
+mstudioseqdesc_t *AnimationBrowser::GetSeqDesc( int index )
+{
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if ( !hdr )
+ return NULL;
+
+ index = TranslateSequenceNumber( index );
+
+ if ( index < 0 || index >= hdr->GetNumSeq() )
+ return NULL;
+
+ return &hdr->pSeqdesc( index );
+}
+
+int AnimationBrowser::TranslateSequenceNumber( int index )
+{
+ if ( index < 0 || index >= m_Filtered.Count() )
+ return NULL;
+
+ // Lookup the true index
+ index = m_Filtered[ index ];
+ return index;
+}
+
+void AnimationBrowser::FindCustomFiles( char const *subdir, CUtlVector< FileNameHandle_t >& files )
+{
+ char search[ 512 ];
+ Q_snprintf( search, sizeof( search ), "%s/*.txt", subdir );
+
+ FileFindHandle_t findHandle;
+ const char *pFileName = filesystem->FindFirst( search, &findHandle );
+ while( pFileName )
+ {
+ if( !filesystem->FindIsDirectory( findHandle ) )
+ {
+ // Strip off the 'sound/' part of the name.
+ char fn[ 512 ];
+ Q_snprintf( fn, sizeof( fn ), "%s/%s", subdir, pFileName );
+
+ FileNameHandle_t fh;
+ fh = filesystem->FindOrAddFileName( fn );
+ files.AddToTail( fh );
+ }
+ pFileName = filesystem->FindNext( findHandle );
+ }
+
+ filesystem->FindClose( findHandle );
+}
+
+void AnimationBrowser::PurgeCustom()
+{
+ for ( int i = 0; i < m_CustomAnimationTabs.Count(); ++i )
+ {
+ if ( m_CustomAnimationTabs[ i ]->m_bDirty )
+ {
+ m_CustomAnimationTabs[ i ]->SaveToFile();
+ }
+ delete m_CustomAnimationTabs[ i ];
+ }
+ m_CustomAnimationTabs.Purge();
+}
+
+void AnimationBrowser::BuildCustomFromFiles( CUtlVector< FileNameHandle_t >& files )
+{
+ PurgeCustom();
+
+ for ( int i = 0; i < files.Count(); ++i )
+ {
+ char fn[ 512 ];
+ if ( !filesystem->String( files[ i ], fn, sizeof( fn ) ) )
+ continue;
+
+ Q_FixSlashes( fn );
+ Q_strlower( fn );
+
+ char basename[ 128 ];
+ Q_FileBase( fn, basename, sizeof( basename ) );
+
+ CCustomAnim *anim = new CCustomAnim( files[ i ] );
+ anim->m_ShortName = basename;
+ anim->LoadFromFile();
+
+ m_CustomAnimationTabs.AddToTail( anim );
+ }
+
+ UpdateCustomTabs();
+}
+
+void AnimationBrowser::RenameCustomFile( int index )
+{
+ if ( index < 0 || index >= m_CustomAnimationTabs.Count() )
+ return;
+
+ CCustomAnim *anim = m_CustomAnimationTabs[ index ];
+ CInputParams params;
+ memset( &params, 0, sizeof( params ) );
+ Q_snprintf( params.m_szDialogTitle, sizeof( params.m_szDialogTitle ), "Custom Animation Group" );
+ Q_strcpy( params.m_szPrompt, "Group Name:" );
+ Q_strcpy( params.m_szInputText, anim->m_ShortName.String() );
+
+ if ( !InputProperties( &params ) )
+ return;
+
+ if ( !params.m_szInputText[ 0 ] )
+ return;
+
+ // No change
+ if ( !Q_stricmp( anim->m_ShortName.String(), params.m_szInputText ) )
+ return;
+
+ char fn[ 512 ];
+ if ( !filesystem->String( anim->m_Handle, fn, sizeof( fn ) ) )
+ {
+ Assert( 0 );
+ return;
+ }
+
+ StudioModel *model = models->GetActiveStudioModel();
+ if ( !model )
+ {
+ return;
+ }
+
+ CStudioHdr *hdr = model->GetStudioHdr();
+ if ( !hdr )
+ {
+ return;
+ }
+
+ // Delete the old file
+ filesystem->RemoveFile( fn, "MOD" );
+
+ anim->m_ShortName = params.m_szInputText;
+
+ char basename[ 128 ];
+ Q_StripExtension( hdr->pszName(), basename, sizeof( basename ) );
+ Q_snprintf( fn, sizeof( fn ), "expressions/%s/animation/%s.txt", basename, params.m_szInputText );
+ Q_FixSlashes( fn );
+ Q_strlower( fn );
+ CreatePath( fn );
+
+ anim->m_Handle = filesystem->FindOrAddFileName( fn );
+
+ anim->m_bDirty = true;
+ UpdateCustomTabs();
+}
+
+void AnimationBrowser::AddCustomFile( const FileNameHandle_t& handle )
+{
+ char fn[ 512 ];
+ if ( !filesystem->String( handle, fn, sizeof( fn ) ) )
+ return;
+
+ Q_FixSlashes( fn );
+ Q_strlower( fn );
+ char basename[ 128 ];
+ Q_FileBase( fn, basename, sizeof( basename ) );
+
+ CCustomAnim *anim = new CCustomAnim( handle );
+ anim->m_ShortName = basename;
+ anim->LoadFromFile();
+ anim->m_bDirty = true;
+
+ if ( m_nCurCell != -1 )
+ {
+ StudioModel *model = models->GetActiveStudioModel();
+ if ( model )
+ {
+ CStudioHdr *hdr = model->GetStudioHdr();
+ if ( hdr )
+ {
+ mstudioseqdesc_t &seqdesc = hdr->pSeqdesc( m_nCurCell );
+ CUtlSymbol sym;
+ sym = seqdesc.pszLabel();
+ anim->m_Animations.AddToTail( sym );
+ }
+ }
+ }
+
+ m_CustomAnimationTabs.AddToTail( anim );
+
+ UpdateCustomTabs();
+}
+
+void AnimationBrowser::DeleteCustomFile( int index )
+{
+ if ( index < 0 || index >= m_CustomAnimationTabs.Count() )
+ return;
+
+ CCustomAnim *anim = m_CustomAnimationTabs[ index ];
+
+ char fn[ 512 ];
+ if ( !filesystem->String( anim->m_Handle, fn, sizeof( fn ) ) )
+ return;
+
+ m_CustomAnimationTabs.Remove( index );
+ filesystem->RemoveFile( fn );
+ delete anim;
+
+ UpdateCustomTabs();
+}
+
+void AnimationBrowser::UpdateCustomTabs()
+{
+ m_pFilterTab->UpdateCustomTabs( m_CustomAnimationTabs );
+}
+
+void AnimationBrowser::OnModelChanged()
+{
+ CUtlVector< FileNameHandle_t > files;
+
+ StudioModel *model = models->GetActiveStudioModel();
+ if ( model )
+ {
+ CStudioHdr *hdr = model->GetStudioHdr();
+ if ( hdr )
+ {
+ char subdir[ 512 ];
+ char basename[ 512 ];
+ Q_StripExtension( hdr->pszName(), basename, sizeof( basename ) );
+ Q_snprintf( subdir, sizeof( subdir ), "expressions/%s/animation", basename );
+ Q_FixSlashes( subdir );
+ Q_strlower( subdir );
+ FindCustomFiles( subdir, files );
+ }
+ }
+
+ BuildCustomFromFiles( files );
+
+ RestoreThumbnailSize();
+
+ // Just reapply filter
+ OnFilter();
+}
+
+ void AnimationBrowser::OnAddCustomAnimationFilter()
+ {
+ StudioModel *model = models->GetActiveStudioModel();
+ if ( !model )
+ {
+ return;
+ }
+
+ CStudioHdr *hdr = model->GetStudioHdr();
+ if ( !hdr )
+ {
+ return;
+ }
+
+ CInputParams params;
+ memset( &params, 0, sizeof( params ) );
+ Q_snprintf( params.m_szDialogTitle, sizeof( params.m_szDialogTitle ), "Custom Animation Group" );
+ Q_strcpy( params.m_szPrompt, "Group Name:" );
+ Q_strcpy( params.m_szInputText, "" );
+
+ if ( !InputProperties( &params ) )
+ return;
+
+ if ( !params.m_szInputText[ 0 ] )
+ return;
+
+ if ( FindCustomFile( params.m_szInputText ) != -1 )
+ {
+ Warning( "Can't add duplicate tab '%s'\n", params.m_szInputText );
+ return;
+ }
+
+ // Create it
+ char fn[ 512 ];
+ char basename[ 512 ];
+ Q_StripExtension( hdr->pszName(), basename, sizeof( basename ) );
+ Q_snprintf( fn, sizeof( fn ), "expressions/%s/animation/%s.txt", basename, params.m_szInputText );
+ Q_FixSlashes( fn );
+ Q_strlower( fn );
+ CreatePath( fn );
+
+ FileNameHandle_t fh = filesystem->FindOrAddFileName( fn );
+ AddCustomFile( fh );
+ }
+
+ int AnimationBrowser::FindCustomFile( char const *shortName )
+ {
+ CUtlSymbol search;
+ search = shortName;
+
+ for ( int i = 0; i < m_CustomAnimationTabs.Count(); ++i )
+ {
+ CCustomAnim *anim = m_CustomAnimationTabs[ i ];
+ if ( anim->m_ShortName == search )
+ return i;
+ }
+ return -1;
+ }
+
+void AnimationBrowser::AddAnimationToCustomFile( int index, char const *animationName )
+{
+ if ( index < 0 || index >= m_CustomAnimationTabs.Count() )
+ return;
+
+ CCustomAnim *anim = m_CustomAnimationTabs[ index ];
+
+ CUtlSymbol search;
+ search = animationName;
+
+ if ( anim->m_Animations.Find( search ) == anim->m_Animations.InvalidIndex() )
+ {
+ anim->m_Animations.AddToTail( search );
+ anim->m_bDirty = true;
+ }
+
+ OnFilter();
+}
+
+void AnimationBrowser::RemoveAnimationFromCustomFile( int index, char const *animationName )
+{
+ if ( index < 0 || index >= m_CustomAnimationTabs.Count() )
+ return;
+
+ CCustomAnim *anim = m_CustomAnimationTabs[ index ];
+
+ CUtlSymbol search;
+ search = animationName;
+
+ int slot = anim->m_Animations.Find( search );
+ if ( slot != anim->m_Animations.InvalidIndex() )
+ {
+ anim->m_Animations.Remove( slot );
+ anim->m_bDirty = true;
+ OnFilter();
+ }
+}
+
+void AnimationBrowser::RemoveAllAnimationsFromCustomFile( int index )
+{
+ if ( index < 0 || index >= m_CustomAnimationTabs.Count() )
+ return;
+
+ CCustomAnim *anim = m_CustomAnimationTabs[ index ];
+ anim->m_Animations.Purge();
+ anim->m_bDirty = true;
+
+ OnFilter();
+} \ No newline at end of file