You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4816 lines
106 KiB
4816 lines
106 KiB
//========= 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(); |
|
} |