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.
1763 lines
42 KiB
1763 lines
42 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
#include "hlfaceposer.h" |
|
#include <stdio.h> |
|
#include "TimelineItem.h" |
|
#include "choreowidgetdrawhelper.h" |
|
#include "mathlib/mathlib.h" |
|
#include "expressions.h" |
|
#include "StudioModel.h" |
|
#include "expclass.h" |
|
#include "mathlib/mathlib.h" |
|
#include "ExpressionTool.h" |
|
#include "choreoevent.h" |
|
#include "choreoscene.h" |
|
#include "choreoactor.h" |
|
#include "choreochannel.h" |
|
#include "ChoreoView.h" |
|
#include "ControlPanel.h" |
|
#include "faceposer_models.h" |
|
#include "MatSysWin.h" |
|
#include "choreoviewcolors.h" |
|
#include "ifaceposersound.h" |
|
#include "curveeditorhelpers.h" |
|
|
|
extern double realtime; |
|
|
|
#define DOUBLE_CLICK_TIME 0.2 |
|
|
|
#define GROW_HANDLE_WIDTH 66 |
|
#define GROW_HANDLE_HEIGHT 8 |
|
#define GROW_HANDLE_INSETPIXELS 8 |
|
|
|
#define TIMELINEITEM_DEFAULT_HEIGHT 100 |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
TimelineItem::TimelineItem( mxWindow *workspace ) |
|
{ |
|
m_pHelper = new CCurveEditorHelper< TimelineItem >( this ); |
|
|
|
m_pWorkspace = workspace; |
|
|
|
m_nDragging = DRAGTYPE_NONE; |
|
m_nLastX = 0; |
|
m_nLastY = 0; |
|
m_nStartX = 0; |
|
m_nStartY = 0; |
|
|
|
SetExpressionInfo( NULL, 0 ); |
|
|
|
m_nNumSelected = 0; |
|
|
|
m_nEditType = 0; |
|
|
|
SetCollapsed( false ); |
|
SetActive( false ); |
|
|
|
m_nUndoSetup = 0; |
|
m_rcBounds.top = 0; |
|
m_rcBounds.bottom = 0; |
|
m_rcBounds.left = 0; |
|
m_rcBounds.right = 0; |
|
m_bVisible = false; |
|
m_flLastClickTime = -1; |
|
|
|
m_nCurrentHeight = TIMELINEITEM_DEFAULT_HEIGHT; |
|
} |
|
|
|
TimelineItem::~TimelineItem( void ) |
|
{ |
|
delete m_pHelper; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void TimelineItem::ResetHeight() |
|
{ |
|
m_nCurrentHeight = TIMELINEITEM_DEFAULT_HEIGHT; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : mx - |
|
// Output : float |
|
//----------------------------------------------------------------------------- |
|
float TimelineItem::GetTimeForMouse( int mx, bool clip /*= false*/ ) |
|
{ |
|
float start, end; |
|
g_pExpressionTool->GetStartAndEndTime( start, end ); |
|
|
|
if ( clip ) |
|
{ |
|
if ( mx < m_rcBounds.left ) |
|
{ |
|
return start; |
|
} |
|
else if ( mx >= m_rcBounds.right ) |
|
{ |
|
return end; |
|
} |
|
} |
|
|
|
float frac = (float)( mx - m_rcBounds.left ) / (float)( m_rcBounds.right - m_rcBounds.left ); |
|
float t = start + frac * ( end - start ); |
|
return t; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : t - |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
int TimelineItem::GetMouseForTime( float t, bool *clipped /*= NULL*/ ) |
|
{ |
|
float start, end; |
|
g_pExpressionTool->GetStartAndEndTime( start, end ); |
|
|
|
float frac = ( t - start ) / ( end - start ); |
|
|
|
if ( frac < 0.0 || frac > 1.0 ) |
|
{ |
|
if ( clipped ) |
|
{ |
|
*clipped = true; |
|
} |
|
} |
|
|
|
int mx = m_rcBounds.left + ( int ) ( frac * (float)( m_rcBounds.right - m_rcBounds.left ) ); |
|
|
|
return mx; |
|
} |
|
|
|
int TimelineItem::NumSamples() |
|
{ |
|
CFlexAnimationTrack *track = GetSafeTrack(); |
|
if ( !track ) |
|
return 0; |
|
|
|
// Aggregate both types of tracks together |
|
return track->GetNumSamples( 0 ) + track->GetNumSamples( 1 ); |
|
// return track->GetNumSamples( m_nEditType ); |
|
} |
|
|
|
CExpressionSample *TimelineItem::GetSample( int idx ) |
|
{ |
|
CFlexAnimationTrack *track = GetSafeTrack(); |
|
if ( !track ) |
|
return NULL; |
|
|
|
if ( idx >= track->GetNumSamples( 0 ) ) |
|
{ |
|
// Rebase and look at left/right track instead |
|
idx -= track->GetNumSamples( 0 ); |
|
return track->GetSample( idx, 1 ); |
|
} |
|
return track->GetSample( idx, 0 ); |
|
} |
|
|
|
CExpressionSample *TimelineItem::GetSampleUnderMouse( int mx, int my, float tolerance /*= FP_TL_SELECTION_TOLERANCE*/ ) |
|
{ |
|
CFlexAnimationTrack *track = GetSafeTrack(); |
|
if ( !track ) |
|
return NULL; |
|
|
|
CChoreoEvent *e = track->GetEvent(); |
|
if ( !e ) |
|
return NULL; |
|
|
|
float closest_dist = 9999999.f; |
|
CExpressionSample *bestsample = NULL; |
|
|
|
// Add a sample point |
|
int height = m_rcBounds.bottom - m_rcBounds.top; |
|
|
|
mx += m_rcBounds.left; |
|
|
|
for ( int i = 0; i < track->GetNumSamples( m_nEditType ); i++ ) |
|
{ |
|
CExpressionSample *sample = track->GetSample( i, m_nEditType ); |
|
|
|
bool clipped = false; |
|
int px = GetMouseForTime( sample->time, &clipped ); |
|
int py = height * ( 1.0f - sample->value ); |
|
|
|
int dx = px - mx; |
|
int dy = py - my; |
|
|
|
float dist = sqrt( (float)(dx * dx + dy * dy) ); |
|
if ( dist < closest_dist ) |
|
{ |
|
bestsample = sample; |
|
closest_dist = dist; |
|
} |
|
} |
|
|
|
// Not close to any of them!!! |
|
if ( ( tolerance != 0.0f ) && |
|
( closest_dist > tolerance ) ) |
|
return NULL; |
|
|
|
return bestsample; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void TimelineItem::DeselectAll( void ) |
|
{ |
|
g_pExpressionTool->DeselectAll(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void TimelineItem::SelectAll( void ) |
|
{ |
|
CFlexAnimationTrack *track = GetSafeTrack(); |
|
if ( !track ) |
|
return; |
|
|
|
for ( int t = 0; t < 2; t++ ) |
|
{ |
|
for ( int i = 0; i < track->GetNumSamples( t ); i++ ) |
|
{ |
|
CExpressionSample *sample = track->GetSample( i, t ); |
|
sample->selected = true; |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void TimelineItem::Delete( void ) |
|
{ |
|
g_pExpressionTool->DeleteSelectedSamples(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : sample - |
|
//----------------------------------------------------------------------------- |
|
void TimelineItem::AddSample( CExpressionSample const& sample ) |
|
{ |
|
CFlexAnimationTrack *track = GetSafeTrack(); |
|
if ( !track ) |
|
return; |
|
|
|
PreDataChanged( "Add sample point" ); |
|
|
|
track->AddSample( sample.time, sample.value, m_nEditType ); |
|
track->Resort( m_nEditType ); |
|
|
|
SetActive( true ); |
|
|
|
PostDataChanged( "Add sample point" ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int TimelineItem::CountSelected( void ) |
|
{ |
|
m_nNumSelected = m_pHelper->CountSelected( false ); |
|
return m_nNumSelected; |
|
} |
|
|
|
void TimelineItem::SetMousePositionForEvent( mxEvent *event ) |
|
{ |
|
POINT pt; |
|
GetCursorPos( &pt ); |
|
ScreenToClient( (HWND)m_pWorkspace->getHandle(), &pt ); |
|
|
|
pt.x -= m_rcBounds.left; |
|
pt.y -= m_rcBounds.top; |
|
|
|
event->x = pt.x; |
|
event->y = pt.y; |
|
} |
|
|
|
int TimelineItem::handleEvent( mxEvent *event ) |
|
{ |
|
int iret = 0; |
|
|
|
// Give helper a shot at the event |
|
if ( m_pHelper->HelperHandleEvent( event ) ) |
|
{ |
|
return 1; |
|
} |
|
|
|
switch ( event->event ) |
|
{ |
|
case mxEvent::KeyDown: |
|
{ |
|
switch ( event->key ) |
|
{ |
|
default: |
|
iret = g_pChoreoView->HandleZoomKey( g_pExpressionTool, event->key ); |
|
break; |
|
case VK_ESCAPE: |
|
DeselectAll(); |
|
DrawSelf(); |
|
break; |
|
case VK_DELETE: |
|
Delete(); |
|
DrawSelf(); |
|
break; |
|
case 'C': |
|
Copy(); |
|
DrawSelf(); |
|
break; |
|
case 'V': |
|
Paste(); |
|
DrawSelf(); |
|
break; |
|
case 'J': |
|
{ |
|
g_pExpressionTool->OnCopyToFlex( g_pExpressionTool->GetScrubberSceneTime(), true ); |
|
} |
|
break; |
|
case 'K': |
|
{ |
|
g_pExpressionTool->OnCopyFromFlex( g_pExpressionTool->GetScrubberSceneTime(), false ); |
|
} |
|
break; |
|
case 188: // VK_OEM_COMMA: |
|
{ |
|
g_pExpressionTool->SetScrubTargetTime( 0.0f ); |
|
} |
|
break; |
|
case 190: // VK_OEM_PERIOD: |
|
{ |
|
CChoreoScene *scene = g_pChoreoView->GetScene(); |
|
if ( scene ) |
|
{ |
|
g_pExpressionTool->SetScrubTargetTime( scene->FindStopTime() ); |
|
} |
|
} |
|
break; |
|
case VK_LEFT: |
|
{ |
|
CChoreoScene *scene = g_pChoreoView->GetScene(); |
|
if ( scene && scene->GetSceneFPS() > 0 ) |
|
{ |
|
float curscrub = g_pExpressionTool->GetScrub(); |
|
curscrub -= ( 1.0f / (float)scene->GetSceneFPS() ); |
|
curscrub = max( curscrub, 0.0f ); |
|
g_pExpressionTool->SetScrubTargetTime( curscrub ); |
|
} |
|
} |
|
break; |
|
case VK_RIGHT: |
|
{ |
|
CChoreoScene *scene = g_pChoreoView->GetScene(); |
|
if ( scene && scene->GetSceneFPS() > 0 ) |
|
{ |
|
float curscrub = g_pExpressionTool->GetScrub(); |
|
curscrub += ( 1.0f / (float)scene->GetSceneFPS() ); |
|
curscrub = min( curscrub, scene->FindStopTime() ); |
|
g_pExpressionTool->SetScrubTargetTime( curscrub ); |
|
} |
|
} |
|
break; |
|
case 191: |
|
{ |
|
if ( g_pChoreoView->IsPlayingScene() ) |
|
{ |
|
g_pChoreoView->StopScene(); |
|
} |
|
} |
|
break; |
|
} |
|
iret = 1; |
|
} |
|
break; |
|
case mxEvent::KeyUp: |
|
{ |
|
switch ( event->key ) |
|
{ |
|
case VK_SPACE: |
|
{ |
|
CFlexAnimationTrack *track = GetSafeTrack(); |
|
if ( track && track->IsComboType() ) |
|
{ |
|
SetEditType( m_nEditType == 0 ? 1 : 0 ); |
|
DrawSelf(); |
|
} |
|
} |
|
break; |
|
} |
|
iret = 1; |
|
} |
|
break; |
|
case mxEvent::MouseDown: |
|
{ |
|
sound->Flush(); |
|
|
|
SetFocus( (HWND)g_pExpressionTool->getHandle() ); |
|
|
|
int height = m_rcBounds.bottom - m_rcBounds.top; |
|
|
|
bool rightbutton = ( event->buttons & mxEvent::MouseRightButton ) ? true : false; |
|
|
|
if ( m_nDragging == DRAGTYPE_NONE ) |
|
{ |
|
bool ctrlDown = ( event->modifiers & mxEvent::KeyCtrl ) ? true : false; |
|
|
|
CExpressionSample *sample = GetSampleUnderMouse( event->x, event->y, ctrlDown ? FP_TL_ADDSAMPLE_TOLERANCE : FP_TL_SELECTION_TOLERANCE ); |
|
|
|
if ( IsMouseOverGrowHandle( (short)event->x, (short)event->y ) ) |
|
{ |
|
m_nDragging = DRAGTYPE_GROW; |
|
m_nLastX = (short)event->x; |
|
m_nLastY = (short)event->y; |
|
|
|
m_nStartX = m_nLastX; |
|
m_nStartY = m_nLastY; |
|
|
|
MouseDrag( (short)event->x, (short)event->y, event->modifiers ); |
|
|
|
DrawGrowRect(); |
|
} |
|
else if ( sample ) |
|
{ |
|
if ( event->modifiers & mxEvent::KeyShift ) |
|
{ |
|
sample->selected = !sample->selected; |
|
DrawSelf(); |
|
} |
|
else if ( sample->selected ) |
|
{ |
|
m_nDragging = rightbutton ? DRAGTYPE_MOVEPOINTS_TIME : DRAGTYPE_MOVEPOINTS_VALUE; |
|
m_nLastX = (short)event->x; |
|
m_nLastY = (short)event->y; |
|
|
|
m_nStartX = m_nLastX; |
|
m_nStartY = m_nLastY; |
|
|
|
PreDataChanged( "Move sample point(s)" ); |
|
|
|
MouseDrag( (short)event->x, (short)event->y, event->modifiers ); |
|
|
|
DrawSelf(); |
|
} |
|
else |
|
{ |
|
if ( !( event->modifiers & mxEvent::KeyShift ) ) |
|
{ |
|
DeselectAll(); |
|
DrawSelf(); |
|
} |
|
|
|
m_nDragging = DRAGTYPE_SELECTION; |
|
m_nLastX = (short)event->x; |
|
m_nLastY = (short)event->y; |
|
|
|
m_nStartX = m_nLastX; |
|
m_nStartY = m_nLastY; |
|
|
|
MouseDrag( (short)event->x, (short)event->y, event->modifiers ); |
|
|
|
DrawFocusRect(); |
|
} |
|
} |
|
else if ( event->modifiers & mxEvent::KeyCtrl ) |
|
{ |
|
CChoreoEvent *e = g_pExpressionTool->GetSafeEvent(); |
|
if ( e ) |
|
{ |
|
// Add a sample point |
|
float t = GetTimeForMouse( (short)event->x + m_rcBounds.left ); |
|
|
|
CExpressionSample sample; |
|
sample.time = FacePoser_SnapTime( t ); |
|
sample.value = 1.0f - (float)( (short)( event->y ) ) / (float)height; |
|
sample.selected = false; |
|
|
|
AddSample( sample ); |
|
|
|
DrawSelf(); |
|
} |
|
} |
|
else |
|
{ |
|
if ( rightbutton ) |
|
{ |
|
POINT pt; |
|
pt.x = event->x; |
|
pt.y = event->y; |
|
ClientToScreen( (HWND)m_pWorkspace->getHandle(), &pt ); |
|
ScreenToClient( (HWND)g_pExpressionTool->getHandle(), &pt ); |
|
event->x = pt.x; |
|
event->y = pt.y; |
|
g_pExpressionTool->ShowContextMenu( event, true ); |
|
return iret; |
|
} |
|
|
|
if ( !( event->modifiers & mxEvent::KeyShift ) ) |
|
{ |
|
DeselectAll(); |
|
DrawSelf(); |
|
} |
|
|
|
m_nDragging = DRAGTYPE_SELECTION; |
|
m_nLastX = (short)event->x; |
|
m_nLastY = (short)event->y; |
|
|
|
m_nStartX = m_nLastX; |
|
m_nStartY = m_nLastY; |
|
|
|
MouseDrag( (short)event->x, (short)event->y, event->modifiers ); |
|
|
|
DrawFocusRect(); |
|
} |
|
} |
|
iret = 1; |
|
} |
|
break; |
|
case mxEvent::MouseDrag: |
|
case mxEvent::MouseMove: |
|
{ |
|
if ( m_nDragging != DRAGTYPE_NONE ) |
|
{ |
|
if ( m_nDragging == DRAGTYPE_SELECTION ) |
|
{ |
|
DrawFocusRect(); |
|
} |
|
else if ( m_nDragging == DRAGTYPE_GROW ) |
|
{ |
|
DrawGrowRect(); |
|
} |
|
|
|
MouseDrag( (short)event->x, (short)event->y, event->modifiers ); |
|
|
|
if ( m_nDragging == DRAGTYPE_SELECTION ) |
|
{ |
|
DrawFocusRect(); |
|
} |
|
else if ( m_nDragging == DRAGTYPE_GROW ) |
|
{ |
|
DrawGrowRect(); |
|
} |
|
|
|
if ( m_nDragging == DRAGTYPE_MOVEPOINTS_TIME || |
|
m_nDragging == DRAGTYPE_MOVEPOINTS_VALUE ) |
|
{ |
|
DrawSelf(); |
|
} |
|
} |
|
else |
|
{ |
|
// See if anything is selected |
|
CountSelected(); |
|
if ( m_nNumSelected <= 0 && g_pExpressionTool->IsFocusItem( this ) ) |
|
{ |
|
// Nothing selected |
|
// Draw auto highlight |
|
DrawAutoHighlight( event ); |
|
} |
|
} |
|
iret = 1; |
|
} |
|
break; |
|
case mxEvent::MouseUp: |
|
{ |
|
bool overgrow = IsMouseOverGrowHandle( (short)event->x, (short)event->y ); |
|
|
|
if ( m_nDragging != DRAGTYPE_NONE ) |
|
{ |
|
if ( m_nDragging == DRAGTYPE_SELECTION ) |
|
{ |
|
DrawFocusRect(); |
|
} |
|
else if ( m_nDragging == DRAGTYPE_GROW ) |
|
{ |
|
DrawGrowRect(); |
|
} |
|
|
|
MouseDrag( (short)event->x, (short)event->y, event->modifiers, true ); |
|
|
|
if ( m_nDragging == DRAGTYPE_GROW ) |
|
{ |
|
// Finish grow by resizing control |
|
int desiredheight = m_nCurrentHeight + event->y - m_nStartY; |
|
if ( desiredheight >= 10 ) |
|
{ |
|
m_nCurrentHeight = desiredheight; |
|
g_pExpressionTool->LayoutItems( true ); |
|
} |
|
} |
|
else if ( m_nDragging != DRAGTYPE_MOVEPOINTS_VALUE && |
|
m_nDragging != DRAGTYPE_MOVEPOINTS_TIME ) |
|
{ |
|
SelectPoints(); |
|
} |
|
else |
|
{ |
|
PostDataChanged( "Move sample point(s)" ); |
|
} |
|
|
|
m_nDragging = DRAGTYPE_NONE; |
|
|
|
DrawSelf(); |
|
} |
|
|
|
bool rightbutton = ( event->buttons & mxEvent::MouseRightButton ) ? true : false; |
|
bool shift = ( event->modifiers & mxEvent::KeyShift ) ? true : false; |
|
bool ctrl = ( event->modifiers & mxEvent::KeyCtrl ) ? true : false; |
|
|
|
if ( !rightbutton && !shift && !ctrl ) |
|
{ |
|
if ( realtime - m_flLastClickTime < DOUBLE_CLICK_TIME ) |
|
{ |
|
if ( overgrow || IsCollapsed() ) |
|
{ |
|
OnDoubleClicked(); |
|
} |
|
} |
|
|
|
m_flLastClickTime = realtime; |
|
} |
|
|
|
iret = 1; |
|
} |
|
break; |
|
} |
|
|
|
return iret; |
|
} |
|
|
|
void TimelineItem::MouseDrag( int x, int y, int modifiers, bool snap /*=false*/ ) |
|
{ |
|
if ( m_nDragging == DRAGTYPE_NONE ) |
|
return; |
|
|
|
int width = m_rcBounds.right - m_rcBounds.left; |
|
int height = m_rcBounds.bottom - m_rcBounds.top; |
|
|
|
if ( m_nDragging == DRAGTYPE_MOVEPOINTS_TIME || |
|
m_nDragging == DRAGTYPE_MOVEPOINTS_VALUE ) |
|
{ |
|
int dx = x - m_nLastX; |
|
int dy = y - m_nLastY; |
|
|
|
if ( !( modifiers & mxEvent::KeyCtrl ) ) |
|
{ |
|
// Zero out motion on other axis |
|
if ( m_nDragging == DRAGTYPE_MOVEPOINTS_VALUE ) |
|
{ |
|
dx = 0; |
|
x = m_nLastX; |
|
} |
|
else |
|
{ |
|
dy = 0; |
|
y = m_nLastY; |
|
} |
|
} |
|
|
|
float dfdx = (float)dx / g_pExpressionTool->GetPixelsPerSecond(); |
|
float dfdy = (float)dy / (float)height; |
|
|
|
g_pExpressionTool->MoveSelectedSamples( dfdx, dfdy, snap ); |
|
|
|
// Update the scrubber |
|
if ( (float)width > 0 ) |
|
{ |
|
float t = GetTimeForMouse( x + m_rcBounds.left ); |
|
g_pExpressionTool->ForceScrubPosition( t ); |
|
|
|
g_pMatSysWindow->Frame(); |
|
} |
|
} |
|
|
|
m_nLastX = x; |
|
m_nLastY = y; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void TimelineItem::DrawFocusRect( void ) |
|
{ |
|
RECT rcFocus; |
|
|
|
rcFocus.left = m_nStartX < m_nLastX ? m_nStartX : m_nLastX; |
|
rcFocus.right = m_nStartX < m_nLastX ? m_nLastX : m_nStartX; |
|
|
|
rcFocus.top = m_nStartY < m_nLastY ? m_nStartY : m_nLastY; |
|
rcFocus.bottom = m_nStartY < m_nLastY ? m_nLastY : m_nStartY; |
|
|
|
POINT offset; |
|
offset.x = m_rcBounds.left; |
|
offset.y = m_rcBounds.top; |
|
ClientToScreen( (HWND)m_pWorkspace->getHandle(), &offset ); |
|
OffsetRect( &rcFocus, offset.x, offset.y ); |
|
|
|
HDC dc = GetDC( NULL ); |
|
|
|
::DrawFocusRect( dc, &rcFocus ); |
|
|
|
ReleaseDC( NULL, dc ); |
|
} |
|
|
|
void TimelineItem::DrawSelf( void ) |
|
{ |
|
CChoreoWidgetDrawHelper drawHelper( m_pWorkspace, m_rcBounds ); |
|
Draw( drawHelper ); |
|
} |
|
|
|
void TimelineItem::Draw( CChoreoWidgetDrawHelper& drawHelper ) |
|
{ |
|
CFlexAnimationTrack *track = GetSafeTrack(); |
|
if ( !track ) |
|
return; |
|
|
|
CChoreoEvent *e = track->GetEvent(); |
|
if ( !e ) |
|
return; |
|
|
|
Assert( e->HasEndTime() ); |
|
|
|
bool active = track && ( IsValid() || IsActive() ); |
|
|
|
float starttime; |
|
float endtime; |
|
|
|
g_pExpressionTool->GetStartAndEndTime( starttime, endtime ); |
|
|
|
CountSelected(); |
|
int scount = GetNumSelected(); |
|
|
|
COLORREF bgColor = RGB( 230, 230, 200 ); |
|
if ( IsCollapsed() && active ) |
|
{ |
|
bgColor = RGB( 200, 230, 200 ); |
|
} |
|
|
|
RECT rcClient = m_rcBounds; |
|
|
|
drawHelper.DrawFilledRect( bgColor, rcClient ); |
|
|
|
COLORREF gray = RGB( 200, 200, 200 ); |
|
|
|
DrawEventEnd( drawHelper ); |
|
|
|
DrawRelativeTags( drawHelper ); |
|
if ( !IsCollapsed() && track ) |
|
{ |
|
if ( m_nEditType == 1 ) |
|
{ |
|
float zero = track->GetZeroValue( m_nEditType, true ); |
|
|
|
drawHelper.DrawColoredLine( RGB( 180, 200, 220 ), PS_SOLID, 1, |
|
rcClient.left, ( rcClient.top * zero + rcClient.bottom * (1 - zero)) , |
|
rcClient.right, ( rcClient.top * zero + rcClient.bottom * (1 - zero)) ); |
|
} |
|
|
|
drawHelper.DrawOutlinedRect( RGB( 100, 150, 200 ), PS_SOLID, 1, rcClient ); |
|
|
|
// Draw grow handle into background... |
|
if ( CanHaveGrowHandle() ) |
|
{ |
|
RECT handleRect; |
|
GetGrowHandleRect( handleRect ); |
|
DrawGrowHandle( drawHelper, handleRect ); |
|
} |
|
|
|
// Draw left/right underneath amount so go backbard |
|
for ( int type = ( track->IsComboType() ? 1 : 0 ); type >= 0; type-- ) |
|
{ |
|
COLORREF lineColor = ( type == m_nEditType ) ? RGB( 0, 0, 255 ) : gray; |
|
COLORREF shadowColor = ( type == m_nEditType ) ? RGB( 150, 150, 250 ) : gray; |
|
COLORREF dotColor = ( type == m_nEditType ) ? RGB( 0, 0, 255 ) : gray; |
|
COLORREF dotColorSelected = ( type == m_nEditType ) ? RGB( 240, 80, 20 ) : gray; |
|
|
|
int height = rcClient.bottom - rcClient.top; |
|
int bottom = rcClient.bottom; |
|
|
|
// Fixme, could look at 1st derivative and do more sampling at high rate of change? |
|
// or near actual sample points! |
|
float linelength = g_pExpressionTool->IsFocusItem( this ) ? 2.0f : 8.0f; |
|
|
|
float timestepperpixel = linelength / g_pExpressionTool->GetPixelsPerSecond(); |
|
|
|
float stoptime = min( endtime, e->GetDuration() ); |
|
|
|
float prev_t = starttime; |
|
float prev_value = track->GetFracIntensity( prev_t, type ); |
|
|
|
CUtlVector< POINT > segments; |
|
|
|
/* |
|
if (type == m_nEditType) |
|
{ |
|
// draw hermite version of time step |
|
float i0, i1, i2; |
|
float time10hz = starttime; |
|
|
|
i0 = track->GetFracIntensity( time10hz, type ); |
|
i1 = i0; |
|
time10hz = starttime + 0.1; |
|
i2 = track->GetFracIntensity( time10hz, type );; |
|
|
|
for ( float t = starttime; t <= stoptime; t += timestepperpixel ) |
|
{ |
|
while (t >= time10hz) |
|
{ |
|
time10hz += 0.1; |
|
i0 = i1; |
|
i1 = i2; |
|
i2 = track->GetFracIntensity( time10hz, type );; |
|
} |
|
|
|
float value = Hermite_Spline( i0, i1, i2, (t - time10hz + 0.1) / 0.1 ); |
|
|
|
int prevx, x; |
|
|
|
bool clipped1, clipped2; |
|
x = GetMouseForTime( t, &clipped1 ); |
|
prevx = GetMouseForTime( prev_t, &clipped2 ); |
|
|
|
//if ( !clipped1 && !clipped2 ) |
|
{ |
|
// Draw segment |
|
//drawHelper.DrawColoredLine( lineColor, PS_SOLID, 1, |
|
// prevx, bottom - prev_value * height, |
|
// x, bottom - value * height ); |
|
|
|
POINT pt; |
|
|
|
if ( segments.Count() == 0 ) |
|
{ |
|
pt.x = prevx; |
|
pt.y = bottom - prev_value * height; |
|
|
|
segments.AddToTail( pt ); |
|
} |
|
|
|
pt.x = x; |
|
pt.y = bottom - value * height; |
|
|
|
segments.AddToTail( pt ); |
|
} |
|
|
|
prev_t = t; |
|
prev_value = value; |
|
} |
|
|
|
if ( segments.Count() >= 2 ) |
|
{ |
|
drawHelper.DrawColoredPolyLine( shadowColor, PS_SOLID, 1, segments ); |
|
} |
|
|
|
segments.RemoveAll(); |
|
} |
|
*/ |
|
for ( float t = starttime; t <= stoptime; t += timestepperpixel ) |
|
{ |
|
float value = track->GetFracIntensity( t, type ); |
|
|
|
int prevx, x; |
|
|
|
bool clipped1, clipped2; |
|
x = GetMouseForTime( t, &clipped1 ); |
|
prevx = GetMouseForTime( prev_t, &clipped2 ); |
|
|
|
//if ( !clipped1 && !clipped2 ) |
|
{ |
|
// Draw segment |
|
//drawHelper.DrawColoredLine( lineColor, PS_SOLID, 1, |
|
// prevx, bottom - prev_value * height, |
|
// x, bottom - value * height ); |
|
|
|
POINT pt; |
|
|
|
if ( segments.Count() == 0 ) |
|
{ |
|
pt.x = prevx; |
|
pt.y = bottom - prev_value * height; |
|
|
|
segments.AddToTail( pt ); |
|
} |
|
|
|
pt.x = x; |
|
pt.y = bottom - value * height; |
|
|
|
segments.AddToTail( pt ); |
|
} |
|
|
|
prev_t = t; |
|
prev_value = value; |
|
} |
|
|
|
if ( segments.Count() >= 2 ) |
|
{ |
|
drawHelper.DrawColoredPolyLine( lineColor, PS_SOLID, 1, segments ); |
|
} |
|
|
|
for ( int sample = 0; sample < track->GetNumSamples( type ); sample++ ) |
|
{ |
|
bool dummy; |
|
CExpressionSample *start = track->GetBoundedSample( sample, dummy, type ); |
|
|
|
/* |
|
int pixel = (int)( ( start->time / event_time ) * width + 0.5f); |
|
int x = m_rcBounds.left + pixel; |
|
float roundedfrac = (float)pixel / (float)width; |
|
*/ |
|
float value = start->value; // track->GetFracIntensity( start->time, type ); |
|
bool clipped = false; |
|
int x = GetMouseForTime( start->time, &clipped ); |
|
if ( clipped ) |
|
continue; |
|
int y = bottom - value * height; |
|
|
|
int dotsize = 6; |
|
int dotSizeSelected = 6; |
|
|
|
COLORREF clr = dotColor; |
|
COLORREF clrSelected = dotColorSelected; |
|
|
|
drawHelper.DrawCircle( |
|
start->selected ? clrSelected : clr, |
|
x, y, |
|
start->selected ? dotSizeSelected : dotsize, |
|
true ); |
|
|
|
|
|
if ( !start->selected ) |
|
continue; |
|
|
|
if ( start->GetCurveType() == CURVE_DEFAULT ) |
|
continue; |
|
|
|
// Draw curve type indicator... |
|
char sz[ 128 ]; |
|
Q_snprintf( sz, sizeof( sz ), "%s", Interpolator_NameForCurveType( start->GetCurveType(), true ) ); |
|
RECT rc; |
|
int fontSize = 9; |
|
rc.top = clamp( y + 5, rcClient.top + 2, rcClient.bottom - 2 - fontSize ); |
|
rc.bottom = rc.top + fontSize + 1; |
|
rc.left = x - 75; |
|
rc.right = x + 175; |
|
drawHelper.DrawColoredText( "Arial", fontSize, 500, shadowColor, rc, sz ); |
|
} |
|
} |
|
} |
|
|
|
if ( track && track->IsComboType() && !IsCollapsed() ) |
|
{ |
|
RECT title = rcClient; |
|
title.left += 10; |
|
title.top += 14; |
|
title.bottom = title.top + 9; |
|
|
|
char sz[ 128 ]; |
|
|
|
if ( m_nEditType == 1 ) |
|
{ |
|
sprintf( sz, "left" ); |
|
|
|
drawHelper.DrawColoredText( "Arial", 9, 500, RGB( 0, 0, 255 ), title, sz ); |
|
|
|
sprintf( sz, "right" ); |
|
|
|
title.top = rcClient.bottom - 22; |
|
title.bottom = rcClient.bottom; |
|
|
|
drawHelper.DrawColoredText( "Arial", 9, 500, RGB( 0, 0, 255 ), title, sz ); |
|
} |
|
|
|
int mid = ( rcClient.top + rcClient.bottom ) / 2; |
|
|
|
title.top = mid - 10; |
|
title.bottom = mid; |
|
|
|
sprintf( sz, "editmode: <%s>", m_nEditType == 0 ? "amount" : "left/right" ); |
|
|
|
drawHelper.DrawColoredText( "Arial", 9, 500, RGB( 0, 0, 255 ), title, sz ); |
|
} |
|
|
|
if ( track ) |
|
{ |
|
RECT title = rcClient; |
|
title.left += 2; |
|
title.top += 2; |
|
title.bottom = title.top + 9; |
|
|
|
char const *name = track->GetFlexControllerName(); |
|
char sz[ 128 ]; |
|
|
|
if ( scount > 0 ) |
|
{ |
|
sprintf( sz, "{%i} - ", scount ); |
|
|
|
int len = drawHelper.CalcTextWidth( "Arial", 9, 500, sz ); |
|
drawHelper.DrawColoredText( "Arial", 9, 500, |
|
RGB( 120, 120, 0 ), title, sz ); |
|
|
|
title.left += len + 2; |
|
} |
|
|
|
sprintf( sz, "%s -", name ); |
|
|
|
int len = drawHelper.CalcTextWidth( "Arial", 9, 500, sz ); |
|
|
|
drawHelper.DrawColoredText( "Arial", 9, 500, |
|
active ? RGB( 0, 150, 100 ) : RGB( 100, 100, 100 ), |
|
title, sz ); |
|
|
|
sprintf( sz, "%s", IsActive() ? "enabled" : "disabled" ); |
|
|
|
title.left += len + 2; |
|
|
|
len = drawHelper.CalcTextWidth( "Arial", 9, 500, sz ); |
|
drawHelper.DrawColoredText( "Arial", 9, 500, |
|
active ? RGB( 0, 150, 100 ) : RGB( 100, 100, 100 ), |
|
title, sz ); |
|
|
|
if ( active ) |
|
{ |
|
title.left += len + 2; |
|
|
|
sprintf( sz, " <%i>", track->GetNumSamples( 0 ) ); |
|
|
|
len = drawHelper.CalcTextWidth( "Arial", 9, 500, sz ); |
|
drawHelper.DrawColoredText( "Arial", 9, 500, RGB( 220, 0, 00 ), title, sz ); |
|
} |
|
} |
|
} |
|
|
|
void TimelineItem::DrawAutoHighlight( mxEvent *event ) |
|
{ |
|
if ( IsCollapsed() ) |
|
return; |
|
|
|
CFlexAnimationTrack *track = GetSafeTrack(); |
|
if ( !track ) |
|
return; |
|
|
|
CExpressionSample *hover = GetSampleUnderMouse( event->x, event->y, 0.0f ); |
|
CChoreoWidgetDrawHelper drawHelper( m_pWorkspace, m_rcBounds, true ); |
|
|
|
RECT rcClient = m_rcBounds; |
|
|
|
// Draw left/right underneath amount so go backbard |
|
int type = m_nEditType; |
|
|
|
COLORREF dotColor = RGB( 0, 0, 255 ); |
|
COLORREF dotColorSelected = RGB( 240, 80, 20 ); |
|
COLORREF clrHighlighted = RGB( 0, 200, 0 ); |
|
|
|
int height = rcClient.bottom - rcClient.top; |
|
int bottom = rcClient.bottom; |
|
|
|
int dotsize = 6; |
|
int dotSizeSelected = 6; |
|
int dotSizeHighlighted = 6; |
|
|
|
COLORREF clr = dotColor; |
|
COLORREF clrSelected = dotColorSelected; |
|
COLORREF bgColor = RGB( 230, 230, 200 ); |
|
|
|
// Fixme, could look at 1st derivative and do more sampling at high rate of change? |
|
// or near actual sample points! |
|
for ( int sample = 0; sample < track->GetNumSamples( type ); sample++ ) |
|
{ |
|
bool dummy; |
|
CExpressionSample *start = track->GetBoundedSample( sample, dummy, type ); |
|
|
|
float value = start->value; |
|
bool clipped = false; |
|
int x = GetMouseForTime( start->time, &clipped ); |
|
if ( clipped ) |
|
continue; |
|
int y = bottom - value * height; |
|
|
|
if ( hover == start ) |
|
{ |
|
drawHelper.DrawCircle( |
|
bgColor, |
|
x, y, |
|
dotSizeHighlighted, |
|
true ); |
|
|
|
drawHelper.DrawCircle( |
|
clrHighlighted, |
|
x, y, |
|
dotSizeHighlighted, |
|
false ); |
|
|
|
|
|
} |
|
else |
|
{ |
|
drawHelper.DrawCircle( |
|
start->selected ? clrSelected : clr, |
|
x, y, |
|
start->selected ? dotSizeSelected : dotsize, |
|
true ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : drawHelper - |
|
//----------------------------------------------------------------------------- |
|
void TimelineItem::DrawRelativeTags( CChoreoWidgetDrawHelper& drawHelper ) |
|
{ |
|
CFlexAnimationTrack *track = GetSafeTrack(); |
|
if ( !track ) |
|
return; |
|
|
|
CChoreoEvent *event = track->GetEvent(); |
|
if ( !event ) |
|
return; |
|
|
|
float duration = event->GetDuration(); |
|
|
|
if ( duration <= 0.0f ) |
|
return; |
|
|
|
CChoreoScene *scene = g_pChoreoView->GetScene(); |
|
if ( !scene ) |
|
return; |
|
|
|
RECT rcClient = m_rcBounds;; |
|
//drawHelper.GetClientRect( rcClient ); |
|
|
|
// 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 = false; |
|
int tagx = GetMouseForTime( tag->GetStartTime() - event->GetStartTime(), &clipped ); |
|
if ( clipped ) |
|
continue; |
|
|
|
drawHelper.DrawColoredLine( RGB( 180, 180, 220 ), PS_SOLID, 1, tagx, rcClient.top, tagx, rcClient.bottom ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
for ( int t = 0; t < event->GetNumTimingTags(); t++ ) |
|
{ |
|
CFlexTimingTag *tag = event->GetTimingTag( t ); |
|
if ( !tag ) |
|
continue; |
|
|
|
bool clipped = false; |
|
int tagx = GetMouseForTime( tag->GetStartTime() - event->GetStartTime(), &clipped ); |
|
if ( clipped ) |
|
continue; |
|
|
|
// Draw relative tag marker |
|
drawHelper.DrawColoredLine( RGB( 220, 180, 180 ), PS_SOLID, 1, tagx, rcClient.top, tagx, rcClient.bottom ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *exp - |
|
// flexnum - |
|
//----------------------------------------------------------------------------- |
|
void TimelineItem::SetExpressionInfo( CFlexAnimationTrack *track, int flexnum ) |
|
{ |
|
m_szTrackName[ 0 ] = 0; |
|
if ( track ) |
|
{ |
|
V_strcpy_safe( m_szTrackName, track->GetFlexControllerName() ); |
|
SetActive( track->IsTrackActive() ); |
|
} |
|
|
|
m_nFlexNum = flexnum; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void TimelineItem::Copy( void ) |
|
{ |
|
if ( !g_pExpressionTool ) |
|
return; |
|
|
|
CFlexAnimationTrack *track = GetSafeTrack(); |
|
if ( !track ) |
|
return; |
|
|
|
g_pExpressionTool->Copy( track ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void TimelineItem::Paste( void ) |
|
{ |
|
if ( !g_pExpressionTool ) |
|
return; |
|
|
|
if ( !g_pExpressionTool->HasCopyData() ) |
|
return; |
|
|
|
CFlexAnimationTrack *track = GetSafeTrack(); |
|
if ( !track ) |
|
return; |
|
|
|
g_pExpressionTool->Paste( track ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void TimelineItem::Clear( bool preserveundo ) |
|
{ |
|
CFlexAnimationTrack *track = GetSafeTrack(); |
|
if ( !track ) |
|
return; |
|
|
|
if ( preserveundo ) |
|
{ |
|
PreDataChanged( "Clear" ); |
|
} |
|
|
|
track->Clear(); |
|
|
|
if ( preserveundo ) |
|
{ |
|
PostDataChanged( "Clear" ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : state - |
|
//----------------------------------------------------------------------------- |
|
void TimelineItem::SetCollapsed( bool state ) |
|
{ |
|
m_bCollapsed = state; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool TimelineItem::IsCollapsed( void ) const |
|
{ |
|
return m_bCollapsed; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
int TimelineItem::GetHeight( void ) |
|
{ |
|
return ( IsCollapsed() ? 12 : m_nCurrentHeight ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : state - |
|
//----------------------------------------------------------------------------- |
|
void TimelineItem::SetActive( bool state ) |
|
{ |
|
CFlexAnimationTrack *track = GetSafeTrack(); |
|
if ( !track ) |
|
return; |
|
|
|
track->SetTrackActive( state ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool TimelineItem::IsActive( void ) |
|
{ |
|
CFlexAnimationTrack *track = GetSafeTrack(); |
|
if ( !track ) |
|
return false; |
|
|
|
return track->IsTrackActive(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool TimelineItem::IsValid( void ) |
|
{ |
|
CFlexAnimationTrack *track = GetSafeTrack(); |
|
if ( !track ) |
|
return false; |
|
|
|
if ( track->GetNumSamples( 0 ) > 0 ) |
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : CFlexAnimationTrack |
|
//----------------------------------------------------------------------------- |
|
CFlexAnimationTrack *TimelineItem::GetSafeTrack( void ) |
|
{ |
|
if ( !g_pExpressionTool ) |
|
return NULL; |
|
|
|
CChoreoEvent *ev = g_pExpressionTool->GetSafeEvent(); |
|
if ( !ev ) |
|
return NULL; |
|
|
|
// Find track by name |
|
for ( int i = 0; i < ev->GetNumFlexAnimationTracks() ; i++ ) |
|
{ |
|
CFlexAnimationTrack *track = ev->GetFlexAnimationTrack( i ); |
|
if ( track && !stricmp( track->GetFlexControllerName(), m_szTrackName ) ) |
|
{ |
|
return track; |
|
} |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : type - |
|
//----------------------------------------------------------------------------- |
|
void TimelineItem::SetEditType( int type ) |
|
{ |
|
Assert( type == 0 || type == 1 ); |
|
|
|
CFlexAnimationTrack *track = GetSafeTrack(); |
|
if ( !track || !track->IsComboType() ) |
|
{ |
|
type = 0; |
|
} |
|
|
|
m_nEditType = type; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
int TimelineItem::GetEditType( void ) |
|
{ |
|
return m_nEditType; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void TimelineItem::SelectPoints( void ) |
|
{ |
|
RECT rcSelection; |
|
|
|
rcSelection.left = m_nStartX < m_nLastX ? m_nStartX : m_nLastX; |
|
rcSelection.right = m_nStartX < m_nLastX ? m_nLastX : m_nStartX; |
|
|
|
rcSelection.top = m_nStartY < m_nLastY ? m_nStartY : m_nLastY; |
|
rcSelection.bottom = m_nStartY < m_nLastY ? m_nLastY : m_nStartY; |
|
|
|
int selW = rcSelection.right - rcSelection.left; |
|
int selH = rcSelection.bottom - rcSelection.top; |
|
|
|
float tolerance = FP_TL_SELECTION_RECTANGLE_TOLERANCE; |
|
// If they are just clicking and releasing in one spot, capture any items w/in a larger tolerance |
|
if ( selW <= 2 && selH <= 2 ) |
|
{ |
|
tolerance = FP_TL_SELECTION_TOLERANCE; |
|
|
|
CExpressionSample *sample = GetSampleUnderMouse( rcSelection.left + selW * 0.5f, rcSelection.top + selH * 0.5f ); |
|
if ( sample ) |
|
{ |
|
sample->selected = true; |
|
return; |
|
} |
|
} |
|
else |
|
{ |
|
InflateRect( &rcSelection, 3, 3 ); |
|
} |
|
|
|
int width = m_rcBounds.right - m_rcBounds.left; |
|
int height = m_rcBounds.bottom - m_rcBounds.top; |
|
|
|
CFlexAnimationTrack *track = GetSafeTrack(); |
|
if ( !track || !width || !height ) |
|
return; |
|
|
|
CChoreoEvent *e = track->GetEvent(); |
|
Assert( e ); |
|
if ( !e ) |
|
return; |
|
|
|
float duration = e->GetDuration(); |
|
|
|
float fleft = (float)GetTimeForMouse( rcSelection.left + m_rcBounds.left ); |
|
float fright = (float)GetTimeForMouse( rcSelection.right + m_rcBounds.left ); |
|
|
|
//fleft *= duration; |
|
//fright *= duration; |
|
|
|
float ftop = (float)rcSelection.top / (float)height; |
|
float fbottom = (float)rcSelection.bottom / (float)height; |
|
|
|
fleft = clamp( fleft, 0.0f, duration ); |
|
fright = clamp( fright, 0.0f, duration ); |
|
ftop = clamp( ftop, 0.0f, 1.0f ); |
|
fbottom = clamp( fbottom, 0.0f, 1.0f ); |
|
|
|
float timestepperpixel = 1.0f / g_pExpressionTool->GetPixelsPerSecond(); |
|
float yfracstepperpixel = 1.0f / (float)height; |
|
|
|
float epsx = tolerance*timestepperpixel; |
|
float epsy = tolerance*yfracstepperpixel; |
|
|
|
for ( int i = 0; i < track->GetNumSamples( m_nEditType ); i++ ) |
|
{ |
|
CExpressionSample *sample = track->GetSample( i, m_nEditType ); |
|
|
|
if ( sample->time + epsx < fleft ) |
|
continue; |
|
|
|
if ( sample->time - epsx > fright ) |
|
continue; |
|
|
|
if ( (1.0f - sample->value ) + epsy < ftop ) |
|
continue; |
|
|
|
if ( (1.0f - sample->value ) - epsy > fbottom ) |
|
continue; |
|
|
|
sample->selected = true; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *undodescription - |
|
//----------------------------------------------------------------------------- |
|
void TimelineItem::PreDataChanged( char const *undodescription ) |
|
{ |
|
if ( m_nUndoSetup == 0 ) |
|
{ |
|
g_pChoreoView->SetDirty( true ); |
|
g_pChoreoView->PushUndo( undodescription ); |
|
} |
|
++m_nUndoSetup; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *redodescription - |
|
//----------------------------------------------------------------------------- |
|
void TimelineItem::PostDataChanged( char const *redodescription ) |
|
{ |
|
--m_nUndoSetup; |
|
if ( m_nUndoSetup == 0 ) |
|
{ |
|
g_pChoreoView->PushRedo( redodescription ); |
|
g_pExpressionTool->InvalidateLayout(); |
|
} |
|
} |
|
|
|
void TimelineItem::SetBounds( const RECT& rect ) |
|
{ |
|
m_rcBounds = rect; |
|
} |
|
|
|
void TimelineItem::GetBounds( RECT& rect ) |
|
{ |
|
rect = m_rcBounds; |
|
} |
|
|
|
void TimelineItem::SetVisible( bool vis ) |
|
{ |
|
m_bVisible = vis; |
|
} |
|
|
|
bool TimelineItem::GetVisible( void ) const |
|
{ |
|
return m_bVisible; |
|
} |
|
|
|
int TimelineItem::GetNumSelected( void ) |
|
{ |
|
return m_nNumSelected; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void TimelineItem::SnapAll() |
|
{ |
|
CFlexAnimationTrack *track = GetSafeTrack(); |
|
if ( !track ) |
|
return; |
|
|
|
for ( int t = 0; t < 2; t++ ) |
|
{ |
|
for ( int i = 0; i < track->GetNumSamples( t ); i++ ) |
|
{ |
|
CExpressionSample *sample = track->GetSample( i, t ); |
|
sample->time = FacePoser_SnapTime( sample->time ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void TimelineItem::SnapSelected() |
|
{ |
|
CFlexAnimationTrack *track = GetSafeTrack(); |
|
if ( !track ) |
|
return; |
|
|
|
for ( int t = 0; t < 2; t++ ) |
|
{ |
|
for ( int i = 0; i < track->GetNumSamples( t ); i++ ) |
|
{ |
|
CExpressionSample *sample = track->GetSample( i, t ); |
|
if ( !sample->selected ) |
|
continue; |
|
|
|
sample->time = FacePoser_SnapTime( sample->time ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : start - |
|
// end - |
|
//----------------------------------------------------------------------------- |
|
void TimelineItem::DeletePoints( float start, float end ) |
|
{ |
|
CFlexAnimationTrack *track = GetSafeTrack(); |
|
if ( !track ) |
|
return; |
|
|
|
for ( int t = 0; t < 2; t++ ) |
|
{ |
|
int num = track->GetNumSamples( t ); |
|
for ( int i = num - 1; i >= 0 ; i-- ) |
|
{ |
|
CExpressionSample *sample = track->GetSample( i, t ); |
|
if ( sample->time < start || sample->time > end ) |
|
continue; |
|
|
|
track->RemoveSample( i, t ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void TimelineItem::OnDoubleClicked() |
|
{ |
|
// Disabled for now by request of BillF |
|
CFlexAnimationTrack *track = GetSafeTrack(); |
|
if ( !track ) |
|
return; |
|
|
|
SetCollapsed( !IsCollapsed() ); |
|
g_pExpressionTool->LayoutItems( true ); |
|
} |
|
|
|
void TimelineItem::DrawEventEnd( CChoreoWidgetDrawHelper& drawHelper ) |
|
{ |
|
CFlexAnimationTrack *track = GetSafeTrack(); |
|
if ( !track ) |
|
return; |
|
|
|
CChoreoEvent *e = track->GetEvent(); |
|
if ( !e ) |
|
return; |
|
|
|
float duration = e->GetDuration(); |
|
if ( !duration ) |
|
return; |
|
|
|
int leftx = GetMouseForTime( duration ); |
|
if ( leftx > m_rcBounds.right ) |
|
return; |
|
|
|
drawHelper.DrawColoredLine( |
|
COLOR_CHOREO_ENDTIME, PS_SOLID, 1, |
|
leftx, m_rcBounds.top, leftx, m_rcBounds.bottom ); |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : helper - |
|
// handleRect - |
|
//----------------------------------------------------------------------------- |
|
void TimelineItem::DrawGrowHandle( CChoreoWidgetDrawHelper& helper, RECT& handleRect ) |
|
{ |
|
Assert(CanHaveGrowHandle()); |
|
|
|
RECT useRect = handleRect; |
|
helper.OffsetSubRect( useRect ); |
|
|
|
POINT region[4]; |
|
int cPoints = 4; |
|
|
|
region[ 0 ].x = useRect.left + GROW_HANDLE_INSETPIXELS; |
|
region[ 0 ].y = useRect.top; |
|
|
|
region[ 1 ].x = useRect.right - GROW_HANDLE_INSETPIXELS; |
|
region[ 1 ].y = useRect.top; |
|
|
|
region[ 2 ].x = useRect.right; |
|
region[ 2 ].y = useRect.bottom; |
|
|
|
region[ 3 ].x = useRect.left; |
|
region[ 3 ].y = useRect.bottom; |
|
|
|
HDC dc = helper.GrabDC(); |
|
|
|
HRGN rgn = CreatePolygonRgn( region, cPoints, ALTERNATE ); |
|
|
|
int oldPF = SetPolyFillMode( dc, ALTERNATE ); |
|
|
|
HBRUSH brBg = CreateSolidBrush( RGB( 150, 150, 150 ) ); |
|
HBRUSH brBorder = CreateSolidBrush( RGB( 200, 200, 200 ) ); |
|
|
|
FillRgn( dc, rgn, brBg ); |
|
FrameRgn( dc, rgn, brBorder, 1, 1 ); |
|
|
|
SetPolyFillMode( dc, oldPF ); |
|
|
|
DeleteObject( rgn ); |
|
|
|
DeleteObject( brBg ); |
|
DeleteObject( brBorder ); |
|
|
|
// draw a line in the middle |
|
int midy = ( handleRect.bottom + handleRect.top ) * 0.5f; |
|
int lineinset = GROW_HANDLE_INSETPIXELS *1.5; |
|
|
|
helper.DrawColoredLine( RGB( 63, 63, 63 ), PS_SOLID, 1, |
|
handleRect.left + lineinset, midy, |
|
handleRect.right - lineinset, midy ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : rc - |
|
//----------------------------------------------------------------------------- |
|
void TimelineItem::GetGrowHandleRect( RECT& rc ) |
|
{ |
|
rc = m_rcBounds; |
|
rc.bottom -= 1; |
|
rc.top = rc.bottom - GROW_HANDLE_HEIGHT; |
|
rc.left = ( rc.right + rc.left ) / 2 - GROW_HANDLE_WIDTH / 2; |
|
rc.right = rc.left + GROW_HANDLE_WIDTH; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool TimelineItem::CanHaveGrowHandle() |
|
{ |
|
if ( IsCollapsed() ) |
|
return false; |
|
|
|
if ( !g_pExpressionTool->IsFocusItem( this ) ) |
|
return false; |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : x - |
|
// y - |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool TimelineItem::IsMouseOverGrowHandle( int x, int y) |
|
{ |
|
if ( !CanHaveGrowHandle() ) |
|
return false; |
|
|
|
RECT rcGrowHandle; |
|
GetGrowHandleRect( rcGrowHandle ); |
|
|
|
POINT pt; |
|
pt.x = x + m_rcBounds.left; |
|
pt.y = y + m_rcBounds.top; |
|
|
|
return PtInRect( &rcGrowHandle, pt ) ? true : false; |
|
} |
|
|
|
void TimelineItem::DrawGrowRect() |
|
{ |
|
RECT rcFocus = m_rcBounds; |
|
rcFocus.bottom = m_rcBounds.top + m_nLastY; |
|
OffsetRect( &rcFocus, -m_rcBounds.left, -m_rcBounds.top ); |
|
|
|
POINT offset; |
|
offset.x = m_rcBounds.left; |
|
offset.y = m_rcBounds.top; |
|
ClientToScreen( (HWND)m_pWorkspace->getHandle(), &offset ); |
|
OffsetRect( &rcFocus, offset.x, offset.y ); |
|
|
|
HDC dc = GetDC( NULL ); |
|
|
|
::DrawFocusRect( dc, &rcFocus ); |
|
|
|
ReleaseDC( NULL, dc ); |
|
} |
|
|
|
void TimelineItem::GetWorkList( bool reflect, CUtlVector< TimelineItem * >& list ) |
|
{ |
|
if ( !reflect ) |
|
{ |
|
list.AddToTail( this ); |
|
} |
|
else |
|
{ |
|
g_pExpressionTool->GetTimelineItems( list ); |
|
} |
|
}
|
|
|