4816 lines
106 KiB
C++
4816 lines
106 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//
|
|
//=============================================================================//
|
|
#include <stdio.h>
|
|
#include "hlfaceposer.h"
|
|
#include "ExpressionTool.h"
|
|
#include "mdlviewer.h"
|
|
#include "choreowidgetdrawhelper.h"
|
|
#include "TimelineItem.h"
|
|
#include "expressions.h"
|
|
#include "expclass.h"
|
|
#include "choreoevent.h"
|
|
#include "StudioModel.h"
|
|
#include "choreoscene.h"
|
|
#include "choreoactor.h"
|
|
#include "choreochannel.h"
|
|
#include "ChoreoView.h"
|
|
#include "InputProperties.h"
|
|
#include "ControlPanel.h"
|
|
#include "FlexPanel.h"
|
|
#include "mxExpressionTray.h"
|
|
#include "ExpressionProperties.h"
|
|
#include "tier1/strtools.h"
|
|
#include "faceposer_models.h"
|
|
#include "UtlBuffer.h"
|
|
#include "filesystem.h"
|
|
#include "iscenetokenprocessor.h"
|
|
#include "MatSysWin.h"
|
|
#include "choreoviewcolors.h"
|
|
#include "scriplib.h"
|
|
#include "EdgeProperties.h"
|
|
|
|
ExpressionTool *g_pExpressionTool = 0;
|
|
|
|
#define TRAY_HEIGHT 55
|
|
|
|
#define TRAY_ITEM_INSET 10
|
|
|
|
#define MAX_TIME_ZOOM 1000
|
|
// 10% per step
|
|
#define TIME_ZOOM_STEP 2
|
|
|
|
void SetupFlexControllerTracks( CStudioHdr *hdr, CChoreoEvent *event );
|
|
|
|
class CExpressionToolWorkspace : public mxWindow
|
|
{
|
|
public:
|
|
CExpressionToolWorkspace( mxWindow *parent );
|
|
~CExpressionToolWorkspace();
|
|
|
|
virtual int handleEvent( mxEvent *event );
|
|
virtual void redraw( void );
|
|
virtual bool PaintBackground( void )
|
|
{
|
|
redraw();
|
|
return false;
|
|
}
|
|
|
|
void RepositionVSlider( void );
|
|
int ComputeVPixelsNeeded( void );
|
|
// Playback tick
|
|
void Think( float dt );
|
|
|
|
void LayoutItems( bool force = false );
|
|
|
|
void HideTimelines( void );
|
|
void CollapseAll( TimelineItem *keepExpanded );
|
|
|
|
void ExpandAll( void );
|
|
void ExpandValid( void );
|
|
void DisableAllExcept( void );
|
|
void EnableValid( void );
|
|
|
|
TimelineItem *GetItem( int number );
|
|
TimelineItem *GetClickedItem( void );
|
|
void ClearClickedItem( void );
|
|
|
|
void OnSnapAll();
|
|
void OnDeleteColumn();
|
|
|
|
void MoveSelectedSamples( float dfdx, float dfdy, bool snap );
|
|
void DeleteSelectedSamples( void );
|
|
int CountSelectedSamples( void );
|
|
void DeselectAll( void );
|
|
void SelectPoints( float start, float end );
|
|
|
|
void DrawEventEnd( CChoreoWidgetDrawHelper& drawHelper );
|
|
|
|
void OnSortByUsed( void );
|
|
void OnSortByName( void );
|
|
|
|
private:
|
|
|
|
int GetItemUnderMouse( int mx, int my );
|
|
|
|
void MouseToToolMouse( int& mx, int& my, char *reason );
|
|
|
|
TimelineItem *m_pItems[ GLOBAL_STUDIO_FLEX_CONTROL_COUNT ];
|
|
|
|
// The scroll bars
|
|
mxScrollbar *m_pVertScrollBar;
|
|
int m_nLastVPixelsNeeded;
|
|
|
|
int m_nTopOffset;
|
|
int m_nScrollbarHeight;
|
|
|
|
int m_nItemGap;
|
|
int m_nFocusItem;
|
|
};
|
|
|
|
CExpressionToolWorkspace::CExpressionToolWorkspace( mxWindow *parent ) :
|
|
mxWindow( parent, 0, 0, 0, 0 )
|
|
{
|
|
HWND wnd = (HWND)getHandle();
|
|
DWORD style = GetWindowLong( wnd, GWL_STYLE );
|
|
style |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
|
|
SetWindowLong( wnd, GWL_STYLE, style );
|
|
|
|
for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
|
|
{
|
|
m_pItems[ i ] = new TimelineItem( this );
|
|
}
|
|
|
|
m_nItemGap = 2;
|
|
|
|
m_nScrollbarHeight = 12;
|
|
m_nTopOffset = 0;
|
|
|
|
m_nLastVPixelsNeeded = -1;
|
|
|
|
m_pVertScrollBar = new mxScrollbar( this, 0, 0, 12, 100, IDC_EXPRESSIONTOOLVSCROLL, mxScrollbar::Vertical );
|
|
|
|
m_nFocusItem = -1;
|
|
|
|
HideTimelines();
|
|
LayoutItems();
|
|
}
|
|
|
|
CExpressionToolWorkspace::~CExpressionToolWorkspace()
|
|
{
|
|
}
|
|
|
|
void CExpressionToolWorkspace::redraw()
|
|
{
|
|
CChoreoWidgetDrawHelper drawHelper( this );
|
|
|
|
DrawEventEnd( drawHelper );
|
|
|
|
for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
|
|
{
|
|
TimelineItem *item = GetItem( i );
|
|
if ( !item )
|
|
continue;
|
|
|
|
if ( !item->GetVisible() )
|
|
continue;
|
|
|
|
RECT rcBounds;
|
|
item->GetBounds( rcBounds );
|
|
|
|
if ( rcBounds.bottom < 0 )
|
|
continue;
|
|
if ( rcBounds.top > h2() )
|
|
continue;
|
|
|
|
item->Draw( drawHelper );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *elem1 -
|
|
// *elem2 -
|
|
// Output : int
|
|
//-----------------------------------------------------------------------------
|
|
int SortFuncByUse(const void *elem1, const void *elem2 )
|
|
{
|
|
TimelineItem *item1 = *( TimelineItem ** )elem1;
|
|
TimelineItem *item2 = *( TimelineItem ** )elem2;
|
|
|
|
if ( item1->IsValid() == item2->IsValid() )
|
|
return 0;
|
|
|
|
if ( !item2->IsValid() && item1->IsValid() )
|
|
return -1;
|
|
|
|
return 1;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *elem1 -
|
|
// *elem2 -
|
|
// Output : int
|
|
//-----------------------------------------------------------------------------
|
|
int SortFuncByName(const void *elem1, const void *elem2 )
|
|
{
|
|
TimelineItem *item1 = *( TimelineItem ** )elem1;
|
|
TimelineItem *item2 = *( TimelineItem ** )elem2;
|
|
|
|
CFlexAnimationTrack *track1 = item1->GetSafeTrack();
|
|
CFlexAnimationTrack *track2 = item2->GetSafeTrack();
|
|
|
|
if ( !track1 || !track2 )
|
|
{
|
|
if ( track1 )
|
|
return -1;
|
|
if ( track2 )
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
return stricmp( track1->GetFlexControllerName(), track2->GetFlexControllerName() );
|
|
}
|
|
|
|
void CExpressionToolWorkspace::OnSortByUsed( void )
|
|
{
|
|
qsort( m_pItems, GLOBAL_STUDIO_FLEX_CONTROL_COUNT, sizeof( TimelineItem * ), SortFuncByUse );
|
|
LayoutItems( false );
|
|
}
|
|
|
|
void CExpressionToolWorkspace::OnSortByName( void )
|
|
{
|
|
qsort( m_pItems, GLOBAL_STUDIO_FLEX_CONTROL_COUNT, sizeof( TimelineItem * ), SortFuncByName );
|
|
LayoutItems( false );
|
|
}
|
|
|
|
void CExpressionToolWorkspace::DrawEventEnd( CChoreoWidgetDrawHelper& drawHelper )
|
|
{
|
|
if ( !g_pExpressionTool )
|
|
return;
|
|
|
|
CChoreoEvent *e = g_pExpressionTool->GetSafeEvent();
|
|
if ( !e )
|
|
return;
|
|
|
|
float duration = e->GetDuration();
|
|
if ( !duration )
|
|
return;
|
|
|
|
int leftx = g_pExpressionTool->GetPixelForTimeValue( duration ) -5;
|
|
if ( leftx >= w2() )
|
|
return;
|
|
|
|
RECT rcClient;
|
|
drawHelper.GetClientRect( rcClient );
|
|
|
|
drawHelper.DrawColoredLine(
|
|
COLOR_CHOREO_ENDTIME, PS_SOLID, 1,
|
|
leftx, rcClient.top, leftx, rcClient.bottom );
|
|
|
|
}
|
|
|
|
int CExpressionToolWorkspace::GetItemUnderMouse( int mx, int my )
|
|
{
|
|
POINT pt;
|
|
pt.x = mx;
|
|
pt.y = my;
|
|
|
|
for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
|
|
{
|
|
TimelineItem *item = GetItem( i );
|
|
if ( !item )
|
|
continue;
|
|
|
|
if ( !item->GetVisible() )
|
|
continue;
|
|
|
|
RECT rc;
|
|
item->GetBounds( rc );
|
|
|
|
if ( PtInRect( &rc, pt ) )
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
void CExpressionToolWorkspace::MouseToToolMouse( int& mx, int& my, char *reason )
|
|
{
|
|
POINT pt;
|
|
pt.x = mx;
|
|
pt.y = my;
|
|
|
|
ClientToScreen( (HWND)getHandle(), &pt );
|
|
ScreenToClient( (HWND)getParent()->getHandle(), &pt );
|
|
|
|
mx = pt.x;
|
|
my = pt.y;
|
|
}
|
|
|
|
int CExpressionToolWorkspace::handleEvent( mxEvent *event )
|
|
{
|
|
MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
|
|
|
|
int iret = 0;
|
|
|
|
switch ( event->event )
|
|
{
|
|
case mxEvent::MouseDown:
|
|
{
|
|
HWND wnd = (HWND)getParent()->getHandle();
|
|
SetFocus( wnd );
|
|
SetWindowPos( wnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
|
|
|
|
{
|
|
int mx = (short)event->x;
|
|
int my = (short)event->y;
|
|
|
|
MouseToToolMouse( mx, my, "CExpressionToolWorkspace mousedown" );
|
|
|
|
g_pExpressionTool->SetClickedPos( mx, my );
|
|
g_pExpressionTool->SetMouseOverPos( mx, my );
|
|
g_pExpressionTool->DrawMouseOverPos();
|
|
|
|
}
|
|
|
|
int oldFocus = m_nFocusItem;
|
|
m_nFocusItem = GetItemUnderMouse( (short)event->x, (short)event->y );
|
|
|
|
if ( oldFocus != -1 &&
|
|
oldFocus != m_nFocusItem )
|
|
{
|
|
TimelineItem *item = GetItem( oldFocus );
|
|
if ( item )
|
|
{
|
|
item->DrawSelf();
|
|
}
|
|
}
|
|
if ( m_nFocusItem != -1 )
|
|
{
|
|
TimelineItem *item = GetItem( m_nFocusItem );
|
|
if ( item )
|
|
{
|
|
RECT rc;
|
|
item->GetBounds( rc );
|
|
|
|
event->x -= rc.left;
|
|
event->y -= rc.top;
|
|
|
|
iret = item->handleEvent( event );
|
|
}
|
|
}
|
|
|
|
iret = 1;
|
|
}
|
|
break;
|
|
case mxEvent::MouseDrag:
|
|
case mxEvent::MouseMove:
|
|
{
|
|
//
|
|
bool handled = false;
|
|
|
|
if ( m_nFocusItem != -1 )
|
|
{
|
|
TimelineItem *item = GetItem( m_nFocusItem );
|
|
if ( item )
|
|
{
|
|
RECT rc;
|
|
item->GetBounds( rc );
|
|
|
|
event->x -= rc.left;
|
|
event->y -= rc.top;
|
|
|
|
iret = item->handleEvent( event );
|
|
|
|
if ( event->event == mxEvent::MouseDrag )
|
|
{
|
|
int mx, my;
|
|
|
|
item->GetLastMouse( mx, my );
|
|
mx += rc.left;
|
|
my += rc.top;
|
|
|
|
MouseToToolMouse( mx, my, "CExpressionToolWorkspace mousedrag" );
|
|
|
|
g_pExpressionTool->SetMouseOverPos( mx, my );
|
|
g_pExpressionTool->DrawMouseOverPos();
|
|
handled = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !handled )
|
|
{
|
|
int mx = (short)event->x;
|
|
int my = (short)event->y;
|
|
|
|
mx += TRAY_ITEM_INSET;
|
|
|
|
MouseToToolMouse( mx, my, "CExpressionToolWorkspace mousemove" );
|
|
|
|
g_pExpressionTool->SetMouseOverPos( mx, my );
|
|
g_pExpressionTool->DrawMouseOverPos();
|
|
}
|
|
}
|
|
break;
|
|
case mxEvent::MouseUp:
|
|
{
|
|
//
|
|
{
|
|
int mx = (short)event->x;
|
|
int my = (short)event->y;
|
|
|
|
MouseToToolMouse( mx, my, "CExpressionToolWorkspace mouseup" );
|
|
|
|
g_pExpressionTool->SetMouseOverPos( mx, my );
|
|
g_pExpressionTool->DrawMouseOverPos();
|
|
|
|
}
|
|
|
|
if ( m_nFocusItem != -1 )
|
|
{
|
|
TimelineItem *item = GetItem( m_nFocusItem );
|
|
if ( item )
|
|
{
|
|
RECT rc;
|
|
item->GetBounds( rc );
|
|
|
|
event->x -= rc.left;
|
|
event->y -= rc.top;
|
|
|
|
iret = item->handleEvent( event );
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case mxEvent::Size:
|
|
{
|
|
RepositionVSlider();
|
|
LayoutItems();
|
|
iret = 1;
|
|
}
|
|
break;
|
|
case mxEvent::MouseWheeled:
|
|
// Tell parent
|
|
{
|
|
if ( event->modifiers & mxEvent::KeyShift )
|
|
{
|
|
CChoreoScene *scene = g_pChoreoView->GetScene();
|
|
if ( scene )
|
|
{
|
|
int tz = g_pChoreoView->GetTimeZoom( g_pExpressionTool->GetToolName() );
|
|
|
|
// Zoom time in / out
|
|
if ( event->height > 0 )
|
|
{
|
|
g_pChoreoView->SetTimeZoom( g_pExpressionTool->GetToolName(), min( tz + TIME_ZOOM_STEP, MAX_TIME_ZOOM ), false );
|
|
}
|
|
else
|
|
{
|
|
g_pChoreoView->SetTimeZoom( g_pExpressionTool->GetToolName(), min( tz - TIME_ZOOM_STEP, MAX_TIME_ZOOM ), false );
|
|
}
|
|
g_pExpressionTool->RepositionHSlider();
|
|
}
|
|
redraw();
|
|
iret = 1;
|
|
return iret;
|
|
}
|
|
|
|
int offset = 0;
|
|
int jump = 50;
|
|
|
|
if ( event->height < 0 )
|
|
{
|
|
offset = m_pVertScrollBar->getValue();
|
|
offset += jump;
|
|
offset = min( offset, m_pVertScrollBar->getMaxValue() );
|
|
}
|
|
else
|
|
{
|
|
offset = m_pVertScrollBar->getValue();
|
|
offset -= jump;
|
|
offset = max( offset, m_pVertScrollBar->getMinValue() );
|
|
}
|
|
|
|
m_pVertScrollBar->setValue( offset );
|
|
InvalidateRect( (HWND)m_pVertScrollBar->getHandle(), NULL, TRUE );
|
|
m_nTopOffset = offset;
|
|
LayoutItems();
|
|
iret = 1;
|
|
}
|
|
break;
|
|
case mxEvent::Action:
|
|
{
|
|
iret = 1;
|
|
switch ( event->action )
|
|
{
|
|
default:
|
|
iret = 0;
|
|
break;
|
|
case IDC_EXPRESSIONTOOLVSCROLL:
|
|
{
|
|
int offset = 0;
|
|
bool processed = true;
|
|
|
|
switch ( event->modifiers )
|
|
{
|
|
case SB_THUMBTRACK:
|
|
offset = event->height;
|
|
break;
|
|
case SB_PAGEUP:
|
|
offset = m_pVertScrollBar->getValue();
|
|
offset -= 100;
|
|
offset = max( offset, m_pVertScrollBar->getMinValue() );
|
|
break;
|
|
case SB_PAGEDOWN:
|
|
offset = m_pVertScrollBar->getValue();
|
|
offset += 100;
|
|
offset = min( offset, m_pVertScrollBar->getMaxValue() );
|
|
break;
|
|
case SB_LINEDOWN:
|
|
offset = m_pVertScrollBar->getValue();
|
|
offset += 10;
|
|
offset = min( offset, m_pVertScrollBar->getMaxValue() );
|
|
break;
|
|
case SB_LINEUP:
|
|
offset = m_pVertScrollBar->getValue();
|
|
offset -= 10;
|
|
offset = max( offset, m_pVertScrollBar->getMinValue() );
|
|
break;
|
|
default:
|
|
processed = false;
|
|
break;
|
|
}
|
|
|
|
if ( processed )
|
|
{
|
|
m_pVertScrollBar->setValue( offset );
|
|
InvalidateRect( (HWND)m_pVertScrollBar->getHandle(), NULL, TRUE );
|
|
m_nTopOffset = offset;
|
|
LayoutItems();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
return iret;
|
|
}
|
|
|
|
void CExpressionToolWorkspace::HideTimelines( void )
|
|
{
|
|
for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
|
|
{
|
|
TimelineItem *item = GetItem( i );
|
|
Assert( item );
|
|
item->SetVisible( false );
|
|
}
|
|
|
|
redraw();
|
|
}
|
|
|
|
|
|
TimelineItem *CExpressionToolWorkspace::GetItem( int number )
|
|
{
|
|
if ( number < 0 || number >= GLOBAL_STUDIO_FLEX_CONTROL_COUNT )
|
|
{
|
|
return NULL;
|
|
}
|
|
return m_pItems[ number ];
|
|
}
|
|
|
|
TimelineItem *CExpressionToolWorkspace::GetClickedItem( void )
|
|
{
|
|
return GetItem( m_nFocusItem );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CExpressionToolWorkspace::ClearClickedItem( void )
|
|
{
|
|
m_nFocusItem = -1;
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : force - force vert scrollbar recomputation
|
|
//-----------------------------------------------------------------------------
|
|
void CExpressionToolWorkspace::LayoutItems( bool force /* = false */ )
|
|
{
|
|
int x = TRAY_ITEM_INSET;
|
|
int y = - m_nTopOffset;
|
|
int width = w2() - 2 * TRAY_ITEM_INSET - m_nScrollbarHeight;
|
|
int height;
|
|
|
|
for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
|
|
{
|
|
TimelineItem *item = GetItem( i );
|
|
if ( !item || !item->GetVisible() )
|
|
continue;
|
|
|
|
height = item->GetHeight();
|
|
|
|
RECT rcBounds;
|
|
rcBounds.left = x;
|
|
rcBounds.top = y;
|
|
rcBounds.right = x + width;
|
|
rcBounds.bottom = y + height;
|
|
|
|
item->SetBounds( rcBounds );
|
|
y += height + m_nItemGap;
|
|
}
|
|
|
|
if ( force || ( ComputeVPixelsNeeded() != m_nLastVPixelsNeeded ) )
|
|
{
|
|
RepositionVSlider();
|
|
}
|
|
|
|
redraw();
|
|
}
|
|
|
|
int CExpressionToolWorkspace::ComputeVPixelsNeeded( void )
|
|
{
|
|
int pixels = 0;
|
|
|
|
// Count visible
|
|
int c = 0;
|
|
for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
|
|
{
|
|
TimelineItem *item = GetItem( i );
|
|
if ( !item || !item->GetVisible() )
|
|
continue;
|
|
|
|
c += item->GetHeight();
|
|
c += m_nItemGap;
|
|
}
|
|
|
|
pixels += c;
|
|
|
|
return pixels;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CExpressionToolWorkspace::RepositionVSlider( void )
|
|
{
|
|
int pixelsneeded = ComputeVPixelsNeeded();
|
|
|
|
if ( pixelsneeded <= ( h2() ))
|
|
{
|
|
m_pVertScrollBar->setVisible( false );
|
|
m_nTopOffset = 0;
|
|
}
|
|
else
|
|
{
|
|
m_pVertScrollBar->setVisible( true );
|
|
}
|
|
|
|
m_pVertScrollBar->setBounds(
|
|
w2() - m_nScrollbarHeight,
|
|
0,
|
|
m_nScrollbarHeight,
|
|
h2() );
|
|
|
|
m_nTopOffset = max( 0, m_nTopOffset );
|
|
m_nTopOffset = min( pixelsneeded, m_nTopOffset );
|
|
|
|
m_pVertScrollBar->setRange( 0, pixelsneeded );
|
|
m_pVertScrollBar->setValue( m_nTopOffset );
|
|
m_pVertScrollBar->setPagesize( h2() );
|
|
|
|
m_nLastVPixelsNeeded = pixelsneeded;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CExpressionToolWorkspace::DisableAllExcept( void )
|
|
{
|
|
TimelineItem *keepExpanded = GetClickedItem();
|
|
if ( !keepExpanded )
|
|
return;
|
|
|
|
g_pChoreoView->SetDirty( true );
|
|
g_pChoreoView->PushUndo( "Disable All Except" );
|
|
|
|
for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
|
|
{
|
|
TimelineItem *item = GetItem( i );
|
|
|
|
item->SetActive( item == keepExpanded ? true : false );
|
|
}
|
|
|
|
LayoutItems();
|
|
g_pChoreoView->PushRedo( "Disable All Except" );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CExpressionToolWorkspace::EnableValid( void )
|
|
{
|
|
g_pChoreoView->SetDirty( true );
|
|
g_pChoreoView->PushUndo( "Enable Valid" );
|
|
for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
|
|
{
|
|
TimelineItem *item = GetItem( i );
|
|
|
|
item->SetActive( item->IsValid() );
|
|
}
|
|
|
|
LayoutItems();
|
|
g_pChoreoView->PushRedo( "Enable Valid" );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CExpressionToolWorkspace::CollapseAll( TimelineItem *keepExpanded )
|
|
{
|
|
for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
|
|
{
|
|
TimelineItem *item = GetItem( i );
|
|
|
|
item->SetCollapsed( item == keepExpanded ? false : true );
|
|
}
|
|
|
|
LayoutItems();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CExpressionToolWorkspace::ExpandAll( void )
|
|
{
|
|
for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
|
|
{
|
|
TimelineItem *item = GetItem( i );
|
|
item->SetCollapsed( false );
|
|
}
|
|
|
|
LayoutItems();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CExpressionToolWorkspace::OnSnapAll()
|
|
{
|
|
g_pChoreoView->SetDirty( true );
|
|
g_pChoreoView->PushUndo( "Snap All" );
|
|
|
|
for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
|
|
{
|
|
TimelineItem *item = GetItem( i );
|
|
item->SnapAll();
|
|
}
|
|
|
|
g_pChoreoView->PushRedo( "Snap All" );
|
|
redraw();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CExpressionToolWorkspace::OnDeleteColumn()
|
|
{
|
|
float t = g_pExpressionTool->GetTimeForClickedPos();
|
|
|
|
float snapped = FacePoser_SnapTime( t );
|
|
int scenefps = FacePoser_GetSceneFPS();
|
|
|
|
if ( scenefps <= 0 )
|
|
{
|
|
Con_Printf( "Can't delete column, scene fps is <= 0 (%i)\n", scenefps );
|
|
return;
|
|
}
|
|
|
|
int clickedframe = ( int ) ( scenefps * snapped + 0.5f );
|
|
|
|
// One half of 1/fps on each side
|
|
float epsilon = epsilon = 0.5f / (float)scenefps;
|
|
|
|
CInputParams params;
|
|
memset( ¶ms, 0, sizeof( params ) );
|
|
strcpy( params.m_szDialogTitle, "Delete Column" );
|
|
strcpy( params.m_szPrompt, "Frame(s) to delete [e.g., 82 or 81-91 ]:" );
|
|
Q_snprintf( params.m_szInputText, sizeof( params.m_szInputText ), "%i", clickedframe );
|
|
|
|
if ( !InputProperties( ¶ms ) )
|
|
return;
|
|
|
|
int deleteframestart;
|
|
int deleteframeend;
|
|
|
|
char *sep = Q_strstr( params.m_szInputText, "-" );
|
|
if ( sep )
|
|
{
|
|
*sep = 0;
|
|
deleteframestart = atoi( params.m_szInputText );
|
|
deleteframeend = atoi( sep + 1 );
|
|
deleteframeend = max( deleteframestart, deleteframeend );
|
|
}
|
|
else
|
|
{
|
|
deleteframestart = atoi( params.m_szInputText );
|
|
deleteframeend = deleteframestart;
|
|
}
|
|
|
|
float start, end;
|
|
|
|
start = (float)deleteframestart / (float)scenefps;
|
|
end = (float)deleteframeend / (float)scenefps;
|
|
|
|
g_pChoreoView->SetDirty( true );
|
|
g_pChoreoView->PushUndo( "Delete Column" );
|
|
|
|
for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
|
|
{
|
|
TimelineItem *item = GetItem( i );
|
|
item->DeletePoints( start - epsilon, end + epsilon );
|
|
}
|
|
|
|
g_pChoreoView->PushRedo( "Delete Column" );
|
|
redraw();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CExpressionToolWorkspace::ExpandValid( void )
|
|
{
|
|
for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
|
|
{
|
|
TimelineItem *item = GetItem( i );
|
|
bool valid = item->IsValid();
|
|
item->SetCollapsed( !valid );
|
|
}
|
|
|
|
LayoutItems();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : int
|
|
//-----------------------------------------------------------------------------
|
|
int CExpressionToolWorkspace::CountSelectedSamples( void )
|
|
{
|
|
int c = 0;
|
|
for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
|
|
{
|
|
TimelineItem *item = GetItem( i );
|
|
Assert( item );
|
|
item->CountSelected();
|
|
c += item->GetNumSelected();
|
|
}
|
|
return c;
|
|
}
|
|
|
|
void CExpressionToolWorkspace::MoveSelectedSamples( float dfdx, float dfdy, bool snap )
|
|
{
|
|
int selecteditems = CountSelectedSamples();
|
|
if ( !selecteditems )
|
|
return;
|
|
|
|
CChoreoEvent *e = g_pExpressionTool->GetSafeEvent();
|
|
if ( !e )
|
|
return;
|
|
|
|
float eventduration = e->GetDuration();
|
|
|
|
for ( int controller = 0; controller < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; controller++ )
|
|
{
|
|
TimelineItem *item = GetItem( controller );
|
|
if ( !item )
|
|
continue;
|
|
|
|
CFlexAnimationTrack *track = item->GetSafeTrack();
|
|
if ( !track )
|
|
continue;
|
|
|
|
// If the track is a combo type track, then move any underlying selected samples, too
|
|
for ( int edittype = 0; edittype <= ( track->IsComboType() ? 1 : 0 ); edittype++ )
|
|
{
|
|
for ( int i = 0; i < (int)track->GetNumSamples( edittype ); i++ )
|
|
{
|
|
CExpressionSample *sample = track->GetSample( i, edittype );
|
|
if ( !sample || !sample->selected )
|
|
continue;
|
|
|
|
sample->time += dfdx;
|
|
sample->time = clamp( sample->time, 0.0f, eventduration );
|
|
|
|
if ( snap )
|
|
{
|
|
sample->time = FacePoser_SnapTime( sample->time );
|
|
}
|
|
|
|
sample->value -= dfdy;
|
|
sample->value = clamp( sample->value, 0.0f, 1.0f );
|
|
}
|
|
}
|
|
|
|
track->Resort();
|
|
|
|
item->DrawSelf();
|
|
}
|
|
}
|
|
|
|
void CExpressionToolWorkspace::DeleteSelectedSamples( void )
|
|
{
|
|
int i, t;
|
|
|
|
int selecteditems = CountSelectedSamples();
|
|
if ( !selecteditems )
|
|
return;
|
|
|
|
g_pChoreoView->SetDirty( true );
|
|
g_pChoreoView->PushUndo( "Delete points" );
|
|
|
|
for ( int controller = 0; controller < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; controller++ )
|
|
{
|
|
TimelineItem *item = GetItem( controller );
|
|
if ( !item )
|
|
continue;
|
|
|
|
CFlexAnimationTrack *track = item->GetSafeTrack();
|
|
if ( !track )
|
|
continue;
|
|
|
|
for ( t = 0; t < 2; t++ )
|
|
{
|
|
for ( i = track->GetNumSamples( t ) - 1; i >= 0 ; i-- )
|
|
{
|
|
CExpressionSample *sample = track->GetSample( i, t );
|
|
if ( !sample->selected )
|
|
continue;
|
|
|
|
track->RemoveSample( i, t );
|
|
}
|
|
}
|
|
|
|
item->DrawSelf();
|
|
}
|
|
|
|
g_pChoreoView->PushRedo( "Delete points" );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CExpressionToolWorkspace::DeselectAll( void )
|
|
{
|
|
int i, t;
|
|
|
|
int selecteditems = CountSelectedSamples();
|
|
if ( !selecteditems )
|
|
return;
|
|
|
|
for ( int controller = 0; controller < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; controller++ )
|
|
{
|
|
TimelineItem *item = GetItem( controller );
|
|
if ( !item )
|
|
continue;
|
|
|
|
CFlexAnimationTrack *track = item->GetSafeTrack();
|
|
if ( !track )
|
|
continue;
|
|
|
|
for ( t = 0; t < 2; t++ )
|
|
{
|
|
for ( i = track->GetNumSamples( t ) - 1; i >= 0 ; i-- )
|
|
{
|
|
CExpressionSample *sample = track->GetSample( i, t );
|
|
sample->selected = false;
|
|
}
|
|
}
|
|
|
|
item->DrawSelf();
|
|
}
|
|
}
|
|
|
|
void CExpressionToolWorkspace::SelectPoints( float start, float end )
|
|
{
|
|
int i, t;
|
|
|
|
for ( int controller = 0; controller < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; controller++ )
|
|
{
|
|
TimelineItem *item = GetItem( controller );
|
|
if ( !item )
|
|
continue;
|
|
|
|
CFlexAnimationTrack *track = item->GetSafeTrack();
|
|
if ( !track )
|
|
continue;
|
|
|
|
for ( t = 0; t < 2; t++ )
|
|
{
|
|
for ( i = track->GetNumSamples( t ) - 1; i >= 0 ; i-- )
|
|
{
|
|
CExpressionSample *sample = track->GetSample( i, t );
|
|
bool inrange = ( sample->time >= start && sample->time <= end );
|
|
sample->selected = inrange;
|
|
}
|
|
}
|
|
|
|
item->DrawSelf();
|
|
}
|
|
}
|
|
|
|
ExpressionTool::ExpressionTool( mxWindow *parent )
|
|
: IFacePoserToolWindow( "ExpressionTool", "Flex Animation" ), mxWindow( parent, 0, 0, 0, 0 )
|
|
{
|
|
m_bSuppressLayout = false;
|
|
|
|
SetAutoProcess( true );
|
|
|
|
m_pWorkspace = new CExpressionToolWorkspace( this );
|
|
|
|
m_nFocusEventGlobalID = -1;
|
|
|
|
m_flScrub = 0.0f;
|
|
m_flScrubTarget = 0.0f;
|
|
m_nDragType = DRAGTYPE_NONE;
|
|
|
|
m_nClickedX = 0;
|
|
m_nClickedY = 0;
|
|
|
|
m_hPrevCursor = 0;
|
|
|
|
m_nStartX = 0;
|
|
m_nStartY = 0;
|
|
|
|
m_nMinX = 0;
|
|
m_nMaxX = 0;
|
|
m_bUseBounds = false;
|
|
|
|
m_pLastEvent = NULL;
|
|
|
|
m_nMousePos[ 0 ] = m_nMousePos[ 1 ] = 0;
|
|
|
|
m_flSelection[ 0 ] = m_flSelection[ 1 ] = 0.0f;
|
|
m_bSelectionActive = false;
|
|
|
|
m_bLayoutIsValid = false;
|
|
m_flPixelsPerSecond = 500.0f;
|
|
|
|
m_flLastDuration = 0.0f;
|
|
m_nScrollbarHeight = 12;
|
|
m_flLeftOffset = 0.0f;
|
|
m_nLastHPixelsNeeded = -1;
|
|
m_pHorzScrollBar = new mxScrollbar( this, 0, 0, 18, 100, IDC_FLEXHSCROLL, mxScrollbar::Horizontal );
|
|
m_pHorzScrollBar->setVisible( false );
|
|
|
|
m_bInSetEvent = false;
|
|
m_flScrubberTimeOffset = 0.0f;
|
|
}
|
|
|
|
ExpressionTool::~ExpressionTool( void )
|
|
{
|
|
}
|
|
|
|
void ExpressionTool::DoTrackLookup( CChoreoEvent *event )
|
|
{
|
|
if ( !event || !models->GetActiveStudioModel() )
|
|
return;
|
|
|
|
//if ( event->GetTrackLookupSet() )
|
|
// return;
|
|
|
|
// Force recompute
|
|
SetEvent( event );
|
|
}
|
|
|
|
#pragma optimize( "g", off )
|
|
|
|
void ExpressionTool::SetEvent( CChoreoEvent *event )
|
|
{
|
|
if ( m_bInSetEvent )
|
|
return;
|
|
|
|
m_bInSetEvent = true;
|
|
|
|
if ( event == m_pLastEvent )
|
|
{
|
|
if ( event )
|
|
{
|
|
float dur = event->GetDuration();
|
|
if ( dur != m_flLastDuration )
|
|
{
|
|
m_flLastDuration = dur;
|
|
m_nLastHPixelsNeeded = -1;
|
|
m_flLeftOffset = 0.0f;
|
|
InvalidateLayout();
|
|
}
|
|
|
|
m_nFocusEventGlobalID = event->GetGlobalID();
|
|
}
|
|
m_bInSetEvent = false;
|
|
return;
|
|
}
|
|
|
|
m_pLastEvent = event;
|
|
|
|
m_pWorkspace->HideTimelines();
|
|
|
|
m_nFocusEventGlobalID = -1;
|
|
if ( event )
|
|
{
|
|
m_nFocusEventGlobalID = event->GetGlobalID();
|
|
|
|
if ( models->GetActiveStudioModel() )
|
|
{
|
|
CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
|
|
if ( hdr )
|
|
{
|
|
// Force re-lookup
|
|
event->SetTrackLookupSet( false );
|
|
|
|
SetupFlexControllerTracks( hdr, event );
|
|
|
|
int itemCount = 0;
|
|
|
|
for ( int i = 0; i < event->GetNumFlexAnimationTracks(); i++ )
|
|
{
|
|
CFlexAnimationTrack *track = event->GetFlexAnimationTrack( i );
|
|
Assert( track );
|
|
if ( !track )
|
|
continue;
|
|
|
|
TimelineItem *item = m_pWorkspace->GetItem( itemCount++ );
|
|
item->SetExpressionInfo( track, track->GetFlexControllerIndex( 0 ) );
|
|
item->SetCollapsed( track->GetNumSamples( 0 ) <= 0 );
|
|
item->SetVisible( true );
|
|
}
|
|
|
|
m_pWorkspace->LayoutItems( true );
|
|
}
|
|
}
|
|
}
|
|
|
|
DeselectAll();
|
|
|
|
if ( event )
|
|
{
|
|
m_flLastDuration = event->GetDuration();
|
|
}
|
|
else
|
|
{
|
|
m_flLastDuration = 0.0f;
|
|
}
|
|
|
|
m_flLeftOffset = 0.0f;
|
|
m_nLastHPixelsNeeded = -1;
|
|
InvalidateLayout();
|
|
|
|
m_bInSetEvent = false;
|
|
}
|
|
|
|
#pragma optimize( "g", on )
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool ExpressionTool::HasCopyData( void )
|
|
{
|
|
return ( m_CopyData[0].Size() != 0 ) ? true : false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *source -
|
|
//-----------------------------------------------------------------------------
|
|
void ExpressionTool::Copy( CFlexAnimationTrack *source )
|
|
{
|
|
for ( int t = 0; t < 2; t++ )
|
|
{
|
|
m_CopyData[ t ].RemoveAll();
|
|
|
|
if ( t == 0 || source->IsComboType() )
|
|
{
|
|
for ( int i = 0 ; i < source->GetNumSamples( t ); i++ )
|
|
{
|
|
CExpressionSample *s = source->GetSample( i, t );
|
|
m_CopyData[ t ].AddToTail( *s );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *destination -
|
|
//-----------------------------------------------------------------------------
|
|
void ExpressionTool::Paste( CFlexAnimationTrack *destination )
|
|
{
|
|
g_pChoreoView->SetDirty( true );
|
|
g_pChoreoView->PushUndo( "Paste" );
|
|
|
|
destination->Clear();
|
|
|
|
for ( int t = 0; t < 2; t++ )
|
|
{
|
|
for ( int i = 0; i < m_CopyData[ t ].Size() ; i++ )
|
|
{
|
|
CExpressionSample *s = &m_CopyData[ t ][ i ];
|
|
|
|
if ( t == 0 || destination->IsComboType() )
|
|
{
|
|
destination->AddSample( s->time, s->value, t );
|
|
}
|
|
}
|
|
|
|
destination->Resort( t );
|
|
}
|
|
g_pChoreoView->PushRedo( "Paste" );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
CChoreoEvent *ExpressionTool::GetSafeEvent( void )
|
|
{
|
|
if ( m_nFocusEventGlobalID == -1 )
|
|
return NULL;
|
|
|
|
if ( !g_pChoreoView )
|
|
return NULL;
|
|
|
|
CChoreoScene *scene = g_pChoreoView->GetScene();
|
|
if ( !scene )
|
|
return NULL;
|
|
|
|
// look to see if it's focused any any event
|
|
for ( int i = 0; i < scene->GetNumEvents() ; i++ )
|
|
{
|
|
CChoreoEvent *e = scene->GetEvent( i );
|
|
if ( !e || e->GetType() != CChoreoEvent::FLEXANIMATION )
|
|
continue;
|
|
|
|
if ( e->GetGlobalID() == m_nFocusEventGlobalID )
|
|
{
|
|
DoTrackLookup( e );
|
|
return e;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : rcHandle -
|
|
//-----------------------------------------------------------------------------
|
|
void ExpressionTool::GetScrubHandleRect( RECT& rcHandle, bool clipped )
|
|
{
|
|
float pixel = 0.0f;
|
|
if ( m_pWorkspace->w2() > 0 )
|
|
{
|
|
pixel = GetPixelForTimeValue( m_flScrub );
|
|
if ( clipped )
|
|
{
|
|
pixel = clamp( pixel, SCRUBBER_HANDLE_WIDTH/2, w2() - SCRUBBER_HANDLE_WIDTH/2 );
|
|
}
|
|
}
|
|
|
|
rcHandle.left = pixel-SCRUBBER_HANDLE_WIDTH/2;
|
|
rcHandle.right = pixel + SCRUBBER_HANDLE_WIDTH/2;
|
|
rcHandle.top = 2 + GetCaptionHeight();
|
|
rcHandle.bottom = rcHandle.top + SCRUBBER_HANDLE_HEIGHT;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : drawHelper -
|
|
// rcHandle -
|
|
//-----------------------------------------------------------------------------
|
|
void ExpressionTool::DrawScrubHandle( CChoreoWidgetDrawHelper& drawHelper, RECT& rcHandle )
|
|
{
|
|
HBRUSH br = CreateSolidBrush( RGB( 0, 150, 100 ) );
|
|
|
|
COLORREF areaBorder = RGB( 230, 230, 220 );
|
|
|
|
drawHelper.DrawColoredLine( areaBorder,
|
|
PS_SOLID, 1, 0, rcHandle.top, w2(), rcHandle.top );
|
|
drawHelper.DrawColoredLine( areaBorder,
|
|
PS_SOLID, 1, 0, rcHandle.bottom, w2(), rcHandle.bottom );
|
|
|
|
drawHelper.DrawFilledRect( br, rcHandle );
|
|
|
|
//
|
|
char sz[ 32 ];
|
|
sprintf( sz, "%.3f", m_flScrub );
|
|
|
|
CChoreoEvent *ev = GetSafeEvent();
|
|
if ( ev )
|
|
{
|
|
float st, ed;
|
|
st = ev->GetStartTime();
|
|
ed = ev->GetEndTime();
|
|
|
|
float dt = ed - st;
|
|
if ( dt > 0.0f )
|
|
{
|
|
sprintf( sz, "%.3f", st + m_flScrub );
|
|
}
|
|
}
|
|
|
|
int len = drawHelper.CalcTextWidth( "Arial", 9, 500, sz );
|
|
|
|
RECT rcText = rcHandle;
|
|
|
|
int textw = rcText.right - rcText.left;
|
|
|
|
rcText.left += ( textw - len ) / 2;
|
|
|
|
drawHelper.DrawColoredText( "Arial", 9, 500, RGB( 255, 255, 255 ), rcText, sz );
|
|
|
|
DeleteObject( br );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *event -
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool ExpressionTool::IsMouseOverScrubHandle( mxEvent *event )
|
|
{
|
|
RECT rcHandle;
|
|
GetScrubHandleRect( rcHandle, true );
|
|
InflateRect( &rcHandle, 2, 2 );
|
|
|
|
POINT pt;
|
|
pt.x = (short)event->x;
|
|
pt.y = (short)event->y;
|
|
if ( PtInRect( &rcHandle, pt ) )
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool ExpressionTool::IsProcessing( void )
|
|
{
|
|
if ( !GetSafeEvent() )
|
|
return false;
|
|
|
|
if ( m_flScrub != m_flScrubTarget )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ExpressionTool::IsScrubbing( void ) const
|
|
{
|
|
bool scrubbing = ( m_nDragType == DRAGTYPE_SCRUBBER ) ? true : false;
|
|
return scrubbing;
|
|
}
|
|
|
|
void ExpressionTool::Think( float dt )
|
|
{
|
|
CChoreoEvent *event = GetSafeEvent();
|
|
if ( !event )
|
|
return;
|
|
|
|
bool scrubbing = IsScrubbing();
|
|
|
|
ScrubThink( dt, scrubbing );
|
|
}
|
|
|
|
void ExpressionTool::SetScrubTime( float t )
|
|
{
|
|
m_flScrub = t;
|
|
CChoreoEvent *e = GetSafeEvent();
|
|
if ( e )
|
|
{
|
|
float realtime = e->GetStartTime() + m_flScrub;
|
|
|
|
g_pChoreoView->SetScrubTime( realtime );
|
|
g_pChoreoView->DrawScrubHandle();
|
|
}
|
|
}
|
|
|
|
void ExpressionTool::SetScrubTargetTime( float t )
|
|
{
|
|
m_flScrubTarget = t;
|
|
CChoreoEvent *e = GetSafeEvent();
|
|
if ( e )
|
|
{
|
|
float realtime = e->GetStartTime() + m_flScrubTarget;
|
|
|
|
g_pChoreoView->SetScrubTargetTime( realtime );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : dt -
|
|
//-----------------------------------------------------------------------------
|
|
void ExpressionTool::ScrubThink( float dt, bool scrubbing )
|
|
{
|
|
CChoreoEvent *event = GetSafeEvent();
|
|
if ( !event )
|
|
return;
|
|
|
|
if ( m_flScrubTarget == m_flScrub && !scrubbing )
|
|
return;
|
|
|
|
float d = m_flScrubTarget - m_flScrub;
|
|
int sign = d > 0.0f ? 1 : -1;
|
|
|
|
float maxmove = dt;
|
|
|
|
if ( sign > 0 )
|
|
{
|
|
if ( d < maxmove )
|
|
{
|
|
SetScrubTime( m_flScrubTarget );
|
|
}
|
|
else
|
|
{
|
|
SetScrubTime( m_flScrub + maxmove );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( -d < maxmove )
|
|
{
|
|
SetScrubTime( m_flScrubTarget );
|
|
}
|
|
else
|
|
{
|
|
SetScrubTime( m_flScrub - maxmove );
|
|
}
|
|
}
|
|
|
|
if ( scrubbing )
|
|
{
|
|
g_pMatSysWindow->Frame();
|
|
}
|
|
}
|
|
|
|
void ExpressionTool::redraw()
|
|
{
|
|
if ( !ToolCanDraw() )
|
|
return;
|
|
|
|
CChoreoWidgetDrawHelper drawHelper( this );
|
|
HandleToolRedraw( drawHelper );
|
|
|
|
COLORREF areaBorder = RGB( 230, 230, 220 );
|
|
|
|
RECT rcSelection;
|
|
GetWorkspaceRect( rcSelection );
|
|
|
|
drawHelper.DrawColoredLine( areaBorder,
|
|
PS_SOLID, 1, 0, rcSelection.top, w2(), rcSelection.top );
|
|
drawHelper.DrawColoredLine( areaBorder,
|
|
PS_SOLID, 1, 0, rcSelection.bottom, w2(), rcSelection.bottom );
|
|
|
|
if ( m_bSelectionActive )
|
|
{
|
|
RECT rcClient;
|
|
drawHelper.GetClientRect( rcClient );
|
|
|
|
int left, right;
|
|
left = GetPixelForTimeValue( m_flSelection[ 0 ] );
|
|
right = GetPixelForTimeValue( m_flSelection[ 1 ] );
|
|
|
|
rcSelection.left = left;
|
|
rcSelection.right = right;
|
|
rcSelection.bottom = TRAY_HEIGHT;
|
|
|
|
drawHelper.DrawFilledRect( RGB( 200, 220, 230 ), rcSelection );
|
|
|
|
drawHelper.DrawColoredLine( RGB( 100, 100, 255 ), PS_SOLID, 3, rcSelection.left, rcSelection.top, rcSelection.left, rcSelection.bottom );
|
|
drawHelper.DrawColoredLine( RGB( 100, 100, 255 ), PS_SOLID, 3, rcSelection.right, rcSelection.top, rcSelection.right, rcSelection.bottom );
|
|
|
|
}
|
|
|
|
CChoreoEvent *ev = GetSafeEvent();
|
|
if ( ev )
|
|
{
|
|
RECT rcText;
|
|
drawHelper.GetClientRect( rcText );
|
|
rcText.top += GetCaptionHeight()+1;
|
|
rcText.bottom = rcText.top + 13;
|
|
rcText.left += 5;
|
|
rcText.right -= 5;
|
|
|
|
OffsetRect( &rcText, 0, 12 );
|
|
|
|
int current, total;
|
|
|
|
g_pChoreoView->GetUndoLevels( current, total );
|
|
if ( total > 0 )
|
|
{
|
|
RECT rcUndo = rcText;
|
|
OffsetRect( &rcUndo, 0, 2 );
|
|
|
|
drawHelper.DrawColoredText( "Small Fonts", 8, FW_NORMAL, RGB( 0, 100, 0 ), rcUndo,
|
|
"Undo: %i/%i", current, total );
|
|
}
|
|
|
|
rcText.left += 60;
|
|
|
|
// Found it, write out description
|
|
//
|
|
drawHelper.DrawColoredText( "Arial", 11, 900, RGB( 200, 150, 100 ), rcText,
|
|
"Event: %s",
|
|
ev->GetName() );
|
|
|
|
OffsetRect( &rcText, 0, 30 );
|
|
|
|
rcText.left = 5;
|
|
|
|
RECT timeRect = rcText;
|
|
|
|
timeRect.right = timeRect.left + 100;
|
|
|
|
char sz[ 32 ];
|
|
|
|
float st, ed;
|
|
|
|
GetStartAndEndTime( st, ed );
|
|
|
|
st += ev->GetStartTime();
|
|
ed += ev->GetStartTime();
|
|
|
|
Q_snprintf( sz, sizeof( sz ), "%.2f", st );
|
|
|
|
drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 0, 0 ), timeRect, sz );
|
|
|
|
timeRect = rcText;
|
|
|
|
Q_snprintf( sz, sizeof( sz ), "%.2f", ed );
|
|
|
|
int textW = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, sz );
|
|
|
|
timeRect.right = w2() - 10;
|
|
timeRect.left = timeRect.right - textW;
|
|
|
|
drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 0, 0 ), timeRect, sz );
|
|
}
|
|
|
|
RECT rcHandle;
|
|
GetScrubHandleRect( rcHandle, true );
|
|
DrawScrubHandle( drawHelper, rcHandle );
|
|
|
|
DrawRelativeTags( drawHelper );
|
|
|
|
RECT rcPos;
|
|
GetMouseOverPosRect( rcPos );
|
|
DrawMouseOverPos( drawHelper, rcPos );
|
|
|
|
DrawEventEnd( drawHelper );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *tag -
|
|
//-----------------------------------------------------------------------------
|
|
bool ExpressionTool::GetTimingTagRect( RECT& rcClient, CChoreoEvent *event, CFlexTimingTag *tag, RECT& rcTag )
|
|
{
|
|
rcTag = rcClient;
|
|
|
|
int tagx = GetPixelForTimeValue( tag->GetStartTime() - event->GetStartTime() );
|
|
|
|
rcTag.top = rcClient.bottom - 6;
|
|
rcTag.bottom = rcTag.top + 6;
|
|
rcTag.left = tagx - 3;
|
|
rcTag.right = tagx + 3;
|
|
|
|
return true;
|
|
}
|
|
|
|
// Get workspace min, max point in terms of tool window
|
|
void ExpressionTool::GetWorkspaceLeftRight( int& left, int& right )
|
|
{
|
|
POINT pt;
|
|
pt.x = TRAY_ITEM_INSET;
|
|
pt.y = 0;
|
|
|
|
ClientToScreen( (HWND)m_pWorkspace->getHandle(), &pt );
|
|
ScreenToClient( (HWND)getHandle(), &pt );
|
|
|
|
left = (short)pt.x;
|
|
|
|
pt.x = m_pWorkspace->w2() - TRAY_ITEM_INSET - 12;
|
|
pt.y = 0;
|
|
|
|
ClientToScreen( (HWND)m_pWorkspace->getHandle(), &pt );
|
|
ScreenToClient( (HWND)getHandle(), &pt );
|
|
|
|
right = (short)pt.x;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : mx -
|
|
// my -
|
|
// Output : CFlexTimingTag
|
|
//-----------------------------------------------------------------------------
|
|
CFlexTimingTag *ExpressionTool::IsMouseOverTag( int mx, int my )
|
|
{
|
|
CChoreoEvent *event = GetSafeEvent();
|
|
if ( !event )
|
|
return NULL;
|
|
|
|
RECT rcClient;
|
|
GetClientRect( (HWND)getHandle(), &rcClient );
|
|
|
|
int left, right;
|
|
|
|
GetWorkspaceLeftRight( left, right );
|
|
|
|
rcClient.left = left;
|
|
rcClient.right = right;
|
|
rcClient.top = GetCaptionHeight();
|
|
rcClient.bottom = rcClient.top + TRAY_HEIGHT;
|
|
|
|
POINT pt;
|
|
pt.x = mx;
|
|
pt.y = my;
|
|
|
|
for ( int i = 0 ; i < event->GetNumTimingTags(); i++ )
|
|
{
|
|
CFlexTimingTag *tag = event->GetTimingTag( i );
|
|
if ( !tag )
|
|
continue;
|
|
|
|
RECT rcTag;
|
|
|
|
if ( !GetTimingTagRect( rcClient, event, tag, rcTag ) )
|
|
continue;
|
|
|
|
if ( !PtInRect( &rcTag, pt ) )
|
|
continue;
|
|
|
|
return tag;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : drawHelper -
|
|
//-----------------------------------------------------------------------------
|
|
void ExpressionTool::DrawRelativeTags( CChoreoWidgetDrawHelper& drawHelper )
|
|
{
|
|
CChoreoEvent *event = GetSafeEvent();
|
|
if ( !event )
|
|
return;
|
|
|
|
float st, ed;
|
|
GetStartAndEndTime( st, ed );
|
|
|
|
if ( event->GetDuration() <= 0.0f )
|
|
return;
|
|
|
|
CChoreoScene *scene = g_pChoreoView->GetScene();
|
|
if ( !scene )
|
|
return;
|
|
|
|
RECT rcClient;
|
|
drawHelper.GetClientRect( rcClient );
|
|
|
|
int left, right;
|
|
|
|
GetWorkspaceLeftRight( left, right );
|
|
|
|
rcClient.top += GetCaptionHeight();
|
|
|
|
rcClient.left = left;
|
|
rcClient.right = right;
|
|
|
|
rcClient.bottom = rcClient.top + TRAY_HEIGHT;
|
|
|
|
// Iterate relative tags
|
|
for ( int i = 0; i < scene->GetNumActors(); i++ )
|
|
{
|
|
CChoreoActor *a = scene->GetActor( i );
|
|
if ( !a )
|
|
continue;
|
|
|
|
for ( int j = 0; j < a->GetNumChannels(); j++ )
|
|
{
|
|
CChoreoChannel *c = a->GetChannel( j );
|
|
if ( !c )
|
|
continue;
|
|
|
|
for ( int k = 0 ; k < c->GetNumEvents(); k++ )
|
|
{
|
|
CChoreoEvent *e = c->GetEvent( k );
|
|
if ( !e )
|
|
continue;
|
|
|
|
// add each tag to combo box
|
|
for ( int t = 0; t < e->GetNumRelativeTags(); t++ )
|
|
{
|
|
CEventRelativeTag *tag = e->GetRelativeTag( t );
|
|
if ( !tag )
|
|
continue;
|
|
|
|
//SendMessage( control, CB_ADDSTRING, 0, (LPARAM)va( "\"%s\" \"%s\"", tag->GetName(), e->GetParameters() ) );
|
|
bool clipped;
|
|
int tagx = GetPixelForTimeValue( tag->GetStartTime() - event->GetStartTime(), &clipped );
|
|
if ( clipped )
|
|
continue;
|
|
|
|
//drawHelper.DrawColoredLine( RGB( 180, 180, 220 ), PS_SOLID, 1, tagx, rcClient.top, tagx, rcClient.bottom );
|
|
|
|
RECT rcMark;
|
|
rcMark = rcClient;
|
|
rcMark.top = rcClient.bottom - 6;
|
|
rcMark.left = tagx - 3;
|
|
rcMark.right = tagx + 3;
|
|
|
|
drawHelper.DrawTriangleMarker( rcMark, RGB( 0, 100, 250 ) );
|
|
|
|
RECT rcText;
|
|
rcText = rcMark;
|
|
rcText.top -= 10;
|
|
|
|
int len = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, tag->GetName() );
|
|
rcText.left = tagx - len / 2;
|
|
rcText.right = rcText.left + len + 2;
|
|
|
|
rcText.bottom = rcText.top + 10;
|
|
|
|
drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 100, 200 ), rcText, tag->GetName() );
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for ( int t = 0; t < event->GetNumTimingTags(); t++ )
|
|
{
|
|
CFlexTimingTag *tag = event->GetTimingTag( t );
|
|
if ( !tag )
|
|
continue;
|
|
|
|
RECT rcMark;
|
|
|
|
if ( !GetTimingTagRect( rcClient, event, tag, rcMark ) )
|
|
continue;
|
|
|
|
drawHelper.DrawTriangleMarker( rcMark, RGB( 250, 100, 0 ) );
|
|
|
|
RECT rcText;
|
|
rcText = rcMark;
|
|
rcText.top -= 20;
|
|
|
|
char text[ 256 ];
|
|
sprintf( text, "%s", tag->GetName() );
|
|
if ( tag->GetLocked() )
|
|
{
|
|
strcat( text, " - locked" );
|
|
}
|
|
|
|
int len = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, text );
|
|
rcText.left = ( rcMark.left + rcMark.right ) / 2 - len / 2;
|
|
rcText.right = rcText.left + len + 2;
|
|
|
|
rcText.bottom = rcText.top + 10;
|
|
|
|
drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 200, 100, 0 ), rcText, text );
|
|
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void ExpressionTool::ShowContextMenu( mxEvent *event, bool include_track_menus )
|
|
{
|
|
// Construct main menu
|
|
mxPopupMenu *pop = new mxPopupMenu();
|
|
|
|
TimelineItem *item = NULL;
|
|
CFlexAnimationTrack *track = NULL;
|
|
|
|
if ( include_track_menus )
|
|
{
|
|
item = m_pWorkspace->GetClickedItem();
|
|
if ( item )
|
|
{
|
|
item->CountSelected();
|
|
track = item->GetSafeTrack();
|
|
}
|
|
}
|
|
|
|
int current, total;
|
|
g_pChoreoView->GetUndoLevels( current, total );
|
|
if ( total > 0 )
|
|
{
|
|
if ( current > 0 )
|
|
{
|
|
pop->add( va( "Undo %s", g_pChoreoView->GetUndoDescription() ), IDC_UNDO_FA );
|
|
}
|
|
|
|
if ( current <= total - 1 )
|
|
{
|
|
pop->add( va( "Redo %s", g_pChoreoView->GetRedoDescription() ), IDC_REDO_FA );
|
|
}
|
|
pop->addSeparator();
|
|
}
|
|
|
|
// Create expand menu
|
|
mxPopupMenu *expand = new mxPopupMenu();
|
|
if ( item && track && item->IsCollapsed() )
|
|
{
|
|
expand->add( va( "Track '%s'", track->GetFlexControllerName() ), IDC_TL_EXPAND );
|
|
}
|
|
expand->add( "All tracks", IDC_EXPANDALL );
|
|
expand->add( "Used tracks", IDC_EXPANDVALID );
|
|
|
|
pop->addMenu( "Expand", expand );
|
|
|
|
mxPopupMenu *collapse = new mxPopupMenu;
|
|
|
|
if ( item && track && !item->IsCollapsed() )
|
|
{
|
|
collapse->add( va( "Track '%s'", track->GetFlexControllerName() ), IDC_TL_COLLAPSE );
|
|
collapse->add( va( "All tracks except '%s'", track->GetFlexControllerName() ), IDC_COLLAPSE_ALL_EXCEPT );
|
|
}
|
|
|
|
collapse->add( "All tracks", IDC_COLLAPSEALL );
|
|
|
|
pop->addMenu( "Collapse", collapse );
|
|
|
|
pop->addSeparator();
|
|
|
|
pop->add( va( "Enable all valid" ), IDC_ENABLE_ALL_VALID );
|
|
|
|
if ( item && track )
|
|
{
|
|
if ( item->IsActive() )
|
|
{
|
|
pop->add( va( "Disable '%s'", track->GetFlexControllerName() ), IDC_TL_DISABLE );
|
|
}
|
|
else
|
|
{
|
|
pop->add( va( "Enable '%s'", track->GetFlexControllerName() ), IDC_TL_ENABLE );
|
|
}
|
|
pop->add( va( "Disable all except '%s'", track->GetFlexControllerName() ), IDC_DISABLE_ALL_EXCEPT );
|
|
|
|
pop->addSeparator();
|
|
pop->add( "Copy", IDC_TL_COPY );
|
|
if ( HasCopyData() )
|
|
{
|
|
pop->add( "Paste", IDC_TL_PASTE );
|
|
}
|
|
|
|
pop->addSeparator();
|
|
if ( item->GetNumSelected() > 0 )
|
|
{
|
|
pop->add( va( "Delete" ), IDC_TL_DELETE );
|
|
pop->add( "Deselect all", IDC_TL_DESELECT );
|
|
pop->add( va( "Scale selected..." ), IDC_FLEX_SCALESAMPLES );
|
|
}
|
|
pop->add( "Select all", IDC_TL_SELECTALL );
|
|
|
|
if ( FacePoser_IsSnapping() )
|
|
{
|
|
mxPopupMenu *snap = new mxPopupMenu();
|
|
|
|
snap->add( va( "All points" ), IDC_TL_SNAPALL );
|
|
snap->add( va( "All points in '%s'", track->GetFlexControllerName() ), IDC_TL_SNAPPOINTS );
|
|
snap->add( va( "Selected points in '%s'", track->GetFlexControllerName() ), IDC_TL_SNAPSELECTED );
|
|
|
|
pop->addSeparator();
|
|
|
|
pop->addMenu( "Snap", snap );
|
|
}
|
|
|
|
if ( track->IsComboType() )
|
|
{
|
|
pop->addSeparator();
|
|
|
|
if ( item->GetEditType() == 0 )
|
|
{
|
|
pop->add( "Edit <left/right>", IDC_TL_EDITLEFTRIGHT );
|
|
}
|
|
else
|
|
{
|
|
pop->add( "Edit <amount>", IDC_TL_EDITNORMAL );
|
|
}
|
|
}
|
|
|
|
pop->addSeparator();
|
|
mxPopupMenu *heightMenu = new mxPopupMenu();
|
|
heightMenu->add( va( "Reset '%s'", track->GetFlexControllerName() ) , IDC_ET_RESET_ITEM_SIZE );
|
|
heightMenu->add( "Reset All", IDC_ET_RESET_ALL_ITEM_SIZES );
|
|
pop->addMenu( "Height", heightMenu );
|
|
|
|
pop->addSeparator();
|
|
pop->add( "Edge Properties...", IDC_ET_EDGEPROPERTIES );
|
|
}
|
|
pop->addSeparator();
|
|
|
|
mxPopupMenu *tagmenu = new mxPopupMenu();
|
|
|
|
CFlexTimingTag *tag = IsMouseOverTag( (short)event->x, (short)event->y );
|
|
if ( tag )
|
|
{
|
|
if ( tag->GetLocked() )
|
|
{
|
|
tagmenu->add( va( "Unlock tag '%s'...", tag->GetName() ), IDC_UNLOCK_TIMING_TAG );
|
|
}
|
|
else
|
|
{
|
|
tagmenu->add( va( "Lock tag '%s'...", tag->GetName() ), IDC_LOCK_TIMING_TAG );
|
|
}
|
|
tagmenu->addSeparator();
|
|
tagmenu->add( va( "Delete tag '%s'...", tag->GetName() ), IDC_DELETE_TIMING_TAG );
|
|
}
|
|
else
|
|
{
|
|
tagmenu->add( "Insert...", IDC_INSERT_TIMING_TAG );
|
|
}
|
|
|
|
bool bMouseOverSelection = IsMouseOverSelection( (short)event->x, (short)event->y );
|
|
|
|
if ( bMouseOverSelection || HasCopiedColumn() )
|
|
{
|
|
mxPopupMenu *selectionMenu = new mxPopupMenu();
|
|
|
|
if ( bMouseOverSelection )
|
|
{
|
|
selectionMenu->add( "Copy samples", IDC_ET_SELECTION_COPY );
|
|
}
|
|
|
|
if ( HasCopiedColumn() )
|
|
{
|
|
selectionMenu->add( "Paste samples", IDC_ET_SELECTION_PASTE );
|
|
}
|
|
|
|
if ( bMouseOverSelection )
|
|
{
|
|
selectionMenu->addSeparator();
|
|
selectionMenu->add( "Delete samples", IDC_ET_SELECTION_DELETE );
|
|
selectionMenu->add( "Delete samples and shift remainder", IDC_ET_SELECTION_EXCISE );
|
|
}
|
|
pop->addMenu( "Column", selectionMenu );
|
|
}
|
|
|
|
|
|
|
|
pop->addMenu( "Timing Tags", tagmenu );
|
|
|
|
if ( FacePoser_IsSnapping() )
|
|
{
|
|
pop->addSeparator();
|
|
pop->add( "Delete keys by frame", IDC_TL_DELETECOLUMN );
|
|
pop->addSeparator();
|
|
}
|
|
|
|
mxPopupMenu *flexmenu = new mxPopupMenu();
|
|
|
|
flexmenu->add( "Copy to sliders", IDC_COPY_TO_FLEX );
|
|
flexmenu->add( "Copy from sliders", IDC_COPY_FROM_FLEX );
|
|
pop->addMenu( "Flex", flexmenu );
|
|
|
|
pop->add( "Create expression...", IDC_NEW_EXPRESSION_FROM_FLEXANIMATION );
|
|
|
|
CChoreoEvent *e = GetSafeEvent();
|
|
if ( e )
|
|
{
|
|
mxPopupMenu *importexport = new mxPopupMenu();
|
|
|
|
importexport->add( "Export flex animation...", IDC_EXPORT_FA );
|
|
importexport->add( "Import flex animation...", IDC_IMPORT_FA );
|
|
|
|
pop->addMenu( "Import/Export", importexport );
|
|
}
|
|
|
|
pop->add( va( "Change scale..." ), IDC_FLEX_CHANGESCALE );
|
|
|
|
mxPopupMenu *sortmenu = new mxPopupMenu();
|
|
sortmenu->add( "Sort by name", IDC_ET_SORT_BY_NAME );
|
|
sortmenu->add( "Sort by used", IDC_ET_SORT_BY_USED );
|
|
|
|
pop->addMenu( "Sort", sortmenu );
|
|
|
|
pop->popup( this, (short)event->x, (short)event->y );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void ExpressionTool::DrawFocusRect( void )
|
|
{
|
|
HDC dc = GetDC( NULL );
|
|
|
|
for ( int i = 0; i < m_FocusRects.Size(); i++ )
|
|
{
|
|
RECT rc = m_FocusRects[ i ].m_rcFocus;
|
|
|
|
::DrawFocusRect( dc, &rc );
|
|
}
|
|
|
|
ReleaseDC( NULL, dc );
|
|
}
|
|
|
|
void ExpressionTool::SetClickedPos( int x, int y )
|
|
{
|
|
m_nClickedX = x;
|
|
m_nClickedY = y;
|
|
}
|
|
|
|
float ExpressionTool::GetTimeForClickedPos( void )
|
|
{
|
|
CChoreoEvent *e = GetSafeEvent();
|
|
if ( !e )
|
|
return 0.0f;
|
|
|
|
float t = GetTimeValueForMouse( m_nClickedX );
|
|
|
|
// Get spline intensity for controller
|
|
float faketime = e->GetStartTime() + t;
|
|
return faketime;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : dragtype -
|
|
// startx -
|
|
// cursor -
|
|
//-----------------------------------------------------------------------------
|
|
void ExpressionTool::StartDragging( int dragtype, int startx, int starty, HCURSOR cursor )
|
|
{
|
|
m_nDragType = dragtype;
|
|
m_nStartX = startx;
|
|
m_nLastX = startx;
|
|
m_nStartY = starty;
|
|
m_nLastY = starty;
|
|
|
|
if ( m_hPrevCursor )
|
|
{
|
|
SetCursor( m_hPrevCursor );
|
|
m_hPrevCursor = NULL;
|
|
}
|
|
m_hPrevCursor = SetCursor( cursor );
|
|
|
|
m_FocusRects.Purge();
|
|
|
|
RECT rc;
|
|
GetWorkspaceRect( rc );
|
|
|
|
RECT rcStart;
|
|
rcStart.left = startx;
|
|
rcStart.right = startx;
|
|
|
|
bool addrect = true;
|
|
switch ( dragtype )
|
|
{
|
|
default:
|
|
case DRAGTYPE_SCRUBBER:
|
|
{
|
|
RECT rcScrub;
|
|
GetScrubHandleRect( rcScrub, true );
|
|
|
|
rcStart = rcScrub;
|
|
rcStart.left = ( rcScrub.left + rcScrub.right ) / 2;
|
|
rcStart.right = rcStart.left;
|
|
rcStart.top = rcScrub.bottom;
|
|
|
|
rcStart.bottom = h2();
|
|
}
|
|
break;
|
|
case DRAGTYPE_FLEXTIMINGTAG:
|
|
{
|
|
rcStart.top = rc.top;
|
|
rcStart.bottom = h2();
|
|
}
|
|
break;
|
|
case DRAGTYPE_SELECTSAMPLES:
|
|
{
|
|
float st = GetTimeValueForMouse( startx );
|
|
rcStart.left = GetPixelForTimeValue( st );
|
|
rcStart.right = rcStart.left;
|
|
|
|
m_nStartX = rcStart.left;
|
|
m_nLastX = rcStart.left;
|
|
}
|
|
case DRAGTYPE_MOVESELECTIONSTART:
|
|
case DRAGTYPE_MOVESELECTIONEND:
|
|
{
|
|
rcStart.top = rc.top;
|
|
rcStart.bottom = rc.bottom;
|
|
}
|
|
break;
|
|
case DRAGTYPE_MOVESELECTION:
|
|
{
|
|
rcStart.top = rc.top;
|
|
rcStart.bottom = rc.bottom;
|
|
|
|
// Compute left/right pixels for selection
|
|
rcStart.left = GetPixelForTimeValue( m_flSelection[ 0 ] );
|
|
rcStart.right = GetPixelForTimeValue( m_flSelection[ 1 ] );
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
if ( addrect )
|
|
{
|
|
AddFocusRect( rcStart );
|
|
}
|
|
|
|
DrawFocusRect();
|
|
}
|
|
|
|
void ExpressionTool::OnMouseMove( mxEvent *event )
|
|
{
|
|
int mx = (short)event->x;
|
|
int my = (short)event->y;
|
|
|
|
event->x = (short)mx;
|
|
|
|
if ( m_nDragType != DRAGTYPE_NONE )
|
|
{
|
|
DrawFocusRect();
|
|
|
|
for ( int i = 0; i < m_FocusRects.Size(); i++ )
|
|
{
|
|
CFocusRect *f = &m_FocusRects[ i ];
|
|
f->m_rcFocus = f->m_rcOrig;
|
|
|
|
switch ( m_nDragType )
|
|
{
|
|
default:
|
|
{
|
|
OffsetRect( &f->m_rcFocus, ( (short)event->x - m_nStartX ), 0 );
|
|
}
|
|
break;
|
|
case DRAGTYPE_SELECTSAMPLES:
|
|
{
|
|
float st = GetTimeValueForMouse( mx );
|
|
int snapx = GetPixelForTimeValue( st );
|
|
f->m_rcFocus.left = min( snapx, m_nStartX );
|
|
f->m_rcFocus.right = max( snapx, m_nStartY );
|
|
|
|
POINT offset;
|
|
offset.x = 0;
|
|
offset.y = 0;
|
|
ClientToScreen( (HWND)getHandle(), &offset );
|
|
OffsetRect( &f->m_rcFocus, offset.x, 0 );
|
|
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
DrawFocusRect();
|
|
}
|
|
else
|
|
{
|
|
if ( m_hPrevCursor )
|
|
{
|
|
SetCursor( m_hPrevCursor );
|
|
m_hPrevCursor = NULL;
|
|
}
|
|
|
|
if ( IsMouseOverScrubHandle( event ) )
|
|
{
|
|
m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
|
|
}
|
|
else if ( IsMouseOverTag( mx, my ) )
|
|
{
|
|
m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
|
|
}
|
|
else if ( IsMouseOverSelection( (short)event->x, (short)event->y ) )
|
|
{
|
|
if ( IsMouseOverSelectionStartEdge( event ) )
|
|
{
|
|
m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
|
|
}
|
|
else if ( IsMouseOverSelectionEndEdge( event ) )
|
|
{
|
|
m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
|
|
}
|
|
else
|
|
{
|
|
if ( event->modifiers & mxEvent::KeyShift )
|
|
{
|
|
m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEALL ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
switch ( m_nDragType )
|
|
{
|
|
default:
|
|
break;
|
|
case DRAGTYPE_FLEXTIMINGTAG:
|
|
{
|
|
ApplyBounds( mx, my );
|
|
}
|
|
break;
|
|
case DRAGTYPE_SCRUBBER:
|
|
{
|
|
ApplyBounds( mx, my );
|
|
if ( w2() > 0 )
|
|
{
|
|
float t = GetTimeValueForMouse( mx );
|
|
t += m_flScrubberTimeOffset;
|
|
ForceScrubPosition( t );
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
m_nLastX = (short)event->x;
|
|
m_nLastY = (short)event->y;
|
|
}
|
|
|
|
int ExpressionTool::handleEvent( mxEvent *event )
|
|
{
|
|
MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
|
|
|
|
int iret = 0;
|
|
|
|
if ( HandleToolEvent( event ) )
|
|
{
|
|
return iret;
|
|
}
|
|
|
|
switch ( event->event )
|
|
{
|
|
case mxEvent::Size:
|
|
{
|
|
int w, h;
|
|
w = event->width;
|
|
h = event->height;
|
|
|
|
m_pWorkspace->setBounds( 5, TRAY_HEIGHT + GetCaptionHeight(), w - 10, h - ( TRAY_HEIGHT + 5 + GetCaptionHeight() ) - m_nScrollbarHeight );
|
|
|
|
m_nLastHPixelsNeeded = 0;
|
|
InvalidateLayout();
|
|
|
|
iret = 1;
|
|
}
|
|
break;
|
|
case mxEvent::MouseWheeled:
|
|
{
|
|
CChoreoScene *scene = g_pChoreoView->GetScene();
|
|
if ( scene )
|
|
{
|
|
int tz = g_pChoreoView->GetTimeZoom( GetToolName() );
|
|
bool shiftdown = ( event->modifiers & mxEvent::KeyShift ) ? true : false;
|
|
int stepMultipiler = shiftdown ? 5 : 1;
|
|
|
|
// Zoom time in / out
|
|
if ( event->height > 0 )
|
|
{
|
|
tz = min( tz + TIME_ZOOM_STEP * stepMultipiler, MAX_TIME_ZOOM );
|
|
}
|
|
else
|
|
{
|
|
tz = max( tz - TIME_ZOOM_STEP * stepMultipiler, TIME_ZOOM_STEP );
|
|
}
|
|
|
|
g_pChoreoView->SetPreservedTimeZoom( this, tz );
|
|
}
|
|
//RepositionHSlider();
|
|
m_pWorkspace->redraw();
|
|
redraw();
|
|
iret = 1;
|
|
}
|
|
break;
|
|
case mxEvent::MouseDown:
|
|
{
|
|
// bool ctrldown = ( event->modifiers & mxEvent::KeyCtrl ) ? true : false;
|
|
bool shiftdown = ( event->modifiers & mxEvent::KeyShift ) ? true : false;
|
|
|
|
iret = 1;
|
|
|
|
int mx = (short)event->x;
|
|
int my = (short)event->y;
|
|
|
|
SetClickedPos( mx, my );
|
|
|
|
SetMouseOverPos( mx, my );
|
|
DrawMouseOverPos();
|
|
|
|
if ( event->buttons & mxEvent::MouseRightButton )
|
|
{
|
|
ShowContextMenu( event, false );
|
|
return iret;
|
|
}
|
|
|
|
if ( m_nDragType == DRAGTYPE_NONE )
|
|
{
|
|
if ( IsMouseOverScrubHandle( event ) )
|
|
{
|
|
if ( w2() > 0 )
|
|
{
|
|
float t = GetTimeValueForMouse( (short)event->x );
|
|
m_flScrubberTimeOffset = m_flScrub - t;
|
|
float maxoffset = 0.5f * (float)SCRUBBER_HANDLE_WIDTH / GetPixelsPerSecond();
|
|
m_flScrubberTimeOffset = clamp( m_flScrubberTimeOffset, -maxoffset, maxoffset );
|
|
t += m_flScrubberTimeOffset;
|
|
ForceScrubPosition( t );
|
|
}
|
|
|
|
StartDragging( DRAGTYPE_SCRUBBER, m_nClickedX, m_nClickedY, LoadCursor( NULL, IDC_SIZEWE ) );
|
|
}
|
|
else if ( IsMouseOverTag( m_nClickedX, m_nClickedY ) )
|
|
{
|
|
StartDragging( DRAGTYPE_FLEXTIMINGTAG, m_nClickedX, m_nClickedY, LoadCursor( NULL, IDC_SIZEWE ) );
|
|
}
|
|
else if ( IsMouseOverPoints( m_nClickedX, m_nClickedY ) )
|
|
{
|
|
if ( !m_bSelectionActive )
|
|
{
|
|
StartDragging( DRAGTYPE_SELECTSAMPLES, m_nClickedX, m_nClickedY, LoadCursor( NULL, IDC_SIZEWE ) );
|
|
}
|
|
else
|
|
{
|
|
// Either move, move edge if ctrl key is held, or deselect
|
|
if ( IsMouseOverSelection( m_nClickedX,m_nClickedY ) )
|
|
{
|
|
if ( IsMouseOverSelectionStartEdge( event ) )
|
|
{
|
|
StartDragging( DRAGTYPE_MOVESELECTIONSTART, m_nClickedX, m_nClickedY, LoadCursor( NULL, IDC_SIZEWE ) );
|
|
}
|
|
else if ( IsMouseOverSelectionEndEdge( event ) )
|
|
{
|
|
StartDragging( DRAGTYPE_MOVESELECTIONEND, m_nClickedX, m_nClickedY, LoadCursor( NULL, IDC_SIZEWE ) );
|
|
}
|
|
else
|
|
{
|
|
if ( shiftdown )
|
|
{
|
|
StartDragging( DRAGTYPE_MOVESELECTION, m_nClickedX, m_nClickedY, LoadCursor( NULL, IDC_SIZEALL ) );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_bSelectionActive = false;
|
|
redraw();
|
|
return iret;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( w2() > 0 )
|
|
{
|
|
float t = GetTimeValueForMouse( (short)event->x );
|
|
|
|
SetScrubTargetTime( t );
|
|
}
|
|
}
|
|
|
|
CalcBounds( m_nDragType );
|
|
}
|
|
}
|
|
break;
|
|
case mxEvent::MouseDrag:
|
|
case mxEvent::MouseMove:
|
|
{
|
|
int mx = (short)event->x;
|
|
int my = (short)event->y;
|
|
|
|
SetMouseOverPos( mx, my );
|
|
DrawMouseOverPos();
|
|
|
|
OnMouseMove( event );
|
|
|
|
iret = 1;
|
|
}
|
|
break;
|
|
case mxEvent::MouseUp:
|
|
{
|
|
if ( event->buttons & mxEvent::MouseRightButton )
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
int mx = (short)event->x;
|
|
int my = (short)event->y;
|
|
|
|
if ( m_nDragType != DRAGTYPE_NONE )
|
|
{
|
|
DrawFocusRect();
|
|
}
|
|
|
|
if ( m_hPrevCursor )
|
|
{
|
|
SetCursor( m_hPrevCursor );
|
|
m_hPrevCursor = 0;
|
|
}
|
|
|
|
switch ( m_nDragType )
|
|
{
|
|
case DRAGTYPE_NONE:
|
|
break;
|
|
case DRAGTYPE_SELECTSAMPLES:
|
|
FinishSelect( m_nStartX, mx );
|
|
break;
|
|
case DRAGTYPE_MOVESELECTION:
|
|
FinishMoveSelection( m_nStartX, mx );
|
|
break;
|
|
case DRAGTYPE_MOVESELECTIONSTART:
|
|
FinishMoveSelectionStart( m_nStartX, mx );
|
|
break;
|
|
case DRAGTYPE_MOVESELECTIONEND:
|
|
FinishMoveSelectionEnd( m_nStartX, mx );
|
|
break;
|
|
case DRAGTYPE_SCRUBBER:
|
|
{
|
|
ApplyBounds( mx, my );
|
|
|
|
// int dx = mx - m_nStartX;
|
|
// int dy = my = m_nStartY;
|
|
|
|
if ( w2() > 0 )
|
|
{
|
|
float t = GetTimeValueForMouse( (short)event->x );
|
|
t += m_flScrubberTimeOffset;
|
|
m_flScrubberTimeOffset = 0.0f;
|
|
ForceScrubPosition( t );
|
|
}
|
|
}
|
|
break;
|
|
case DRAGTYPE_FLEXTIMINGTAG:
|
|
{
|
|
ApplyBounds( mx, my );
|
|
|
|
// int dx = mx - m_nStartX;
|
|
// int dy = my = m_nStartY;
|
|
|
|
// Compute dx, dy and apply to sections
|
|
//Con_Printf( "dx == %i\n", dx );
|
|
CFlexTimingTag *tag = IsMouseOverTag( m_nStartX, m_nStartY );
|
|
CChoreoEvent *ev = GetSafeEvent();
|
|
if ( tag && g_pChoreoView && ev && ev->GetDuration() )
|
|
{
|
|
float t = GetTimeValueForMouse( mx );
|
|
|
|
float percent = t / ev->GetDuration();
|
|
|
|
g_pChoreoView->SetDirty( true );
|
|
|
|
g_pChoreoView->PushUndo( "Move Timing Tag" );
|
|
|
|
if ( tag->GetLocked() )
|
|
{
|
|
// Resample all control points on right/left
|
|
// of locked tags all the way to the next lock or edge
|
|
ResampleControlPoints( tag, percent );
|
|
}
|
|
|
|
tag->SetPercentage( percent );
|
|
|
|
g_pChoreoView->PushRedo( "Move Timing Tag" );
|
|
}
|
|
|
|
LayoutItems( true );
|
|
redraw();
|
|
}
|
|
break;
|
|
}
|
|
|
|
m_nDragType = DRAGTYPE_NONE;
|
|
|
|
SetMouseOverPos( mx, my );
|
|
DrawMouseOverPos();
|
|
|
|
iret = 1;
|
|
}
|
|
break;
|
|
case mxEvent::Action:
|
|
{
|
|
iret = 1;
|
|
switch ( event->action )
|
|
{
|
|
default:
|
|
iret = 0;
|
|
break;
|
|
case IDC_ET_RESET_ITEM_SIZE:
|
|
OnResetItemSize();
|
|
break;
|
|
case IDC_ET_RESET_ALL_ITEM_SIZES:
|
|
OnResetAllItemSizes();
|
|
break;
|
|
case IDC_ET_SELECTION_DELETE:
|
|
OnDeleteSelection( false );
|
|
break;
|
|
case IDC_ET_SELECTION_EXCISE:
|
|
OnDeleteSelection( true );
|
|
break;
|
|
case IDC_ET_SELECTION_COPY:
|
|
OnCopyColumn();
|
|
break;
|
|
case IDC_ET_SELECTION_PASTE:
|
|
OnPasteColumn();
|
|
break;
|
|
case IDC_ET_SORT_BY_USED:
|
|
OnSortByUsed();
|
|
break;
|
|
case IDC_ET_SORT_BY_NAME:
|
|
OnSortByName();
|
|
break;
|
|
case IDC_EXPORT_FA:
|
|
OnExportFlexAnimation();
|
|
break;
|
|
case IDC_IMPORT_FA:
|
|
OnImportFlexAnimation();
|
|
break;
|
|
case IDC_LOCK_TIMING_TAG:
|
|
LockTimingTag();
|
|
break;
|
|
case IDC_UNLOCK_TIMING_TAG:
|
|
UnlockTimingTag();
|
|
break;
|
|
case IDC_DELETE_TIMING_TAG:
|
|
DeleteFlexTimingTag( m_nClickedX, m_nClickedY );
|
|
break;
|
|
case IDC_INSERT_TIMING_TAG:
|
|
AddFlexTimingTag( m_nClickedX );
|
|
break;
|
|
case IDC_EXPANDALL:
|
|
m_pWorkspace->ExpandAll();
|
|
break;
|
|
case IDC_COLLAPSEALL:
|
|
m_pWorkspace->CollapseAll( NULL );
|
|
break;
|
|
case IDC_COLLAPSE_ALL_EXCEPT:
|
|
m_pWorkspace->CollapseAll( m_pWorkspace->GetClickedItem() );
|
|
break;
|
|
case IDC_EXPANDVALID:
|
|
m_pWorkspace->ExpandValid();
|
|
break;
|
|
case IDC_COPY_TO_FLEX:
|
|
OnCopyToFlex( true );
|
|
break;
|
|
case IDC_COPY_FROM_FLEX:
|
|
OnCopyFromFlex( false );
|
|
break;
|
|
case IDC_NEW_EXPRESSION_FROM_FLEXANIMATION:
|
|
OnNewExpression();
|
|
break;
|
|
case IDC_UNDO_FA:
|
|
OnUndo();
|
|
break;
|
|
case IDC_REDO_FA:
|
|
OnRedo();
|
|
break;
|
|
case IDC_TL_EDITNORMAL:
|
|
{
|
|
TimelineItem *item = m_pWorkspace->GetClickedItem();
|
|
if ( item )
|
|
{
|
|
item->SetEditType( 0 );
|
|
item->DrawSelf();
|
|
}
|
|
}
|
|
break;
|
|
case IDC_TL_EDITLEFTRIGHT:
|
|
{
|
|
TimelineItem *item = m_pWorkspace->GetClickedItem();
|
|
if ( item )
|
|
{
|
|
item->SetEditType( 1 );
|
|
item->DrawSelf();
|
|
}
|
|
}
|
|
break;
|
|
case IDC_TL_EXPAND:
|
|
{
|
|
TimelineItem *item = m_pWorkspace->GetClickedItem();
|
|
if ( item )
|
|
{
|
|
item->SetCollapsed( false );
|
|
}
|
|
LayoutItems();
|
|
}
|
|
break;
|
|
case IDC_TL_COLLAPSE:
|
|
{
|
|
TimelineItem *item = m_pWorkspace->GetClickedItem();
|
|
if ( item )
|
|
{
|
|
item->SetCollapsed( true );
|
|
}
|
|
LayoutItems();
|
|
}
|
|
break;
|
|
case IDC_TL_ENABLE:
|
|
{
|
|
TimelineItem *item = m_pWorkspace->GetClickedItem();
|
|
if ( item )
|
|
{
|
|
g_pChoreoView->SetDirty( true );
|
|
g_pChoreoView->PushUndo( "Enable item" );
|
|
|
|
item->SetActive( true );
|
|
|
|
g_pChoreoView->PushRedo( "Enable item" );
|
|
|
|
item->DrawSelf();
|
|
}
|
|
}
|
|
break;
|
|
case IDC_TL_DISABLE:
|
|
{
|
|
TimelineItem *item = m_pWorkspace->GetClickedItem();
|
|
if ( item )
|
|
{
|
|
g_pChoreoView->SetDirty( true );
|
|
g_pChoreoView->PushUndo( "Disable item" );
|
|
|
|
item->SetActive( false );
|
|
|
|
g_pChoreoView->PushRedo( "Disable item" );
|
|
|
|
item->DrawSelf();
|
|
}
|
|
}
|
|
break;
|
|
case IDC_TL_COPY:
|
|
{
|
|
TimelineItem *item = m_pWorkspace->GetClickedItem();
|
|
if ( item )
|
|
{
|
|
item->Copy();
|
|
}
|
|
}
|
|
break;
|
|
case IDC_TL_PASTE:
|
|
{
|
|
TimelineItem *item = m_pWorkspace->GetClickedItem();
|
|
if ( item )
|
|
{
|
|
item->Paste();
|
|
item->DrawSelf();
|
|
}
|
|
}
|
|
break;
|
|
case IDC_TL_DELETE:
|
|
{
|
|
TimelineItem *item = m_pWorkspace->GetClickedItem();
|
|
if ( item )
|
|
{
|
|
item->Delete();
|
|
item->DrawSelf();
|
|
}
|
|
}
|
|
break;
|
|
case IDC_TL_DESELECT:
|
|
{
|
|
TimelineItem *item = m_pWorkspace->GetClickedItem();
|
|
if ( item )
|
|
{
|
|
item->DeselectAll();
|
|
item->DrawSelf();
|
|
}
|
|
}
|
|
break;
|
|
case IDC_TL_SELECTALL:
|
|
{
|
|
TimelineItem *item = m_pWorkspace->GetClickedItem();
|
|
if ( item )
|
|
{
|
|
item->SelectAll();
|
|
item->DrawSelf();
|
|
}
|
|
}
|
|
break;
|
|
case IDC_DISABLE_ALL_EXCEPT:
|
|
{
|
|
m_pWorkspace->DisableAllExcept();
|
|
}
|
|
break;
|
|
case IDC_ENABLE_ALL_VALID:
|
|
{
|
|
m_pWorkspace->EnableValid();
|
|
}
|
|
break;
|
|
case IDC_TL_SNAPSELECTED:
|
|
{
|
|
TimelineItem *item = m_pWorkspace->GetClickedItem();
|
|
if ( item )
|
|
{
|
|
g_pChoreoView->SetDirty( true );
|
|
g_pChoreoView->PushUndo( "Snap Selected" );
|
|
|
|
item->SnapSelected();
|
|
|
|
g_pChoreoView->PushRedo( "Snap Selected" );
|
|
item->DrawSelf();
|
|
}
|
|
}
|
|
break;
|
|
case IDC_TL_SNAPPOINTS:
|
|
{
|
|
TimelineItem *item = m_pWorkspace->GetClickedItem();
|
|
if ( item )
|
|
{
|
|
g_pChoreoView->SetDirty( true );
|
|
g_pChoreoView->PushUndo( "Snap Item" );
|
|
|
|
item->SnapAll();
|
|
|
|
g_pChoreoView->PushRedo( "Snap Item" );
|
|
item->DrawSelf();
|
|
}
|
|
}
|
|
break;
|
|
case IDC_TL_DELETECOLUMN:
|
|
{
|
|
m_pWorkspace->OnDeleteColumn();
|
|
}
|
|
break;
|
|
case IDC_TL_SNAPALL:
|
|
{
|
|
m_pWorkspace->OnSnapAll();
|
|
}
|
|
break;
|
|
case IDC_FLEXHSCROLL:
|
|
{
|
|
int offset = 0;
|
|
bool processed = true;
|
|
|
|
switch ( event->modifiers )
|
|
{
|
|
case SB_THUMBTRACK:
|
|
offset = event->height;
|
|
break;
|
|
case SB_PAGEUP:
|
|
offset = m_pHorzScrollBar->getValue();
|
|
offset -= 20;
|
|
offset = max( offset, m_pHorzScrollBar->getMinValue() );
|
|
break;
|
|
case SB_PAGEDOWN:
|
|
offset = m_pHorzScrollBar->getValue();
|
|
offset += 20;
|
|
offset = min( offset, m_pHorzScrollBar->getMaxValue() );
|
|
break;
|
|
case SB_LINEUP:
|
|
offset = m_pHorzScrollBar->getValue();
|
|
offset -= 10;
|
|
offset = max( offset, m_pHorzScrollBar->getMinValue() );
|
|
break;
|
|
case SB_LINEDOWN:
|
|
offset = m_pHorzScrollBar->getValue();
|
|
offset += 10;
|
|
offset = min( offset, m_pHorzScrollBar->getMaxValue() );
|
|
break;
|
|
default:
|
|
processed = false;
|
|
break;
|
|
}
|
|
|
|
if ( processed )
|
|
{
|
|
MoveTimeSliderToPos( offset );
|
|
}
|
|
}
|
|
break;
|
|
case IDC_FLEX_CHANGESCALE:
|
|
{
|
|
OnChangeScale();
|
|
}
|
|
break;
|
|
case IDC_FLEX_SCALESAMPLES:
|
|
{
|
|
OnScaleSamples();
|
|
}
|
|
break;
|
|
case IDC_ET_EDGEPROPERTIES:
|
|
{
|
|
OnEdgeProperties();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case mxEvent::KeyDown:
|
|
case mxEvent::KeyUp:
|
|
{
|
|
TimelineItem *item = m_pWorkspace->GetClickedItem();
|
|
if ( item )
|
|
{
|
|
iret = item->handleEvent( event );
|
|
}
|
|
|
|
if ( !iret )
|
|
{
|
|
switch ( event->key )
|
|
{
|
|
default:
|
|
break;
|
|
case VK_ESCAPE:
|
|
{
|
|
DeselectAll();
|
|
iret = 1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
return iret;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : false -
|
|
//-----------------------------------------------------------------------------
|
|
void ExpressionTool::LayoutItems( bool force /*= false*/ )
|
|
{
|
|
m_pWorkspace->LayoutItems( force );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *event -
|
|
//-----------------------------------------------------------------------------
|
|
void ExpressionTool::AddFlexTimingTag( int mx )
|
|
{
|
|
Assert( g_pChoreoView );
|
|
|
|
CChoreoEvent *event = GetSafeEvent();
|
|
if ( !event )
|
|
return;
|
|
|
|
if ( event->GetType() != CChoreoEvent::FLEXANIMATION )
|
|
{
|
|
Con_ErrorPrintf( "Timing Tag: Can only tag FLEXANIMATION events\n" );
|
|
return;
|
|
}
|
|
|
|
CInputParams params;
|
|
memset( ¶ms, 0, sizeof( params ) );
|
|
|
|
strcpy( params.m_szDialogTitle, "Event Tag Name" );
|
|
strcpy( params.m_szPrompt, "Name:" );
|
|
|
|
strcpy( params.m_szInputText, "" );
|
|
|
|
if ( !InputProperties( ¶ms ) )
|
|
return;
|
|
|
|
if ( strlen( params.m_szInputText ) <= 0 )
|
|
{
|
|
Con_ErrorPrintf( "Timing Tag Name: No name entered!\n" );
|
|
return;
|
|
}
|
|
|
|
// Convert click to frac
|
|
float t = GetTimeValueForMouse( mx );
|
|
float frac = 0.0f;
|
|
if ( event->GetDuration() )
|
|
{
|
|
frac = t / event->GetDuration();
|
|
frac = clamp( frac, 0.0f, 1.0f );
|
|
}
|
|
|
|
g_pChoreoView->SetDirty( true );
|
|
|
|
g_pChoreoView->PushUndo( "Add Timing Tag" );
|
|
|
|
event->AddTimingTag( params.m_szInputText, frac, true );
|
|
|
|
g_pChoreoView->PushRedo( "Add Timing Tag" );
|
|
|
|
// Redraw this window
|
|
m_pWorkspace->redraw();
|
|
redraw();
|
|
}
|
|
|
|
void ExpressionTool::DeleteFlexTimingTag( int mx, int my )
|
|
{
|
|
Assert( g_pChoreoView );
|
|
|
|
CChoreoEvent *event = GetSafeEvent();
|
|
if ( !event )
|
|
return;
|
|
|
|
CFlexTimingTag *tag = IsMouseOverTag( mx, my );
|
|
if ( !tag )
|
|
return;
|
|
|
|
g_pChoreoView->SetDirty( true );
|
|
|
|
g_pChoreoView->PushUndo( "Delete Timing Tag" );
|
|
|
|
event->RemoveTimingTag( tag->GetName() );
|
|
|
|
g_pChoreoView->PushRedo( "Delete Timing Tag" );
|
|
|
|
LayoutItems( true );
|
|
// Redraw this window
|
|
redraw();
|
|
|
|
}
|
|
|
|
void ExpressionTool::LockTimingTag( void )
|
|
{
|
|
Assert( g_pChoreoView );
|
|
|
|
CChoreoEvent *event = GetSafeEvent();
|
|
if ( !event )
|
|
return;
|
|
|
|
CFlexTimingTag *tag = IsMouseOverTag( m_nClickedX, m_nClickedY );
|
|
if ( !tag )
|
|
return;
|
|
|
|
if ( tag->GetLocked() )
|
|
return;
|
|
|
|
g_pChoreoView->SetDirty( true );
|
|
|
|
g_pChoreoView->PushUndo( "Lock Timing Tag" );
|
|
|
|
tag->SetLocked( true );
|
|
|
|
g_pChoreoView->PushRedo( "Lock Timing Tag" );
|
|
|
|
redraw();
|
|
}
|
|
|
|
void ExpressionTool::UnlockTimingTag( void )
|
|
{
|
|
Assert( g_pChoreoView );
|
|
|
|
CChoreoEvent *event = GetSafeEvent();
|
|
if ( !event )
|
|
return;
|
|
|
|
CFlexTimingTag *tag = IsMouseOverTag( m_nClickedX, m_nClickedY );
|
|
if ( !tag )
|
|
return;
|
|
|
|
if ( !tag->GetLocked() )
|
|
return;
|
|
|
|
g_pChoreoView->SetDirty( true );
|
|
|
|
g_pChoreoView->PushUndo( "Unlock Timing Tag" );
|
|
|
|
tag->SetLocked( false );
|
|
|
|
g_pChoreoView->PushRedo( "Unlock Timing Tag" );
|
|
|
|
redraw();
|
|
}
|
|
|
|
void ExpressionTool::ApplyBounds( int& mx, int& my )
|
|
{
|
|
if ( !m_bUseBounds )
|
|
return;
|
|
|
|
mx = clamp( mx, m_nMinX, m_nMaxX );
|
|
}
|
|
|
|
void ExpressionTool::CalcBounds( int movetype )
|
|
{
|
|
switch ( movetype )
|
|
{
|
|
default:
|
|
case DRAGTYPE_NONE:
|
|
m_bUseBounds = false;
|
|
m_nMinX = 0;
|
|
m_nMaxX = 0;
|
|
break;
|
|
case DRAGTYPE_SCRUBBER:
|
|
m_bUseBounds = true;
|
|
m_nMinX = 0;
|
|
m_nMaxX = w2();
|
|
break;
|
|
case DRAGTYPE_FLEXTIMINGTAG:
|
|
{
|
|
m_bUseBounds = true;
|
|
|
|
int left, right;
|
|
GetWorkspaceLeftRight( left, right );
|
|
|
|
m_nMinX = left;
|
|
m_nMaxX = right;
|
|
|
|
RECT rcClient;
|
|
rcClient.left = left;
|
|
rcClient.right = right;
|
|
rcClient.top = 0;
|
|
rcClient.bottom = TRAY_HEIGHT;
|
|
|
|
CFlexTimingTag *tag = IsMouseOverTag( m_nStartX, m_nStartY );
|
|
if ( tag &&
|
|
tag->GetOwner() )
|
|
{
|
|
CChoreoEvent *e = tag->GetOwner();
|
|
|
|
float st = e->GetStartTime();
|
|
float ed = e->GetEndTime();
|
|
|
|
if ( ed > st )
|
|
{
|
|
|
|
|
|
// Find previous tag, if any
|
|
CFlexTimingTag *prev = NULL;
|
|
CFlexTimingTag *next = NULL;
|
|
|
|
for ( int i = 0; i < e->GetNumTimingTags(); i++ )
|
|
{
|
|
CFlexTimingTag *test = e->GetTimingTag( i );
|
|
if ( test != tag )
|
|
continue;
|
|
|
|
// Found it
|
|
if ( i > 0 )
|
|
{
|
|
prev = e->GetTimingTag( i - 1 );
|
|
}
|
|
|
|
if ( i + 1 < e->GetNumTimingTags() )
|
|
{
|
|
next = e->GetTimingTag( i + 1 );
|
|
}
|
|
break;
|
|
}
|
|
|
|
if ( prev )
|
|
{
|
|
// Compute x pixel of prev tag
|
|
float frac = ( prev->GetStartTime() - st ) / ( ed - st );
|
|
if ( frac >= 0.0f && frac <= 1.0f )
|
|
{
|
|
int tagx = rcClient.left + (int)( frac * (float)( rcClient.right - rcClient.left ) );
|
|
|
|
m_nMinX = max( m_nMinX, tagx + 5 );
|
|
}
|
|
}
|
|
|
|
if ( next )
|
|
{
|
|
// Compute x pixel of next tag
|
|
float frac = ( next->GetStartTime() - st ) / ( ed - st );
|
|
if ( frac >= 0.0f && frac <= 1.0f )
|
|
{
|
|
int tagx = rcClient.left + (int)( frac * (float)( rcClient.right - rcClient.left ) );
|
|
m_nMaxX = min( m_nMaxX, tagx - 5 );
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *tag -
|
|
// newposition -
|
|
//-----------------------------------------------------------------------------
|
|
void ExpressionTool::ResampleControlPoints( CFlexTimingTag *tag, float newposition )
|
|
{
|
|
CChoreoEvent *e = tag->GetOwner();
|
|
if ( !e )
|
|
return;
|
|
|
|
float duration = e->GetDuration();
|
|
|
|
float leftedge = 0.0f;
|
|
float rightedge = duration;
|
|
|
|
// Find neighboring locked tags, if any
|
|
CFlexTimingTag *prev = NULL;
|
|
CFlexTimingTag *next = NULL;
|
|
|
|
int i;
|
|
for ( i = 0; i < e->GetNumTimingTags(); i++ )
|
|
{
|
|
CFlexTimingTag *test = e->GetTimingTag( i );
|
|
if ( test != tag )
|
|
continue;
|
|
|
|
// Found it
|
|
if ( i > 0 )
|
|
{
|
|
int i1 = i - 1;
|
|
while ( 1 )
|
|
{
|
|
if ( i1 < 0 )
|
|
{
|
|
prev = NULL;
|
|
break;
|
|
}
|
|
|
|
prev = e->GetTimingTag( i1 );
|
|
if ( prev->GetLocked() )
|
|
break;
|
|
|
|
i1--;
|
|
}
|
|
}
|
|
|
|
if ( i + 1 < e->GetNumTimingTags() )
|
|
{
|
|
int i1 = i + 1;
|
|
while ( 1 )
|
|
{
|
|
if ( i1 >= e->GetNumTimingTags() )
|
|
{
|
|
next = NULL;
|
|
break;
|
|
}
|
|
|
|
next = e->GetTimingTag( i1 );
|
|
if ( next->GetLocked() )
|
|
break;
|
|
|
|
i1++;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if ( prev )
|
|
{
|
|
leftedge = prev->GetPercentage() * duration;
|
|
}
|
|
|
|
if ( next )
|
|
{
|
|
rightedge = next->GetPercentage() * duration;
|
|
}
|
|
|
|
// Now, using the tags old position as a pivot, rescale intervening
|
|
// sample points based on size delta of new vs old range
|
|
float oldpivot = tag->GetPercentage() * duration;
|
|
float newpivot = newposition * duration;
|
|
|
|
float oldleftrange = oldpivot - leftedge;
|
|
float oldrightrange = rightedge - oldpivot;
|
|
|
|
float newleftrange = newpivot - leftedge;
|
|
float newrightrange = rightedge - newpivot;
|
|
|
|
if ( oldleftrange <= 0.0f ||
|
|
oldrightrange <= 0.0f ||
|
|
newleftrange <= 0.0f ||
|
|
newrightrange <= 0.0f )
|
|
{
|
|
Con_Printf( "Range problem!!! avoiding division by zero\n" );
|
|
return;
|
|
}
|
|
|
|
for ( i = 0 ; i < e->GetNumFlexAnimationTracks(); i++ )
|
|
{
|
|
CFlexAnimationTrack *track = e->GetFlexAnimationTrack( i );
|
|
if ( !track )
|
|
continue;
|
|
|
|
for ( int t = 0; t < ( track->IsComboType() ? 2 : 1 ); t++ )
|
|
{
|
|
for ( int j = 0; j < track->GetNumSamples( t ); j++ )
|
|
{
|
|
CExpressionSample *s = track->GetSample( j, t );
|
|
if ( !s )
|
|
continue;
|
|
|
|
float oldtime = s->time;
|
|
|
|
// In old range?
|
|
if ( oldtime < leftedge )
|
|
continue;
|
|
if ( oldtime > rightedge )
|
|
continue;
|
|
|
|
// In left or right side( tiebreak toward left )
|
|
float newtime = oldtime;
|
|
|
|
if ( oldtime <= oldpivot )
|
|
{
|
|
float n = ( oldtime - leftedge ) / oldleftrange;
|
|
newtime = leftedge + n * newleftrange;
|
|
}
|
|
else
|
|
{
|
|
float n = ( oldtime - oldpivot ) / oldrightrange;
|
|
newtime = newpivot + n * newrightrange;
|
|
}
|
|
|
|
//newtime = FacePoser_SnapTime( newtime );
|
|
|
|
s->time = newtime;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void ExpressionTool::OnNewExpression( void )
|
|
{
|
|
CChoreoEvent *e = GetSafeEvent();
|
|
if ( !e )
|
|
return;
|
|
|
|
CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
|
|
if ( !hdr )
|
|
{
|
|
Con_ErrorPrintf( "ExpressionTool::OnNewExpression: Can't create new face pose, must load a model first!\n" );
|
|
return;
|
|
}
|
|
|
|
CExpClass *active = expressions->GetActiveClass();
|
|
if ( !active )
|
|
{
|
|
Con_ErrorPrintf( "ExpressionTool::OnNewExpression: Can't create new face pose, must load an expression file first!\n" );
|
|
return;
|
|
}
|
|
|
|
g_pExpressionTrayTool->Deselect();
|
|
|
|
float t = GetTimeValueForMouse( m_nClickedX );
|
|
|
|
// Get spline intensity for controller
|
|
float faketime = e->GetStartTime() + t;
|
|
|
|
float settings[ GLOBAL_STUDIO_FLEX_CONTROL_COUNT ];
|
|
float weights[ GLOBAL_STUDIO_FLEX_CONTROL_COUNT ];
|
|
memset( settings, 0, sizeof( settings ) );
|
|
memset( weights, 0, sizeof( settings ) );
|
|
|
|
for ( int i = 0 ; i < e->GetNumFlexAnimationTracks(); i++ )
|
|
{
|
|
CFlexAnimationTrack *track = e->GetFlexAnimationTrack( i );
|
|
if ( !track )
|
|
continue;
|
|
|
|
// Disabled
|
|
if ( !track->IsTrackActive() )
|
|
continue;
|
|
|
|
// Map track flex controller to global name
|
|
if ( track->IsComboType() )
|
|
{
|
|
for ( int side = 0; side < 2; side++ )
|
|
{
|
|
int controller = track->GetFlexControllerIndex( side );
|
|
if ( controller != -1 )
|
|
{
|
|
// Get spline intensity for controller
|
|
float flIntensity = track->GetIntensity( faketime, side );
|
|
|
|
settings[ controller ] = flIntensity;
|
|
weights[ controller ] = 1.0f;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int controller = track->GetFlexControllerIndex( 0 );
|
|
if ( controller != -1 )
|
|
{
|
|
// Get spline intensity for controller
|
|
float flIntensity = track->GetIntensity( faketime, 0 );
|
|
|
|
settings[ controller ] = flIntensity;
|
|
weights[ controller ] = 1.0f;
|
|
}
|
|
}
|
|
}
|
|
|
|
CExpressionParams params;
|
|
memset( ¶ms, 0, sizeof( params ) );
|
|
|
|
strcpy( params.m_szDialogTitle, "Add Expression" );
|
|
strcpy( params.m_szName, "" );
|
|
strcpy( params.m_szDescription, "" );
|
|
|
|
if ( !ExpressionProperties( ¶ms ) )
|
|
return;
|
|
|
|
if ( ( strlen( params.m_szName ) <= 0 ) ||
|
|
!stricmp( params.m_szName, "unnamed" ) )
|
|
{
|
|
Con_ErrorPrintf( "You must type in a valid name\n" );
|
|
return;
|
|
}
|
|
|
|
if ( ( strlen( params.m_szDescription ) <= 0 ) ||
|
|
!stricmp( params.m_szDescription, "description" ) )
|
|
{
|
|
Con_ErrorPrintf( "You must type in a valid description\n" );
|
|
return;
|
|
}
|
|
|
|
active->AddExpression( params.m_szName, params.m_szDescription, settings, weights, true, true );
|
|
}
|
|
|
|
LocalFlexController_t FindFlexControllerIndexByName( StudioModel *model, char const *searchname )
|
|
{
|
|
if ( !model )
|
|
return LocalFlexController_t(-1);
|
|
|
|
CStudioHdr *hdr = model->GetStudioHdr();
|
|
if ( !hdr )
|
|
return LocalFlexController_t(-1);
|
|
|
|
for ( LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++ )
|
|
{
|
|
char const *name = hdr->pFlexcontroller( i )->pszName();
|
|
if ( !name )
|
|
continue;
|
|
|
|
if ( strcmp( name, searchname ) )
|
|
continue;
|
|
|
|
return i;
|
|
}
|
|
return LocalFlexController_t(-1);
|
|
}
|
|
|
|
void ExpressionTool::OnCopyToFlex( bool isEdited )
|
|
{
|
|
// local time in the expression tool for the last mouse click
|
|
float t = GetTimeValueForMouse( m_nClickedX );
|
|
|
|
CChoreoEvent *e = GetSafeEvent();
|
|
if ( !e )
|
|
return;
|
|
|
|
float scenetime = e->GetStartTime() + t;
|
|
|
|
OnCopyToFlex( scenetime, isEdited );
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void ExpressionTool::OnCopyToFlex( float scenetime, bool isEdited )
|
|
{
|
|
CChoreoEvent *e = GetSafeEvent();
|
|
if ( !e )
|
|
return;
|
|
|
|
if ( scenetime < e->GetStartTime() || scenetime > e->GetEndTime() )
|
|
return;
|
|
|
|
bool needundo = false;
|
|
|
|
float *settings = NULL;
|
|
float *weights = NULL;
|
|
CExpression *exp = NULL;
|
|
CExpClass *active = expressions->GetActiveClass();
|
|
if ( active )
|
|
{
|
|
|
|
int index = active->GetSelectedExpression();
|
|
if ( index != -1 )
|
|
{
|
|
exp = active->GetExpression( index );
|
|
if ( exp )
|
|
{
|
|
needundo = true;
|
|
settings = exp->GetSettings();
|
|
weights = exp->GetWeights();
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( needundo && exp )
|
|
{
|
|
exp->PushUndoInformation();
|
|
active->SetDirty( true );
|
|
}
|
|
|
|
g_pFlexPanel->ResetSliders( false, true );
|
|
|
|
StudioModel *model = models->GetActiveStudioModel();
|
|
|
|
for ( int i = 0 ; i < e->GetNumFlexAnimationTracks(); i++ )
|
|
{
|
|
CFlexAnimationTrack *track = e->GetFlexAnimationTrack( i );
|
|
if ( !track )
|
|
continue;
|
|
|
|
// Disabled
|
|
if ( !track->IsTrackActive() )
|
|
continue;
|
|
|
|
// Map track flex controller to global name
|
|
for ( int side = 0; side < 1 + track->IsComboType(); side++ )
|
|
{
|
|
int controller = track->GetFlexControllerIndex( side );
|
|
if ( controller != -1 )
|
|
{
|
|
// Get spline intensity for controller
|
|
float flIntensity = track->GetIntensity( scenetime, side );
|
|
|
|
g_pFlexPanel->SetSlider( controller, flIntensity );
|
|
g_pFlexPanel->SetInfluence( controller, 1.0f );
|
|
g_pFlexPanel->SetEdited( controller, isEdited );
|
|
if( model )
|
|
{
|
|
LocalFlexController_t raw = track->GetRawFlexControllerIndex( side );
|
|
if ( raw != LocalFlexController_t(-1) )
|
|
{
|
|
model->SetFlexController( raw, flIntensity );
|
|
}
|
|
}
|
|
if ( settings && weights )
|
|
{
|
|
settings[ controller ] = flIntensity;
|
|
weights[ controller ] = 1.0f;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( needundo && exp )
|
|
{
|
|
exp->PushRedoInformation();
|
|
}
|
|
}
|
|
|
|
void ExpressionTool::OnCopyFromFlex( bool isEdited )
|
|
{
|
|
// local time in the expression tool for the last mouse click
|
|
float t = GetTimeValueForMouse( m_nClickedX );
|
|
|
|
CChoreoEvent *e = GetSafeEvent();
|
|
if ( !e )
|
|
return;
|
|
|
|
float scenetime = e->GetStartTime() + t;
|
|
|
|
OnCopyFromFlex( scenetime, isEdited );
|
|
|
|
return;
|
|
}
|
|
|
|
void ExpressionTool::OnSetSingleKeyFromFlex( char const *sliderName )
|
|
{
|
|
CChoreoEvent *e = GetSafeEvent();
|
|
if ( !e || !e->GetDuration() )
|
|
return;
|
|
|
|
float scenetime = g_pChoreoView->GetScene()->GetTime();
|
|
|
|
if ( scenetime < e->GetStartTime() || scenetime > e->GetEndTime() )
|
|
return;
|
|
|
|
scenetime = FacePoser_SnapTime( scenetime );
|
|
|
|
float relativetime = scenetime - e->GetStartTime();
|
|
|
|
// Get spline intensity for controller
|
|
|
|
float setting;
|
|
float influence;
|
|
float minvalue, maxvalue;
|
|
|
|
g_pChoreoView->SetDirty( true );
|
|
g_pChoreoView->PushUndo( "Set Single Key" );
|
|
|
|
for (int j = 0; j < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; j++)
|
|
{
|
|
if ( !g_pFlexPanel->IsValidSlider( j ) )
|
|
continue;
|
|
|
|
if ( Q_stricmp( g_pFlexPanel->getLabel(), sliderName ) )
|
|
continue;
|
|
|
|
setting = g_pFlexPanel->GetSliderRawValue( j );
|
|
influence = g_pFlexPanel->GetInfluence( j );
|
|
|
|
// g_pFlexPanel->SetEdited( j, isEdited );
|
|
|
|
g_pFlexPanel->GetSliderRange( j, minvalue, maxvalue );
|
|
|
|
bool found = false;
|
|
for ( int i = 0 ; i < e->GetNumFlexAnimationTracks() && !found; i++ )
|
|
{
|
|
CFlexAnimationTrack *track = e->GetFlexAnimationTrack( i );
|
|
if ( !track )
|
|
continue;
|
|
|
|
for ( int side = 0; side < 1 + track->IsComboType(); side++ )
|
|
{
|
|
if ( track->GetFlexControllerIndex( side ) != j )
|
|
continue;
|
|
|
|
float normalized = setting;
|
|
if ( side == 0 )
|
|
{
|
|
if ( minvalue != maxvalue )
|
|
{
|
|
normalized = ( setting - minvalue ) / ( maxvalue - minvalue );
|
|
}
|
|
if (track->IsInverted())
|
|
{
|
|
normalized = 1.0 - normalized;
|
|
}
|
|
}
|
|
|
|
found = true;
|
|
|
|
int nSampleCount = track->GetNumSamples( side );
|
|
|
|
int j = 0;
|
|
for ( ; j < nSampleCount; ++j )
|
|
{
|
|
CExpressionSample *s = track->GetSample( j, side );
|
|
if ( s->time == relativetime )
|
|
break;
|
|
}
|
|
|
|
if ( j >= nSampleCount )
|
|
{
|
|
track->AddSample( relativetime, normalized, side );
|
|
track->Resort( side );
|
|
}
|
|
else
|
|
{
|
|
CExpressionSample *s = track->GetSample( j, side );
|
|
s->value = normalized;
|
|
}
|
|
|
|
track->SetTrackActive( true );
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
g_pChoreoView->PushRedo( "Set Single Key" );
|
|
|
|
m_pWorkspace->redraw();
|
|
redraw();
|
|
}
|
|
|
|
void ExpressionTool::OnCopyFromFlex( float scenetime, bool isEdited )
|
|
{
|
|
CChoreoEvent *e = GetSafeEvent();
|
|
if ( !e || !e->GetDuration() )
|
|
return;
|
|
|
|
if ( scenetime < e->GetStartTime() || scenetime > e->GetEndTime() )
|
|
return;
|
|
|
|
scenetime = FacePoser_SnapTime( scenetime );
|
|
|
|
float relativetime = scenetime - e->GetStartTime();
|
|
|
|
// Get spline intensity for controller
|
|
|
|
float setting;
|
|
float influence;
|
|
float minvalue, maxvalue;
|
|
|
|
g_pChoreoView->SetDirty( true );
|
|
g_pChoreoView->PushUndo( "Copy from Flex" );
|
|
|
|
for (int j = 0; j < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; j++)
|
|
{
|
|
if ( !g_pFlexPanel->IsValidSlider( j ) )
|
|
continue;
|
|
|
|
setting = g_pFlexPanel->GetSliderRawValue( j );
|
|
//setting = g_pFlexPanel->GetSlider( j );
|
|
influence = g_pFlexPanel->GetInfluence( j );
|
|
|
|
g_pFlexPanel->SetEdited( j, isEdited );
|
|
|
|
g_pFlexPanel->GetSliderRange( j, minvalue, maxvalue );
|
|
|
|
// Found it
|
|
if ( !influence )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
bool found = false;
|
|
for ( int i = 0 ; i < e->GetNumFlexAnimationTracks() && !found; i++ )
|
|
{
|
|
CFlexAnimationTrack *track = e->GetFlexAnimationTrack( i );
|
|
if ( !track )
|
|
continue;
|
|
|
|
for ( int side = 0; side < 1 + track->IsComboType(); side++ )
|
|
{
|
|
if ( track->GetFlexControllerIndex( side ) != j )
|
|
continue;
|
|
|
|
float normalized = setting;
|
|
if ( side == 0 )
|
|
{
|
|
if ( minvalue != maxvalue )
|
|
{
|
|
normalized = ( setting - minvalue ) / ( maxvalue - minvalue );
|
|
}
|
|
if (track->IsInverted())
|
|
{
|
|
normalized = 1.0 - normalized;
|
|
}
|
|
}
|
|
|
|
found = true;
|
|
|
|
track->AddSample( relativetime, normalized, side );
|
|
track->Resort( side );
|
|
track->SetTrackActive( true );
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
g_pChoreoView->PushRedo( "Copy from Flex" );
|
|
|
|
m_pWorkspace->redraw();
|
|
redraw();
|
|
}
|
|
|
|
bool ExpressionTool::SetFlexAnimationTrackFromExpression( int mx, int my, CExpClass *cl, CExpression *exp )
|
|
{
|
|
CChoreoEvent *e = GetSafeEvent();
|
|
if ( !e | !e->GetDuration() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( !exp )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Convert screen to client
|
|
POINT pt;
|
|
pt.x = mx;
|
|
pt.y = my;
|
|
|
|
ScreenToClient( (HWND)getHandle(), &pt );
|
|
|
|
if ( pt.x < 0 || pt.y < 0 )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( pt.x > w2() || pt.y > h2() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
float t = GetTimeValueForMouse( (short)pt.x );
|
|
|
|
// Get spline intensity for controller
|
|
// Get spline intensity for controller
|
|
float relativetime = t;
|
|
float faketime = e->GetStartTime() + relativetime;
|
|
|
|
faketime = FacePoser_SnapTime( faketime );
|
|
|
|
float *settings = exp->GetSettings();
|
|
float *influence = exp->GetWeights();
|
|
|
|
if ( !settings || !influence )
|
|
return false;
|
|
|
|
g_pChoreoView->SetDirty( true );
|
|
g_pChoreoView->PushUndo( "Copy from Expression" );
|
|
|
|
for ( int i = 0 ; i < e->GetNumFlexAnimationTracks(); i++ )
|
|
{
|
|
CFlexAnimationTrack *track = e->GetFlexAnimationTrack( i );
|
|
if ( !track )
|
|
continue;
|
|
|
|
if ( track->IsComboType() )
|
|
{
|
|
int left = track->GetFlexControllerIndex( 0 );
|
|
int right = track->GetFlexControllerIndex( 1 );
|
|
|
|
float leftval = settings[ left ];
|
|
float leftinfluence = influence[ left ];
|
|
float rightval = settings[ right ];
|
|
float rightinfluence = influence[ right ];
|
|
|
|
if ( leftinfluence || rightinfluence )
|
|
{
|
|
|
|
//Con_Printf( "%s %i(side %i): amount %f inf %f\n", track->GetFlexControllerName(), j, side, s, inf );
|
|
|
|
float mag, leftright;
|
|
|
|
if (leftval < rightval)
|
|
{
|
|
mag = rightval;
|
|
leftright = 1.0 - (leftval / rightval) * 0.5;
|
|
}
|
|
else if (leftval > rightval)
|
|
{
|
|
mag = leftval;
|
|
leftright = (rightval / leftval) * 0.5;
|
|
}
|
|
else
|
|
{
|
|
mag = leftval;
|
|
leftright = 0.5;
|
|
}
|
|
|
|
track->AddSample( relativetime, mag * leftinfluence, 0 );
|
|
track->AddSample( relativetime, leftright, 1 );
|
|
|
|
track->Resort( 0 );
|
|
track->Resort( 1 );
|
|
|
|
track->SetTrackActive( true );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int j = track->GetFlexControllerIndex( 0 );
|
|
|
|
float s = settings[ j ];
|
|
float inf = influence[ j ];
|
|
|
|
if ( inf )
|
|
{
|
|
track->AddSample( relativetime, s, 0 );
|
|
|
|
track->Resort( 0 );
|
|
|
|
track->SetTrackActive( true );
|
|
}
|
|
}
|
|
}
|
|
|
|
g_pChoreoView->PushRedo( "Copy from Expression" );
|
|
|
|
m_pWorkspace->redraw();
|
|
redraw();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ExpressionTool::PaintBackground()
|
|
{
|
|
redraw();
|
|
return false;
|
|
}
|
|
|
|
void ExpressionTool::OnExportFlexAnimation( void )
|
|
{
|
|
CChoreoEvent *event = GetSafeEvent();
|
|
if ( !event )
|
|
return;
|
|
|
|
// Create flexanimations dir
|
|
CreatePath( "flexanimations/foo" );
|
|
|
|
char fafilename[ 512 ];
|
|
if ( !FacePoser_ShowSaveFileNameDialog( fafilename, sizeof( fafilename ), "flexanimations", "*.vfa" ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
Q_DefaultExtension( fafilename, ".vfa", sizeof( fafilename ) );
|
|
|
|
Con_Printf( "Exporting events to %s\n", fafilename );
|
|
|
|
CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
|
|
|
|
CChoreoScene::FileSaveFlexAnimations( buf, 0, event );
|
|
|
|
// Write it out baby
|
|
FileHandle_t fh = filesystem->Open( fafilename, "wt" );
|
|
if (fh)
|
|
{
|
|
filesystem->Write( buf.Base(), buf.TellPut(), fh );
|
|
filesystem->Close(fh);
|
|
}
|
|
else
|
|
{
|
|
Con_Printf( "Unable to write file %s!!!\n", fafilename );
|
|
}
|
|
}
|
|
|
|
void ExpressionTool::OnImportFlexAnimation( void )
|
|
{
|
|
CChoreoEvent *event = GetSafeEvent();
|
|
if ( !event )
|
|
return;
|
|
|
|
char fafilename[ 512 ];
|
|
if ( !FacePoser_ShowOpenFileNameDialog( fafilename, sizeof( fafilename ), "flexanimations", "*.vfa" ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( !filesystem->FileExists( fafilename ) )
|
|
return;
|
|
|
|
char fullpath[ 512 ];
|
|
filesystem->RelativePathToFullPath( fafilename, "MOD", fullpath, sizeof( fullpath ) );
|
|
|
|
LoadScriptFile( (char *)fullpath );
|
|
|
|
tokenprocessor->GetToken( true );
|
|
if ( stricmp( tokenprocessor->CurrentToken(), "flexanimations" ) )
|
|
{
|
|
Con_Printf( "ExpressionTool::OnImportFlexAnimation: %s, expecting \"flexanimations\"\n",
|
|
fullpath );
|
|
}
|
|
else
|
|
{
|
|
g_pChoreoView->SetDirty( true );
|
|
g_pChoreoView->PushUndo( "Import flex animations" );
|
|
|
|
CChoreoScene::ParseFlexAnimations( tokenprocessor, event, true );
|
|
|
|
// Force a full reset
|
|
m_pLastEvent = NULL;
|
|
SetEvent( event );
|
|
|
|
g_pChoreoView->PushRedo( "Import flex animations" );
|
|
|
|
Con_Printf( "Parsed flex animations from %s\n", fullpath );
|
|
}
|
|
}
|
|
|
|
void ExpressionTool::OnUndo( void )
|
|
{
|
|
g_pChoreoView->Undo();
|
|
}
|
|
|
|
void ExpressionTool::OnRedo( void )
|
|
{
|
|
g_pChoreoView->Redo();
|
|
}
|
|
|
|
void ExpressionTool::ForceScrubPositionFromSceneTime( float scenetime )
|
|
{
|
|
CChoreoEvent *e = GetSafeEvent();
|
|
if ( !e || !e->GetDuration() )
|
|
return;
|
|
|
|
float t = scenetime - e->GetStartTime();
|
|
m_flScrub = t;
|
|
m_flScrubTarget = t;
|
|
|
|
DrawScrubHandles();
|
|
}
|
|
|
|
void ExpressionTool::ForceScrubPosition( float frac )
|
|
{
|
|
m_flScrub = frac;
|
|
m_flScrubTarget = frac;
|
|
|
|
CChoreoEvent *e = GetSafeEvent();
|
|
if ( e )
|
|
{
|
|
float realtime = e->GetStartTime() + frac;
|
|
|
|
g_pChoreoView->SetScrubTime( realtime );
|
|
g_pChoreoView->SetScrubTargetTime( realtime );
|
|
|
|
g_pChoreoView->DrawScrubHandle();
|
|
}
|
|
|
|
DrawScrubHandles();
|
|
}
|
|
|
|
void ExpressionTool::DrawScrubHandles()
|
|
{
|
|
RECT rcHandle;
|
|
GetScrubHandleRect( rcHandle, true );
|
|
|
|
RECT rcTray = rcHandle;
|
|
rcTray.left = 0;
|
|
rcTray.right = w2();
|
|
|
|
CChoreoWidgetDrawHelper drawHelper( this, rcTray );
|
|
DrawScrubHandle( drawHelper, rcHandle );
|
|
}
|
|
|
|
void ExpressionTool::SetMouseOverPos( int x, int y )
|
|
{
|
|
m_nMousePos[ 0 ] = x;
|
|
m_nMousePos[ 1 ] = y;
|
|
}
|
|
|
|
void ExpressionTool::GetMouseOverPos( int &x, int& y )
|
|
{
|
|
x = m_nMousePos[ 0 ];
|
|
y = m_nMousePos[ 1 ];
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : rcPos -
|
|
//-----------------------------------------------------------------------------
|
|
void ExpressionTool::GetMouseOverPosRect( RECT& rcPos )
|
|
{
|
|
rcPos.top = GetCaptionHeight() + 12;
|
|
rcPos.left = w2() - 200;
|
|
rcPos.right = w2() - 5;
|
|
rcPos.bottom = rcPos.top + 13;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : drawHelper -
|
|
// rcPos -
|
|
//-----------------------------------------------------------------------------
|
|
void ExpressionTool::DrawMouseOverPos( CChoreoWidgetDrawHelper& drawHelper, RECT& rcPos )
|
|
{
|
|
// Compute time for pixel x
|
|
float t = GetTimeValueForMouse( m_nMousePos[ 0 ] );
|
|
CChoreoEvent *e = GetSafeEvent();
|
|
if ( !e )
|
|
return;
|
|
|
|
t += e->GetStartTime();
|
|
|
|
float snapped = FacePoser_SnapTime( t );
|
|
|
|
// Found it, write out description
|
|
//
|
|
char sz[ 128 ];
|
|
if ( t != snapped )
|
|
{
|
|
Q_snprintf( sz, sizeof( sz ), "%s", FacePoser_DescribeSnappedTime( t ) );
|
|
}
|
|
else
|
|
{
|
|
Q_snprintf( sz, sizeof( sz ), "%.3f", t );
|
|
}
|
|
|
|
int len = drawHelper.CalcTextWidth( "Arial", 11, 900, sz );
|
|
|
|
RECT rcText = rcPos;
|
|
rcText.left = max( rcPos.left, rcPos.right - len );
|
|
|
|
drawHelper.DrawColoredText( "Arial", 11, 900, RGB( 255, 50, 70 ), rcText, sz );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void ExpressionTool::DrawMouseOverPos()
|
|
{
|
|
RECT rcPos;
|
|
GetMouseOverPosRect( rcPos );
|
|
|
|
CChoreoWidgetDrawHelper drawHelper( this, rcPos );
|
|
DrawMouseOverPos( drawHelper, rcPos );
|
|
}
|
|
|
|
int ExpressionTool::CountSelectedSamples( void )
|
|
{
|
|
return m_pWorkspace->CountSelectedSamples();
|
|
}
|
|
|
|
void ExpressionTool::MoveSelectedSamples( float dfdx, float dfdy, bool snap )
|
|
{
|
|
m_pWorkspace->MoveSelectedSamples( dfdx, dfdy, snap );
|
|
}
|
|
|
|
void ExpressionTool::DeleteSelectedSamples( void )
|
|
{
|
|
m_pWorkspace->DeleteSelectedSamples();
|
|
}
|
|
|
|
void ExpressionTool::DeselectAll( void )
|
|
{
|
|
m_pWorkspace->DeselectAll();
|
|
m_bSelectionActive = false;
|
|
redraw();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : start -
|
|
// end -
|
|
//-----------------------------------------------------------------------------
|
|
void ExpressionTool::SelectPoints( float starttime, float endtime )
|
|
{
|
|
// Make sure order is correct
|
|
if ( endtime < starttime )
|
|
{
|
|
float temp = endtime;
|
|
endtime = starttime;
|
|
starttime = temp;
|
|
}
|
|
|
|
DeselectAll();
|
|
|
|
m_flSelection[ 0 ] = starttime;
|
|
m_flSelection[ 1 ] = endtime;
|
|
m_bSelectionActive = true;
|
|
|
|
// Select any words that span the selection
|
|
//
|
|
m_pWorkspace->SelectPoints( starttime, endtime );
|
|
|
|
redraw();
|
|
}
|
|
|
|
void ExpressionTool::FinishMoveSelection( int startx, int mx )
|
|
{
|
|
float start = GetTimeValueForMouse( startx );
|
|
float end = GetTimeValueForMouse( mx );
|
|
|
|
float delta = end - start;
|
|
|
|
for ( int i = 0; i < 2; i++ )
|
|
{
|
|
m_flSelection[ i ] += delta;
|
|
}
|
|
|
|
SelectPoints( m_flSelection[ 0 ], m_flSelection[ 1 ] );
|
|
|
|
redraw();
|
|
}
|
|
|
|
void ExpressionTool::FinishMoveSelectionStart( int startx, int mx )
|
|
{
|
|
float start = GetTimeValueForMouse( startx );
|
|
float end = GetTimeValueForMouse( mx );
|
|
|
|
float delta = end - start;
|
|
|
|
m_flSelection[ 0 ] += delta;
|
|
|
|
SelectPoints( m_flSelection[ 0 ], m_flSelection[ 1 ] );
|
|
|
|
redraw();
|
|
}
|
|
|
|
void ExpressionTool::FinishMoveSelectionEnd( int startx, int mx )
|
|
{
|
|
float start = GetTimeValueForMouse( startx );
|
|
float end = GetTimeValueForMouse( mx );
|
|
|
|
float delta = end - start;
|
|
|
|
m_flSelection[ 1 ] += delta;
|
|
|
|
SelectPoints( m_flSelection[ 0 ], m_flSelection[ 1 ] );
|
|
|
|
redraw();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : startx -
|
|
// mx -
|
|
//-----------------------------------------------------------------------------
|
|
void ExpressionTool::FinishSelect( int startx, int mx )
|
|
{
|
|
// Don't select really small areas
|
|
if ( abs( startx - mx ) < 1 )
|
|
return;
|
|
|
|
float start = GetTimeValueForMouse( startx );
|
|
float end = GetTimeValueForMouse( mx );
|
|
|
|
SelectPoints( start, end );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : mx -
|
|
// my -
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool ExpressionTool::IsMouseOverPoints( int mx, int my )
|
|
{
|
|
RECT rc;
|
|
GetWorkspaceRect( rc );
|
|
|
|
// Over tag
|
|
if ( my > TRAY_HEIGHT )
|
|
return false;
|
|
|
|
if ( my <= 12 + GetCaptionHeight() )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : mx -
|
|
// my -
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool ExpressionTool::IsMouseOverSelection( int mx, int my )
|
|
{
|
|
if ( !m_bSelectionActive )
|
|
return false;
|
|
|
|
if ( !IsMouseOverPoints( mx, my ) )
|
|
return false;
|
|
|
|
float t = GetTimeValueForMouse( mx );
|
|
|
|
if ( t >= m_flSelection[ 0 ] &&
|
|
t <= m_flSelection[ 1 ] )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ExpressionTool::IsMouseOverSelectionStartEdge( mxEvent *event )
|
|
{
|
|
int mx, my;
|
|
mx = (short)event->x;
|
|
my = (short)event->y;
|
|
|
|
if ( !(event->modifiers & mxEvent::KeyCtrl ) )
|
|
return false;
|
|
|
|
if ( !IsMouseOverSelection( mx, my ) )
|
|
return false;
|
|
|
|
int left;
|
|
|
|
left = GetPixelForTimeValue( m_flSelection[ 0 ] );
|
|
|
|
if ( abs( left - mx ) <= 2 )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ExpressionTool::IsMouseOverSelectionEndEdge( mxEvent *event )
|
|
{
|
|
int mx, my;
|
|
mx = (short)event->x;
|
|
my = (short)event->y;
|
|
|
|
if ( !(event->modifiers & mxEvent::KeyCtrl ) )
|
|
return false;
|
|
|
|
if ( !IsMouseOverSelection( mx, my ) )
|
|
return false;
|
|
|
|
int right;
|
|
|
|
right = GetPixelForTimeValue( m_flSelection[ 1 ] );
|
|
|
|
if ( abs( right - mx ) <= 2 )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : &rc -
|
|
//-----------------------------------------------------------------------------
|
|
void ExpressionTool::GetWorkspaceRect( RECT &rc )
|
|
{
|
|
GetClientRect( (HWND)getHandle(), &rc );
|
|
|
|
rc.top = TRAY_HEIGHT - 17;
|
|
rc.bottom = TRAY_HEIGHT - 1;
|
|
//InflateRect( &rc, -1, -1 );
|
|
}
|
|
|
|
void ExpressionTool::AddFocusRect( RECT& rc )
|
|
{
|
|
RECT rcFocus = rc;
|
|
|
|
POINT offset;
|
|
offset.x = 0;
|
|
offset.y = 0;
|
|
ClientToScreen( (HWND)getHandle(), &offset );
|
|
OffsetRect( &rcFocus, offset.x, offset.y );
|
|
|
|
// Convert to screen space?
|
|
CFocusRect fr;
|
|
fr.m_rcFocus = rcFocus;
|
|
fr.m_rcOrig = rcFocus;
|
|
|
|
m_FocusRects.AddToTail( fr );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : int
|
|
//-----------------------------------------------------------------------------
|
|
int ExpressionTool::ComputeHPixelsNeeded( void )
|
|
{
|
|
CChoreoEvent *event = GetSafeEvent();
|
|
if ( !event )
|
|
return 0;
|
|
|
|
int pixels = 0;
|
|
float maxtime = event->GetDuration();
|
|
pixels = (int)( ( maxtime + 5.0 ) * GetPixelsPerSecond() + 10 );
|
|
|
|
return pixels;
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void ExpressionTool::RepositionHSlider( void )
|
|
{
|
|
int pixelsneeded = ComputeHPixelsNeeded();
|
|
|
|
if ( pixelsneeded <= w2() )
|
|
{
|
|
m_pHorzScrollBar->setVisible( false );
|
|
}
|
|
else
|
|
{
|
|
m_pHorzScrollBar->setVisible( true );
|
|
}
|
|
m_pHorzScrollBar->setBounds( 0, h2() - m_nScrollbarHeight, w2(), m_nScrollbarHeight );
|
|
|
|
m_flLeftOffset = max( 0.f, m_flLeftOffset );
|
|
m_flLeftOffset = min( (float)pixelsneeded, m_flLeftOffset );
|
|
|
|
m_pHorzScrollBar->setRange( 0, pixelsneeded );
|
|
m_pHorzScrollBar->setValue( m_flLeftOffset );
|
|
m_pHorzScrollBar->setPagesize( w2() );
|
|
|
|
m_nLastHPixelsNeeded = pixelsneeded;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : float
|
|
//-----------------------------------------------------------------------------
|
|
float ExpressionTool::GetPixelsPerSecond( void )
|
|
{
|
|
return m_flPixelsPerSecond * (float)g_pChoreoView->GetTimeZoom( GetToolName() ) / 100.0f;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : x -
|
|
//-----------------------------------------------------------------------------
|
|
void ExpressionTool::MoveTimeSliderToPos( int x )
|
|
{
|
|
m_flLeftOffset = x;
|
|
m_pHorzScrollBar->setValue( m_flLeftOffset );
|
|
InvalidateRect( (HWND)m_pHorzScrollBar->getHandle(), NULL, TRUE );
|
|
InvalidateLayout();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void ExpressionTool::InvalidateLayout( void )
|
|
{
|
|
if ( m_bSuppressLayout )
|
|
return;
|
|
|
|
if ( ComputeHPixelsNeeded() != m_nLastHPixelsNeeded )
|
|
{
|
|
RepositionHSlider();
|
|
}
|
|
|
|
m_bLayoutIsValid = false;
|
|
m_pWorkspace->redraw();
|
|
redraw();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : time -
|
|
// *clipped -
|
|
// Output : int
|
|
//-----------------------------------------------------------------------------
|
|
int ExpressionTool::GetPixelForTimeValue( float time, bool *clipped /*=NULL*/ )
|
|
{
|
|
int left, right;
|
|
|
|
GetWorkspaceLeftRight( left, right );
|
|
|
|
if ( clipped )
|
|
{
|
|
*clipped = false;
|
|
}
|
|
|
|
float st, ed;
|
|
GetStartAndEndTime( st, ed );
|
|
|
|
float frac = ( time - st ) / ( ed - st );
|
|
if ( frac < 0.0 || frac > 1.0 )
|
|
{
|
|
if ( clipped )
|
|
{
|
|
*clipped = true;
|
|
}
|
|
}
|
|
|
|
int pixel = left + ( int )( frac * (right - left ) );
|
|
return pixel;
|
|
}
|
|
|
|
void ExpressionTool::OnChangeScale( void )
|
|
{
|
|
CChoreoScene *scene = g_pChoreoView->GetScene();
|
|
if ( !scene )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Zoom time in / out
|
|
CInputParams params;
|
|
memset( ¶ms, 0, sizeof( params ) );
|
|
|
|
strcpy( params.m_szDialogTitle, "Change Zoom" );
|
|
strcpy( params.m_szPrompt, "New scale (e.g., 2.5x):" );
|
|
|
|
Q_snprintf( params.m_szInputText, sizeof( params.m_szInputText ), "%.2f", (float)g_pChoreoView->GetTimeZoom( GetToolName() ) / 100.0f );
|
|
|
|
if ( !InputProperties( ¶ms ) )
|
|
return;
|
|
|
|
g_pChoreoView->SetTimeZoom( GetToolName(), clamp( (int)( 100.0f * atof( params.m_szInputText ) ), 1, MAX_TIME_ZOOM ), false );
|
|
|
|
m_nLastHPixelsNeeded = -1;
|
|
InvalidateLayout();
|
|
Con_Printf( "Zoom factor %i %%\n", g_pChoreoView->GetTimeZoom( GetToolName() ) );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : st -
|
|
// ed -
|
|
//-----------------------------------------------------------------------------
|
|
void ExpressionTool::GetStartAndEndTime( float& st, float& ed )
|
|
{
|
|
st = m_flLeftOffset / GetPixelsPerSecond();
|
|
int left, right;
|
|
GetWorkspaceLeftRight( left, right );
|
|
if ( right <= left )
|
|
{
|
|
ed = st;
|
|
}
|
|
else
|
|
{
|
|
ed = st + (float)( right - left ) / GetPixelsPerSecond();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : -
|
|
// Output : float
|
|
//-----------------------------------------------------------------------------
|
|
float ExpressionTool::GetEventEndTime()
|
|
{
|
|
CChoreoEvent *ev = GetSafeEvent();
|
|
if ( !ev )
|
|
return 1.0f;
|
|
|
|
return ev->GetDuration();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : mx -
|
|
// clip -
|
|
// Output : float
|
|
//-----------------------------------------------------------------------------
|
|
float ExpressionTool::GetTimeValueForMouse( int mx, bool clip /*=false*/)
|
|
{
|
|
int left, right;
|
|
|
|
GetWorkspaceLeftRight( left, right );
|
|
|
|
float st, ed;
|
|
GetStartAndEndTime( st, ed );
|
|
|
|
if ( clip )
|
|
{
|
|
if ( mx < 0 )
|
|
{
|
|
return st;
|
|
}
|
|
if ( mx > w2() )
|
|
{
|
|
return ed;
|
|
}
|
|
}
|
|
|
|
float frac = (float)( mx - left ) / (float)( right - left );
|
|
return st + frac * ( ed - st );
|
|
}
|
|
|
|
void ExpressionTool::DrawEventEnd( CChoreoWidgetDrawHelper& drawHelper )
|
|
{
|
|
CChoreoEvent *e = GetSafeEvent();
|
|
if ( !e )
|
|
return;
|
|
|
|
float duration = e->GetDuration();
|
|
if ( !duration )
|
|
return;
|
|
|
|
int leftx = GetPixelForTimeValue( duration );
|
|
if ( leftx >= w2() )
|
|
return;
|
|
|
|
RECT rcClient;
|
|
drawHelper.GetClientRect( rcClient );
|
|
|
|
drawHelper.DrawColoredLine(
|
|
COLOR_CHOREO_ENDTIME, PS_SOLID, 1,
|
|
leftx, rcClient.top + TRAY_HEIGHT, leftx, rcClient.bottom );
|
|
|
|
}
|
|
|
|
void ExpressionTool::OnSortByUsed( void )
|
|
{
|
|
m_pWorkspace->OnSortByUsed();
|
|
}
|
|
|
|
void ExpressionTool::OnSortByName( void )
|
|
{
|
|
m_pWorkspace->OnSortByName();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *item -
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool ExpressionTool::IsFocusItem( TimelineItem *item )
|
|
{
|
|
return m_pWorkspace->GetClickedItem() == item;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Delete a vertical column of samples between the selection
|
|
// markers. If excise_time is true, shifts remaining samples left
|
|
// Input : excise_time -
|
|
//-----------------------------------------------------------------------------
|
|
void ExpressionTool::OnDeleteSelection( bool excise_time )
|
|
{
|
|
if ( !m_bSelectionActive )
|
|
return;
|
|
|
|
// Force selection of everything again!
|
|
SelectPoints( m_flSelection[ 0 ], m_flSelection[ 1 ] );
|
|
|
|
int i, t;
|
|
|
|
char const *undotext = excise_time ? "Excise column" : "Delete column";
|
|
|
|
float shift_left_time = m_flSelection[ 1 ] - m_flSelection[ 0 ];
|
|
Assert( shift_left_time > 0.0f );
|
|
|
|
g_pChoreoView->SetDirty( true );
|
|
g_pChoreoView->PushUndo( undotext );
|
|
|
|
for ( int controller = 0; controller < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; controller++ )
|
|
{
|
|
TimelineItem *item = m_pWorkspace->GetItem( controller );
|
|
if ( !item )
|
|
continue;
|
|
|
|
CFlexAnimationTrack *track = item->GetSafeTrack();
|
|
if ( !track )
|
|
continue;
|
|
|
|
for ( t = 0; t < 2; t++ )
|
|
{
|
|
for ( i = track->GetNumSamples( t ) - 1; i >= 0 ; i-- )
|
|
{
|
|
CExpressionSample *sample = track->GetSample( i, t );
|
|
if ( !sample->selected )
|
|
continue;
|
|
|
|
track->RemoveSample( i, t );
|
|
}
|
|
|
|
if ( !excise_time )
|
|
continue;
|
|
|
|
|
|
// Now shift things after m_flSelection[0] to the left
|
|
for ( i = track->GetNumSamples( t ) - 1; i >= 0 ; i-- )
|
|
{
|
|
CExpressionSample *sample = track->GetSample( i, t );
|
|
if ( sample->time < m_flSelection[ 1 ] )
|
|
continue;
|
|
|
|
// Shift it
|
|
sample->time -= shift_left_time;
|
|
}
|
|
}
|
|
|
|
item->DrawSelf();
|
|
}
|
|
|
|
g_pChoreoView->PushRedo( undotext );
|
|
|
|
// Clear selection and redraw()
|
|
DeselectAll();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void ExpressionTool::OnResetItemSize()
|
|
{
|
|
TimelineItem *item = m_pWorkspace->GetClickedItem();
|
|
if ( !item )
|
|
return;
|
|
|
|
item->ResetHeight();
|
|
m_pWorkspace->LayoutItems( true );
|
|
redraw();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void ExpressionTool::OnResetAllItemSizes()
|
|
{
|
|
for ( int controller = 0; controller < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; controller++ )
|
|
{
|
|
TimelineItem *item = m_pWorkspace->GetItem( controller );
|
|
if ( !item )
|
|
continue;
|
|
item->ResetHeight();
|
|
}
|
|
|
|
m_pWorkspace->LayoutItems( true );
|
|
redraw();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void ExpressionTool::OnScaleSamples()
|
|
{
|
|
int t, i;
|
|
|
|
//Scale samples
|
|
CInputParams params;
|
|
memset( ¶ms, 0, sizeof( params ) );
|
|
|
|
strcpy( params.m_szDialogTitle, "Scale selected samples" );
|
|
strcpy( params.m_szPrompt, "Factor:" );
|
|
strcpy( params.m_szInputText, "1.0" );
|
|
|
|
if ( !InputProperties( ¶ms ) )
|
|
return;
|
|
|
|
float scale_factor = atof( params.m_szInputText );
|
|
if( scale_factor <= 0.0f )
|
|
{
|
|
Con_Printf( "Can't scale to %.2f\n", scale_factor );
|
|
}
|
|
|
|
char const *undotext = "Scale samples";
|
|
|
|
g_pChoreoView->SetDirty( true );
|
|
g_pChoreoView->PushUndo( undotext );
|
|
|
|
for ( int controller = 0; controller < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; controller++ )
|
|
{
|
|
TimelineItem *item = m_pWorkspace->GetItem( controller );
|
|
if ( !item )
|
|
continue;
|
|
|
|
CFlexAnimationTrack *track = item->GetSafeTrack();
|
|
if ( !track )
|
|
continue;
|
|
|
|
for ( t = 0; t < 2; t++ )
|
|
{
|
|
for ( i = track->GetNumSamples( t ) - 1; i >= 0 ; i-- )
|
|
{
|
|
CExpressionSample *sample = track->GetSample( i, t );
|
|
if ( !sample->selected )
|
|
continue;
|
|
|
|
// Scale it
|
|
float curvalue = sample->value;
|
|
curvalue *= scale_factor;
|
|
// Clamp it
|
|
curvalue = clamp( curvalue, 0.0f, 1.0f );
|
|
sample->value = curvalue;
|
|
}
|
|
}
|
|
}
|
|
|
|
g_pChoreoView->PushRedo( undotext );
|
|
|
|
m_pWorkspace->redraw();
|
|
redraw();
|
|
}
|
|
|
|
void ExpressionTool::OnModelChanged()
|
|
{
|
|
SetEvent( NULL );
|
|
redraw();
|
|
}
|
|
|
|
void ExpressionTool::OnEdgeProperties()
|
|
{
|
|
TimelineItem *item = m_pWorkspace->GetClickedItem();
|
|
if ( !item )
|
|
return;
|
|
|
|
CFlexAnimationTrack *track = item->GetSafeTrack();
|
|
if ( !track )
|
|
return;
|
|
|
|
CEdgePropertiesParams params;
|
|
Q_memset( ¶ms, 0, sizeof( params ) );
|
|
Q_strcpy( params.m_szDialogTitle, "Edge Properties" );
|
|
|
|
params.SetFromFlexTrack( track );
|
|
|
|
if ( !EdgeProperties( ¶ms ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
char const *undotext = "Change Edge Properties";
|
|
|
|
g_pChoreoView->SetDirty( true );
|
|
g_pChoreoView->PushUndo( undotext );
|
|
|
|
// Apply changes.
|
|
params.ApplyToTrack( track );
|
|
|
|
g_pChoreoView->PushRedo( undotext );
|
|
|
|
m_pWorkspace->redraw();
|
|
redraw();
|
|
}
|
|
|
|
float ExpressionTool::GetScrubberSceneTime()
|
|
{
|
|
CChoreoEvent *ev = GetSafeEvent();
|
|
if ( !ev )
|
|
return 0.0f;
|
|
|
|
float curtime = GetScrub();
|
|
curtime += ev->GetStartTime();
|
|
return curtime;
|
|
}
|
|
|
|
void ExpressionTool::GetTimelineItems( CUtlVector< TimelineItem * >& list )
|
|
{
|
|
for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
|
|
{
|
|
TimelineItem *item = m_pWorkspace->GetItem( i );
|
|
if ( !item )
|
|
continue;
|
|
|
|
list.AddToTail( item );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
bool ExpressionTool::HasCopiedColumn()
|
|
{
|
|
return m_ColumnCopy.m_bActive;
|
|
}
|
|
|
|
void ExpressionTool::OnCopyColumn()
|
|
{
|
|
m_ColumnCopy.Reset();
|
|
|
|
m_ColumnCopy.m_bActive = true;
|
|
m_ColumnCopy.m_flCopyTimes[ 0 ] = m_flSelection[ 0 ];
|
|
m_ColumnCopy.m_flCopyTimes[ 1 ] = m_flSelection[ 1 ];
|
|
|
|
for ( int controller = 0; controller < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; ++controller )
|
|
{
|
|
TimelineItem *item = m_pWorkspace->GetItem( controller );
|
|
if ( !item )
|
|
continue;
|
|
|
|
CFlexAnimationTrack *track = item->GetSafeTrack();
|
|
if ( !track )
|
|
continue;
|
|
|
|
for ( int t = 0; t < 2; t++ )
|
|
{
|
|
for ( int i = track->GetNumSamples( t ) - 1; i >= 0 ; i-- )
|
|
{
|
|
CExpressionSample *sample = track->GetSample( i, t );
|
|
if ( !sample->selected )
|
|
continue;
|
|
|
|
// Add to dictionary
|
|
CExpressionSample copy( *sample );
|
|
copy.selected = false;
|
|
|
|
int tIndex = m_ColumnCopy.m_Data.Find( track->GetFlexControllerName() );
|
|
if ( tIndex == m_ColumnCopy.m_Data.InvalidIndex() )
|
|
{
|
|
tIndex = m_ColumnCopy.m_Data.Insert( track->GetFlexControllerName() );
|
|
}
|
|
|
|
CColumnCopier::CTrackData &data = m_ColumnCopy.m_Data[ tIndex ];
|
|
data.m_Samples[ t ].AddToTail( copy );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ExpressionTool::OnPasteColumn()
|
|
{
|
|
if ( !m_ColumnCopy.m_bActive )
|
|
{
|
|
Msg( "Nothing to paste\n" );
|
|
return;
|
|
}
|
|
|
|
float flPasteTime = GetTimeForClickedPos();
|
|
|
|
float flPasteEndTime = flPasteTime + m_ColumnCopy.m_flCopyTimes[ 1 ] - m_ColumnCopy.m_flCopyTimes[ 0 ];
|
|
|
|
// Clear selection and redraw()
|
|
DeselectAll();
|
|
|
|
// Select everthing in the paste region so we can delete the existing stuff
|
|
SelectPoints( flPasteTime, flPasteEndTime );
|
|
|
|
int i, t;
|
|
|
|
char const *undotext = "Paste column";
|
|
|
|
g_pChoreoView->SetDirty( true );
|
|
g_pChoreoView->PushUndo( undotext );
|
|
|
|
for ( int controller = 0; controller < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; controller++ )
|
|
{
|
|
TimelineItem *item = m_pWorkspace->GetItem( controller );
|
|
if ( !item )
|
|
continue;
|
|
|
|
CFlexAnimationTrack *track = item->GetSafeTrack();
|
|
if ( !track )
|
|
continue;
|
|
|
|
int tIndex = m_ColumnCopy.m_Data.Find( track->GetFlexControllerName() );
|
|
|
|
for ( t = 0; t < 2; t++ )
|
|
{
|
|
// Remove all selected samples
|
|
for ( i = track->GetNumSamples( t ) - 1; i >= 0 ; i-- )
|
|
{
|
|
CExpressionSample *sample = track->GetSample( i, t );
|
|
if ( !sample->selected )
|
|
continue;
|
|
|
|
track->RemoveSample( i, t );
|
|
}
|
|
|
|
// Now add the new samples, if any in the time selection
|
|
if ( tIndex != m_ColumnCopy.m_Data.InvalidIndex() )
|
|
{
|
|
CColumnCopier::CTrackData &data = m_ColumnCopy.m_Data[ tIndex ];
|
|
|
|
for ( int j = 0; j < data.m_Samples[ t ].Count(); ++j )
|
|
{
|
|
CExpressionSample *s = &data.m_Samples[ t ][ j ];
|
|
CExpressionSample *newSample = track->AddSample( s->time - m_ColumnCopy.m_flCopyTimes[ 0 ] + flPasteTime, s->value, t );
|
|
newSample->selected = true;
|
|
}
|
|
}
|
|
track->Resort( t );
|
|
}
|
|
|
|
item->DrawSelf();
|
|
}
|
|
|
|
g_pChoreoView->PushRedo( undotext );
|
|
}
|
|
|
|
void ExpressionTool::ClearColumnCopy()
|
|
{
|
|
m_ColumnCopy.Reset();
|
|
} |