diff options
Diffstat (limited to 'utils/hlfaceposer/AnimationBrowser.cpp')
| -rw-r--r-- | utils/hlfaceposer/AnimationBrowser.cpp | 1500 |
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( ¶ms, 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( ¶ms ) ) + 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( ¶ms, 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( ¶ms ) ) + 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 |