summaryrefslogtreecommitdiff
path: root/utils/hlfaceposer/choreoview.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /utils/hlfaceposer/choreoview.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'utils/hlfaceposer/choreoview.cpp')
-rw-r--r--utils/hlfaceposer/choreoview.cpp11647
1 files changed, 11647 insertions, 0 deletions
diff --git a/utils/hlfaceposer/choreoview.cpp b/utils/hlfaceposer/choreoview.cpp
new file mode 100644
index 0000000..440e69b
--- /dev/null
+++ b/utils/hlfaceposer/choreoview.cpp
@@ -0,0 +1,11647 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//===========================================================================//
+#include "cbase.h"
+#include <stdio.h>
+#include <mxtk/mxWindow.h>
+#include "mdlviewer.h"
+#include "hlfaceposer.h"
+#include "StudioModel.h"
+#include "expressions.h"
+#include "expclass.h"
+#include "ChoreoView.h"
+#include "choreoevent.h"
+#include "choreoactor.h"
+#include "choreochannel.h"
+#include "choreoscene.h"
+#include "choreowidget.h"
+#include "choreoactorwidget.h"
+#include "choreochannelwidget.h"
+#include "choreoglobaleventwidget.h"
+#include "choreowidgetdrawhelper.h"
+#include "choreoeventwidget.h"
+#include "viewerSettings.h"
+#include "filesystem.h"
+#include "choreoviewcolors.h"
+#include "ActorProperties.h"
+#include "ChannelProperties.h"
+#include "EventProperties.h"
+#include "GlobalEventProperties.h"
+#include "ifaceposersound.h"
+#include "snd_wave_source.h"
+#include "ifaceposerworkspace.h"
+#include "PhonemeEditor.h"
+#include "iscenetokenprocessor.h"
+#include "InputProperties.h"
+#include "filesystem.h"
+#include "ExpressionTool.h"
+#include "ControlPanel.h"
+#include "faceposer_models.h"
+#include "choiceproperties.h"
+#include "MatSysWin.h"
+#include "tier1/strtools.h"
+#include "GestureTool.h"
+#include "npcevent.h"
+#include "RampTool.h"
+#include "SceneRampTool.h"
+#include "KeyValues.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+#include "cclookup.h"
+#include "iclosecaptionmanager.h"
+#include "AddSoundEntry.h"
+#include "isoundcombiner.h"
+#include <vgui/ILocalize.h>
+#include "scriplib.h"
+#include "WaveBrowser.h"
+#include "filesystem_init.h"
+#include "flexpanel.h"
+#include "tier3/choreoutils.h"
+#include "tier2/p4helpers.h"
+
+
+using namespace vgui;
+
+extern vgui::ILocalize *g_pLocalize;
+
+// 10x magnification
+#define MAX_TIME_ZOOM 1000
+#define TIME_ZOOM_STEP 4
+
+#define PHONEME_FILTER 0.08f
+#define PHONEME_DELAY 0.0f
+
+#define SCRUBBER_HEIGHT 15
+#define TIMELINE_NUMBERS_HEIGHT 11
+
+#define COPYPASTE_FILENAME "scenes/copydatavcd.txt"
+
+extern double realtime;
+extern bool NameLessFunc( const char *const& name1, const char *const& name2 );
+
+// Try to keep shifted times at same absolute time
+static void RescaleExpressionTimes( CChoreoEvent *event, float newstart, float newend )
+{
+ if ( !event || event->GetType() != CChoreoEvent::FLEXANIMATION )
+ return;
+
+ // Did it actually change
+ if ( newstart == event->GetStartTime() &&
+ newend == event->GetEndTime() )
+ {
+ return;
+ }
+
+ float newduration = newend - newstart;
+
+ float dt = 0.0f;
+ //If the end is moving, leave tags stay where they are (dt == 0.0f)
+ if ( newstart != event->GetStartTime() )
+ {
+ // Otherwise, if the new start is later, then tags need to be shifted backwards
+ dt -= ( newstart - event->GetStartTime() );
+ }
+
+ int count = event->GetNumFlexAnimationTracks();
+ int i;
+
+ for ( i = 0; i < count; i++ )
+ {
+ CFlexAnimationTrack *track = event->GetFlexAnimationTrack( i );
+ if ( !track )
+ continue;
+
+ for ( int type = 0; type < 2; type++ )
+ {
+ int sampleCount = track->GetNumSamples( type );
+ for ( int sample = sampleCount - 1; sample >= 0 ; sample-- )
+ {
+ CExpressionSample *s = track->GetSample( sample, type );
+ if ( !s )
+ continue;
+
+ s->time += dt;
+
+ if ( s->time > newduration || s->time < 0.0f )
+ {
+ track->RemoveSample( sample, type );
+ }
+ }
+ }
+ }
+}
+
+static void RescaleRamp( CChoreoEvent *event, float newduration )
+{
+ float oldduration = event->GetDuration();
+
+ if ( fabs( oldduration - newduration ) < 0.000001f )
+ return;
+
+ if ( newduration <= 0.0f )
+ return;
+
+ float midpointtime = oldduration * 0.5f;
+ float newmidpointtime = newduration * 0.5f;
+
+ int count = event->GetRampCount();
+ int i;
+
+ for ( i = 0; i < count; i++ )
+ {
+ CExpressionSample *sample = event->GetRamp( i );
+ if ( !sample )
+ continue;
+
+ float t = sample->time;
+ if ( t < midpointtime )
+ continue;
+
+ float timefromend = oldduration - t;
+
+ // There's room to just shift it
+ if ( timefromend <= newmidpointtime )
+ {
+ t = newduration - timefromend;
+ }
+ else
+ {
+ // No room, rescale them instead
+ float frac = ( t - midpointtime ) / midpointtime;
+ t = newmidpointtime + frac * newmidpointtime;
+ }
+
+ sample->time = t;
+ }
+}
+
+bool DoesAnyActorHaveAssociatedModelLoaded( CChoreoScene *scene )
+{
+ if ( !scene )
+ return false;
+
+ int c = scene->GetNumActors();
+ int i;
+ for ( i = 0; i < c; i++ )
+ {
+ CChoreoActor *a = scene->GetActor( i );
+ if ( !a )
+ continue;
+
+ char const *modelname = a->GetFacePoserModelName();
+ if ( !modelname )
+ continue;
+
+ if ( !modelname[ 0 ] )
+ continue;
+
+ char mdlname[ 256 ];
+ Q_strncpy( mdlname, modelname, sizeof( mdlname ) );
+ Q_FixSlashes( mdlname );
+
+ int idx = models->FindModelByFilename( mdlname );
+ if ( idx >= 0 )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *a -
+// Output : StudioModel
+//-----------------------------------------------------------------------------
+StudioModel *FindAssociatedModel( CChoreoScene *scene, CChoreoActor *a )
+{
+ if ( !a || !scene )
+ return NULL;
+
+ Assert( models->GetActiveStudioModel() );
+
+ StudioModel *model = NULL;
+ if ( a->GetFacePoserModelName()[ 0 ] )
+ {
+ int idx = models->FindModelByFilename( a->GetFacePoserModelName() );
+ if ( idx >= 0 )
+ {
+ model = models->GetStudioModel( idx );
+ return model;
+ }
+ }
+
+ // Is there any loaded model with the actorname in it?
+ int c = models->Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ char const *modelname = models->GetModelName( i );
+ if ( !Q_stricmp( modelname, a->GetName() ) )
+ {
+ return models->GetStudioModel( i );
+ }
+ }
+
+ // Does any actor have an associated model which is loaded
+ if ( DoesAnyActorHaveAssociatedModelLoaded( scene ) )
+ {
+ // Then return NULL here so we don't override with the default an actor who has a valid model going
+ return NULL;
+ }
+
+ // Couldn't find it and nobody else has a loaded associated model, so just use the default model
+ if ( !model )
+ {
+ model = models->GetActiveStudioModel();
+ }
+ return model;
+}
+
+
+CChoreoView *g_pChoreoView = 0;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *parent -
+// x -
+// y -
+// w -
+// h -
+// id -
+//-----------------------------------------------------------------------------
+CChoreoView::CChoreoView( mxWindow *parent, int x, int y, int w, int h, int id )
+: IFacePoserToolWindow( "CChoreoView", "Choreography" ), mxWindow( parent, x, y, w, h )
+{
+ m_bRampOnly = false;
+
+ m_bForceProcess = false;
+
+ m_bSuppressLayout = true;
+
+ SetAutoProcess( true );
+
+ m_flLastMouseClickTime = -1.0f;
+ m_bProcessSequences = true;
+
+ m_flPlaybackRate = 1.0f;
+
+ m_pScene = NULL;
+
+ m_flScrub = 0.0f;
+ m_flScrubTarget = 0.0f;
+
+ m_bCanDraw = false;
+
+ m_bRedoPending = false;
+ m_nUndoLevel = 0;
+
+ CChoreoEventWidget::LoadImages();
+
+ CChoreoWidget::m_pView = this;
+
+ setId( id );
+
+ m_flLastSpeedScale = 0.0f;
+ m_bResetSpeedScale = false;
+
+ m_nTopOffset = 0;
+ m_flLeftOffset = 0.0f;
+ m_nLastHPixelsNeeded = -1;
+ m_nLastVPixelsNeeded = -1;
+
+
+ m_nStartRow = 45;
+ m_nLabelWidth = 140;
+ m_nRowHeight = 35;
+
+ m_bSimulating = false;
+ m_bPaused = false;
+
+ m_bForward = true;
+
+ m_flStartTime = 0.0f;
+ m_flEndTime = 0.0f;
+ m_flFrameTime = 0.0f;
+
+ m_bAutomated = false;
+ m_nAutomatedAction = SCENE_ACTION_UNKNOWN;
+ m_flAutomationDelay = 0.0f;
+ m_flAutomationTime = 0.0f;
+
+ m_pVertScrollBar = new mxScrollbar( this, 0, 0, 18, 100, IDC_CHOREOVSCROLL, mxScrollbar::Vertical );
+ m_pHorzScrollBar = new mxScrollbar( this, 0, 0, 18, 100, IDC_CHOREOHSCROLL, mxScrollbar::Horizontal );
+
+ m_bLayoutIsValid = false;
+ m_flPixelsPerSecond = 150.0f;
+
+ m_btnPlay = new mxBitmapButton( this, 2, 4, 16, 16, IDC_PLAYSCENE, "gfx/hlfaceposer/play.bmp" );
+ m_btnPause = new mxBitmapButton( this, 18, 4, 16, 16, IDC_PAUSESCENE, "gfx/hlfaceposer/pause.bmp" );
+ m_btnStop = new mxBitmapButton( this, 34, 4, 16, 16, IDC_STOPSCENE, "gfx/hlfaceposer/stop.bmp" );
+
+ m_pPlaybackRate = new mxSlider( this, 0, 0, 16, 16, IDC_CHOREO_PLAYBACKRATE );
+ m_pPlaybackRate->setRange( 0.0, 2.0, 40 );
+ m_pPlaybackRate->setValue( m_flPlaybackRate );
+
+ ShowButtons( false );
+
+ m_nFontSize = 12;
+
+ for ( int i = 0; i < MAX_ACTORS; i++ )
+ {
+ m_ActorExpanded[ i ].expanded = true;
+ }
+
+ SetChoreoFile( "" );
+
+ //SetFocus( (HWND)getHandle() );
+ if ( workspacefiles->GetNumStoredFiles( IWorkspaceFiles::CHOREODATA ) >= 1 )
+ {
+ LoadSceneFromFile( workspacefiles->GetStoredFile( IWorkspaceFiles::CHOREODATA, 0 ) );
+ }
+
+ ClearABPoints();
+
+ m_pClickedActor = NULL;
+ m_pClickedChannel = NULL;
+ m_pClickedEvent = NULL;
+ m_pClickedGlobalEvent = NULL;
+ m_nClickedX = 0;
+ m_nClickedY = 0;
+ m_nSelectedEvents = 0;
+ m_nClickedTag = -1;
+ m_nClickedChannelCloseCaptionButton = CChoreoChannelWidget::CLOSECAPTION_NONE;
+
+ // Mouse dragging
+ m_bDragging = false;
+ m_xStart = 0;
+ m_yStart = 0;
+ m_nDragType = DRAGTYPE_NONE;
+ m_hPrevCursor = 0;
+
+ m_nMinX = 0;
+ m_nMaxX = 0;
+ m_bUseBounds = false;
+
+ m_nScrollbarHeight = 12;
+ m_nInfoHeight = 30;
+
+ ClearStatusArea();
+
+ SetDirty( false );
+
+ m_bCanDraw = true;
+ m_bSuppressLayout = false;
+ m_flScrubberTimeOffset = 0.0f;
+
+ m_bShowCloseCaptionData = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : closing -
+//-----------------------------------------------------------------------------
+bool CChoreoView::Close( void )
+{
+ if ( m_pScene && m_bDirty )
+ {
+ int retval = mxMessageBox( NULL, va( "Save changes to scene '%s'?", GetChoreoFile() ), g_appTitle, MX_MB_YESNOCANCEL );
+ if ( retval == 2 )
+ {
+ return false;
+ }
+ if ( retval == 0 )
+ {
+ Save();
+ }
+ }
+
+ if ( m_pScene )
+ {
+ UnloadScene();
+ }
+ return true;
+}
+
+bool CChoreoView::CanClose()
+{
+ if ( m_pScene )
+ {
+ workspacefiles->StartStoringFiles( IWorkspaceFiles::CHOREODATA );
+ workspacefiles->StoreFile( IWorkspaceFiles::CHOREODATA, GetChoreoFile() );
+ workspacefiles->FinishStoringFiles( IWorkspaceFiles::CHOREODATA );
+ }
+
+ if ( m_pScene && m_bDirty && !Close() )
+ {
+ return false;
+ }
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called just before window is destroyed
+//-----------------------------------------------------------------------------
+void CChoreoView::OnDelete()
+{
+ if ( m_pScene )
+ {
+ UnloadScene();
+ }
+
+ CChoreoWidget::m_pView = NULL;
+
+ CChoreoEventWidget::DestroyImages();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CChoreoView::~CChoreoView()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::ReportSceneClearToTools( void )
+{
+ if ( m_pScene )
+ {
+ m_pScene->ResetSimulation();
+ }
+
+ g_pPhonemeEditor->ClearEvent();
+ g_pExpressionTool->LayoutItems( true );
+ g_pExpressionTool->redraw();
+ g_pGestureTool->redraw();
+ g_pRampTool->redraw();
+ g_pSceneRampTool->redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Find a time that's less than input on the granularity:
+// e.g., 3.01 granularity 0.05 will be 3.00, 3.05 will be 3.05
+// Input : input -
+// granularity -
+// Output : float
+//-----------------------------------------------------------------------------
+float SnapTime( float input, float granularity )
+{
+ float base = (float)(int)input;
+ float multiplier = (float)(int)( 1.0f / granularity );
+ float fracpart = input - (int)input;
+
+ fracpart *= multiplier;
+
+ fracpart = (float)(int)fracpart;
+ fracpart *= granularity;
+
+ return base + fracpart;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+// rc -
+// left -
+// right -
+//-----------------------------------------------------------------------------
+void CChoreoView::DrawTimeLine( CChoreoWidgetDrawHelper& drawHelper, RECT& rc, float left, float right )
+{
+ RECT rcFill = m_rcTimeLine;
+ rcFill.bottom -= TIMELINE_NUMBERS_HEIGHT;
+ drawHelper.DrawFilledRect( COLOR_CHOREO_DARKBACKGROUND, rcFill );
+
+ RECT rcLabel;
+ float granularity = 0.5f / ((float)GetTimeZoom( GetToolName() ) / 100.0f);
+
+ drawHelper.DrawColoredLine( COLOR_CHOREO_TIMELINE, PS_SOLID, 1, rc.left, GetStartRow() - 1, rc.right, GetStartRow() - 1 );
+
+ float f = SnapTime( left, granularity );
+ while ( f < right )
+ {
+ float frac = ( f - left ) / ( right - left );
+ if ( frac >= 0.0f && frac <= 1.0f )
+ {
+ rcLabel.left = GetLabelWidth() + (int)( frac * ( rc.right - GetLabelWidth() ) );
+ rcLabel.bottom = GetStartRow() - 1;
+ rcLabel.top = rcLabel.bottom - 10;
+
+ if ( f != left )
+ {
+ drawHelper.DrawColoredLine( RGB( 220, 220, 240 ), PS_DOT, 1,
+ rcLabel.left, GetStartRow(), rcLabel.left, h2() );
+ }
+
+ char sz[ 32 ];
+ sprintf( sz, "%.2f", f );
+
+ int textWidth = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, sz );
+
+ rcLabel.right = rcLabel.left + textWidth;
+
+ OffsetRect( &rcLabel, -textWidth / 2, 0 );
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, COLOR_CHOREO_TEXT, rcLabel, sz );
+
+ }
+ f += granularity;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CChoreoView::PaintBackground( void )
+{
+ redraw();
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+//-----------------------------------------------------------------------------
+void CChoreoView::DrawSceneABTicks( CChoreoWidgetDrawHelper& drawHelper )
+{
+ RECT rcThumb;
+
+ float scenestart = m_rgABPoints[ 0 ].active ? m_rgABPoints[ 0 ].time : 0.0f;
+ float sceneend = m_rgABPoints[ 1 ].active ? m_rgABPoints[ 1 ].time : 0.0f;
+
+ if ( scenestart )
+ {
+ int markerstart = GetPixelForTimeValue( scenestart );
+
+ rcThumb.left = markerstart - 4;
+ rcThumb.right = markerstart + 4;
+ rcThumb.top = 2 + GetCaptionHeight() + SCRUBBER_HEIGHT;
+ rcThumb.bottom = rcThumb.top + 8;
+
+ drawHelper.DrawTriangleMarker( rcThumb, COLOR_CHOREO_TICKAB );
+ }
+
+ if ( sceneend )
+ {
+ int markerend = GetPixelForTimeValue( sceneend );
+
+ rcThumb.left = markerend - 4;
+ rcThumb.right = markerend + 4;
+ rcThumb.top = 2 + GetCaptionHeight() + SCRUBBER_HEIGHT;
+ rcThumb.bottom = rcThumb.top + 8;
+
+ drawHelper.DrawTriangleMarker( rcThumb, COLOR_CHOREO_TICKAB );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+// rc -
+//-----------------------------------------------------------------------------
+void CChoreoView::DrawRelativeTagLines( CChoreoWidgetDrawHelper& drawHelper, RECT& rc )
+{
+ if ( !m_pScene )
+ return;
+
+ RECT rcClip;
+ GetClientRect( (HWND)getHandle(), &rcClip );
+ rcClip.top = GetStartRow();
+ rcClip.bottom -= ( m_nInfoHeight + m_nScrollbarHeight );
+ rcClip.right -= m_nScrollbarHeight;
+
+ drawHelper.StartClipping( rcClip );
+
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *a = m_SceneActors[ i ];
+ if ( !a )
+ continue;
+
+ for ( int j = 0; j < a->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *c = a->GetChannel( j );
+ if ( !c )
+ continue;
+
+ for ( int k = 0; k < c->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *e = c->GetEvent( k );
+ if ( !e )
+ continue;
+
+ CChoreoEvent *event = e->GetEvent();
+ if ( !event )
+ continue;
+
+ if ( !event->IsUsingRelativeTag() )
+ continue;
+
+ // Using it, find the tag and figure out the time for it
+ CEventRelativeTag *tag = m_pScene->FindTagByName(
+ event->GetRelativeWavName(),
+ event->GetRelativeTagName() );
+
+ if ( !tag )
+ continue;
+
+ // Found it, draw a vertical line
+ //
+ float tagtime = tag->GetStartTime();
+
+ // Convert to pixel value
+ bool clipped = false;
+ int pixel = GetPixelForTimeValue( tagtime, &clipped );
+ if ( clipped )
+ continue;
+
+ drawHelper.DrawColoredLine( RGB( 180, 180, 220 ), PS_SOLID, 1,
+ pixel, rcClip.top, pixel, rcClip.bottom );
+ }
+ }
+ }
+
+ drawHelper.StopClipping();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw the background markings (actor names, etc)
+// Input : drawHelper -
+// rc -
+//-----------------------------------------------------------------------------
+void CChoreoView::DrawBackground( CChoreoWidgetDrawHelper& drawHelper, RECT& rc )
+{
+ RECT rcClip;
+ GetClientRect( (HWND)getHandle(), &rcClip );
+ rcClip.top = GetStartRow();
+ rcClip.bottom -= ( m_nInfoHeight + m_nScrollbarHeight );
+ rcClip.right -= m_nScrollbarHeight;
+
+ int i;
+
+ for ( i = 0; i < m_SceneGlobalEvents.Size(); i++ )
+ {
+ CChoreoGlobalEventWidget *event = m_SceneGlobalEvents[ i ];
+ if ( event )
+ {
+ event->redraw( drawHelper );
+ }
+ }
+
+ drawHelper.StartClipping( rcClip );
+
+ for ( i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *actorW = m_SceneActors[ i ];
+ if ( !actorW )
+ continue;
+
+ actorW->redraw( drawHelper );
+ }
+
+ drawHelper.StopClipping();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::redraw()
+{
+ if ( !ToolCanDraw() )
+ return;
+
+ if ( m_bSuppressLayout )
+ return;
+
+ LayoutScene();
+
+ CChoreoWidgetDrawHelper drawHelper( this, COLOR_CHOREO_BACKGROUND );
+ HandleToolRedraw( drawHelper );
+
+ if ( !m_bCanDraw )
+ return;
+
+ RECT rc;
+ rc.left = 0;
+ rc.top = GetCaptionHeight();
+ rc.right = drawHelper.GetWidth();
+ rc.bottom = drawHelper.GetHeight();
+
+ RECT rcInfo;
+ rcInfo.left = rc.left;
+ rcInfo.right = rc.right - m_nScrollbarHeight;
+ rcInfo.bottom = rc.bottom - m_nScrollbarHeight;
+ rcInfo.top = rcInfo.bottom - m_nInfoHeight;
+
+ drawHelper.StartClipping( rcInfo );
+
+ RedrawStatusArea( drawHelper, rcInfo );
+
+ drawHelper.StopClipping();
+
+ RECT rcClip = rc;
+ rcClip.bottom -= ( m_nInfoHeight + m_nScrollbarHeight );
+
+ drawHelper.StartClipping( rcClip );
+
+ if ( !m_pScene )
+ {
+ char sz[ 256 ];
+ sprintf( sz, "No choreography scene file (.vcd) loaded" );
+
+ int pointsize = 18;
+ int textlen = drawHelper.CalcTextWidth( "Arial", pointsize, FW_NORMAL, sz );
+
+ RECT rcText;
+ rcText.top = ( rc.bottom - rc.top ) / 2 - pointsize / 2;
+ rcText.bottom = rcText.top + pointsize + 10;
+ rcText.left = rc.right / 2 - textlen / 2;
+ rcText.right = rcText.left + textlen;
+
+ drawHelper.DrawColoredText( "Arial", pointsize, FW_NORMAL, COLOR_CHOREO_LIGHTTEXT, rcText, sz );
+
+ drawHelper.StopClipping();
+ return;
+ }
+
+ DrawTimeLine( drawHelper, rc, m_flStartTime, m_flEndTime );
+
+ bool clipped = false;
+ int finishx = GetPixelForTimeValue( m_pScene->FindStopTime(), &clipped );
+ if ( !clipped )
+ {
+ drawHelper.DrawColoredLine( COLOR_CHOREO_ENDTIME, PS_DOT, 1, finishx, rc.top + GetStartRow(), finishx, rc.bottom );
+ }
+
+ DrawRelativeTagLines( drawHelper, rc );
+ DrawBackground( drawHelper, rc );
+
+ DrawSceneABTicks( drawHelper );
+
+ drawHelper.StopClipping();
+
+ if ( m_UndoStack.Size() > 0 )
+ {
+ int length = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL,
+ "undo %i/%i", m_nUndoLevel, m_UndoStack.Size() );
+ RECT rcText = rc;
+ rcText.top = rc.top + 48;
+ rcText.bottom = rcText.top + 10;
+ rcText.left = GetLabelWidth() - length - 20;
+ rcText.right = rcText.left + length;
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 100, 180, 100 ), rcText,
+ "undo %i/%i", m_nUndoLevel, m_UndoStack.Size() );
+ }
+
+ DrawScrubHandle( drawHelper );
+
+ char sz[ 48 ];
+ sprintf( sz, "Speed: %.2fx", m_flPlaybackRate );
+
+ int fontsize = 9;
+
+ int length = drawHelper.CalcTextWidth( "Arial", fontsize, FW_NORMAL, sz);
+
+ RECT rcText = rc;
+ rcText.top = rc.top + 25;
+ rcText.bottom = rcText.top + 10;
+ rcText.left = GetLabelWidth() + 20;
+ rcText.right = rcText.left + length;
+
+ drawHelper.DrawColoredText( "Arial", fontsize, FW_NORMAL,
+ RGB( 50, 50, 50 ), rcText, sz );
+
+ sprintf( sz, "Zoom: %.2fx", (float)GetTimeZoom( GetToolName() ) / 100.0f );
+
+ length = drawHelper.CalcTextWidth( "Arial", fontsize, FW_NORMAL, sz);
+
+ rcText = rc;
+ rcText.left = 5;
+ rcText.top = rc.top + 48;
+ rcText.bottom = rcText.top + 10;
+ rcText.right = rcText.left + length;
+
+ drawHelper.DrawColoredText( "Arial", fontsize, FW_NORMAL,
+ RGB( 50, 50, 50 ), rcText, sz );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : current -
+// number -
+// Output : int
+//-----------------------------------------------------------------------------
+void CChoreoView::GetUndoLevels( int& current, int& number )
+{
+ current = m_nUndoLevel;
+ number = m_UndoStack.Size();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : time -
+// *clipped -
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoView::GetPixelForTimeValue( float time, bool *clipped /*=NULL*/ )
+{
+ if ( clipped )
+ {
+ *clipped = false;
+ }
+
+ float frac = ( time - m_flStartTime ) / ( m_flEndTime - m_flStartTime );
+ if ( frac < 0.0 || frac > 1.0 )
+ {
+ if ( clipped )
+ {
+ *clipped = true;
+ }
+ }
+
+ int pixel = GetLabelWidth() + (int)( frac * ( w2() - GetLabelWidth() ) );
+ return pixel;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// clip -
+// Output : float
+//-----------------------------------------------------------------------------
+float CChoreoView::GetTimeValueForMouse( int mx, bool clip /*=false*/)
+{
+ RECT rc = m_rcTimeLine;
+ rc.left = GetLabelWidth();
+
+ if ( clip )
+ {
+ if ( mx < rc.left )
+ {
+ return m_flStartTime;
+ }
+ if ( mx > rc.right )
+ {
+ return m_flEndTime;
+ }
+ }
+
+ float frac = (float)( mx - rc.left ) / (float)( rc.right - rc.left );
+
+ return m_flStartTime + frac * ( m_flEndTime - m_flStartTime );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : time -
+//-----------------------------------------------------------------------------
+void CChoreoView::SetStartTime( float time )
+{
+ m_flStartTime = time;
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+float CChoreoView::GetStartTime( void )
+{
+ return m_flStartTime;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+float CChoreoView::GetEndTime( void )
+{
+ return m_flEndTime;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+float CChoreoView::GetPixelsPerSecond( void )
+{
+ return m_flPixelsPerSecond * (float)GetTimeZoom( GetToolName() ) / 100.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// origmx -
+// Output : float
+//-----------------------------------------------------------------------------
+float CChoreoView::GetTimeDeltaForMouseDelta( int mx, int origmx )
+{
+ float t1, t2;
+
+ t2 = GetTimeValueForMouse( mx );
+ t1 = GetTimeValueForMouse( origmx );
+
+ return t2 - t1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+//-----------------------------------------------------------------------------
+void CChoreoView::PlaceABPoint( int mx )
+{
+ m_rgABPoints[ ( m_nCurrentABPoint) & 0x01 ].time = GetTimeValueForMouse( mx );
+ m_rgABPoints[ ( m_nCurrentABPoint) & 0x01 ].active = true;
+ m_nCurrentABPoint++;
+
+ if ( m_rgABPoints[ 0 ].active && m_rgABPoints [ 1 ].active &&
+ m_rgABPoints[ 0 ].time > m_rgABPoints[ 1 ].time )
+ {
+ float temp = m_rgABPoints[ 0 ].time;
+ m_rgABPoints[ 0 ].time = m_rgABPoints[ 1 ].time;
+ m_rgABPoints[ 1 ].time = temp;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::ClearABPoints( void )
+{
+ memset( m_rgABPoints, 0, sizeof( m_rgABPoints ) );
+ m_nCurrentABPoint = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CChoreoView::IsMouseOverTimeline( int mx, int my )
+{
+ POINT pt;
+ pt.x = mx;
+ pt.y = my;
+
+ RECT rcCheck = m_rcTimeLine;
+ rcCheck.bottom -= TIMELINE_NUMBERS_HEIGHT;
+
+ if ( PtInRect( &rcCheck, pt ) )
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+//-----------------------------------------------------------------------------
+void CChoreoView::ShowContextMenu( int mx, int my )
+{
+ CChoreoActorWidget *a = NULL;
+ CChoreoChannelWidget *c = NULL;
+ CChoreoEventWidget *e = NULL;
+ CChoreoGlobalEventWidget *ge = NULL;
+ int ct = -1;
+ CEventAbsoluteTag *at = NULL;
+ int clickedCloseCaptionButton = CChoreoChannelWidget::CLOSECAPTION_NONE;
+
+ GetObjectsUnderMouse( mx, my, &a, &c, &e, &ge, &ct, &at, &clickedCloseCaptionButton );
+
+ m_pClickedActor = a;
+ m_pClickedChannel = c;
+ m_pClickedEvent = e;
+ m_pClickedGlobalEvent = ge;
+ m_nClickedX = mx;
+ m_nClickedY = my;
+ m_nClickedTag = ct;
+ m_pClickedAbsoluteTag = at;
+ m_nClickedChannelCloseCaptionButton = clickedCloseCaptionButton;
+
+ // Construct main
+ mxPopupMenu *pop = new mxPopupMenu();
+
+ if ( a && c )
+ {
+ if (!e)
+ {
+ pop->add( "Expression...", IDC_ADDEVENT_EXPRESSION );
+ pop->add( "WAV File...", IDC_ADDEVENT_SPEAK );
+ pop->add( "Gesture...", IDC_ADDEVENT_GESTURE );
+ pop->add( "NULL Gesture...", IDC_ADDEVENT_NULLGESTURE );
+ pop->add( "Look at actor...", IDC_ADDEVENT_LOOKAT );
+ pop->add( "Move to actor...", IDC_ADDEVENT_MOVETO );
+ pop->add( "Face actor...", IDC_ADDEVENT_FACE );
+ pop->add( "Fire Trigger...", IDC_ADDEVENT_FIRETRIGGER );
+ pop->add( "Generic(AI)...", IDC_ADDEVENT_GENERIC );
+ pop->add( "Sequence...", IDC_ADDEVENT_SEQUENCE );
+ pop->add( "Flex animation...", IDC_ADDEVENT_FLEXANIMATION );
+ pop->add( "Sub-scene...", IDC_ADDEVENT_SUBSCENE );
+ pop->add( "Interrupt...", IDC_ADDEVENT_INTERRUPT );
+ pop->add( "Permit Responses...", IDC_ADDEVENT_PERMITRESPONSES );
+
+ pop->addSeparator();
+ }
+ else
+ {
+ pop->add( va( "Edit Event '%s'...", e->GetEvent()->GetName() ), IDC_EDITEVENT );
+ switch ( e->GetEvent()->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ {
+ pop->add( va( "Edit Event '%s' in expression tool", e->GetEvent()->GetName() ), IDC_EXPRESSIONTOOL );
+ }
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ pop->add( va( "Edit Event '%s' in gesture tool", e->GetEvent()->GetName() ), IDC_GESTURETOOL );
+ }
+ break;
+ }
+
+ if ( e->GetEvent()->HasEndTime() )
+ {
+ pop->add( "Timing Tag...", IDC_ADDTIMINGTAG );
+ }
+
+ pop->addSeparator();
+ }
+ }
+
+ // Construct "New..."
+ mxPopupMenu *newMenu = new mxPopupMenu();
+ {
+ newMenu->add( "Actor...", IDC_ADDACTOR );
+ if ( a )
+ {
+ newMenu->add( "Channel...", IDC_ADDCHANNEL );
+ }
+ newMenu->add( "Section Pause...", IDC_ADDEVENT_PAUSE );
+ newMenu->add( "Loop...", IDC_ADDEVENT_LOOP );
+ newMenu->add( "Fire Completion...", IDC_ADDEVENT_STOPPOINT );
+ }
+ pop->addMenu( "New", newMenu );
+
+ // Now construct "Edit..."
+ if ( a || c || e || ge )
+ {
+ mxPopupMenu *editMenu = new mxPopupMenu();
+ {
+ if ( a )
+ {
+ editMenu->add( va( "Actor '%s'...", a->GetActor()->GetName() ), IDC_EDITACTOR );
+ }
+ if ( c )
+ {
+ editMenu->add( va( "Channel '%s'...", c->GetChannel()->GetName() ), IDC_EDITCHANNEL );
+ }
+ if ( ge )
+ {
+ switch ( ge->GetEvent()->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::SECTION:
+ {
+ editMenu->add( va( "Section Pause '%s'...", ge->GetEvent()->GetName() ), IDC_EDITGLOBALEVENT );
+ }
+ break;
+ case CChoreoEvent::LOOP:
+ {
+ editMenu->add( va( "Loop Point '%s'...", ge->GetEvent()->GetName() ), IDC_EDITGLOBALEVENT );
+ }
+ break;
+ case CChoreoEvent::STOPPOINT:
+ {
+ editMenu->add( va( "Fire Completion '%s'...", ge->GetEvent()->GetName() ), IDC_EDITGLOBALEVENT );
+ }
+ break;
+ }
+ }
+ }
+
+ pop->addMenu( "Edit", editMenu );
+
+ }
+
+ // Move up/down
+ if ( a || c )
+ {
+ mxPopupMenu *moveUpMenu = new mxPopupMenu();
+ mxPopupMenu *moveDownMenu = new mxPopupMenu();
+
+ if ( a )
+ {
+ moveUpMenu->add( va( "Move '%s' up", a->GetActor()->GetName() ), IDC_MOVEACTORUP );
+ moveDownMenu->add( va( "Move '%s' down", a->GetActor()->GetName() ), IDC_MOVEACTORDOWN );
+ }
+ if ( c )
+ {
+ moveUpMenu->add( va( "Move '%s' up", c->GetChannel()->GetName() ), IDC_MOVECHANNELUP );
+ moveDownMenu->add( va( "Move '%s' down", c->GetChannel()->GetName() ), IDC_MOVECHANNELDOWN );
+ }
+
+ pop->addMenu( "Move Up", moveUpMenu );
+ pop->addMenu( "Move Down", moveDownMenu );
+ }
+
+ // Delete
+ if ( a || c || e || ge || (ct != -1) )
+ {
+ mxPopupMenu *deleteMenu = new mxPopupMenu();
+ if ( a )
+ {
+ deleteMenu->add( va( "Actor '%s'", a->GetActor()->GetName() ), IDC_DELETEACTOR );
+ }
+ if ( c )
+ {
+ deleteMenu->add( va( "Channel '%s'", c->GetChannel()->GetName() ), IDC_DELETECHANNEL );
+ }
+ if ( e )
+ {
+ deleteMenu->add( va( "Event '%s'", e->GetEvent()->GetName() ), IDC_DELETEEVENT );
+ }
+ if ( ge )
+ {
+ switch ( ge->GetEvent()->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::SECTION:
+ {
+ deleteMenu->add( va( "Section Pause '%s'...", ge->GetEvent()->GetName() ), IDC_DELETEGLOBALEVENT );
+ }
+ break;
+ case CChoreoEvent::LOOP:
+ {
+ deleteMenu->add( va( "Loop Point '%s'...", ge->GetEvent()->GetName() ), IDC_DELETEGLOBALEVENT );
+ }
+ break;
+ case CChoreoEvent::STOPPOINT:
+ {
+ deleteMenu->add( va( "Fire Completion '%s'...", ge->GetEvent()->GetName() ), IDC_DELETEGLOBALEVENT );
+ }
+ break;
+ }
+ }
+ if ( e && ct != -1 )
+ {
+ CEventRelativeTag *tag = e->GetEvent()->GetRelativeTag( ct );
+ if ( tag )
+ {
+ deleteMenu->add( va( "Relative Tag '%s'...", tag->GetName() ), IDC_DELETERELATIVETAG );
+ }
+ }
+ pop->addMenu( "Delete", deleteMenu );
+ }
+
+ // Select
+ {
+ mxPopupMenu *selectMenu = new mxPopupMenu();
+ selectMenu->add( "Select All", IDC_SELECTALL );
+ selectMenu->add( "Deselect All", IDC_DESELECTALL );
+ selectMenu->addSeparator();
+
+ selectMenu->add( "All events before", IDC_SELECTEVENTS_ALL_BEFORE );
+ selectMenu->add( "All events after", IDC_SELECTEVENTS_ALL_AFTER );
+ selectMenu->add( "Active events before", IDC_SELECTEVENTS_ACTIVE_BEFORE );
+ selectMenu->add( "Active events after", IDC_SELECTEVENTS_ACTIVE_AFTER );
+ selectMenu->add( "Channel events before", IDC_SELECTEVENTS_CHANNEL_BEFORE );
+ selectMenu->add( "Channel events after", IDC_SELECTEVENTS_CHANNEL_AFTER );
+
+ if ( a || c )
+ {
+ selectMenu->addSeparator();
+ if ( a )
+ {
+ selectMenu->add( va( "All events in actor '%s'", a->GetActor()->GetName() ), IDC_CV_ALLEVENTS_ACTOR );
+ }
+ if ( c )
+ {
+ selectMenu->add( va( "All events in channel '%s'", c->GetChannel()->GetName() ), IDC_CV_ALLEVENTS_CHANNEL );
+ }
+ }
+ pop->addMenu( "Select/Deselect", selectMenu );
+ }
+
+ // Quick delete for events
+ if ( e )
+ {
+ pop->addSeparator();
+
+ switch ( e->GetEvent()->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ {
+ pop->add( va( "Edit event '%s' in expression tool", e->GetEvent()->GetName() ), IDC_EXPRESSIONTOOL );
+ }
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ pop->add( va( "Edit event '%s' in gesture tool", e->GetEvent()->GetName() ), IDC_GESTURETOOL );
+ }
+ break;
+ }
+
+ pop->add( va( "Move event '%s' to back", e->GetEvent()->GetName() ), IDC_MOVETOBACK );
+ if ( CountSelectedEvents() > 1 )
+ {
+ pop->add( va( "Delete events" ), IDC_DELETEEVENT );
+ pop->addSeparator();
+ pop->add( "Enable events", IDC_CV_ENABLEEVENTS );
+ pop->add( "Disable events", IDC_CV_DISABLEEVENTS );
+ }
+ else
+ {
+ pop->add( va( "Delete event '%s'", e->GetEvent()->GetName() ), IDC_DELETEEVENT );
+ pop->addSeparator();
+ if ( e->GetEvent()->GetActive() )
+ {
+ pop->add( va( "Disable event '%s'", e->GetEvent()->GetName() ), IDC_CV_DISABLEEVENTS );
+ }
+ else
+ {
+ pop->add( va( "Enable event '%s'", e->GetEvent()->GetName() ), IDC_CV_ENABLEEVENTS );
+ }
+ }
+ }
+
+ if ( m_rgABPoints[ 0 ].active && m_rgABPoints[ 1 ].active )
+ {
+ pop->addSeparator();
+ mxPopupMenu *timeMenu = new mxPopupMenu();
+ timeMenu->add( "Insert empty space between marks (shifts events right)", IDC_INSERT_TIME );
+ timeMenu->add( "Delete events between marks (shifts remaining events left)", IDC_DELETE_TIME );
+ pop->addMenu( "Time Marks", timeMenu );
+ }
+
+
+ // Copy/paste
+ if ( CanPaste() || e )
+ {
+ pop->addSeparator();
+
+ if ( CountSelectedEvents() > 1 )
+ {
+ pop->add( va( "Copy events to clipboard" ), IDC_COPYEVENTS );
+ }
+ else if ( e )
+ {
+ pop->add( va( "Copy event '%s' to clipboard", e->GetEvent()->GetName() ), IDC_COPYEVENTS );
+ }
+
+ if ( CanPaste() )
+ {
+ pop->add( va( "Paste events" ), IDC_PASTEEVENTS );
+ }
+ }
+
+ // Export / import
+ pop->addSeparator();
+
+ if ( e )
+ {
+ mxPopupMenu *exportMenu = new mxPopupMenu();
+ if ( CountSelectedEvents() > 1 )
+ {
+ exportMenu->add( va( "Export events to .vce..." ), IDC_EXPORTEVENTS );
+ }
+ else if ( e )
+ {
+ exportMenu->add( va( "Export event '%s' to .vce...", e->GetEvent()->GetName() ), IDC_EXPORTEVENTS );
+ }
+ exportMenu->add( va( "Export as .vcd..." ), IDC_EXPORT_VCD );
+ pop->addMenu( "Export", exportMenu );
+ }
+
+ mxPopupMenu *importMenu = new mxPopupMenu();
+ importMenu->add( va( "Import events from .vce..." ), IDC_IMPORTEVENTS );
+ importMenu->add( va( "Merge from .vcd..." ), IDC_IMPORT_VCD );
+ pop->addMenu( "Import", importMenu );
+
+ bool bShowAlignLeft = ( CountSelectedEvents() + CountSelectedGlobalEvents() ) > 1 ? true : false;
+
+ if ( e && ( ( CountSelectedEvents() > 1 ) || bShowAlignLeft ) )
+ {
+ pop->addSeparator();
+
+ mxPopupMenu *alignMenu = new mxPopupMenu();
+ alignMenu->add( "Align Left", IDC_CV_ALIGN_LEFT );
+ if ( CountSelectedEvents() > 1 )
+ {
+ alignMenu->add( "Align Right", IDC_CV_ALIGN_RIGHT );
+ alignMenu->add( "Size to Smallest", IDC_CV_SAMESIZE_SMALLEST );
+ alignMenu->add( "Size to Largest", IDC_CV_SAMESIZE_LARGEST );
+ }
+ pop->addMenu( "Align", alignMenu );
+ }
+
+ // Misc.
+ pop->addSeparator();
+ pop->add( va( "Change scale..." ), IDC_CV_CHANGESCALE );
+ pop->add( va( "Check sequences" ), IDC_CV_CHECKSEQLENGTHS );
+ pop->add( va( "Process sequences" ), IDC_CV_PROCESSSEQUENCES );
+ pop->add( va( m_bRampOnly ? "Ramp normal" : "Ramp only" ), IDC_CV_TOGGLERAMPONLY );
+ pop->setChecked( IDC_CV_PROCESSSEQUENCES, m_bProcessSequences );
+
+ bool onmaster= ( m_pClickedChannel &&
+ m_pClickedChannel->GetCaptionClickedEvent() &&
+ m_pClickedChannel->GetCaptionClickedEvent()->GetCloseCaptionType() == CChoreoEvent::CC_MASTER ) ? true : false;
+ bool ondisabled = ( m_pClickedChannel &&
+ m_pClickedChannel->GetCaptionClickedEvent() &&
+ m_pClickedChannel->GetCaptionClickedEvent()->GetCloseCaptionType() == CChoreoEvent::CC_DISABLED ) ? true : false;
+
+ // The close captioning menu
+ if ( m_bShowCloseCaptionData && ( AreSelectedEventsCombinable() || AreSelectedEventsInSpeakGroup() || onmaster || ondisabled ) )
+ {
+ pop->addSeparator();
+ if ( AreSelectedEventsCombinable() )
+ {
+ pop->add( "Combine Speak Events", IDC_CV_COMBINESPEAKEVENTS );
+ }
+ if ( AreSelectedEventsInSpeakGroup() )
+ {
+ pop->add( "Uncombine Speak Events", IDC_CV_REMOVESPEAKEVENTFROMGROUP );
+ }
+ if ( onmaster )
+ {
+ // Can only change tokens for "combined" files
+ if ( m_pClickedChannel->GetCaptionClickedEvent()->GetNumSlaves() >= 1 )
+ {
+ pop->add( "Change Token", IDC_CV_CHANGECLOSECAPTIONTOKEN );
+ }
+ pop->add( "Disable captions", IDC_CV_TOGGLECLOSECAPTIONS );
+ }
+ if ( ondisabled )
+ {
+ pop->add( "Enable captions", IDC_CV_TOGGLECLOSECAPTIONS );
+ }
+ }
+
+ // Undo/redo
+ if ( CanUndo() || CanRedo() )
+ {
+
+ pop->addSeparator();
+
+ if ( CanUndo() )
+ {
+ pop->add( va( "Undo %s", GetUndoDescription() ), IDC_CVUNDO );
+ }
+ if ( CanRedo() )
+ {
+ pop->add( va( "Redo %s", GetRedoDescription() ), IDC_CVREDO );
+ }
+ }
+
+ if ( m_pScene )
+ {
+ // Associate map file
+ pop->addSeparator();
+ pop->add( va( "Associate .bsp (%s)", m_pScene->GetMapname() ), IDC_ASSOCIATEBSP );
+ if ( a )
+ {
+ if ( a->GetActor() && a->GetActor()->GetFacePoserModelName()[0] )
+ {
+ pop->add( va( "Change .mdl for %s", a->GetActor()->GetName() ), IDC_ASSOCIATEMODEL );
+ }
+ else
+ {
+ pop->add( va( "Associate .mdl with %s", a->GetActor()->GetName() ), IDC_ASSOCIATEMODEL );
+ }
+ }
+ }
+
+ pop->popup( this, mx, my );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::AssociateModel( void )
+{
+ if ( !m_pScene )
+ return;
+
+ CChoreoActorWidget *actor = m_pClickedActor;
+ if ( !actor )
+ return;
+
+ CChoreoActor *a = actor->GetActor();
+ if ( !a )
+ return;
+
+ CChoiceParams params;
+ strcpy( params.m_szDialogTitle, "Associate Model" );
+
+ params.m_bPositionDialog = false;
+ params.m_nLeft = 0;
+ params.m_nTop = 0;
+ strcpy( params.m_szPrompt, "Choose model:" );
+
+ params.m_Choices.RemoveAll();
+
+ params.m_nSelected = -1;
+ int oldsel = -1;
+
+ int c = models->Count();
+ ChoiceText text;
+ for ( int i = 0; i < c; i++ )
+ {
+ char const *modelname = models->GetModelName( i );
+
+ strcpy( text.choice, modelname );
+
+ if ( !stricmp( a->GetName(), modelname ) )
+ {
+ params.m_nSelected = i;
+ oldsel = -1;
+ }
+
+ params.m_Choices.AddToTail( text );
+ }
+
+ // Add an extra entry which is "No association"
+ strcpy( text.choice, "No Associated Model" );
+ params.m_Choices.AddToTail( text );
+
+ if ( !ChoiceProperties( &params ) )
+ return;
+
+ if ( params.m_nSelected == oldsel )
+ return;
+
+ // Chose something new...
+ if ( params.m_nSelected >= 0 &&
+ params.m_nSelected < params.m_Choices.Count() )
+ {
+ AssociateModelToActor( a, params.m_nSelected );
+ }
+ else
+ {
+ // Chose "No association"
+ AssociateModelToActor( a, -1 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+// modelindex -
+//-----------------------------------------------------------------------------
+void CChoreoView::AssociateModelToActor( CChoreoActor *actor, int modelindex )
+{
+ Assert( actor );
+
+ SetDirty( true );
+
+ PushUndo( "Associate model" );
+
+ // Chose something new...
+ if ( modelindex >= 0 &&
+ modelindex < models->Count() )
+ {
+ actor->SetFacePoserModelName( models->GetModelFileName( modelindex ) );
+ }
+ else
+ {
+ // Chose "No Associated Model"
+ actor->SetFacePoserModelName( "" );
+ }
+
+ RecomputeWaves();
+
+ PushRedo( "Associate model" );
+}
+
+void CChoreoView::AssociateBSP( void )
+{
+ if ( !m_pScene )
+ return;
+
+ // Strip game directory and slash
+ char mapname[ 512 ];
+ if ( !FacePoser_ShowOpenFileNameDialog( mapname, sizeof( mapname ), "maps", "*.bsp" ) )
+ {
+ return;
+ }
+
+ m_pScene->SetMapname( mapname );
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::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 );
+}
+
+int CChoreoView::GetSelectedEventWidgets( CUtlVector< CChoreoEventWidget * >& events )
+{
+ events.RemoveAll();
+
+ int c = 0;
+
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *actor = m_SceneActors[ i ];
+ if ( !actor )
+ continue;
+
+ for ( int j = 0; j < actor->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *channel = actor->GetChannel( j );
+ if ( !channel )
+ continue;
+
+ for ( int k = 0; k < channel->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *event = channel->GetEvent( k );
+ if ( !event )
+ continue;
+
+ if ( event->IsSelected() )
+ {
+ events.AddToTail( event );
+ c++;
+ }
+ }
+ }
+ }
+
+ return c;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : events -
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoView::GetSelectedEvents( CUtlVector< CChoreoEvent * >& events )
+{
+ events.RemoveAll();
+
+ int c = 0;
+
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *actor = m_SceneActors[ i ];
+ if ( !actor )
+ continue;
+
+ for ( int j = 0; j < actor->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *channel = actor->GetChannel( j );
+ if ( !channel )
+ continue;
+
+ for ( int k = 0; k < channel->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *event = channel->GetEvent( k );
+ if ( !event )
+ continue;
+
+ if ( event->IsSelected() )
+ {
+ events.AddToTail( event->GetEvent() );
+ c++;
+ }
+ }
+ }
+ }
+
+ return c;
+}
+
+int CChoreoView::CountSelectedGlobalEvents( void )
+{
+ int c = 0;
+ for ( int i = 0; i < m_SceneGlobalEvents.Size(); i++ )
+ {
+ CChoreoGlobalEventWidget *event = m_SceneGlobalEvents[ i ];
+ if ( !event || !event->IsSelected() )
+ continue;
+
+ ++c;
+ }
+ return c;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoView::CountSelectedEvents( void )
+{
+ int c = 0;
+
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *actor = m_SceneActors[ i ];
+ if ( !actor )
+ continue;
+
+ for ( int j = 0; j < actor->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *channel = actor->GetChannel( j );
+ if ( !channel )
+ continue;
+
+ for ( int k = 0; k < channel->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *event = channel->GetEvent( k );
+ if ( !event )
+ continue;
+
+ if ( event->IsSelected() )
+ c++;
+
+ }
+ }
+ }
+
+ return c;
+}
+
+bool CChoreoView::IsMouseOverEvent( CChoreoEventWidget *ew, int mx, int my )
+{
+ int tolerance = DRAG_EVENT_EDGE_TOLERANCE;
+
+ RECT bounds = ew->getBounds();
+ mx -= bounds.left;
+ my -= bounds.top;
+
+ if ( mx <= -tolerance )
+ {
+ return false;
+ }
+
+ CChoreoEvent *event = ew->GetEvent();
+ if ( event )
+ {
+ if ( event->HasEndTime() )
+ {
+ int rightside = ew->GetDurationRightEdge() ? ew->GetDurationRightEdge() : ew->w();
+
+ if ( mx > rightside + tolerance )
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+bool CChoreoView::IsMouseOverEventEdge( CChoreoEventWidget *ew, bool bLeftEdge, int mx, int my )
+{
+ int tolerance = DRAG_EVENT_EDGE_TOLERANCE;
+
+ RECT bounds = ew->getBounds();
+ mx -= bounds.left;
+ my -= bounds.top;
+
+ CChoreoEvent *event = ew->GetEvent();
+ if ( event && event->HasEndTime() )
+ {
+ if ( mx > -tolerance && mx <= tolerance )
+ {
+ return bLeftEdge;
+ }
+
+ int rightside = ew->GetDurationRightEdge() ? ew->GetDurationRightEdge() : ew->w();
+
+ if ( mx >= rightside - tolerance )
+ {
+ if ( mx > rightside + tolerance )
+ {
+ return false;
+ }
+ else
+ {
+ return !bLeftEdge;
+ }
+ }
+ }
+
+ return false;
+}
+
+int CChoreoView::GetEarliestEventIndex( CUtlVector< CChoreoEventWidget * >& events )
+{
+ int best = -1;
+ float minTime = FLT_MAX;
+
+ int c = events.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ CChoreoEvent *e = events[ i ]->GetEvent();
+ float t = e->GetStartTime();
+ if ( t < minTime )
+ {
+ minTime = t;
+ best = i;
+ }
+ }
+
+ return best;
+}
+
+int CChoreoView::GetLatestEventIndex( CUtlVector< CChoreoEventWidget * >& events )
+{
+ int best = -1;
+ float maxTime = FLT_MIN;
+
+ int c = events.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ CChoreoEvent *e = events[ i ]->GetEvent();
+ float t = e->GetEndTime();
+ if ( t > maxTime )
+ {
+ maxTime = t;
+ best = i;
+ }
+ }
+
+ return best;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoView::ComputeEventDragType( int mx, int my )
+{
+ int tolerance = DRAG_EVENT_EDGE_TOLERANCE;
+
+ // Iterate the events and see who's closest
+ CChoreoEventWidget *ew = GetEventUnderCursorPos( mx, my );
+ if ( !ew )
+ {
+ return DRAGTYPE_NONE;
+ }
+
+ // Deal with small windows by lowering tolerance
+ if ( ew->w() < 4 * tolerance )
+ {
+ tolerance = 2;
+ }
+
+ int tagnum = GetTagUnderCursorPos( ew, mx, my );
+ if ( tagnum != -1 && CountSelectedEvents() <= 1 )
+ {
+ return DRAGTYPE_EVENTTAG_MOVE;
+ }
+
+ CEventAbsoluteTag *tag = GetAbsoluteTagUnderCursorPos( ew, mx, my );
+ if ( tag != NULL && CountSelectedEvents() <= 1 )
+ {
+ return DRAGTYPE_EVENTABSTAG_MOVE;
+ }
+
+ if ( CountSelectedEvents() > 1 )
+ {
+ CUtlVector< CChoreoEventWidget * > events;
+ GetSelectedEventWidgets( events );
+
+ int iStart, iEnd;
+ iStart = GetEarliestEventIndex( events );
+ iEnd = GetLatestEventIndex( events );
+
+ if ( events.IsValidIndex( iStart ) )
+ {
+ // See if mouse is over left edge of starting event
+ if ( IsMouseOverEventEdge( events[ iStart ], true, mx, my ) )
+ {
+ return DRAGTYPE_RESCALELEFT;
+ }
+ }
+ if ( events.IsValidIndex( iEnd ) )
+ {
+ if ( IsMouseOverEventEdge( events[ iEnd ], false, mx, my ) )
+ {
+ return DRAGTYPE_RESCALERIGHT;
+ }
+ }
+
+ return DRAGTYPE_EVENT_MOVE;
+ }
+
+ CChoreoEvent *event = ew->GetEvent();
+ if ( event )
+ {
+ if ( event->IsFixedLength() || !event->HasEndTime() )
+ {
+ return DRAGTYPE_EVENT_MOVE;
+ }
+ }
+
+ if ( IsMouseOverEventEdge( ew, true, mx, my ) )
+ {
+ if ( GetAsyncKeyState( VK_SHIFT ) )
+ return DRAGTYPE_EVENT_STARTTIME_RESCALE;
+ return DRAGTYPE_EVENT_STARTTIME;
+ }
+
+ if ( IsMouseOverEventEdge( ew, false, mx, my ) )
+ {
+ if ( GetAsyncKeyState( VK_SHIFT ) )
+ return DRAGTYPE_EVENT_ENDTIME_RESCALE;
+ return DRAGTYPE_EVENT_ENDTIME;
+ }
+
+ if ( IsMouseOverEvent( ew, mx, my ) )
+ {
+ return DRAGTYPE_EVENT_MOVE;
+ }
+
+ return DRAGTYPE_NONE;
+}
+
+void CChoreoView::StartDraggingSceneEndTime( int mx, int my )
+{
+ m_nDragType = DRAGTYPE_SCENE_ENDTIME;
+
+ m_FocusRects.Purge();
+
+ RECT rcFocus;
+ rcFocus.left = mx;
+ rcFocus.top = 0;
+ rcFocus.bottom = h2();
+ rcFocus.right = rcFocus.left + 2;
+
+ POINT offset;
+ offset.x = 0;
+ offset.y = 0;
+ ClientToScreen( (HWND)getHandle(), &offset );
+ OffsetRect( &rcFocus, offset.x, offset.y );
+
+ CFocusRect fr;
+ fr.m_rcFocus = rcFocus;
+ fr.m_rcOrig = rcFocus;
+
+ // Relative tag events don't move
+ m_FocusRects.AddToTail( fr );
+
+ m_xStart = mx;
+ m_yStart = my;
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
+
+ DrawFocusRect();
+
+ m_bDragging = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::StartDraggingEvent( int mx, int my )
+{
+ m_nDragType = ComputeEventDragType( mx, my );
+ if ( m_nDragType == DRAGTYPE_NONE )
+ {
+ if( m_pClickedGlobalEvent )
+ {
+ m_nDragType = DRAGTYPE_EVENT_MOVE;
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ m_FocusRects.Purge();
+
+ // Go through all selected events
+ RECT rcFocus;
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *actor = m_SceneActors[ i ];
+ if ( !actor )
+ continue;
+
+ for ( int j = 0; j < actor->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *channel = actor->GetChannel( j );
+ if ( !channel )
+ continue;
+
+ for ( int k = 0; k < channel->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *event = channel->GetEvent( k );
+ if ( !event )
+ continue;
+
+ if ( !event->IsSelected() )
+ continue;
+
+ if ( event == m_pClickedEvent &&
+ ( m_nClickedTag != -1 || m_pClickedAbsoluteTag ) )
+ {
+ int leftEdge = 0;
+ int tagWidth = 1;
+ if ( !m_pClickedAbsoluteTag )
+ {
+ CEventRelativeTag *tag = event->GetEvent()->GetRelativeTag( m_nClickedTag );
+ if ( tag )
+ {
+ // Determine left edcge
+ RECT bounds;
+ bounds = event->getBounds();
+ if ( bounds.right - bounds.left > 0 )
+ {
+ leftEdge = (int)( tag->GetPercentage() * (float)( bounds.right - bounds.left ) + 0.5f );
+ }
+ }
+ }
+ else
+ {
+ // Determine left edcge
+ RECT bounds;
+ bounds = event->getBounds();
+ if ( bounds.right - bounds.left > 0 )
+ {
+ leftEdge = (int)( m_pClickedAbsoluteTag->GetPercentage() * (float)( bounds.right - bounds.left ) + 0.5f );
+ }
+ }
+
+ rcFocus.left = event->x() + leftEdge - tagWidth;
+ rcFocus.top = event->y() - tagWidth;
+ rcFocus.right = rcFocus.left + 2 * tagWidth;
+ rcFocus.bottom = event->y() + event->h();
+ }
+ else
+ {
+ rcFocus.left = event->x();
+ rcFocus.top = event->y();
+ if ( event->GetDurationRightEdge() )
+ {
+ rcFocus.right = event->x() + event->GetDurationRightEdge();
+ }
+ else
+ {
+ rcFocus.right = rcFocus.left + event->w();
+ }
+ rcFocus.bottom = rcFocus.top + event->h();
+ }
+
+ POINT offset;
+ offset.x = 0;
+ offset.y = 0;
+ ClientToScreen( (HWND)getHandle(), &offset );
+ OffsetRect( &rcFocus, offset.x, offset.y );
+
+ CFocusRect fr;
+ fr.m_rcFocus = rcFocus;
+ fr.m_rcOrig = rcFocus;
+
+ // Relative tag events don't move
+ m_FocusRects.AddToTail( fr );
+ }
+ }
+ }
+
+ for ( int i = 0; i < m_SceneGlobalEvents.Count(); i++ )
+ {
+ CChoreoGlobalEventWidget *gew = m_SceneGlobalEvents[ i ];
+ if ( !gew )
+ continue;
+
+ if ( !gew->IsSelected() )
+ continue;
+
+ rcFocus.left = gew->x() + gew->w() / 2;
+ rcFocus.top = 0;
+ rcFocus.right = rcFocus.left + 2;
+ rcFocus.bottom = h2();
+
+ POINT offset;
+ offset.x = 0;
+ offset.y = 0;
+ ClientToScreen( (HWND)getHandle(), &offset );
+ OffsetRect( &rcFocus, offset.x, offset.y );
+
+ CFocusRect fr;
+ fr.m_rcFocus = rcFocus;
+ fr.m_rcOrig = rcFocus;
+
+ m_FocusRects.AddToTail( fr );
+ }
+
+ m_xStart = mx;
+ m_yStart = my;
+ m_hPrevCursor = NULL;
+ switch ( m_nDragType )
+ {
+ default:
+ break;
+ case DRAGTYPE_EVENTTAG_MOVE:
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
+ break;
+ case DRAGTYPE_EVENTABSTAG_MOVE:
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_IBEAM ) );
+ break;
+ case DRAGTYPE_EVENT_MOVE:
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEALL ) );
+ break;
+ case DRAGTYPE_EVENT_STARTTIME:
+ case DRAGTYPE_EVENT_STARTTIME_RESCALE:
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
+ break;
+ case DRAGTYPE_EVENT_ENDTIME:
+ case DRAGTYPE_EVENT_ENDTIME_RESCALE:
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
+ break;
+ case DRAGTYPE_RESCALELEFT:
+ case DRAGTYPE_RESCALERIGHT:
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
+ break;
+ }
+
+ DrawFocusRect();
+
+ m_bDragging = true;
+}
+
+bool CChoreoView::IsMouseOverSceneEndTime( int mx )
+{
+ // See if mouse if over scene end time instead
+ if ( m_pScene )
+ {
+ float endtime = m_pScene->FindStopTime();
+
+ bool clip = false;
+ int lastpixel = GetPixelForTimeValue( endtime, &clip );
+ if ( !clip )
+ {
+ if ( abs( mx - lastpixel ) < DRAG_EVENT_EDGE_TOLERANCE )
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+//-----------------------------------------------------------------------------
+void CChoreoView::MouseStartDrag( mxEvent *event, int mx, int my )
+{
+ bool isrightbutton = event->buttons & mxEvent::MouseRightButton ? true : false;
+
+ if ( m_bDragging )
+ {
+ return;
+ }
+
+ GetObjectsUnderMouse( mx, my, &m_pClickedActor, &m_pClickedChannel, &m_pClickedEvent, &m_pClickedGlobalEvent, &m_nClickedTag, &m_pClickedAbsoluteTag, &m_nClickedChannelCloseCaptionButton );
+
+ if ( m_pClickedEvent )
+ {
+ CChoreoEvent *e = m_pClickedEvent->GetEvent();
+ Assert( e );
+
+ int dtPreview = ComputeEventDragType( mx, my );
+ // Shift clicking on exact edge shouldn't toggle selection state
+ bool bIsEdgeRescale = ( dtPreview == DRAGTYPE_EVENT_ENDTIME_RESCALE || dtPreview == DRAGTYPE_EVENT_STARTTIME_RESCALE );
+
+ if ( !( event->modifiers & ( mxEvent::KeyCtrl | mxEvent::KeyShift ) ) )
+ {
+ if ( !m_pClickedEvent->IsSelected() )
+ {
+ DeselectAll();
+ }
+ TraverseWidgets( &CChoreoView::Select, m_pClickedEvent );
+ }
+ else if ( !bIsEdgeRescale )
+ {
+ m_pClickedEvent->SetSelected( !m_pClickedEvent->IsSelected() );
+ }
+
+ switch ( m_pClickedEvent->GetEvent()->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ {
+ g_pExpressionTool->SetEvent( e );
+ g_pFlexPanel->SetEvent( e );
+ }
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ g_pGestureTool->SetEvent( e );
+ }
+ break;
+ case CChoreoEvent::SPEAK:
+ {
+ g_pWaveBrowser->SetEvent( e );
+ }
+ break;
+ }
+
+ if ( e->HasEndTime() )
+ {
+ g_pRampTool->SetEvent( e );
+ }
+
+ redraw();
+ StartDraggingEvent( mx, my );
+ }
+ else if ( m_pClickedGlobalEvent )
+ {
+ if ( !( event->modifiers & ( mxEvent::KeyCtrl | mxEvent::KeyShift ) ) )
+ {
+ if ( !m_pClickedGlobalEvent->IsSelected() )
+ {
+ DeselectAll();
+ }
+ TraverseWidgets( &CChoreoView::Select, m_pClickedGlobalEvent );
+ }
+ else
+ {
+ m_pClickedGlobalEvent->SetSelected( !m_pClickedGlobalEvent->IsSelected() );
+ }
+
+ redraw();
+ StartDraggingEvent( mx, my );
+ }
+ else if ( IsMouseOverScrubArea( event ) )
+ {
+ if ( IsMouseOverScrubHandle( event ) )
+ {
+ m_nDragType = DRAGTYPE_SCRUBBER;
+
+ m_bDragging = true;
+
+ 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;
+
+ ClampTimeToSelectionInterval( t );
+
+ SetScrubTime( t );
+ SetScrubTargetTime( t );
+
+ redraw();
+
+ RECT rcScrub;
+ GetScrubHandleRect( rcScrub, true );
+
+ m_FocusRects.Purge();
+
+ // Go through all selected events
+ RECT rcFocus;
+
+ rcFocus.top = GetStartRow();
+ rcFocus.bottom = h2() - m_nScrollbarHeight - m_nInfoHeight;
+ rcFocus.left = ( rcScrub.left + rcScrub.right ) / 2;
+ rcFocus.right = rcFocus.left;
+
+ POINT pt;
+ pt.x = pt.y = 0;
+ ClientToScreen( (HWND)getHandle(), &pt );
+
+ OffsetRect( &rcFocus, pt.x, pt.y );
+
+ CFocusRect fr;
+ fr.m_rcFocus = rcFocus;
+ fr.m_rcOrig = rcFocus;
+
+ m_FocusRects.AddToTail( fr );
+
+ m_xStart = mx;
+ m_yStart = my;
+
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
+
+ DrawFocusRect();
+ }
+ else
+ {
+ float t = GetTimeValueForMouse( mx );
+
+ ClampTimeToSelectionInterval( t );
+
+ SetScrubTargetTime( t );
+
+ // Unpause the scene
+ m_bPaused = false;
+ redraw();
+ }
+ }
+ else if ( IsMouseOverSceneEndTime( mx ) )
+ {
+ redraw();
+ StartDraggingSceneEndTime( mx, my );
+ }
+ else if ( m_pClickedChannel &&
+ m_nClickedChannelCloseCaptionButton != CChoreoChannelWidget::CLOSECAPTION_NONE &&
+ m_nClickedChannelCloseCaptionButton != CChoreoChannelWidget::CLOSECAPTION_CAPTION )
+ {
+ switch ( m_nClickedChannelCloseCaptionButton )
+ {
+ default:
+ case CChoreoChannelWidget::CLOSECAPTION_EXPANDCOLLAPSE:
+ {
+ OnToggleCloseCaptionTags();
+ }
+ break;
+ case CChoreoChannelWidget::CLOSECAPTION_PREVLANGUAGE:
+ {
+ // Change language
+ int id = GetCloseCaptionLanguageId();
+ --id;
+ if ( id < 0 )
+ {
+ id = CC_NUM_LANGUAGES - 1;
+ Assert( id >= 0 );
+ }
+ SetCloseCaptionLanguageId( id );
+ redraw();
+ }
+ break;
+ case CChoreoChannelWidget::CLOSECAPTION_NEXTLANGUAGE:
+ {
+ int id = GetCloseCaptionLanguageId();
+ ++id;
+ if ( id >= CC_NUM_LANGUAGES )
+ {
+ id = 0;
+ }
+ SetCloseCaptionLanguageId( id );
+ redraw();
+ }
+ break;
+ case CChoreoChannelWidget::CLOSECAPTION_SELECTOR:
+ {
+ SetDirty( true );
+
+ PushUndo( "Change selector" );
+
+ m_pClickedChannel->HandleSelectorClicked();
+
+ PushRedo( "Change selector" );
+
+ redraw();
+ }
+ break;
+ }
+ }
+ else
+ {
+ if ( !( event->modifiers & ( mxEvent::KeyCtrl | mxEvent::KeyShift ) ) )
+ {
+ DeselectAll();
+
+ if ( !isrightbutton )
+ {
+ if ( realtime - m_flLastMouseClickTime < 0.3f )
+ {
+ OnDoubleClicked();
+ m_flLastMouseClickTime = -1.0f;
+ }
+ else
+ {
+ m_flLastMouseClickTime = realtime;
+ }
+ }
+
+ redraw();
+ }
+ }
+
+ CalcBounds( m_nDragType );
+}
+
+void CChoreoView::OnDoubleClicked()
+{
+ if ( m_pClickedChannel )
+ {
+ switch (m_nClickedChannelCloseCaptionButton )
+ {
+ default:
+ break;
+ case CChoreoChannelWidget::CLOSECAPTION_NONE:
+ {
+ SetDirty( true );
+ PushUndo( "Enable/disable Channel" );
+
+ m_pClickedChannel->GetChannel()->SetActive( !m_pClickedChannel->GetChannel()->GetActive() );
+
+ PushRedo( "Enable/disable Channel" );
+ }
+ break;
+ case CChoreoChannelWidget::CLOSECAPTION_CAPTION:
+ {
+ CChoreoEvent *e = m_pClickedChannel->GetCaptionClickedEvent();
+ if ( e && e->GetNumSlaves() >= 1 )
+ {
+ OnChangeCloseCaptionToken( e );
+ }
+ }
+ break;
+ }
+
+ return;
+ }
+
+ if ( m_pClickedActor )
+ {
+ SetDirty( true );
+ PushUndo( "Enable/disable Actor" );
+
+ m_pClickedActor->GetActor()->SetActive( !m_pClickedActor->GetActor()->GetActive() );
+
+ PushRedo( "Enable/disable Actor" );
+ return;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+//-----------------------------------------------------------------------------
+void CChoreoView::MouseContinueDrag( mxEvent *event, int mx, int my )
+{
+ if ( !m_bDragging )
+ return;
+
+ DrawFocusRect();
+
+ ApplyBounds( mx, my );
+
+ for ( int i = 0; i < m_FocusRects.Size(); i++ )
+ {
+ CFocusRect *f = &m_FocusRects[ i ];
+ f->m_rcFocus = f->m_rcOrig;
+
+ switch ( m_nDragType )
+ {
+ default:
+ case DRAGTYPE_SCRUBBER:
+ {
+ float t = GetTimeValueForMouse( mx );
+ t += m_flScrubberTimeOffset;
+
+ ClampTimeToSelectionInterval( t );
+
+ float dt = t - m_flScrub;
+
+ SetScrubTargetTime( t );
+
+ m_bSimulating = true;
+ ScrubThink( dt, true, this );
+
+ SetScrubTime( t );
+
+ OffsetRect( &f->m_rcFocus, ( mx - m_xStart ), 0 );
+ }
+ break;
+ case DRAGTYPE_EVENT_MOVE:
+ case DRAGTYPE_EVENTTAG_MOVE:
+ case DRAGTYPE_EVENTABSTAG_MOVE:
+ {
+ int dx = mx - m_xStart;
+ int dy = my - m_yStart;
+ if ( m_pClickedEvent )
+ {
+ bool shiftdown = ( event->modifiers & mxEvent::KeyShift ) ? true : false;
+
+
+ // Only allow jumping channels if shift is down
+ if ( !shiftdown )
+ {
+ dy = 0;
+ }
+ if ( abs( dy ) < m_pClickedEvent->GetItemHeight() )
+ {
+ dy = 0;
+ }
+ if ( m_nSelectedEvents > 1 )
+ {
+ dy = 0;
+ }
+ if ( m_nDragType == DRAGTYPE_EVENTTAG_MOVE || m_nDragType == DRAGTYPE_EVENTABSTAG_MOVE )
+ {
+ dy = 0;
+ }
+
+ if ( m_pClickedEvent->GetEvent()->IsUsingRelativeTag() )
+ {
+ dx = 0;
+ }
+ }
+ else
+ {
+ dy = 0;
+ }
+ OffsetRect( &f->m_rcFocus, dx, dy );
+ }
+ break;
+ case DRAGTYPE_EVENT_STARTTIME:
+ case DRAGTYPE_EVENT_STARTTIME_RESCALE:
+ f->m_rcFocus.left += ( mx - m_xStart );
+ break;
+ case DRAGTYPE_EVENT_ENDTIME:
+ case DRAGTYPE_EVENT_ENDTIME_RESCALE:
+ f->m_rcFocus.right += ( mx - m_xStart );
+ break;
+ case DRAGTYPE_SCENE_ENDTIME:
+ OffsetRect( &f->m_rcFocus, ( mx - m_xStart ), 0 );
+ break;
+ case DRAGTYPE_RESCALELEFT:
+ case DRAGTYPE_RESCALERIGHT:
+ //f->m_rcFocus.right += ( mx - m_xStart );
+ break;
+ }
+ }
+
+ if ( m_nDragType == DRAGTYPE_RESCALELEFT ||
+ m_nDragType == DRAGTYPE_RESCALERIGHT )
+ {
+ int c = m_FocusRects.Count();
+ int m_nStart = INT_MAX;
+ int m_nEnd = INT_MIN;
+
+ for ( int i = 0; i < c; ++i )
+ {
+ CFocusRect *f = &m_FocusRects[ i ];
+ if ( f->m_rcFocus.left < m_nStart )
+ {
+ m_nStart = f->m_rcFocus.left;
+ }
+ if ( f->m_rcFocus.right > m_nEnd )
+ {
+ m_nEnd = f->m_rcFocus.right;
+ }
+ }
+
+ // Now figure out rescaling logic
+ int dxPixels = mx - m_xStart;
+
+ int oldSize = m_nEnd - m_nStart;
+ if ( oldSize > 0 )
+ {
+ float rescale = 1.0f;
+ if ( m_nDragType == DRAGTYPE_RESCALERIGHT )
+ {
+ rescale = (float)( oldSize + dxPixels )/(float)oldSize;
+ }
+ else
+ {
+ rescale = (float)( oldSize - dxPixels )/(float)oldSize;
+ }
+
+ for ( int i = 0; i < c; ++i )
+ {
+ CFocusRect *f = &m_FocusRects[ i ];
+ int w = f->m_rcFocus.right - f->m_rcFocus.left;
+ if ( m_nDragType == DRAGTYPE_RESCALERIGHT )
+ {
+ f->m_rcFocus.left = m_nStart + ( int )( rescale * (float)( f->m_rcFocus.left - m_nStart ) + 0.5f );
+ f->m_rcFocus.right = f->m_rcFocus.left + ( int )( rescale * (float)w + 0.5f );
+ }
+ else
+ {
+ f->m_rcFocus.right = m_nEnd - ( int )( rescale * (float)( m_nEnd - f->m_rcFocus.right ) + 0.5f );
+ f->m_rcFocus.left = f->m_rcFocus.right - ( int )( rescale * (float)w + 0.5f );
+ }
+ }
+ }
+ }
+ DrawFocusRect();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+//-----------------------------------------------------------------------------
+void CChoreoView::MouseMove( int mx, int my )
+{
+ if ( m_bDragging )
+ return;
+
+ int dragtype = ComputeEventDragType( mx, my );
+ if ( dragtype == DRAGTYPE_NONE )
+ {
+ CChoreoGlobalEventWidget *ge = NULL;
+ GetObjectsUnderMouse( mx, my, NULL, NULL, NULL, &ge, NULL, NULL, NULL );
+ if ( ge )
+ {
+ dragtype = DRAGTYPE_EVENT_MOVE;
+ }
+
+ if ( dragtype == DRAGTYPE_NONE )
+ {
+ if ( IsMouseOverSceneEndTime( mx ) )
+ {
+ dragtype = DRAGTYPE_SCENE_ENDTIME;
+ }
+ }
+ }
+
+ if ( m_hPrevCursor )
+ {
+ SetCursor( m_hPrevCursor );
+ m_hPrevCursor = NULL;
+ }
+ switch ( dragtype )
+ {
+ default:
+ break;
+ case DRAGTYPE_EVENTTAG_MOVE:
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
+ break;
+ case DRAGTYPE_EVENTABSTAG_MOVE:
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_IBEAM ) );
+ break;
+ case DRAGTYPE_EVENT_MOVE:
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEALL ) );
+ break;
+ case DRAGTYPE_EVENT_STARTTIME:
+ case DRAGTYPE_EVENT_STARTTIME_RESCALE:
+ case DRAGTYPE_EVENT_ENDTIME:
+ case DRAGTYPE_EVENT_ENDTIME_RESCALE:
+ case DRAGTYPE_SCENE_ENDTIME:
+ case DRAGTYPE_RESCALELEFT:
+ case DRAGTYPE_RESCALERIGHT:
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *e -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CChoreoView::CheckGestureLength( CChoreoEvent *e, bool bCheckOnly )
+{
+ Assert( e );
+ if ( !e )
+ return false;
+
+ if ( e->GetType() != CChoreoEvent::GESTURE )
+ {
+ Con_Printf( "CheckGestureLength: called on non-GESTURE event %s\n", e->GetName() );
+ return false;
+ }
+
+ StudioModel *model = FindAssociatedModel( e->GetScene(), e->GetActor() );
+ if ( !model )
+ return false;
+
+ CStudioHdr *pStudioHdr = model->GetStudioHdr();
+ if ( !pStudioHdr )
+ return false;
+
+ return UpdateGestureLength( e, pStudioHdr, model->GetPoseParameters(), bCheckOnly );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *e -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CChoreoView::DefaultGestureLength( CChoreoEvent *e, bool bCheckOnly )
+{
+ Assert( e );
+ if ( !e )
+ return false;
+
+ if ( e->GetType() != CChoreoEvent::GESTURE )
+ {
+ Con_Printf( "DefaultGestureLength: called on non-GESTURE event %s\n", e->GetName() );
+ return false;
+ }
+
+ StudioModel *model = FindAssociatedModel( e->GetScene(), e->GetActor() );
+ if ( !model )
+ return false;
+
+ if ( !model->GetStudioHdr() )
+ return false;
+
+ int iSequence = model->LookupSequence( e->GetParameters() );
+ if ( iSequence < 0 )
+ return false;
+
+ bool bret = false;
+
+ float seqduration = model->GetDuration( iSequence );
+ if ( seqduration != 0.0f )
+ {
+ bret = true;
+ if ( !bCheckOnly )
+ {
+ e->SetEndTime( e->GetStartTime() + seqduration );
+ }
+ }
+
+ return bret;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *e -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CChoreoView::AutoaddGestureKeys( CChoreoEvent *e, bool bCheckOnly )
+{
+ if ( !e )
+ return false;
+
+ StudioModel *model = FindAssociatedModel( e->GetScene(), e->GetActor() );
+ if ( !model )
+ return false;
+
+ CStudioHdr *pStudioHdr = model->GetStudioHdr();
+ if ( !pStudioHdr )
+ return false;
+
+ return AutoAddGestureKeys( e, pStudioHdr, model->GetPoseParameters(), bCheckOnly );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CChoreoView::CheckSequenceLength( CChoreoEvent *e, bool bCheckOnly )
+{
+ Assert( e );
+ if ( !e )
+ return false;
+
+ if ( e->GetType() != CChoreoEvent::SEQUENCE )
+ {
+ Con_Printf( "CheckSequenceLength: called on non-SEQUENCE event %s\n", e->GetName() );
+ return false;
+ }
+
+ StudioModel *model = FindAssociatedModel( e->GetScene(), e->GetActor() );
+ if ( !model )
+ return false;
+
+ CStudioHdr *pStudioHdr = model->GetStudioHdr();
+ if ( !pStudioHdr )
+ return false;
+
+ return UpdateSequenceLength( e, pStudioHdr, model->GetPoseParameters(), bCheckOnly, true );
+}
+
+void CChoreoView::FinishDraggingSceneEndTime( mxEvent *event, int mx, int my )
+{
+ DrawFocusRect();
+
+ m_FocusRects.Purge();
+
+ m_bDragging = false;
+
+ float mouse_dt = GetTimeDeltaForMouseDelta( mx, m_xStart );
+ if ( !mouse_dt )
+ {
+ return;
+ }
+
+ SetDirty( true );
+
+ const char *desc = "Change Scene Duration";
+
+ PushUndo( desc );
+
+ float newendtime = GetTimeValueForMouse( mx );
+ float oldendtime = m_pScene->FindStopTime();
+
+ float scene_dt = newendtime - oldendtime;
+
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *a = m_SceneActors[ i ];
+ if ( !a )
+ continue;
+
+ for ( int j = 0; j < a->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *channel = a->GetChannel( j );
+ if ( !channel )
+ continue;
+
+ int k;
+
+ CChoreoEvent *finalGesture = NULL;
+ for ( k = channel->GetNumEvents() - 1; k >= 0; k-- )
+ {
+ CChoreoEventWidget *event = channel->GetEvent( k );
+ CChoreoEvent *e = event->GetEvent();
+ if ( e->GetType() != CChoreoEvent::GESTURE )
+ continue;
+
+ if ( !finalGesture )
+ {
+ finalGesture = e;
+ }
+ else
+ {
+ if ( e->GetStartTime() > finalGesture->GetStartTime() )
+ {
+ finalGesture = e;
+ }
+ }
+ }
+
+
+ for ( k = channel->GetNumEvents() - 1; k >= 0; k-- )
+ {
+ CChoreoEventWidget *event = channel->GetEvent( k );
+ CChoreoEvent *e = event->GetEvent();
+
+ // Event starts after new end time, kill it
+ if ( e->GetStartTime() > newendtime )
+ {
+ channel->GetChannel()->RemoveEvent( e );
+ m_pScene->DeleteReferencedObjects( e );
+ continue;
+ }
+
+ // No change to normal events that end earlier than new time (but do change gestures)
+ if ( e->GetEndTime() < newendtime &&
+ e != finalGesture )
+ {
+ continue;
+ }
+
+ float dt = scene_dt;
+ if ( e->GetType() == CChoreoEvent::GESTURE )
+ {
+ if ( e->GetEndTime() < newendtime )
+ {
+ dt = newendtime - e->GetEndTime();
+ }
+ }
+
+ float newduration = e->GetDuration() + dt;
+ RescaleRamp( e, newduration );
+ switch ( e->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ e->RescaleGestureTimes( e->GetStartTime(), e->GetEndTime() + dt, true );
+ }
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ {
+ RescaleExpressionTimes( e, e->GetStartTime(), e->GetEndTime() + dt );
+ }
+ break;
+ }
+ e->OffsetEndTime( dt );
+ e->SnapTimes();
+ e->ResortRamp();
+ }
+ }
+ }
+
+ // Remove event and move to new object
+ DeleteSceneWidgets();
+
+ m_nDragType = DRAGTYPE_NONE;
+
+ if ( m_hPrevCursor )
+ {
+ SetCursor( m_hPrevCursor );
+ m_hPrevCursor = 0;
+ }
+
+ PushRedo( desc );
+
+ CreateSceneWidgets();
+
+ InvalidateLayout();
+
+ g_pExpressionTool->LayoutItems( true );
+ g_pExpressionTool->redraw();
+ g_pGestureTool->redraw();
+ g_pRampTool->redraw();
+ g_pSceneRampTool->redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called after association changes to reset .wav file images
+// Input : -
+//-----------------------------------------------------------------------------
+void CChoreoView::RecomputeWaves()
+{
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *a = m_SceneActors[ i ];
+ if ( !a )
+ continue;
+
+ for ( int j = 0; j < a->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *c = a->GetChannel( j );
+ if ( !c )
+ continue;
+
+ for ( int k = 0; k < c->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *e = c->GetEvent( k );
+ if ( !e )
+ continue;
+
+ e->RecomputeWave();
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+//-----------------------------------------------------------------------------
+void CChoreoView::FinishDraggingEvent( mxEvent *event, int mx, int my )
+{
+ DrawFocusRect();
+
+ m_FocusRects.Purge();
+
+ m_bDragging = false;
+
+ float dt = GetTimeDeltaForMouseDelta( mx, m_xStart );
+ if ( !dt )
+ {
+ if ( m_pScene && m_pClickedEvent && m_pClickedEvent->GetEvent()->GetType() == CChoreoEvent::SPEAK )
+ {
+ // Show phone wav in wav viewer
+ char sndname[ 512 ];
+ Q_strncpy( sndname, FacePoser_TranslateSoundName( m_pClickedEvent->GetEvent() ), sizeof( sndname ) );
+ if ( sndname[ 0 ] )
+ {
+ SetCurrentWaveFile( va( "sound/%s", sndname ), m_pClickedEvent->GetEvent() );
+ }
+ else
+ {
+ Warning( "Unable to resolve sound name for '%s', check actor associations\n", m_pClickedEvent->GetEvent()->GetName() );
+ }
+ }
+ return;
+ }
+
+ SetDirty( true );
+
+ char const *desc = "";
+
+ switch ( m_nDragType )
+ {
+ default:
+ case DRAGTYPE_EVENT_MOVE:
+ desc = "Event Move";
+ break;
+ case DRAGTYPE_EVENT_STARTTIME:
+ case DRAGTYPE_EVENT_STARTTIME_RESCALE:
+ desc = "Change Start Time";
+ break;
+ case DRAGTYPE_EVENT_ENDTIME:
+ case DRAGTYPE_EVENT_ENDTIME_RESCALE:
+ desc = "Change End Time";
+ break;
+ case DRAGTYPE_EVENTTAG_MOVE:
+ desc = "Move Event Tag";
+ break;
+ case DRAGTYPE_EVENTABSTAG_MOVE:
+ desc = "Move Abs Event Tag";
+ break;
+ case DRAGTYPE_RESCALELEFT:
+ case DRAGTYPE_RESCALERIGHT:
+ desc = "Rescale Time";
+ break;
+ }
+ PushUndo( desc );
+
+ CUtlVector< CChoreoEvent * > rescaleHelper;
+
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *actor = m_SceneActors[ i ];
+ if ( !actor )
+ continue;
+
+ for ( int j = 0; j < actor->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *channel = actor->GetChannel( j );
+ if ( !channel )
+ continue;
+
+ for ( int k = 0; k < channel->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *event = channel->GetEvent( k );
+ if ( !event )
+ continue;
+
+ if ( !event->IsSelected() )
+ continue;
+
+ // Figure out true dt
+ CChoreoEvent *e = event->GetEvent();
+ if ( e )
+ {
+ switch ( m_nDragType )
+ {
+ default:
+ case DRAGTYPE_EVENT_MOVE:
+ e->OffsetTime( dt );
+ e->SnapTimes();
+ break;
+ case DRAGTYPE_EVENT_STARTTIME:
+ case DRAGTYPE_EVENT_STARTTIME_RESCALE:
+ {
+ float newduration = e->GetDuration() - dt;
+ RescaleRamp( e, newduration );
+ switch ( e->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ e->RescaleGestureTimes( e->GetStartTime() + dt, e->GetEndTime(), m_nDragType == DRAGTYPE_EVENT_STARTTIME );
+ }
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ {
+ RescaleExpressionTimes( e, e->GetStartTime() + dt, e->GetEndTime() );
+ }
+ break;
+ }
+ e->OffsetStartTime( dt );
+ e->SnapTimes();
+ e->ResortRamp();
+ }
+ break;
+ case DRAGTYPE_EVENT_ENDTIME:
+ case DRAGTYPE_EVENT_ENDTIME_RESCALE:
+ {
+ float newduration = e->GetDuration() + dt;
+ RescaleRamp( e, newduration );
+ switch ( e->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ e->RescaleGestureTimes( e->GetStartTime(), e->GetEndTime() + dt, m_nDragType == DRAGTYPE_EVENT_ENDTIME );
+ }
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ {
+ RescaleExpressionTimes( e, e->GetStartTime(), e->GetEndTime() + dt );
+ }
+ break;
+ }
+ e->OffsetEndTime( dt );
+ e->SnapTimes();
+ e->ResortRamp();
+ }
+ break;
+ case DRAGTYPE_RESCALELEFT:
+ case DRAGTYPE_RESCALERIGHT:
+ {
+ rescaleHelper.AddToTail( e );
+ }
+ break;
+ case DRAGTYPE_EVENTTAG_MOVE:
+ {
+ // Get current x position
+ if ( m_nClickedTag != -1 )
+ {
+ CEventRelativeTag *tag = e->GetRelativeTag( m_nClickedTag );
+ if ( tag )
+ {
+ float dx = mx - m_xStart;
+ // Determine left edcge
+ RECT bounds;
+ bounds = event->getBounds();
+ if ( bounds.right - bounds.left > 0 )
+ {
+ int left = bounds.left + (int)( tag->GetPercentage() * (float)( bounds.right - bounds.left ) + 0.5f );
+
+ left += dx;
+
+ if ( left < bounds.left )
+ {
+ left = bounds.left;
+ }
+ else if ( left >= bounds.right )
+ {
+ left = bounds.right - 1;
+ }
+
+ // Now convert back to a percentage
+ float frac = (float)( left - bounds.left ) / (float)( bounds.right - bounds.left );
+
+ tag->SetPercentage( frac );
+ }
+ }
+ }
+ }
+ break;
+ case DRAGTYPE_EVENTABSTAG_MOVE:
+ {
+ // Get current x position
+ if ( m_pClickedAbsoluteTag != NULL )
+ {
+ CEventAbsoluteTag *tag = m_pClickedAbsoluteTag;
+ if ( tag )
+ {
+ float dx = mx - m_xStart;
+ // Determine left edcge
+ RECT bounds;
+ bounds = event->getBounds();
+ if ( bounds.right - bounds.left > 0 )
+ {
+ int left = bounds.left + (int)( tag->GetPercentage() * (float)( bounds.right - bounds.left ) + 0.5f );
+
+ left += dx;
+
+ if ( left < bounds.left )
+ {
+ left = bounds.left;
+ }
+ else if ( left >= bounds.right )
+ {
+ left = bounds.right - 1;
+ }
+
+ // Now convert back to a percentage
+ float frac = (float)( left - bounds.left ) / (float)( bounds.right - bounds.left );
+
+ tag->SetPercentage( frac );
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ switch ( e->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::SPEAK:
+ {
+ // Try and load wav to get length
+ CAudioSource *wave = sound->LoadSound( va( "sound/%s", FacePoser_TranslateSoundName( e ) ) );
+ if ( wave )
+ {
+ e->SetEndTime( e->GetStartTime() + wave->GetRunningLength() );
+ delete wave;
+ }
+ }
+ break;
+ case CChoreoEvent::SEQUENCE:
+ {
+ CheckSequenceLength( e, false );
+ }
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ CheckGestureLength( e, false );
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ if ( rescaleHelper.Count() > 0 )
+ {
+ int i;
+ // Determine start and end times for existing "selection"
+ float flStart = FLT_MAX;
+ float flEnd = FLT_MIN;
+ for ( i = 0; i < rescaleHelper.Count(); ++i )
+ {
+ CChoreoEvent *e = rescaleHelper[ i ];
+ float st = e->GetStartTime();
+ float ed = e->GetEndTime();
+
+ if ( st < flStart )
+ {
+ flStart = st;
+ }
+ if ( ed > flEnd )
+ {
+ flEnd = ed;
+ }
+ }
+
+ float flSelectionDuration = flEnd - flStart;
+ if ( flSelectionDuration > 0.0f )
+ {
+ float flNewDuration = 0.0f;
+ if ( m_nDragType == DRAGTYPE_RESCALELEFT )
+ {
+ flNewDuration = max( 0.1f, flSelectionDuration - dt );
+ }
+ else
+ {
+ flNewDuration = max( 0.1f, flSelectionDuration + dt );
+ }
+ float flScale = flNewDuration / flSelectionDuration;
+
+ for ( i = 0; i < rescaleHelper.Count(); ++i )
+ {
+ CChoreoEvent *e = rescaleHelper[ i ];
+ float st = e->GetStartTime();
+ float et = e->HasEndTime() ? e->GetEndTime() : e->GetStartTime();
+ float flTimeFromStart = st - flStart;
+ float flTimeFromEnd = flEnd - et;
+ float flDuration = e->GetDuration();
+
+ float flNewStartTime = 0.0f;
+ float flNewDuration = 0.0f;
+
+ if ( m_nDragType == DRAGTYPE_RESCALELEFT )
+ {
+ float flNewEndTime = flEnd - flTimeFromEnd * flScale;
+ if ( !e->HasEndTime() || e->IsFixedLength() )
+ {
+ e->OffsetTime( flNewEndTime - flDuration - st );
+ continue;
+ }
+ flNewDuration = flDuration * flScale;
+ flNewStartTime = flNewEndTime - flNewDuration;
+ }
+ else
+ {
+ flNewStartTime = flTimeFromStart * flScale + flStart;
+ if ( !e->HasEndTime() || e->IsFixedLength() )
+ {
+ e->OffsetTime( flNewStartTime - st );
+ continue;
+ }
+ flNewDuration = flDuration * flScale;
+ }
+
+ RescaleRamp( e, flNewDuration );
+ switch ( e->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ e->RescaleGestureTimes( flNewStartTime, flNewStartTime + flNewDuration, m_nDragType == DRAGTYPE_EVENT_STARTTIME || m_nDragType == DRAGTYPE_EVENT_ENDTIME );
+ }
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ {
+ RescaleExpressionTimes( e, flNewStartTime, flNewStartTime + flNewDuration );
+ }
+ break;
+ }
+
+ e->SetStartTime( flNewStartTime );
+ Assert( e->HasEndTime() );
+ e->SetEndTime( flNewStartTime + flNewDuration );
+ }
+ }
+ }
+
+ for ( int i = 0; i < m_SceneGlobalEvents.Count(); i++ )
+ {
+ CChoreoGlobalEventWidget *gew = m_SceneGlobalEvents[ i ];
+ if ( !gew || !gew->IsSelected() )
+ continue;
+
+ CChoreoEvent *e = gew->GetEvent();
+ if ( !e )
+ continue;
+
+ e->OffsetTime( dt );
+ e->SnapTimes();
+ }
+
+ m_nDragType = DRAGTYPE_NONE;
+
+ if ( m_hPrevCursor )
+ {
+ SetCursor( m_hPrevCursor );
+ m_hPrevCursor = 0;
+ }
+
+ CChoreoEvent *e = m_pClickedEvent ? m_pClickedEvent->GetEvent() : NULL;
+
+ if ( e )
+ {
+ // See if event is moving to a new owner
+ CChoreoChannelWidget *chOrig, *chNew;
+
+ int dy = my - m_yStart;
+ bool shiftdown = ( event->modifiers & mxEvent::KeyShift ) ? true : false;
+ if ( !shiftdown )
+ {
+ dy = 0;
+ }
+
+ if ( abs( dy ) < m_pClickedEvent->GetItemHeight() )
+ {
+ my = m_yStart;
+ }
+
+ chNew = GetChannelUnderCursorPos( mx, my );
+
+ InvalidateLayout();
+
+ mx = m_xStart;
+ my = m_yStart;
+
+ chOrig = m_pClickedChannel;
+
+ if ( chOrig && chNew && chOrig != chNew )
+ {
+ // Swap underlying objects
+ CChoreoChannel *pOrigChannel, *pNewChannel;
+
+ pOrigChannel = chOrig->GetChannel();
+ pNewChannel = chNew->GetChannel();
+
+ Assert( pOrigChannel && pNewChannel );
+
+ // Remove event and move to new object
+ DeleteSceneWidgets();
+
+ pOrigChannel->RemoveEvent( e );
+ pNewChannel->AddEvent( e );
+
+ e->SetChannel( pNewChannel );
+ e->SetActor( pNewChannel->GetActor() );
+
+ CreateSceneWidgets();
+ }
+ else
+ {
+ if ( e && e->GetType() == CChoreoEvent::SPEAK )
+ {
+ // Show phone wav in wav viewer
+ SetCurrentWaveFile( va( "sound/%s", FacePoser_TranslateSoundName( e ) ), e );
+ }
+ }
+ }
+
+ PushRedo( desc );
+ InvalidateLayout();
+
+ if ( e )
+ {
+ switch ( e->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ {
+ g_pExpressionTool->SetEvent( e );
+ g_pFlexPanel->SetEvent( e );
+ }
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ g_pGestureTool->SetEvent( e );
+ }
+ break;
+ }
+
+ if ( e->HasEndTime() )
+ {
+ g_pRampTool->SetEvent( e );
+ }
+ }
+ g_pExpressionTool->LayoutItems( true );
+ g_pExpressionTool->redraw();
+ g_pGestureTool->redraw();
+ g_pRampTool->redraw();
+ g_pSceneRampTool->redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+//-----------------------------------------------------------------------------
+void CChoreoView::MouseFinishDrag( mxEvent *event, int mx, int my )
+{
+ if ( !m_bDragging )
+ return;
+
+ ApplyBounds( mx, my );
+
+ switch ( m_nDragType )
+ {
+ case DRAGTYPE_SCRUBBER:
+ {
+ DrawFocusRect();
+
+ m_FocusRects.Purge();
+
+ float t = GetTimeValueForMouse( mx );
+ t += m_flScrubberTimeOffset;
+ m_flScrubberTimeOffset = 0.0f;
+
+ ClampTimeToSelectionInterval( t );
+
+ SetScrubTime( t );
+ SetScrubTargetTime( t );
+
+ m_bDragging = false;
+ m_nDragType = DRAGTYPE_NONE;
+
+ redraw();
+ }
+ break;
+ case DRAGTYPE_EVENT_MOVE:
+ case DRAGTYPE_EVENT_STARTTIME:
+ case DRAGTYPE_EVENT_STARTTIME_RESCALE:
+ case DRAGTYPE_EVENT_ENDTIME:
+ case DRAGTYPE_EVENT_ENDTIME_RESCALE:
+ case DRAGTYPE_EVENTTAG_MOVE:
+ case DRAGTYPE_EVENTABSTAG_MOVE:
+ case DRAGTYPE_RESCALELEFT:
+ case DRAGTYPE_RESCALERIGHT:
+ FinishDraggingEvent( event, mx, my );
+ break;
+ case DRAGTYPE_SCENE_ENDTIME:
+ FinishDraggingSceneEndTime( event, mx, my );
+ break;
+ default:
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoView::handleEvent( mxEvent *event )
+{
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ int iret = 0;
+
+ if ( HandleToolEvent( event ) )
+ {
+ return iret;
+ }
+
+ switch ( event->event )
+ {
+ case mxEvent::MouseWheeled:
+ {
+ CChoreoScene *scene = GetScene();
+ if ( scene )
+ {
+ int tz = 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 );
+ }
+
+ SetTimeZoom( GetToolName(), tz, true );
+
+ CUtlVector< CChoreoEvent * > selected;
+ RememberSelectedEvents( selected );
+
+ DeleteSceneWidgets();
+ CreateSceneWidgets();
+
+ ReselectEvents( selected );
+
+ InvalidateLayout();
+ Con_Printf( "Zoom factor %i %%\n", GetTimeZoom( GetToolName() ) );
+ }
+ iret = 1;
+ }
+ break;
+ case mxEvent::Size:
+ {
+ // Force scroll bars to recompute
+ ForceScrollBarsToRecompute( false );
+
+ InvalidateLayout();
+ PositionControls();
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseDown:
+ {
+ if ( !m_bDragging )
+ {
+ if ( event->buttons & mxEvent::MouseRightButton )
+ {
+ if ( IsMouseOverTimeline( (short)event->x, (short)event->y ) )
+ {
+ PlaceABPoint( (short)event->x );
+ redraw();
+ }
+ else if ( IsMouseOverScrubArea( event ) )
+ {
+ float t = GetTimeValueForMouse( (short)event->x );
+
+ ClampTimeToSelectionInterval( t );
+
+ SetScrubTime( t );
+ SetScrubTargetTime( t );
+
+ sound->Flush();
+
+ // Unpause the scene
+ m_bPaused = false;
+
+ redraw();
+ }
+ else
+ {
+ // Show right click menu
+ ShowContextMenu( (short)event->x, (short)event->y );
+ }
+ }
+ else
+ {
+ if ( IsMouseOverTimeline( (short)event->x, (short)event->y ) )
+ {
+ ClearABPoints();
+ redraw();
+ }
+ else
+ {
+ // Handle mouse dragging here
+ MouseStartDrag( event, (short)event->x, (short)event->y );
+ }
+ }
+ }
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseDrag:
+ {
+ MouseContinueDrag( event, (short)event->x, (short)event->y );
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseUp:
+ {
+ MouseFinishDrag( event, (short)event->x, (short)event->y );
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseMove:
+ {
+ MouseMove( (short)event->x, (short)event->y );
+ UpdateStatusArea( (short)event->x, (short)event->y );
+ iret = 1;
+ }
+ break;
+ case mxEvent::KeyDown:
+ {
+ iret = 1;
+
+ switch ( event->key )
+ {
+ default:
+ iret = 0;
+ break;
+ case 'E':
+ if ( GetAsyncKeyState( VK_CONTROL ) )
+ {
+ OnPlaceNextSpeakEvent();
+ }
+ break;
+ case VK_ESCAPE:
+ DeselectAll();
+ break;
+ case 'C':
+ CopyEvents();
+ iret = 1;
+ break;
+ case 'V':
+ PasteEvents();
+ redraw();
+ break;
+ case VK_DELETE:
+ {
+ if ( IsActiveTool() )
+ {
+ DeleteSelectedEvents();
+ }
+ }
+ break;
+ case VK_RETURN:
+ {
+ CUtlVector< CChoreoEvent * > events;
+ GetSelectedEvents( events );
+ if ( events.Count() == 1 )
+ {
+ if ( GetAsyncKeyState( VK_MENU ) )
+ {
+ EditEvent( events[ 0 ] );
+ redraw();
+ iret = 1;
+ }
+ }
+ }
+ break;
+ case 'Z': // Undo/Redo
+ {
+ if ( GetAsyncKeyState( VK_CONTROL ) )
+ {
+ if ( GetAsyncKeyState( VK_SHIFT ) )
+ {
+ if ( CanRedo() )
+ {
+ Con_Printf( "Redo %s\n", GetRedoDescription() );
+ Redo();
+ iret = 1;
+ }
+ }
+ else
+ {
+ if ( CanUndo() )
+ {
+ Con_Printf( "Undo %s\n", GetUndoDescription() );
+ Undo();
+ iret = 1;
+ }
+ }
+ }
+ }
+ break;
+
+ case VK_SPACE:
+ {
+ if ( IsPlayingScene() )
+ {
+ StopScene();
+ }
+ }
+ break;
+ case 188: // VK_OEM_COMMA:
+ {
+ SetScrubTargetTime( 0.0f );
+ }
+ break;
+ case 190: // VK_OEM_PERIOD:
+ {
+ CChoreoScene *scene = GetScene();
+ if ( scene )
+ {
+ SetScrubTargetTime( scene->FindStopTime() );
+ }
+ }
+ break;
+ case VK_LEFT:
+ {
+ CChoreoScene *scene = GetScene();
+ if ( scene && scene->GetSceneFPS() > 0 )
+ {
+ float curscrub = m_flScrub;
+ curscrub -= ( 1.0f / (float)scene->GetSceneFPS() );
+ curscrub = max( curscrub, 0.0f );
+ SetScrubTargetTime( curscrub );
+ }
+ }
+ break;
+ case VK_RIGHT:
+ {
+ CChoreoScene *scene = GetScene();
+ if ( scene && scene->GetSceneFPS() > 0 )
+ {
+ float curscrub = m_flScrub;
+ curscrub += ( 1.0f / (float)scene->GetSceneFPS() );
+ curscrub = min( curscrub, scene->FindStopTime() );
+ SetScrubTargetTime( curscrub );
+ }
+ }
+ break;
+ case VK_HOME:
+ {
+ MoveTimeSliderToPos( 0 );
+ }
+ break;
+ case VK_END:
+ {
+ float maxtime = m_pScene->FindStopTime() - 1.0f;
+ int pixels = (int)( maxtime * GetPixelsPerSecond() );
+ MoveTimeSliderToPos( pixels - 1 );
+ }
+ break;
+ case VK_PRIOR: // PgUp
+ {
+ int window = w2() - GetLabelWidth();
+ m_flLeftOffset = max( m_flLeftOffset - (float)window, 0.0f );
+ MoveTimeSliderToPos( (int)m_flLeftOffset );
+ }
+ break;
+ case VK_NEXT: // PgDown
+ {
+ int window = w2() - GetLabelWidth();
+ int pixels = ComputeHPixelsNeeded();
+ m_flLeftOffset = min( m_flLeftOffset + (float)window, (float)pixels );
+ MoveTimeSliderToPos( (int)m_flLeftOffset );
+ }
+ break;
+ }
+ }
+ break;
+ case mxEvent::Action:
+ {
+ iret = 1;
+ switch ( event->action )
+ {
+ default:
+ {
+ iret = 0;
+ int lang_index = event->action - IDC_CV_CC_LANGUAGESTART;
+ if ( lang_index >= 0 && lang_index < CC_NUM_LANGUAGES )
+ {
+ iret = 1;
+ SetCloseCaptionLanguageId( lang_index );
+ }
+ }
+ break;
+ case IDC_CV_TOGGLECLOSECAPTIONS:
+ {
+ OnToggleCloseCaptionsForEvent();
+ }
+ break;
+ case IDC_CV_CHANGECLOSECAPTIONTOKEN:
+ {
+ if ( m_pClickedChannel )
+ {
+ CChoreoEvent *e = m_pClickedChannel->GetCaptionClickedEvent();
+ if ( e && e->GetNumSlaves() >= 1 )
+ {
+ OnChangeCloseCaptionToken( e );
+ }
+ }
+ }
+ break;
+ case IDC_CV_REMOVESPEAKEVENTFROMGROUP:
+ {
+ OnRemoveSpeakEventFromGroup();
+ }
+ break;
+ case IDC_CV_COMBINESPEAKEVENTS:
+ {
+ OnCombineSpeakEvents();
+ }
+ break;
+ case IDC_CV_CC_SHOW:
+ {
+ OnToggleCloseCaptionTags();
+ }
+ break;
+ case IDC_CV_TOGGLERAMPONLY:
+ {
+ m_bRampOnly = !m_bRampOnly;
+ redraw();
+ }
+ break;
+ case IDC_CV_PROCESSSEQUENCES:
+ {
+ m_bProcessSequences = !m_bProcessSequences;
+ }
+ break;
+ case IDC_CV_CHECKSEQLENGTHS:
+ {
+ OnCheckSequenceLengths();
+ }
+ break;
+ case IDC_CV_CHANGESCALE:
+ {
+ OnChangeScale();
+ }
+ break;
+ case IDC_CHOREO_PLAYBACKRATE:
+ {
+ m_flPlaybackRate = m_pPlaybackRate->getValue();
+ redraw();
+ }
+ break;
+ case IDC_COPYEVENTS:
+ CopyEvents();
+ break;
+ case IDC_PASTEEVENTS:
+ PasteEvents();
+ redraw();
+ break;
+ case IDC_IMPORTEVENTS:
+ ImportEvents();
+ redraw();
+ break;
+ case IDC_EXPORTEVENTS:
+ ExportEvents();
+ redraw();
+ break;
+ case IDC_EXPORT_VCD:
+ ExportVCD();
+ redraw();
+ break;
+ case IDC_IMPORT_VCD:
+ ImportVCD();
+ redraw();
+ break;
+ case IDC_EXPRESSIONTOOL:
+ OnExpressionTool();
+ break;
+ case IDC_GESTURETOOL:
+ OnGestureTool();
+ break;
+ case IDC_ASSOCIATEBSP:
+ AssociateBSP();
+ break;
+ case IDC_ASSOCIATEMODEL:
+ AssociateModel();
+ break;
+ case IDC_CVUNDO:
+ Undo();
+ break;
+ case IDC_CVREDO:
+ Redo();
+ break;
+ case IDC_SELECTALL:
+ SelectAll();
+ break;
+ case IDC_DESELECTALL:
+ DeselectAll();
+ break;
+ case IDC_PLAYSCENE:
+ Con_Printf( "Commencing playback\n" );
+ PlayScene( true );
+ break;
+ case IDC_PAUSESCENE:
+ Con_Printf( "Pausing playback\n" );
+ PauseScene();
+ break;
+ case IDC_STOPSCENE:
+ Con_Printf( "Canceling playback\n" );
+ StopScene();
+ break;
+ case IDC_CHOREOVSCROLL:
+ {
+ int offset = 0;
+ bool processed = true;
+
+ switch ( event->modifiers )
+ {
+ case SB_THUMBTRACK:
+ offset = event->height;
+ break;
+ case SB_PAGEUP:
+ offset = m_pVertScrollBar->getValue();
+ offset -= 20;
+ offset = max( offset, m_pVertScrollBar->getMinValue() );
+ break;
+ case SB_PAGEDOWN:
+ offset = m_pVertScrollBar->getValue();
+ offset += 20;
+ 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;
+ InvalidateLayout();
+ }
+ }
+ break;
+ case IDC_CHOREOHSCROLL:
+ {
+ 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_ADDACTOR:
+ {
+ NewActor();
+ }
+ break;
+ case IDC_EDITACTOR:
+ {
+ CChoreoActorWidget *actor = m_pClickedActor;
+ if ( actor )
+ {
+ EditActor( actor->GetActor() );
+ }
+ }
+ break;
+ case IDC_DELETEACTOR:
+ {
+ CChoreoActorWidget *actor = m_pClickedActor;
+ if ( actor )
+ {
+ DeleteActor( actor->GetActor() );
+ }
+ }
+ break;
+ case IDC_MOVEACTORUP:
+ {
+ CChoreoActorWidget *actor = m_pClickedActor;
+ if ( actor )
+ {
+ MoveActorUp( actor->GetActor() );
+ }
+ }
+ break;
+ case IDC_MOVEACTORDOWN:
+ {
+ CChoreoActorWidget *actor = m_pClickedActor;
+ if ( actor )
+ {
+ MoveActorDown( actor->GetActor() );
+ }
+ }
+ break;
+ case IDC_CHANNELOPEN:
+ {
+ CActorBitmapButton *btn = static_cast< CActorBitmapButton * >( event->widget );
+ if ( btn )
+ {
+ CChoreoActorWidget *a = btn->GetActor();
+ if ( a )
+ {
+ a->ShowChannels( true );
+ }
+ }
+ }
+ break;
+ case IDC_CHANNELCLOSE:
+ {
+ CActorBitmapButton *btn = static_cast< CActorBitmapButton * >( event->widget );
+ if ( btn )
+ {
+ CChoreoActorWidget *a = btn->GetActor();
+ if ( a )
+ {
+ a->ShowChannels( false );
+ }
+ }
+ }
+ break;
+ case IDC_ADDEVENT_INTERRUPT:
+ {
+ AddEvent( CChoreoEvent::INTERRUPT );
+ }
+ break;
+ case IDC_ADDEVENT_PERMITRESPONSES:
+ {
+ AddEvent( CChoreoEvent::PERMIT_RESPONSES );
+ }
+ break;
+ case IDC_ADDEVENT_EXPRESSION:
+ {
+ AddEvent( CChoreoEvent::EXPRESSION );
+ }
+ break;
+ case IDC_ADDEVENT_FLEXANIMATION:
+ {
+ AddEvent( CChoreoEvent::FLEXANIMATION );
+ }
+ break;
+ case IDC_ADDEVENT_GESTURE:
+ {
+ AddEvent( CChoreoEvent::GESTURE );
+ }
+ break;
+ case IDC_ADDEVENT_NULLGESTURE:
+ {
+ AddEvent( CChoreoEvent::GESTURE, 1 );
+ }
+ break;
+ case IDC_ADDEVENT_LOOKAT:
+ {
+ AddEvent( CChoreoEvent::LOOKAT );
+ }
+ break;
+ case IDC_ADDEVENT_MOVETO:
+ {
+ AddEvent( CChoreoEvent::MOVETO );
+ }
+ break;
+ case IDC_ADDEVENT_FACE:
+ {
+ AddEvent( CChoreoEvent::FACE );
+ }
+ break;
+ case IDC_ADDEVENT_SPEAK:
+ {
+ AddEvent( CChoreoEvent::SPEAK );
+ }
+ break;
+ case IDC_ADDEVENT_FIRETRIGGER:
+ {
+ AddEvent( CChoreoEvent::FIRETRIGGER );
+ }
+ break;
+ case IDC_ADDEVENT_GENERIC:
+ {
+ AddEvent( CChoreoEvent::GENERIC );
+ }
+ break;
+ case IDC_ADDEVENT_SUBSCENE:
+ {
+ AddEvent( CChoreoEvent::SUBSCENE );
+ }
+ break;
+ case IDC_ADDEVENT_SEQUENCE:
+ {
+ AddEvent( CChoreoEvent::SEQUENCE );
+ }
+ break;
+ case IDC_EDITEVENT:
+ {
+ CChoreoEventWidget *event = m_pClickedEvent;
+ if ( event )
+ {
+ EditEvent( event->GetEvent() );
+ redraw();
+ }
+ }
+ break;
+ case IDC_DELETEEVENT:
+ {
+ DeleteSelectedEvents();
+ }
+ break;
+ case IDC_CV_ENABLEEVENTS:
+ {
+ EnableSelectedEvents( true );
+ }
+ break;
+ case IDC_CV_DISABLEEVENTS:
+ {
+ EnableSelectedEvents( false );
+ }
+ break;
+ case IDC_MOVETOBACK:
+ {
+ CChoreoEventWidget *event = m_pClickedEvent;
+ if ( event )
+ {
+ MoveEventToBack( event->GetEvent() );
+ }
+ }
+ break;
+ case IDC_DELETERELATIVETAG:
+ {
+ CChoreoEventWidget *event = m_pClickedEvent;
+ if ( event && m_nClickedTag >= 0 )
+ {
+ DeleteEventRelativeTag( event->GetEvent(), m_nClickedTag );
+ }
+ }
+ break;
+ case IDC_ADDTIMINGTAG:
+ {
+ AddEventRelativeTag();
+ }
+ break;
+ case IDC_ADDEVENT_PAUSE:
+ {
+ AddGlobalEvent( CChoreoEvent::SECTION );
+ }
+ break;
+ case IDC_ADDEVENT_LOOP:
+ {
+ AddGlobalEvent( CChoreoEvent::LOOP );
+ }
+ break;
+ case IDC_ADDEVENT_STOPPOINT:
+ {
+ AddGlobalEvent( CChoreoEvent::STOPPOINT );
+ }
+ break;
+ case IDC_EDITGLOBALEVENT:
+ {
+ CChoreoGlobalEventWidget *event = m_pClickedGlobalEvent;
+ if ( event )
+ {
+ EditGlobalEvent( event->GetEvent() );
+ redraw();
+ }
+ }
+ break;
+ case IDC_DELETEGLOBALEVENT:
+ {
+ CChoreoGlobalEventWidget *event = m_pClickedGlobalEvent;
+ if ( event )
+ {
+ DeleteGlobalEvent( event->GetEvent() );
+ }
+ }
+ break;
+ case IDC_ADDCHANNEL:
+ {
+ NewChannel();
+ }
+ break;
+ case IDC_EDITCHANNEL:
+ {
+ CChoreoChannelWidget *channel = m_pClickedChannel;
+ if ( channel )
+ {
+ EditChannel( channel->GetChannel() );
+ }
+ }
+ break;
+ case IDC_DELETECHANNEL:
+ {
+ CChoreoChannelWidget *channel = m_pClickedChannel;
+ if ( channel )
+ {
+ DeleteChannel( channel->GetChannel() );
+ }
+ }
+ break;
+ case IDC_MOVECHANNELUP:
+ {
+ CChoreoChannelWidget *channel = m_pClickedChannel;
+ if ( channel )
+ {
+ MoveChannelUp( channel->GetChannel() );
+ }
+ }
+ break;
+ case IDC_MOVECHANNELDOWN:
+ {
+ CChoreoChannelWidget *channel = m_pClickedChannel;
+ if ( channel )
+ {
+ MoveChannelDown( channel->GetChannel() );
+ }
+ }
+ break;
+ case IDC_CV_ALLEVENTS_CHANNEL:
+ {
+ CChoreoChannelWidget *channel = m_pClickedChannel;
+ if ( channel )
+ {
+ SelectAllEventsInChannel( channel );
+ }
+ }
+ break;
+ case IDC_CV_ALLEVENTS_ACTOR:
+ {
+ CChoreoActorWidget *actor = m_pClickedActor;
+ if ( actor )
+ {
+ SelectAllEventsInActor( actor );
+ }
+ }
+ break;
+ case IDC_SELECTEVENTS_ALL_BEFORE:
+ {
+ SelectionParams_t params;
+ Q_memset( &params, 0, sizeof( params ) );
+ params.forward = false;
+ params.time = GetTimeValueForMouse( m_nClickedX );
+ params.type = SelectionParams_t::SP_ALL;
+
+ SelectEvents( params );
+ }
+ break;
+ case IDC_SELECTEVENTS_ALL_AFTER:
+ {
+ SelectionParams_t params;
+ Q_memset( &params, 0, sizeof( params ) );
+ params.forward = true;
+ params.time = GetTimeValueForMouse( m_nClickedX );
+ params.type = SelectionParams_t::SP_ALL;
+
+ SelectEvents( params );
+ }
+ break;
+ case IDC_SELECTEVENTS_ACTIVE_BEFORE:
+ {
+ SelectionParams_t params;
+ Q_memset( &params, 0, sizeof( params ) );
+ params.forward = false;
+ params.time = GetTimeValueForMouse( m_nClickedX );
+ params.type = SelectionParams_t::SP_ACTIVE;
+
+ SelectEvents( params );
+ }
+ break;
+ case IDC_SELECTEVENTS_ACTIVE_AFTER:
+ {
+ SelectionParams_t params;
+ Q_memset( &params, 0, sizeof( params ) );
+ params.forward = true;
+ params.time = GetTimeValueForMouse( m_nClickedX );
+ params.type = SelectionParams_t::SP_ACTIVE;
+
+ SelectEvents( params );
+ }
+ break;
+ case IDC_SELECTEVENTS_CHANNEL_BEFORE:
+ {
+ SelectionParams_t params;
+ Q_memset( &params, 0, sizeof( params ) );
+ params.forward = false;
+ params.time = GetTimeValueForMouse( m_nClickedX );
+ params.type = SelectionParams_t::SP_CHANNEL;
+
+ SelectEvents( params );
+ }
+ break;
+ case IDC_SELECTEVENTS_CHANNEL_AFTER:
+ {
+ SelectionParams_t params;
+ Q_memset( &params, 0, sizeof( params ) );
+ params.forward = true;
+ params.time = GetTimeValueForMouse( m_nClickedX );
+ params.type = SelectionParams_t::SP_CHANNEL;
+
+ SelectEvents( params );
+ }
+ break;
+ case IDC_INSERT_TIME:
+ {
+ OnInsertTime();
+ }
+ break;
+ case IDC_DELETE_TIME:
+ {
+ OnDeleteTime();
+ }
+ break;
+ case IDC_CV_ALIGN_LEFT:
+ {
+ OnAlign( true );
+ }
+ break;
+ case IDC_CV_ALIGN_RIGHT:
+ {
+ OnAlign( false );
+ }
+ break;
+ case IDC_CV_SAMESIZE_SMALLEST:
+ {
+ OnMakeSameSize( true );
+ }
+ break;
+ case IDC_CV_SAMESIZE_LARGEST:
+ {
+ OnMakeSameSize( false );
+ }
+ break;
+ }
+
+ if ( iret == 1 )
+ {
+ SetActiveTool( this );
+ }
+ }
+ break;
+ }
+ return iret;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::PlayScene( bool forward )
+{
+ m_bForward = forward;
+ if ( !m_pScene )
+ return;
+
+ sound->Flush();
+
+ // Make sure phonemes are loaded
+ FacePoser_EnsurePhonemesLoaded();
+
+ // Unpause
+ if ( m_bSimulating && m_bPaused )
+ {
+ m_bPaused = false;
+ return;
+ }
+
+ m_bSimulating = true;
+ m_bPaused = false;
+
+// float soundlatency = max( sound->GetAmountofTimeAhead(), 0.0f );
+// soundlatency = min( 0.5f, soundlatency );
+
+ float soundlatency = 0.0f;
+
+ float sceneendtime = m_pScene->FindStopTime();
+
+ m_pScene->SetSoundFileStartupLatency( soundlatency );
+
+ if ( m_rgABPoints[ 0 ].active ||
+ m_rgABPoints[ 1 ].active )
+ {
+ if ( m_rgABPoints[ 0 ].active &&
+ m_rgABPoints[ 1 ].active )
+ {
+ float st = m_rgABPoints[ 0 ].time;
+ float ed = m_rgABPoints[ 1 ].time;
+
+ m_pScene->ResetSimulation( m_bForward, st, ed );
+
+ SetScrubTime( m_bForward ? st : ed );
+ SetScrubTargetTime( m_bForward ? ed : st );
+ }
+ else
+ {
+ float startonly = m_rgABPoints[ 0 ].active ? m_rgABPoints[ 0 ].time : m_rgABPoints[ 1 ].time;
+
+ m_pScene->ResetSimulation( m_bForward, startonly );
+
+ SetScrubTime( m_bForward ? startonly : sceneendtime );
+ SetScrubTargetTime( m_bForward ? sceneendtime : startonly );
+ }
+ }
+ else
+ {
+ // NO start end/loop
+ m_pScene->ResetSimulation( m_bForward );
+
+ SetScrubTime( m_bForward ? 0 : sceneendtime );
+ SetScrubTargetTime( m_bForward ? sceneendtime : 0 );
+ }
+
+ if ( g_viewerSettings.speedScale == 0.0f )
+ {
+ m_flLastSpeedScale = g_viewerSettings.speedScale;
+ m_bResetSpeedScale = true;
+
+ g_viewerSettings.speedScale = 1.0f;
+
+ Con_Printf( "Resetting speed scale to 1.0\n" );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : x -
+//-----------------------------------------------------------------------------
+void CChoreoView::MoveTimeSliderToPos( int x )
+{
+ m_flLeftOffset = (float)x;
+ m_pHorzScrollBar->setValue( (int)m_flLeftOffset );
+ InvalidateRect( (HWND)m_pHorzScrollBar->getHandle(), NULL, TRUE );
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::PauseScene( void )
+{
+ if ( !m_bSimulating )
+ return;
+
+ m_bPaused = true;
+ sound->StopAll();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Apply expression to actor's face
+// Input : *event -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+void CChoreoView::ProcessExpression( CChoreoScene *scene, CChoreoEvent *event )
+{
+ Assert( event->GetType() == CChoreoEvent::EXPRESSION );
+
+ StudioModel *model = FindAssociatedModel( scene, event->GetActor() );
+ if ( !model )
+ return;
+
+ CStudioHdr *hdr = model->GetStudioHdr();
+ if ( !hdr )
+ {
+ return;
+ }
+
+ CExpClass *p = expressions->FindClass( event->GetParameters(), true );
+ if ( !p )
+ {
+ return;
+ }
+
+ CExpression *exp = p->FindExpression( event->GetParameters2() );
+ if ( !exp )
+ {
+ return;
+ }
+
+ CChoreoActor *a = event->GetActor();
+ if ( !a )
+ return;
+
+ CChoreoActorWidget *actor = NULL;
+
+ int i;
+ for ( i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ actor = m_SceneActors[ i ];
+ if ( !actor )
+ continue;
+
+ if ( actor->GetActor() == a )
+ break;
+ }
+
+ if ( !actor || i >= m_SceneActors.Size() )
+ return;
+
+ float *settings = exp->GetSettings();
+ Assert( settings );
+ float *weights = exp->GetWeights();
+ Assert( weights );
+ float *current = actor->GetSettings();
+ Assert( current );
+
+ float flIntensity = event->GetIntensity( scene->GetTime() );
+
+ // blend in target values for correct actor
+ for ( LocalFlexController_t i = (LocalFlexController_t)0; i < hdr->numflexcontrollers(); i++ )
+ {
+ mstudioflexcontroller_t *pFlex = hdr->pFlexcontroller( i );
+ int j = pFlex->localToGlobal;
+ if ( j < 0 )
+ continue;
+ float s = clamp( weights[j] * flIntensity, 0.0, 1.0 );
+ current[ j ] = current[j] * (1.0f - s) + settings[ j ] * s;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *hdr -
+// *event -
+//-----------------------------------------------------------------------------
+void SetupFlexControllerTracks( CStudioHdr *hdr, CChoreoEvent *event )
+{
+ Assert( hdr );
+ Assert( event );
+
+ if ( !hdr )
+ return;
+
+ if ( !event )
+ return;
+
+ // Already done
+ if ( event->GetTrackLookupSet() )
+ return;
+
+ /*
+ // FIXME: Brian hooked this stuff up for some took work, but at this point the .mdl files don't look like they've been updated to include the remapping data yet...
+ int c = hdr->numflexcontrollerremaps();
+ for ( i = 0; i < c; ++i )
+ {
+ mstudioflexcontrollerremap_t *remap = hdr->pFlexcontrollerRemap( i );
+ Msg( "remap %s\n", remap->pszName() );
+ Msg( " type %d\n", remap->remaptype );
+ Msg( " num remaps %d (stereo %s)\n", remap->numremaps, remap->stereo ? "true" : "false" );
+ for ( int j = 0 ; j < remap->numremaps; ++j )
+ {
+ int index = remap->pRemapControlIndex( j );
+ Msg( " %d: maps to %d (%s) with %s\n", j, index, hdr->pFlexcontroller( index )->pszName(), remap->pRemapControl( j ) );
+ }
+ }
+ */
+
+ // Unlink stuff in case it doesn't exist
+ int nTrackCount = event->GetNumFlexAnimationTracks();
+ for ( int i = 0; i < nTrackCount; ++i )
+ {
+ CFlexAnimationTrack *pTrack = event->GetFlexAnimationTrack( i );
+ pTrack->SetFlexControllerIndex( LocalFlexController_t(-1), -1, 0 );
+ pTrack->SetFlexControllerIndex( LocalFlexController_t(-1), -1, 1 );
+ }
+
+ for ( LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); ++i )
+ {
+ int j = hdr->pFlexcontroller( i )->localToGlobal;
+
+ char const *name = hdr->pFlexcontroller( i )->pszName();
+ if ( !name )
+ continue;
+
+ bool combo = false;
+ // Look up or create all necessary tracks
+ if ( strncmp( "right_", name, 6 ) == 0 )
+ {
+ combo = true;
+ name = &name[6];
+ }
+
+ CFlexAnimationTrack *track = event->FindTrack( name );
+ if ( !track )
+ {
+ track = event->AddTrack( name );
+ Assert( track );
+ }
+
+ track->SetFlexControllerIndex( i, j, 0 );
+ if ( combo )
+ {
+ track->SetFlexControllerIndex( LocalFlexController_t(i + 1), hdr->pFlexcontroller( LocalFlexController_t(i + 1) )->localToGlobal, 1 );
+ track->SetComboType( true );
+ }
+
+ float orig_min = track->GetMin( );
+ float orig_max = track->GetMax( );
+
+ // set range
+ if (hdr->pFlexcontroller( i )->min == 0.0f || hdr->pFlexcontroller( i )->max == 1.0f)
+ {
+ track->SetInverted( false );
+ track->SetMin( hdr->pFlexcontroller( i )->min );
+ track->SetMax( hdr->pFlexcontroller( i )->max );
+ }
+ else
+ {
+ // invert ranges for wide ranged, makes sense considering flexcontroller names...
+ track->SetInverted( true );
+ track->SetMin( hdr->pFlexcontroller( i )->max );
+ track->SetMax( hdr->pFlexcontroller( i )->min );
+ }
+
+ // resample track based on this models dynamic range
+ if (track->GetNumSamples( 0 ) > 0)
+ {
+ float range = track->GetMax( ) - track->GetMin( );
+
+ for (int i = 0; i < track->GetNumSamples( 0 ); i++)
+ {
+ CExpressionSample *sample = track->GetSample( i, 0 );
+ float rangedValue = orig_min * (1 - sample->value) + orig_max * sample->value;
+ sample->value = clamp( (rangedValue - track->GetMin( )) / range, 0.0, 1.0 );
+ }
+ }
+
+ // skip next flex since we've already assigned it
+ if ( combo )
+ {
+ i++;
+ }
+ }
+
+ event->SetTrackLookupSet( true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Apply flexanimation to actor's face
+// Input : *event -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+void CChoreoView::ProcessFlexAnimation( CChoreoScene *scene, CChoreoEvent *event )
+{
+ Assert( event->GetType() == CChoreoEvent::FLEXANIMATION );
+
+ StudioModel *model = FindAssociatedModel( scene, event->GetActor() );
+ if ( !model )
+ return;
+
+ CStudioHdr *hdr = model->GetStudioHdr();
+ if ( !hdr )
+ {
+ return;
+ }
+
+ CChoreoActor *a = event->GetActor();
+
+ CChoreoActorWidget *actor = NULL;
+
+ int i;
+ for ( i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ actor = m_SceneActors[ i ];
+ if ( !actor )
+ continue;
+
+ if ( !stricmp( actor->GetActor()->GetName(), a->GetName() ) )
+ break;
+ }
+
+ if ( !actor || i >= m_SceneActors.Size() )
+ return;
+
+ float *current = actor->GetSettings();
+ Assert( current );
+
+ if ( !event->GetTrackLookupSet() )
+ {
+ SetupFlexControllerTracks( hdr, event );
+ }
+
+ float weight = event->GetIntensity( scene->GetTime() );
+
+ CChoreoEventWidget *eventwidget = FindWidgetForEvent( event );
+ bool bUpdateSliders = (eventwidget && eventwidget->IsSelected() && model == models->GetActiveStudioModel() );
+
+ // Iterate animation tracks
+ for ( i = 0; i < event->GetNumFlexAnimationTracks(); i++ )
+ {
+ CFlexAnimationTrack *track = event->GetFlexAnimationTrack( i );
+ if ( !track )
+ continue;
+
+ // Disabled
+ if ( !track->IsTrackActive() )
+ {
+ if ( bUpdateSliders )
+ {
+ for ( int side = 0; side < 1 + track->IsComboType(); side++ )
+ {
+ int controller = track->GetFlexControllerIndex( side );
+ if ( controller != -1 && !g_pFlexPanel->IsEdited( controller ))
+ {
+ g_pFlexPanel->SetSlider( controller, 0.0 );
+ g_pFlexPanel->SetInfluence( controller, 0.0f );
+ }
+ }
+ }
+ 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( scene->GetTime(), side );
+
+ if (bUpdateSliders && !g_pFlexPanel->IsEdited( controller ) )
+ {
+ g_pFlexPanel->SetSlider( controller, flIntensity );
+ g_pFlexPanel->SetInfluence( controller, 1.0f );
+ }
+
+ flIntensity = current[ controller ] * (1 - weight) + flIntensity * weight;
+ current[ controller ] = flIntensity;
+ }
+ }
+ }
+ else
+ {
+ int controller = track->GetFlexControllerIndex( 0 );
+ if ( controller != -1 )
+ {
+ // Get spline intensity for controller
+ float flIntensity = track->GetIntensity( scene->GetTime(), 0 );
+
+ if (bUpdateSliders && !g_pFlexPanel->IsEdited( controller ) )
+ {
+ g_pFlexPanel->SetSlider( controller, flIntensity );
+ g_pFlexPanel->SetInfluence( controller, 1.0f );
+ }
+
+ flIntensity = current[ controller ] * (1 - weight) + flIntensity * weight;
+ current[ controller ] = flIntensity;
+ }
+ }
+ }
+}
+
+
+#include "mapentities.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Apply lookat target
+// Input : *event -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+void CChoreoView::ProcessLookat( CChoreoScene *scene, CChoreoEvent *event )
+{
+ Assert( event->GetType() == CChoreoEvent::LOOKAT );
+
+ if ( !event->GetActor() )
+ return;
+
+ CChoreoActor *a = event->GetActor();
+
+ Assert( a );
+
+ StudioModel *model = FindAssociatedModel( scene, a );
+ if ( !model )
+ {
+ return;
+ }
+
+ float flIntensity = event->GetIntensity( scene->GetTime() );
+
+ // clamp in-ramp to 0.3 seconds
+ float flDuration = scene->GetTime() - event->GetStartTime();
+ float flMaxIntensity = flDuration < 0.3f ? SimpleSpline( flDuration / 0.3f ) : 1.0f;
+ flDuration = event->GetEndTime() - scene->GetTime();
+ flMaxIntensity = min( flMaxIntensity, flDuration < 0.3f ? SimpleSpline( flDuration / 0.3f ) : 1.0f );
+ flIntensity = clamp( flIntensity, 0.0f, flMaxIntensity );
+
+ if (!stricmp( event->GetParameters(), a->GetName() ) || !stricmp( event->GetParameters(), "!self" ))
+ {
+ model->AddLookTargetSelf( flIntensity );
+ }
+ else if ( !stricmp( event->GetParameters(), "player" ) ||
+ !stricmp( event->GetParameters(), "!player" ) )
+ {
+ Vector vecTarget = model->m_origin;
+ vecTarget.z = 0;
+
+ model->AddLookTarget( vecTarget, flIntensity );
+ }
+ else
+ {
+ mapentities->CheckUpdateMap( scene->GetMapname() );
+
+ Vector orgActor;
+ Vector orgTarget;
+ QAngle anglesActor;
+ QAngle anglesDummy;
+
+ if ( event->GetPitch() != 0 ||
+ event->GetYaw() != 0 )
+ {
+ QAngle angles( -(float)event->GetPitch(),
+ (float)event->GetYaw(),
+ 0 );
+
+ matrix3x4_t matrix;
+
+ AngleMatrix( model->m_angles, matrix );
+
+ Vector vecForward;
+ AngleVectors( angles, &vecForward );
+
+ Vector eyeTarget;
+ VectorRotate( vecForward, matrix, eyeTarget );
+ VectorScale( eyeTarget, 75, eyeTarget );
+
+ model->AddLookTarget( eyeTarget, flIntensity );
+ }
+ else
+ {
+ if ( mapentities->LookupOrigin( a->GetName(), orgActor, anglesActor ) )
+ {
+ if ( mapentities->LookupOrigin( event->GetParameters(), orgTarget, anglesDummy ) )
+ {
+ Vector delta = orgTarget - orgActor;
+
+ matrix3x4_t matrix;
+ Vector lookTarget;
+
+ // Rotate around actor's placed forward direction since we look straight down x in faceposer/hlmv
+ AngleMatrix( anglesActor, matrix );
+ VectorIRotate( delta, matrix, lookTarget );
+
+ model->AddLookTarget( lookTarget, flIntensity );
+ return;
+ }
+ }
+ // hack up something based on the name.
+ {
+ const char *cp = event->GetParameters();
+ float value = 0.0;
+ while (*cp)
+ {
+ value += *cp++;
+ }
+ value = cos( value );
+ value = acos( value );
+ QAngle angles( 0.0, value * 45 / M_PI, 0.0 );
+
+ matrix3x4_t matrix;
+ AngleMatrix( model->m_angles, matrix );
+
+ Vector vecForward;
+ AngleVectors( angles, &vecForward );
+
+ Vector eyeTarget;
+ VectorRotate( vecForward, matrix, eyeTarget );
+ VectorScale( eyeTarget, 75, eyeTarget );
+
+ model->AddLookTarget( eyeTarget, flIntensity );
+ }
+
+ }
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns a target for Faceing
+// Input : *event -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CChoreoView::GetTarget( CChoreoScene *scene, CChoreoEvent *event, Vector &vecTarget, QAngle &vecAngle )
+{
+ if ( !event->GetActor() )
+ return false;
+
+ CChoreoActor *a = event->GetActor();
+
+ Assert( a );
+
+ StudioModel *model = FindAssociatedModel( scene, a );
+ if ( !model )
+ {
+ return false;
+ }
+
+ if (!stricmp( event->GetParameters(), a->GetName() ))
+ {
+ vecTarget = vec3_origin;
+ return true;
+ }
+ else if ( !stricmp( event->GetParameters(), "player" ) ||
+ !stricmp( event->GetParameters(), "!player" ) )
+ {
+ vecTarget = model->m_origin;
+ vecTarget.z = 0;
+ vecAngle = model->m_angles;
+
+ return true;
+ }
+ else
+ {
+ mapentities->CheckUpdateMap( scene->GetMapname() );
+
+ Vector orgActor;
+ Vector orgTarget;
+ QAngle anglesActor;
+ QAngle anglesDummy;
+
+ if ( event->GetPitch() != 0 ||
+ event->GetYaw() != 0 )
+ {
+ QAngle angles( -(float)event->GetPitch(),
+ (float)event->GetYaw(),
+ 0 );
+
+ matrix3x4_t matrix;
+
+ AngleMatrix( model->m_angles, matrix );
+
+ QAngle angles2 = angles;
+ angles2.x *= 0.6f;
+ angles2.y *= 0.8f;
+
+ Vector vecForward, vecForward2;
+ AngleVectors( angles, &vecForward );
+ AngleVectors( angles2, &vecForward2 );
+
+ VectorNormalize( vecForward );
+ VectorNormalize( vecForward2 );
+
+ Vector eyeTarget, headTarget;
+
+ VectorRotate( vecForward, matrix, eyeTarget );
+ VectorRotate( vecForward2, matrix, headTarget );
+
+ VectorScale( eyeTarget, 150, eyeTarget );
+
+ VectorScale( headTarget, 150, vecTarget );
+ return true;
+
+ }
+ else
+ {
+ if ( mapentities->LookupOrigin( a->GetName(), orgActor, anglesActor ) )
+ {
+ if ( mapentities->LookupOrigin( event->GetParameters(), orgTarget, anglesDummy ) )
+ {
+ Vector delta = orgTarget - orgActor;
+
+ matrix3x4_t matrix;
+ Vector lookTarget;
+
+ // Rotate around actor's placed forward direction since we look straight down x in faceposer/hlmv
+ AngleMatrix( anglesActor, matrix );
+ VectorIRotate( delta, matrix, vecTarget );
+
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Apply lookat target
+// Input : *event -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+void CChoreoView::ProcessFace( CChoreoScene *scene, CChoreoEvent *event )
+{
+ Assert( event->GetType() == CChoreoEvent::FACE );
+
+ if ( !event->GetActor() )
+ return;
+
+ CChoreoActor *a = event->GetActor();
+
+ Assert( a );
+
+ StudioModel *model = FindAssociatedModel( scene, a );
+ if ( !model )
+ {
+ return;
+ }
+
+ Vector vecTarget;
+ QAngle vecAngle;
+
+ if (!GetTarget( scene, event, vecTarget, vecAngle ))
+ {
+ return;
+ }
+
+ /*
+ // FIXME: this is broke
+ float goalYaw = -(vecAngle.y > 180 ? 360 - vecAngle.y : vecAngle.y );
+
+ float intensity = event->GetIntensity( scene->GetTime() );
+
+ float diff = goalYaw * intensity;
+ float dir = 1.0;
+
+ if (diff < 0)
+ {
+ diff = -diff;
+ dir = -1;
+ }
+
+ float spineintensity = 0 * max( 0.0, (intensity - 0.5) / 0.5 );
+ float goalSpineYaw = min( diff * (1.0 - spineintensity), 30 );
+ //float idealYaw = info->m_flInitialYaw + (diff - m_goalBodyYaw * dir - m_goalSpineYaw * dir) * dir;
+ // float idealYaw = UTIL_AngleMod( info->m_flInitialYaw + diff * intensity );
+
+ // FIXME: this is broke
+ // model->SetSpineYaw( goalSpineYaw * dir);
+ // model->SetBodyYaw( goalBodyYaw * dir );
+
+ // Msg("yaw %.1f : %.1f (%.1f)\n", info->m_flInitialYaw, idealYaw, intensity );
+ */
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *scene -
+// *event -
+//-----------------------------------------------------------------------------
+void CChoreoView::ProcessLoop( CChoreoScene *scene, CChoreoEvent *event )
+{
+ Assert( event->GetType() == CChoreoEvent::LOOP );
+
+ // Don't loop when dragging scrubber!
+ if ( IsScrubbing() )
+ return;
+
+ float backtime = (float)atof( event->GetParameters() );
+
+ bool process = true;
+ int counter = event->GetLoopCount();
+ if ( counter != -1 )
+ {
+ int remaining = event->GetNumLoopsRemaining();
+ if ( remaining <= 0 )
+ {
+ process = false;
+ }
+ else
+ {
+ event->SetNumLoopsRemaining( --remaining );
+ }
+ }
+
+ if ( !process )
+ return;
+
+ scene->LoopToTime( backtime );
+ SetScrubTime( backtime );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add a gesture layer
+// Input : *event -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+void CChoreoView::ProcessGesture( CChoreoScene *scene, CChoreoEvent *event )
+{
+ Assert( event->GetType() == CChoreoEvent::GESTURE );
+
+ // NULL event is just a placeholder
+ if ( !Q_stricmp( event->GetName(), "NULL" ) )
+ {
+ return;
+ }
+
+ StudioModel *model = FindAssociatedModel( scene, event->GetActor() );
+ if ( !model )
+ return;
+
+ if ( !event->GetActor() )
+ return;
+
+ CChoreoActor *a = event->GetActor();
+
+ Assert( a );
+
+ int iSequence = model->LookupSequence( event->GetParameters() );
+ if (iSequence < 0)
+ return;
+
+ // Get spline intensity for controller
+ float eventlocaltime = scene->GetTime() - event->GetStartTime();
+
+ float referencetime = event->GetOriginalPercentageFromPlaybackPercentage( eventlocaltime / event->GetDuration() ) * event->GetDuration();
+
+ float resampledtime = event->GetStartTime() + referencetime;
+
+ float cycle = event->GetCompletion( resampledtime );
+
+ int iLayer = model->GetNewAnimationLayer( a->FindChannelIndex( event->GetChannel() ) );
+
+ model->SetOverlaySequence( iLayer, iSequence, event->GetIntensity( scene->GetTime() ) );
+ model->SetOverlayRate( iLayer, cycle, 0.0 );
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Apply a sequence
+// Input : *event -
+//-----------------------------------------------------------------------------
+void CChoreoView::ProcessSequence( CChoreoScene *scene, CChoreoEvent *event )
+{
+ Assert( event->GetType() == CChoreoEvent::SEQUENCE );
+
+ if ( !m_bProcessSequences )
+ {
+ return;
+ }
+
+ StudioModel *model = FindAssociatedModel( scene, event->GetActor() );
+ if ( !model )
+ return;
+
+ if ( !event->GetActor() )
+ return;
+
+ CChoreoActor *a = event->GetActor();
+
+ Assert( a );
+
+ int iSequence = model->LookupSequence( event->GetParameters() );
+ if (iSequence < 0)
+ return;
+
+ float flFrameRate;
+ float flGroundSpeed;
+ model->GetSequenceInfo( iSequence, &flFrameRate, &flGroundSpeed );
+
+ float cycle;
+ bool looping = model->GetSequenceLoops( iSequence );
+ if (looping)
+ {
+ float dt = scene->GetTime() - event->m_flPrevTime;
+ event->m_flPrevTime = scene->GetTime();
+ dt = clamp( dt, 0.0, 0.1 );
+ cycle = event->m_flPrevCycle + flFrameRate * dt;
+ cycle = cycle - (int)cycle;
+ event->m_flPrevCycle = cycle;
+ }
+ else
+ {
+ float dt = scene->GetTime() - event->GetStartTime();
+ cycle = flFrameRate * dt;
+ cycle = cycle - (int)(cycle);
+ }
+
+ // FIXME: shouldn't sequences always be lower priority than gestures?
+ int iLayer = model->GetNewAnimationLayer( a->FindChannelIndex( event->GetChannel() ) );
+ model->SetOverlaySequence( iLayer, iSequence, event->GetIntensity( scene->GetTime() ) );
+ model->SetOverlayRate( iLayer, cycle, 0.0 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Apply a walking animation
+// Input : *event -
+//-----------------------------------------------------------------------------
+void CChoreoView::ProcessMoveto( CChoreoScene *scene, CChoreoEvent *event )
+{
+ Assert( event->GetType() == CChoreoEvent::MOVETO );
+
+ if ( !m_bProcessSequences )
+ {
+ return;
+ }
+
+ StudioModel *model = FindAssociatedModel( scene, event->GetActor() );
+ if ( !model )
+ return;
+
+ if ( !event->GetActor() )
+ return;
+
+ int iSequence = GetMovetoSequence( scene, event, model );
+ if (iSequence < 0)
+ return;
+
+ float flFrameRate;
+ float flGroundSpeed;
+ model->GetSequenceInfo( iSequence, &flFrameRate, &flGroundSpeed );
+
+ float dt = scene->GetTime() - event->GetStartTime();
+ float cycle = flFrameRate * dt;
+ cycle = cycle - (int)(cycle);
+
+ float idealAccel = 100;
+
+ // accel to ideal
+ float t1 = flGroundSpeed / idealAccel;
+
+ float intensity = 1.0;
+
+ if (dt < t1)
+ {
+ intensity = dt / t1;
+ }
+ else if (event->GetDuration() - dt < t1)
+ {
+ intensity = (event->GetDuration() - dt) / t1;
+ }
+
+ // movement should always be higher priority than postures, but not gestures....grrr, any way to tell them apart?
+ int iLayer = model->GetNewAnimationLayer( 0 /* a->FindChannelIndex( event->GetChannel() ) */ );
+ model->SetOverlaySequence( iLayer, iSequence, intensity );
+ model->SetOverlayRate( iLayer, cycle, 0.0 );
+}
+
+
+
+int CChoreoView::GetMovetoSequence( CChoreoScene *scene, CChoreoEvent *event, StudioModel *model )
+{
+ // FIXME: needs to pull from event (activity or sequence?)
+ if ( !event->GetParameters2() || !event->GetParameters2()[0] )
+ return model->LookupSequence( "walk_all" );
+
+ // Custom distance styles are appended to param2 with a space as a separator
+ const char *pszAct = Q_strstr( event->GetParameters2(), " " );
+ if ( pszAct )
+ {
+ char szActName[256];
+ Q_strncpy( szActName, event->GetParameters2(), sizeof(szActName) );
+ szActName[ (pszAct-event->GetParameters2()) ] = '\0';
+ pszAct = szActName;
+ }
+ else
+ {
+ pszAct = event->GetParameters2();
+ }
+
+ if ( !Q_strcmp( pszAct, "Walk" ) )
+ {
+ pszAct = "ACT_WALK";
+ }
+ else if ( !Q_strcmp( pszAct, "Run" ) )
+ {
+ pszAct = "ACT_RUN";
+ }
+ else if ( !Q_strcmp( pszAct, "CrouchWalk" ) )
+ {
+ pszAct = "ACT_WALK_CROUCH";
+ }
+
+ int iSequence = model->LookupActivity( pszAct );
+
+ if (iSequence == -1)
+ {
+ return model->LookupSequence( "walk_all" );
+ }
+ return iSequence;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Process a pause event
+// Input : *event -
+//-----------------------------------------------------------------------------
+void CChoreoView::ProcessPause( CChoreoScene *scene, CChoreoEvent *event )
+{
+ Assert( event->GetType() == CChoreoEvent::SECTION );
+
+ // Don't pause if scrubbing
+ bool scrubbing = ( m_nDragType == DRAGTYPE_SCRUBBER ) ? true : false;
+ if ( scrubbing )
+ return;
+
+ PauseScene();
+
+ m_bAutomated = false;
+ m_nAutomatedAction = SCENE_ACTION_UNKNOWN;
+ m_flAutomationDelay = 0.0f;
+ m_flAutomationTime = 0.0f;
+
+ // Check for auto resume/cancel
+ ParseFromMemory( (char *)event->GetParameters(), strlen( event->GetParameters() ) );
+ if ( tokenprocessor->TokenAvailable() )
+ {
+ tokenprocessor->GetToken( false );
+ if ( !stricmp( tokenprocessor->CurrentToken(), "automate" ) )
+ {
+ if ( tokenprocessor->TokenAvailable() )
+ {
+ tokenprocessor->GetToken( false );
+ if ( !stricmp( tokenprocessor->CurrentToken(), "Cancel" ) )
+ {
+ m_nAutomatedAction = SCENE_ACTION_CANCEL;
+ }
+ else if ( !stricmp( tokenprocessor->CurrentToken(), "Resume" ) )
+ {
+ m_nAutomatedAction = SCENE_ACTION_RESUME;
+ }
+
+ if ( tokenprocessor->TokenAvailable() &&
+ m_nAutomatedAction != SCENE_ACTION_UNKNOWN )
+ {
+ tokenprocessor->GetToken( false );
+ m_flAutomationDelay = (float)atof( tokenprocessor->CurrentToken() );
+
+ if ( m_flAutomationDelay > 0.0f )
+ {
+ // Success
+ m_bAutomated = true;
+ m_flAutomationTime = 0.0f;
+ }
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Main event processor
+// Input : *event -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+void CChoreoView::ProcessEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event )
+{
+ if ( !event || !event->GetActive() )
+ return;
+
+ CChoreoActor *actor = event->GetActor();
+ if ( actor && !actor->GetActive() )
+ {
+ return;
+ }
+
+ CChoreoChannel *channel = event->GetChannel();
+ if ( channel && !channel->GetActive() )
+ {
+ return;
+ }
+
+ switch( event->GetType() )
+ {
+ case CChoreoEvent::EXPRESSION:
+ ProcessExpression( scene, event );
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ ProcessFlexAnimation( scene, event );
+ break;
+ case CChoreoEvent::LOOKAT:
+ ProcessLookat( scene, event );
+ break;
+ case CChoreoEvent::FACE:
+ ProcessFace( scene, event );
+ break;
+ case CChoreoEvent::GESTURE:
+ ProcessGesture( scene, event );
+ break;
+ case CChoreoEvent::SEQUENCE:
+ ProcessSequence( scene, event );
+ break;
+ case CChoreoEvent::SUBSCENE:
+ ProcessSubscene( scene, event );
+ break;
+ case CChoreoEvent::SPEAK:
+ ProcessSpeak( scene, event );
+ break;
+ case CChoreoEvent::MOVETO:
+ ProcessMoveto( scene, event );
+ break;
+ case CChoreoEvent::STOPPOINT:
+ // Nothing
+ break;
+ case CChoreoEvent::INTERRUPT:
+ ProcessInterrupt( scene, event );
+ break;
+ case CChoreoEvent::PERMIT_RESPONSES:
+ ProcessPermitResponses( scene, event );
+ break;
+ default:
+ break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Main event completion checker
+// Input : *event -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CChoreoView::CheckEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event )
+{
+ if ( !event || !event->GetActive() )
+ return true;
+
+ CChoreoActor *actor = event->GetActor();
+ if ( actor && !actor->GetActive() )
+ {
+ return true;
+ }
+
+ CChoreoChannel *channel = event->GetChannel();
+ if ( channel && !channel->GetActive() )
+ {
+ return true;
+ }
+
+ switch( event->GetType() )
+ {
+ case CChoreoEvent::EXPRESSION:
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ break;
+ case CChoreoEvent::LOOKAT:
+ break;
+ case CChoreoEvent::GESTURE:
+ break;
+ case CChoreoEvent::SEQUENCE:
+ break;
+ case CChoreoEvent::SUBSCENE:
+ break;
+ case CChoreoEvent::SPEAK:
+ break;
+ case CChoreoEvent::MOVETO:
+ break;
+ case CChoreoEvent::INTERRUPT:
+ break;
+ case CChoreoEvent::PERMIT_RESPONSES:
+ break;
+ default:
+ break;
+ }
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::PauseThink( void )
+{
+ // FIXME: Game code would check for conditions being met
+
+ if ( !m_bAutomated )
+ return;
+
+ m_flAutomationTime += fabs( m_flFrameTime );
+
+ RECT rcPauseRect;
+ rcPauseRect.left = 0;
+ rcPauseRect.right = w2();
+ rcPauseRect.top = GetCaptionHeight() + SCRUBBER_HEIGHT;
+ rcPauseRect.bottom = rcPauseRect.top + 10;
+
+ CChoreoWidgetDrawHelper drawHelper( this,
+ rcPauseRect,
+ COLOR_CHOREO_BACKGROUND );
+
+ DrawSceneABTicks( drawHelper );
+
+ if ( m_flAutomationDelay > 0.0f &&
+ m_flAutomationTime < m_flAutomationDelay )
+ {
+ char sz[ 256 ];
+ sprintf( sz, "Pause %.2f/%.2f", m_flAutomationTime, m_flAutomationDelay );
+
+ int textlen = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, sz );
+
+ RECT rcText;
+ GetScrubHandleRect( rcText, true );
+
+ rcText.left = ( rcText.left + rcText.right ) / 2;
+ rcText.left -= ( textlen * 0.5f );
+ rcText.right = rcText.left + textlen + 1;
+
+ rcText.top = rcPauseRect.top;
+ rcText.bottom = rcPauseRect.bottom;
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, COLOR_CHOREO_PLAYBACKTICKTEXT, rcText, sz );
+
+ return;
+ }
+
+ // Time to act
+ m_bAutomated = false;
+
+ switch ( m_nAutomatedAction )
+ {
+ case SCENE_ACTION_RESUME:
+ m_bPaused = false;
+ sound->StopAll();
+ break;
+ case SCENE_ACTION_CANCEL:
+ FinishSimulation();
+ break;
+ default:
+ break;
+ }
+
+ m_nAutomatedAction = SCENE_ACTION_UNKNOWN;
+ m_flAutomationTime = 0.0f;
+ m_flAutomationDelay = 0.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Conclude simulation
+//-----------------------------------------------------------------------------
+void CChoreoView::FinishSimulation( void )
+{
+ if ( !m_bSimulating )
+ return;
+
+// m_pScene->ResetSimulation();
+
+ m_bSimulating = false;
+ m_bPaused = false;
+
+ sound->StopAll();
+
+ if ( m_bResetSpeedScale )
+ {
+ m_bResetSpeedScale = false;
+ g_viewerSettings.speedScale = m_flLastSpeedScale;
+ m_flLastSpeedScale = 0.0f;
+
+ Con_Printf( "Resetting speed scale to %f\n", m_flLastSpeedScale );
+ }
+
+ models->ClearOverlaysSequences();
+
+ // redraw();
+}
+
+void CChoreoView::SceneThink( float time )
+{
+ if ( !m_pScene )
+ return;
+
+ if ( m_bSimulating )
+ {
+ if ( m_bPaused )
+ {
+ PauseThink();
+ }
+ else
+ {
+ m_pScene->SetSoundFileStartupLatency( 0.0f );
+
+ models->CheckResetFlexes();
+
+ ResetTargetSettings();
+
+ models->ClearOverlaysSequences();
+
+ // Tell scene to go
+ m_pScene->Think( time );
+
+ // Move flexes toward their targets
+ UpdateCurrentSettings();
+ }
+ }
+ else
+ {
+ FinishSimulation();
+ }
+
+ if ( !ShouldProcessSpeak() )
+ {
+ bool autoprocess = ShouldAutoProcess();
+ bool anyscrub = IsAnyToolScrubbing() ;
+ bool anyprocessing = IsAnyToolProcessing();
+
+ //Con_Printf( "autoprocess %i anyscrub %i anyprocessing %i\n",
+ // autoprocess ? 1 : 0,
+ // anyscrub ? 1 : 0,
+ // anyprocessing ? 1 : 0 );
+
+ if ( !anyscrub &&
+ !anyprocessing &&
+ autoprocess &&
+ !m_bForceProcess )
+ {
+ sound->StopAll();
+
+ // why clear lookat?
+ //models->ClearModelTargets( false );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::LayoutScene( void )
+{
+ if ( !m_pScene )
+ return;
+
+ if ( m_bLayoutIsValid )
+ return;
+
+ m_pScene->ReconcileTags();
+
+ RECT rc;
+ GetClientRect( (HWND)getHandle(), &rc );
+
+ RECT rcClient = rc;
+ rcClient.top += GetStartRow();
+ OffsetRect( &rcClient, 0, -m_nTopOffset );
+
+ m_flStartTime = m_flLeftOffset / GetPixelsPerSecond();
+
+ m_flEndTime = m_flStartTime + (float)( rcClient.right - GetLabelWidth() ) / GetPixelsPerSecond();
+
+ m_rcTimeLine = rcClient;
+ m_rcTimeLine.top = GetCaptionHeight() + SCRUBBER_HEIGHT;
+ m_rcTimeLine.bottom = m_rcTimeLine.top + 44;
+
+ int currentRow = rcClient.top + 2;
+ int itemHeight;
+
+ // Draw actors
+ int i;
+ for ( i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *a = m_SceneActors[ i ];
+ Assert( a );
+ if ( !a )
+ {
+ continue;
+ }
+
+ // Figure out rectangle
+ itemHeight = a->GetItemHeight();
+
+ RECT rcActor = rcClient;
+ rcActor.top = currentRow;
+ rcActor.bottom = currentRow + itemHeight;
+
+ a->Layout( rcActor );
+
+ currentRow += itemHeight;
+ }
+
+ // Draw section tabs
+ for ( i = 0; i < m_SceneGlobalEvents.Size(); i++ )
+ {
+ CChoreoGlobalEventWidget *e = m_SceneGlobalEvents[ i ];
+ if ( !e )
+ continue;
+
+ RECT rcEvent;
+ rcEvent = m_rcTimeLine;
+
+ float frac = ( e->GetEvent()->GetStartTime() - m_flStartTime ) / ( m_flEndTime - m_flStartTime );
+
+ rcEvent.left = GetLabelWidth() + rcEvent.left + (int)( frac * ( m_rcTimeLine.right - m_rcTimeLine.left - GetLabelWidth() ) );
+ rcEvent.left -= 4;
+ rcEvent.right = rcEvent.left + 8;
+ rcEvent.bottom += 0;
+ rcEvent.top = rcEvent.bottom - 8;
+
+ if ( rcEvent.left + 10 < GetLabelWidth() )
+ {
+ e->setVisible( false );
+ }
+ else
+ {
+ e->setVisible( true );
+ }
+
+ // OffsetRect( &rcEvent, GetLabelWidth(), 0 );
+
+ e->Layout( rcEvent );
+ }
+
+ m_bLayoutIsValid = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::DeleteSceneWidgets( void )
+{
+ bool oldcandraw = m_bCanDraw;
+
+ m_bCanDraw = false;
+
+ int i;
+ CChoreoWidget *w;
+
+ ClearStatusArea();
+
+ for( i = 0 ; i < m_SceneActors.Size(); i++ )
+ {
+ w = m_SceneActors[ i ];
+ m_ActorExpanded[ i ].expanded = ((CChoreoActorWidget *)w)->GetShowChannels();
+ delete w;
+ }
+
+ m_SceneActors.RemoveAll();
+
+ for( i = 0 ; i < m_SceneGlobalEvents.Size(); i++ )
+ {
+ w = m_SceneGlobalEvents[ i ];
+ delete w;
+ }
+
+ m_SceneGlobalEvents.RemoveAll();
+
+ m_bCanDraw = oldcandraw;
+
+ // Make sure nobody is still pointing at us
+ m_pClickedActor = NULL;
+ m_pClickedChannel = NULL;
+ m_pClickedEvent = NULL;
+ m_pClickedGlobalEvent = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::InvalidateLayout( void )
+{
+ if ( m_bSuppressLayout )
+ return;
+
+ if ( ComputeHPixelsNeeded() != m_nLastHPixelsNeeded )
+ {
+ RepositionHSlider();
+ }
+
+ if ( ComputeVPixelsNeeded() != m_nLastVPixelsNeeded )
+ {
+ RepositionVSlider();
+ }
+
+ // Recheck gesture start/end times
+ if ( m_pScene )
+ {
+ m_pScene->ReconcileGestureTimes();
+ m_pScene->ReconcileCloseCaption();
+ }
+
+ m_bLayoutIsValid = false;
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::CreateSceneWidgets( void )
+{
+ DeleteSceneWidgets();
+
+ m_bSuppressLayout = true;
+
+ int i;
+ for ( i = 0; i < m_pScene->GetNumActors(); i++ )
+ {
+ CChoreoActor *a = m_pScene->GetActor( i );
+ Assert( a );
+ if ( !a )
+ continue;
+
+ CChoreoActorWidget *actorWidget = new CChoreoActorWidget( NULL );
+ Assert( actorWidget );
+
+ actorWidget->SetActor( a );
+ actorWidget->Create();
+
+ m_SceneActors.AddToTail( actorWidget );
+
+ actorWidget->ShowChannels( m_ActorExpanded[ i ].expanded );
+ }
+
+ // Find global events
+ for ( i = 0; i < m_pScene->GetNumEvents(); i++ )
+ {
+ CChoreoEvent *e = m_pScene->GetEvent( i );
+ if ( !e || e->GetActor() )
+ continue;
+
+ CChoreoGlobalEventWidget *eventWidget = new CChoreoGlobalEventWidget( NULL );
+ Assert( eventWidget );
+
+ eventWidget->SetEvent( e );
+ eventWidget->Create();
+
+ m_SceneGlobalEvents.AddToTail( eventWidget );
+ }
+
+ m_bSuppressLayout = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoView::GetLabelWidth( void )
+{
+ return m_nLabelWidth;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoView::GetStartRow( void )
+{
+ return m_nStartRow + GetCaptionHeight() + SCRUBBER_HEIGHT;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoView::GetRowHeight( void )
+{
+ return m_nRowHeight;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoView::GetFontSize( void )
+{
+ return m_nFontSize;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoView::ComputeVPixelsNeeded( void )
+{
+ int pixels = 0;
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *actor = m_SceneActors[ i ];
+ if ( !actor )
+ continue;
+
+ pixels += actor->GetItemHeight() + 2;
+ }
+
+ pixels += GetStartRow() + 15;
+
+// pixels += m_nInfoHeight;
+
+ //pixels += 30;
+ return pixels;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoView::ComputeHPixelsNeeded( void )
+{
+ if ( !m_pScene )
+ {
+ return 0;
+ }
+
+ int pixels = 0;
+ float maxtime = m_pScene->FindStopTime();
+ if ( maxtime < 5.0 )
+ {
+ maxtime = 5.0f;
+ }
+ pixels = (int)( ( maxtime + 5.0 ) * GetPixelsPerSecond() );
+
+ return pixels;
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::RepositionVSlider( void )
+{
+ int pixelsneeded = ComputeVPixelsNeeded();
+
+ if ( pixelsneeded <= ( h2() - GetStartRow() ))
+ {
+ m_pVertScrollBar->setVisible( false );
+ m_nTopOffset = 0;
+ }
+ else
+ {
+ m_pVertScrollBar->setVisible( true );
+ }
+
+ m_pVertScrollBar->setBounds( w2() - m_nScrollbarHeight, GetStartRow(), m_nScrollbarHeight, h2() - m_nScrollbarHeight - GetStartRow() );
+
+ //int visiblepixels = h2() - m_nScrollbarHeight - GetStartRow();
+ //m_nTopOffset = min( pixelsneeded - visiblepixels, m_nTopOffset );
+ 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() - GetStartRow() );
+
+ m_nLastVPixelsNeeded = pixelsneeded;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::RepositionHSlider( void )
+{
+ int pixelsneeded = ComputeHPixelsNeeded();
+
+ int w = w2();
+ int lw = GetLabelWidth();
+
+ if ( pixelsneeded <= ( w - lw ) )
+ {
+ m_pHorzScrollBar->setVisible( false );
+ }
+ else
+ {
+ m_pHorzScrollBar->setVisible( true );
+ }
+ m_pHorzScrollBar->setBounds( 0, h2() - m_nScrollbarHeight, w - m_nScrollbarHeight, m_nScrollbarHeight );
+
+ m_flLeftOffset = max( 0.f, m_flLeftOffset );
+ m_flLeftOffset = min( (float)pixelsneeded, m_flLeftOffset );
+
+ m_pHorzScrollBar->setRange( 0, pixelsneeded );
+ m_pHorzScrollBar->setValue( (int)m_flLeftOffset );
+ m_pHorzScrollBar->setPagesize(w - lw );
+
+ m_nLastHPixelsNeeded = pixelsneeded;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : dirty -
+//-----------------------------------------------------------------------------
+void CChoreoView::SetDirty( bool dirty, bool clearundo /*=true*/ )
+{
+ bool changed = dirty != m_bDirty;
+
+ m_bDirty = dirty;
+
+ if ( !dirty && clearundo )
+ {
+ WipeUndo();
+ redraw();
+ }
+
+ if ( changed )
+ {
+ SetPrefix( m_bDirty ? "* " : "" );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::New( void )
+{
+ if ( m_pScene )
+ {
+ Close( );
+ if ( m_pScene )
+ {
+ return;
+ }
+ }
+
+ char scenefile[ 512 ];
+ if ( FacePoser_ShowSaveFileNameDialog( scenefile, sizeof( scenefile ), "scenes", "*.vcd" ) )
+ {
+ Q_DefaultExtension( scenefile, ".vcd", sizeof( scenefile ) );
+
+ m_pScene = new CChoreoScene( this );
+ g_MDLViewer->InitGridSettings();
+ SetChoreoFile( scenefile );
+ m_pScene->SetPrintFunc( Con_Printf );
+
+ ShowButtons( true );
+
+ SetDirty( false );
+ }
+
+ if ( !m_pScene )
+ return;
+
+ // Get first actor name
+ CActorParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ strcpy( params.m_szDialogTitle, "Create Actor" );
+ strcpy( params.m_szName, "" );
+
+ if ( !ActorProperties( &params ) )
+ return;
+
+ if ( strlen( params.m_szName ) <= 0 )
+ return;
+
+ SetDirty( true );
+
+ PushUndo( "Create Actor" );
+
+ Con_Printf( "Creating scene %s with actor '%s'\n", GetChoreoFile(), params.m_szName );
+
+ CChoreoActor *actor = m_pScene->AllocActor();
+ if ( actor )
+ {
+ actor->SetName( params.m_szName );
+ }
+
+ PushRedo( "Create Actor" );
+
+ CreateSceneWidgets();
+ // Redraw
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::Save( void )
+{
+ if ( !m_pScene )
+ return;
+
+ if ( !MakeFileWriteablePrompt( GetChoreoFile(), "VCD File" ) )
+ {
+ Con_Printf( "Not saving changes to %s\n", GetChoreoFile() );
+ return;
+ }
+
+ Con_Printf( "Saving changes to %s\n", GetChoreoFile() );
+
+ CP4AutoEditAddFile checkout( GetChoreoFile() );
+ if ( !m_pScene->SaveToFile( GetChoreoFile() ) )
+ {
+ mxMessageBox( this, va( "Unable to write \"%s\"", GetChoreoFile() ),
+ "SaveToFile", MX_MB_OK | MX_MB_ERROR );
+ }
+
+ g_MDLViewer->OnVCDSaved();
+
+ // Refresh the suffix
+ SetChoreoFile( GetChoreoFile() );
+
+ SetDirty( false, false );
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::SaveAs( void )
+{
+ if ( !m_pScene )
+ return;
+
+ char scenefile[ 512 ];
+ if ( !FacePoser_ShowSaveFileNameDialog( scenefile, sizeof( scenefile ), "scenes", "*.vcd" ) )
+ return;
+
+ Q_DefaultExtension( scenefile, ".vcd", sizeof( scenefile ) );
+
+ Con_Printf( "Saving %s\n", scenefile );
+
+ MakeFileWriteable( scenefile );
+
+ // Change filename
+ SetChoreoFile( scenefile );
+
+ // Write it out baby
+ CP4AutoEditAddFile checkout( scenefile );
+ if (!m_pScene->SaveToFile( GetChoreoFile() ))
+ {
+ mxMessageBox( this, va( "Unable to write \"%s\"", GetChoreoFile() ),
+ "SaveToFile", MX_MB_OK | MX_MB_ERROR );
+ }
+
+ g_MDLViewer->OnVCDSaved();
+
+ SetDirty( false, false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::Load( void )
+{
+ char scenefile[ 512 ];
+ if ( !FacePoser_ShowOpenFileNameDialog( scenefile, sizeof( scenefile ), "scenes", "*.vcd" ) )
+ {
+ return;
+ }
+
+ Q_DefaultExtension( scenefile, ".vcd", sizeof( scenefile ) );
+
+ LoadSceneFromFile( scenefile );
+
+ m_nextFileList.RemoveAll();
+}
+
+void CChoreoView::LoadNext( void )
+{
+ if (GetChoreoFile() == NULL)
+ return;
+
+ char fixedupFile[ 512 ];
+ V_FixupPathName( fixedupFile, sizeof( fixedupFile ), GetChoreoFile() );
+
+ char relativeFile[ 512 ];
+ filesystem->FullPathToRelativePath( fixedupFile, relativeFile, sizeof( relativeFile ) );
+
+ char relativePath[ 512 ];
+ Q_ExtractFilePath( relativeFile, relativePath, sizeof( relativePath ) );
+
+ if (m_nextFileList.Count() == 0)
+ {
+ // iterate files in the local directory
+ char path[ 512 ];
+ strcpy( path, relativePath );
+ strcat( path, "/*.vcd" );
+
+ FileFindHandle_t hFindFile;
+ char const *fn = filesystem->FindFirstEx( path, "MOD", &hFindFile );
+ if ( fn )
+ {
+ while ( fn )
+ {
+ // Don't do anything with directories
+ if ( !filesystem->FindIsDirectory( hFindFile ) )
+ {
+ CUtlString s = fn;
+ m_nextFileList.AddToTail( s );
+ }
+
+ fn = filesystem->FindNext( hFindFile );
+ }
+
+ filesystem->FindClose( hFindFile );
+ }
+ }
+
+ // look for a match, then pick the next in the list
+ const char *fileBase;
+ fileBase = V_UnqualifiedFileName( fixedupFile );
+
+ for (int i = 0; i < m_nextFileList.Count(); i++)
+ {
+ if (!stricmp( fileBase, m_nextFileList[i] ))
+ {
+ char fileName[512];
+ strcpy( fileName, relativePath );
+ if (i < m_nextFileList.Count() - 1)
+ {
+ strcat( fileName, m_nextFileList[i+1] );
+ }
+ else
+ {
+ strcat( fileName, m_nextFileList[0] );
+ }
+
+ LoadSceneFromFile( fileName );
+ break;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *filename -
+//-----------------------------------------------------------------------------
+void CChoreoView::LoadSceneFromFile( const char *filename )
+{
+ if ( filename[ 0 ] == '/' ||
+ filename[ 0 ] == '\\' )
+ {
+ ++filename;
+ }
+
+ char fn[ 512 ];
+ Q_strncpy( fn, filename, sizeof( fn ) );
+ if ( m_pScene )
+ {
+ Close();
+ if ( m_pScene )
+ {
+ return;
+ }
+ }
+
+ m_pScene = LoadScene( fn );
+ g_MDLViewer->InitGridSettings();
+ if ( !m_pScene )
+ return;
+
+ g_MDLViewer->OnFileLoaded( fn );
+
+ ShowButtons( true );
+
+ CChoreoWidget::m_pScene = m_pScene;
+ SetChoreoFile( fn );
+
+ bool cleaned = FixupSequenceDurations( m_pScene, false );
+
+ SetDirty( cleaned );
+
+ DeleteSceneWidgets();
+ CreateSceneWidgets();
+
+ // Force scroll bars to recompute
+ ForceScrollBarsToRecompute( false );
+
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : closing -
+//-----------------------------------------------------------------------------
+void CChoreoView::UnloadScene( void )
+{
+ InvalidateLayout();
+ ReportSceneClearToTools();
+
+ ClearStatusArea();
+
+ delete m_pScene;
+ m_pScene = NULL;
+ SetDirty( false );
+ SetChoreoFile( "" );
+ g_MDLViewer->InitGridSettings();
+ CChoreoWidget::m_pScene = NULL;
+
+ DeleteSceneWidgets();
+
+ m_pVertScrollBar->setVisible( false );
+ m_pHorzScrollBar->setVisible( false );
+
+ ShowButtons( false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *channel -
+//-----------------------------------------------------------------------------
+void CChoreoView::DeleteChannel( CChoreoChannel *channel )
+{
+ if ( !channel || !m_pScene )
+ return;
+
+ SetDirty( true );
+
+ PushUndo( "Delete Channel" );
+
+ DeleteSceneWidgets();
+
+ // Delete channel and it's children
+ // Find the appropriate actor
+ for ( int i = 0; i < m_pScene->GetNumActors(); i++ )
+ {
+ CChoreoActor *a = m_pScene->GetActor( i );
+ if ( !a )
+ continue;
+
+ if ( a->FindChannelIndex( channel ) == -1 )
+ continue;
+
+ Con_Printf( "Deleting %s\n", channel->GetName() );
+ a->RemoveChannel( channel );
+
+ m_pScene->DeleteReferencedObjects( channel );
+ break;
+ }
+
+ ReportSceneClearToTools();
+
+ CreateSceneWidgets();
+
+ PushRedo( "Delete Channel" );
+
+ // Redraw
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::NewChannel( void )
+{
+ if ( !m_pScene )
+ return;
+
+ if ( !m_pScene->GetNumActors() )
+ {
+ Con_Printf( "You must create an actor before you can add a channel\n" );
+ return;
+ }
+
+ CChannelParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ strcpy( params.m_szDialogTitle, "Create Channel" );
+ strcpy( params.m_szName, "" );
+ params.m_bShowActors = true;
+ strcpy( params.m_szSelectedActor, "" );
+ params.m_pScene = m_pScene;
+
+ if ( !ChannelProperties( &params ) )
+ {
+ return;
+ }
+
+ if ( strlen( params.m_szName ) <= 0 )
+ {
+ return;
+ }
+
+ CChoreoActor *actor = m_pScene->FindActor( params.m_szSelectedActor );
+ if ( !actor )
+ {
+ Con_Printf( "Can't add channel %s, actor %s doesn't exist\n", params.m_szName, params.m_szSelectedActor );
+ return;
+ }
+
+ SetDirty( true );
+
+ PushUndo( "Add Channel" );
+
+ DeleteSceneWidgets();
+
+ CChoreoChannel *channel = m_pScene->AllocChannel();
+ if ( !channel )
+ {
+ Con_Printf( "Unable to allocate channel %s!\n", params.m_szName );
+ }
+ else
+ {
+ channel->SetName( params.m_szName );
+ channel->SetActor( actor );
+ actor->AddChannel( channel );
+ }
+
+ CreateSceneWidgets();
+
+ PushRedo( "Add Channel" );
+
+ // Redraw
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *channel -
+//-----------------------------------------------------------------------------
+void CChoreoView::MoveChannelUp( CChoreoChannel *channel )
+{
+ SetDirty( true );
+
+ PushUndo( "Move Channel Up" );
+
+ DeleteSceneWidgets();
+
+ // Find the appropriate actor
+ for ( int i = 0; i < m_pScene->GetNumActors(); i++ )
+ {
+ CChoreoActor *a = m_pScene->GetActor( i );
+ if ( !a )
+ continue;
+
+ int index = a->FindChannelIndex( channel );
+ if ( index == -1 )
+ continue;
+
+ if ( index != 0 )
+ {
+ Con_Printf( "Moving %s up\n", channel->GetName() );
+ a->SwapChannels( index, index - 1 );
+ }
+ break;
+ }
+
+ CreateSceneWidgets();
+
+ PushRedo( "Move Channel Up" );
+
+ // Redraw
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *channel -
+//-----------------------------------------------------------------------------
+void CChoreoView::MoveChannelDown( CChoreoChannel *channel )
+{
+ SetDirty( true );
+
+ PushUndo( "Move Channel Down" );
+
+ DeleteSceneWidgets();
+
+ // Find the appropriate actor
+ for ( int i = 0; i < m_pScene->GetNumActors(); i++ )
+ {
+ CChoreoActor *a = m_pScene->GetActor( i );
+ if ( !a )
+ continue;
+
+ int index = a->FindChannelIndex( channel );
+ if ( index == -1 )
+ continue;
+
+ if ( index < a->GetNumChannels() - 1 )
+ {
+ Con_Printf( "Moving %s down\n", channel->GetName() );
+ a->SwapChannels( index, index + 1 );
+ }
+ break;
+ }
+
+ CreateSceneWidgets();
+
+ PushRedo( "Move Channel Down" );
+
+ // Redraw
+ InvalidateLayout();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *channel -
+//-----------------------------------------------------------------------------
+void CChoreoView::EditChannel( CChoreoChannel *channel )
+{
+ if ( !channel )
+ return;
+
+ CChannelParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ strcpy( params.m_szDialogTitle, "Edit Channel" );
+ V_strcpy_safe( params.m_szName, channel->GetName() );
+
+ if ( !ChannelProperties( &params ) )
+ return;
+
+ if ( strlen( params.m_szName ) <= 0 )
+ return;
+
+ SetDirty( true );
+
+ PushUndo( "Edit Channel" );
+
+ channel->SetName( params.m_szName );
+
+ PushRedo( "Edit Channel" );
+
+ // Redraw
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+//-----------------------------------------------------------------------------
+void CChoreoView::DeleteActor( CChoreoActor *actor )
+{
+ if ( !actor || !m_pScene )
+ return;
+
+ SetDirty( true );
+
+ PushUndo( "Delete Actor" );
+
+ DeleteSceneWidgets();
+
+ // Delete channel and it's children
+ // Find the appropriate actor
+ Con_Printf( "Deleting %s\n", actor->GetName() );
+ m_pScene->RemoveActor( actor );
+
+ m_pScene->DeleteReferencedObjects( actor );
+
+ ReportSceneClearToTools();
+
+ CreateSceneWidgets();
+
+ PushRedo( "Delete Actor" );
+
+ // Redraw
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::NewActor( void )
+{
+ if ( !m_pScene )
+ {
+ Con_ErrorPrintf( "You must load or create a scene file first\n" );
+ return;
+ }
+
+ CActorParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ strcpy( params.m_szDialogTitle, "Create Actor" );
+ strcpy( params.m_szName, "" );
+
+ if ( !ActorProperties( &params ) )
+ return;
+
+ if ( strlen( params.m_szName ) <= 0 )
+ return;
+
+ SetDirty( true );
+
+ PushUndo( "Add Actor" );
+
+ DeleteSceneWidgets();
+
+ Con_Printf( "Adding new actor '%s'\n", params.m_szName );
+
+ CChoreoActor *actor = m_pScene->AllocActor();
+ if ( actor )
+ {
+ actor->SetName( params.m_szName );
+ }
+
+ CreateSceneWidgets();
+
+ PushRedo( "Add Actor" );
+
+ // Redraw
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+//-----------------------------------------------------------------------------
+void CChoreoView::MoveActorUp( CChoreoActor *actor )
+{
+ DeleteSceneWidgets();
+
+ int index = m_pScene->FindActorIndex( actor );
+ // found it and it's not first
+ if ( index != -1 && index != 0 )
+ {
+ Con_Printf( "Moving %s up\n", actor->GetName() );
+
+ SetDirty( true );
+
+ PushUndo( "Move Actor Up" );
+
+ m_pScene->SwapActors( index, index - 1 );
+
+ PushRedo( "Move Actor Up" );
+ }
+
+ CreateSceneWidgets();
+ // Redraw
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+//-----------------------------------------------------------------------------
+void CChoreoView::MoveActorDown( CChoreoActor *actor )
+{
+ DeleteSceneWidgets();
+
+ int index = m_pScene->FindActorIndex( actor );
+ // found it and it's not first
+ if ( index != -1 && ( index < m_pScene->GetNumActors() - 1 ) )
+ {
+ Con_Printf( "Moving %s down\n", actor->GetName() );
+
+ SetDirty( true );
+
+ PushUndo( "Move Actor Down" );
+
+ m_pScene->SwapActors( index, index + 1 );
+
+ PushRedo( "Move Actor Down" );
+ }
+
+ CreateSceneWidgets();
+ // Redraw
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+//-----------------------------------------------------------------------------
+void CChoreoView::EditActor( CChoreoActor *actor )
+{
+ if ( !actor )
+ return;
+
+ CActorParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ strcpy( params.m_szDialogTitle, "Edit Actor" );
+ V_strcpy_safe( params.m_szName, actor->GetName() );
+
+ if ( !ActorProperties( &params ) )
+ return;
+
+ if ( strlen( params.m_szName ) <= 0 )
+ return;
+
+ SetDirty( true );
+
+ PushUndo( "Edit Actor" );
+
+ actor->SetName( params.m_szName );
+
+ PushRedo( "Edit Actor" );
+
+ // Redraw
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : type -
+//-----------------------------------------------------------------------------
+void CChoreoView::AddEvent( int type, int subtype /*= 0*/, char const *defaultparameters /*= NULL*/ )
+{
+ int mx, my;
+ mx = m_nClickedX;
+ my = m_nClickedY;
+ CChoreoChannelWidget *channel = m_pClickedChannel;
+ if ( !channel || !channel->GetChannel() )
+ {
+ CChoreoActorWidget *actor = m_pClickedActor;
+ if ( actor )
+ {
+ if ( actor->GetNumChannels() <= 0 )
+ return;
+
+ channel = actor->GetChannel( 0 );
+ if ( !channel || !channel->GetChannel() )
+ return;
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ // Convert click position local to this window
+ POINT pt;
+ pt.x = mx;
+ pt.y = my;
+
+ CEventParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ if ( defaultparameters )
+ {
+ Q_strncpy( params.m_szParameters, defaultparameters, sizeof( params.m_szParameters ) );
+ }
+
+ strcpy( params.m_szDialogTitle, "Create Event" );
+
+ params.m_nType = type;
+ params.m_pScene = m_pScene;
+
+ params.m_bFixedLength = false;
+ params.m_bResumeCondition = false;
+ params.m_flStartTime = GetTimeValueForMouse( pt.x );
+ params.m_bCloseCaptionNoAttenuate = false;
+ params.m_bForceShortMovement = false;
+ params.m_bSyncToFollowingGesture = false;
+ params.m_bDisabled = false;
+ params.m_bPlayOverScript = false;
+
+ switch ( type )
+ {
+ case CChoreoEvent::EXPRESSION:
+ case CChoreoEvent::FLEXANIMATION:
+ case CChoreoEvent::GESTURE:
+ case CChoreoEvent::SEQUENCE:
+ case CChoreoEvent::LOOKAT:
+ case CChoreoEvent::MOVETO:
+ case CChoreoEvent::FACE:
+ case CChoreoEvent::SUBSCENE:
+ case CChoreoEvent::INTERRUPT:
+ case CChoreoEvent::GENERIC:
+ case CChoreoEvent::PERMIT_RESPONSES:
+ params.m_bHasEndTime = true;
+ params.m_flEndTime = params.m_flStartTime + 0.5f;
+ if ( type == CChoreoEvent::GESTURE && subtype == 1 )
+ {
+ strcpy( params.m_szDialogTitle, "Create <NULL> Gesture" );
+ strcpy( params.m_szName, "NULL" );
+ }
+ break;
+ case CChoreoEvent::SPEAK:
+ params.m_bFixedLength = true;
+ params.m_bHasEndTime = false;
+ params.m_flEndTime = -1.0f;
+ break;
+ default:
+ params.m_bHasEndTime = false;
+ params.m_flEndTime = -1.0f;
+ break;
+ }
+
+ params.m_bUsesTag = false;
+
+ while (1)
+ {
+ SetScrubTargetTime( m_flScrub );
+ FinishSimulation();
+ sound->Flush();
+
+ m_bForceProcess = true;
+ if (!EventProperties( &params ))
+ {
+ m_bForceProcess = false;
+ return;
+ }
+ m_bForceProcess = false;
+
+ if ( Q_strlen( params.m_szName ) <= 0 )
+ {
+ mxMessageBox( this, va( "Event must have a valid name" ),
+ "Edit Event", MX_MB_OK | MX_MB_ERROR );
+ continue;
+ }
+
+ if ( Q_strlen( params.m_szParameters ) <= 0 )
+ {
+ bool shouldBreak = false;
+
+ switch ( params.m_nType )
+ {
+ case CChoreoEvent::FLEXANIMATION:
+ case CChoreoEvent::INTERRUPT:
+ case CChoreoEvent::PERMIT_RESPONSES:
+ shouldBreak = true;
+ break;
+ case CChoreoEvent::GESTURE:
+ if ( subtype == 1 )
+ {
+ shouldBreak = true;
+ }
+ break;
+ default:
+ // Have to have a non-null parameters block
+ break;
+ }
+
+ if ( !shouldBreak )
+ {
+ mxMessageBox( this, va( "No parameters specified for %s\n", params.m_szName ),
+ "Edit Event", MX_MB_OK | MX_MB_ERROR );
+ continue;
+ }
+ }
+
+ break;
+ }
+
+ SetDirty( true );
+
+ PushUndo( "Add Event" );
+
+ CChoreoEvent *event = m_pScene->AllocEvent();
+ if ( event )
+ {
+ event->SetType( (CChoreoEvent::EVENTTYPE)type );
+ event->SetName( params.m_szName );
+ event->SetParameters( params.m_szParameters );
+ event->SetParameters2( params.m_szParameters2 );
+ event->SetParameters3( params.m_szParameters3 );
+ event->SetStartTime( params.m_flStartTime );
+
+ event->SetResumeCondition( params.m_bResumeCondition );
+ event->SetLockBodyFacing( params.m_bLockBodyFacing );
+ event->SetDistanceToTarget( params.m_flDistanceToTarget );
+ event->SetForceShortMovement( params.m_bForceShortMovement );
+ event->SetSyncToFollowingGesture( params.m_bSyncToFollowingGesture );
+ event->SetActive( !params.m_bDisabled );
+ event->SetPlayOverScript( params.m_bPlayOverScript );
+
+ if ( params.m_bUsesTag )
+ {
+ event->SetUsingRelativeTag( true, params.m_szTagName, params.m_szTagWav );
+ }
+ else
+ {
+ event->SetUsingRelativeTag( false );
+ }
+ CChoreoChannel *pchannel = channel->GetChannel();
+
+ event->SetChannel( pchannel );
+ event->SetActor( pchannel->GetActor() );
+
+ if ( params.m_bHasEndTime &&
+ params.m_flEndTime != -1.0 &&
+ params.m_flEndTime > params.m_flStartTime )
+ {
+ event->SetEndTime( params.m_flEndTime );
+ }
+ else
+ {
+ event->SetEndTime( -1.0f );
+ }
+
+ switch ( event->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::SUBSCENE:
+ {
+ // Just grab end time
+ CChoreoScene *scene = LoadScene( event->GetParameters() );
+ if ( scene )
+ {
+ event->SetEndTime( params.m_flStartTime + scene->FindStopTime() );
+ }
+ delete scene;
+ }
+ break;
+ case CChoreoEvent::SEQUENCE:
+ {
+ CheckSequenceLength( event, false );
+ // AutoaddSequenceKeys( event);
+ }
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ DefaultGestureLength( event, false );
+ AutoaddGestureKeys( event, false );
+ }
+ break;
+ case CChoreoEvent::LOOKAT:
+ case CChoreoEvent::FACE:
+ {
+ if ( params.usepitchyaw )
+ {
+ event->SetPitch( params.pitch );
+ event->SetYaw( params.yaw );
+ }
+ else
+ {
+ event->SetPitch( 0 );
+ event->SetYaw( 0 );
+ }
+ }
+ break;
+ case CChoreoEvent::SPEAK:
+ {
+ // Try and load wav to get length
+ CAudioSource *wave = sound->LoadSound( va( "sound/%s", FacePoser_TranslateSoundName( event ) ) );
+ if ( wave )
+ {
+ event->SetEndTime( params.m_flStartTime + wave->GetRunningLength() );
+ delete wave;
+ }
+
+ event->SetSuppressingCaptionAttenuation( params.m_bCloseCaptionNoAttenuate );
+ }
+ break;
+ }
+ event->SnapTimes();
+
+ DeleteSceneWidgets();
+
+ // Add to appropriate channel
+ pchannel->AddEvent( event );
+
+ CreateSceneWidgets();
+
+ // Redraw
+ InvalidateLayout();
+ }
+
+ PushRedo( "Add Event" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds a scene "pause" event
+//-----------------------------------------------------------------------------
+void CChoreoView::AddGlobalEvent( CChoreoEvent::EVENTTYPE type )
+{
+ int mx, my;
+ mx = m_nClickedX;
+ my = m_nClickedY;
+
+ // Convert click position local to this window
+ POINT pt;
+ pt.x = mx;
+ pt.y = my;
+
+ CGlobalEventParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ params.m_nType = type;
+
+ switch ( type )
+ {
+ default:
+ Assert( 0 );
+ strcpy( params.m_szDialogTitle, "???" );
+ break;
+ case CChoreoEvent::SECTION:
+ {
+ strcpy( params.m_szDialogTitle, "Add Pause Point" );
+ }
+ break;
+ case CChoreoEvent::LOOP:
+ {
+ strcpy( params.m_szDialogTitle, "Add Loop Point" );
+ }
+ break;
+ case CChoreoEvent::STOPPOINT:
+ {
+ strcpy( params.m_szDialogTitle, "Add Fire Completion" );
+ }
+ break;
+ }
+ strcpy( params.m_szName, "" );
+ strcpy( params.m_szAction, "" );
+
+ params.m_flStartTime = GetTimeValueForMouse( pt.x );
+
+ if ( !GlobalEventProperties( &params ) )
+ return;
+
+ if ( strlen( params.m_szName ) <= 0 )
+ {
+ Con_Printf( "Pause section event must have a valid name\n" );
+ return;
+ }
+
+ if ( strlen( params.m_szAction ) <= 0 )
+ {
+ Con_Printf( "No action specified for section pause\n" );
+ return;
+ }
+
+ char undotext[ 256 ];
+ undotext[0]=0;
+ switch( type )
+ {
+ default:
+ Assert( 0 );
+ break;
+ case CChoreoEvent::SECTION:
+ {
+ Q_strcpy( undotext, "Add Section Pause" );
+ }
+ break;
+ case CChoreoEvent::LOOP:
+ {
+ Q_strcpy( undotext, "Add Loop Point" );
+ }
+ break;
+ case CChoreoEvent::STOPPOINT:
+ {
+ Q_strcpy( undotext, "Add Fire Completion" );
+ }
+ break;
+ }
+
+ SetDirty( true );
+
+ PushUndo( undotext );
+
+ CChoreoEvent *event = m_pScene->AllocEvent();
+ if ( event )
+ {
+ event->SetType( type );
+ event->SetName( params.m_szName );
+ event->SetParameters( params.m_szAction );
+ event->SetStartTime( params.m_flStartTime );
+ event->SetEndTime( -1.0f );
+
+ switch ( type )
+ {
+ default:
+ break;
+ case CChoreoEvent::LOOP:
+ {
+ event->SetLoopCount( params.m_nLoopCount );
+ event->SetParameters( va( "%f", params.m_flLoopTime ) );
+ }
+ break;
+ }
+
+ event->SnapTimes();
+
+ DeleteSceneWidgets();
+
+ CreateSceneWidgets();
+
+ // Redraw
+ InvalidateLayout();
+ }
+
+ PushRedo( undotext );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+//-----------------------------------------------------------------------------
+void CChoreoView::EditGlobalEvent( CChoreoEvent *event )
+{
+ if ( !event )
+ return;
+
+ CGlobalEventParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ params.m_nType = event->GetType();
+
+ switch ( event->GetType() )
+ {
+ default:
+ Assert( 0 );
+ strcpy( params.m_szDialogTitle, "???" );
+ break;
+ case CChoreoEvent::SECTION:
+ {
+ strcpy( params.m_szDialogTitle, "Edit Pause Point" );
+ V_strcpy_safe( params.m_szAction, event->GetParameters() );
+ }
+ break;
+ case CChoreoEvent::LOOP:
+ {
+ strcpy( params.m_szDialogTitle, "Edit Loop Point" );
+ strcpy( params.m_szAction, "" );
+ params.m_flLoopTime = (float)atof( event->GetParameters() );
+ params.m_nLoopCount = event->GetLoopCount();
+ }
+ break;
+ case CChoreoEvent::STOPPOINT:
+ {
+ strcpy( params.m_szDialogTitle, "Edit Fire Completion" );
+ strcpy( params.m_szAction, "" );
+ }
+ break;
+ }
+
+ strcpy( params.m_szName, event->GetName() );
+
+ params.m_flStartTime = event->GetStartTime();
+
+ if ( !GlobalEventProperties( &params ) )
+ return;
+
+ if ( strlen( params.m_szName ) <= 0 )
+ {
+ Con_Printf( "Event %s must have a valid name\n", event->GetName() );
+ return;
+ }
+
+ if ( strlen( params.m_szAction ) <= 0 )
+ {
+ Con_Printf( "No action specified for %s\n", event->GetName() );
+ return;
+ }
+
+ SetDirty( true );
+
+ char undotext[ 256 ];
+ undotext[0]=0;
+ switch( event->GetType() )
+ {
+ default:
+ Assert( 0 );
+ break;
+ case CChoreoEvent::SECTION:
+ {
+ Q_strcpy( undotext, "Edit Section Pause" );
+ }
+ break;
+ case CChoreoEvent::LOOP:
+ {
+ Q_strcpy( undotext, "Edit Loop Point" );
+ }
+ break;
+ case CChoreoEvent::STOPPOINT:
+ {
+ Q_strcpy( undotext, "Edit Fire Completion" );
+ }
+ break;
+ }
+
+ PushUndo( undotext );
+
+ event->SetName( params.m_szName );
+ event->SetStartTime( params.m_flStartTime );
+ event->SetEndTime( -1.0f );
+
+ switch ( event->GetType() )
+ {
+ default:
+ {
+ event->SetParameters( params.m_szAction );
+ }
+ break;
+ case CChoreoEvent::LOOP:
+ {
+ event->SetLoopCount( params.m_nLoopCount );
+ event->SetParameters( va( "%f", params.m_flLoopTime ) );
+ }
+ break;
+ }
+
+ event->SnapTimes();
+
+ PushRedo( undotext );
+
+ // Redraw
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+//-----------------------------------------------------------------------------
+void CChoreoView::DeleteGlobalEvent( CChoreoEvent *event )
+{
+ if ( !event || !m_pScene )
+ return;
+
+ SetDirty( true );
+
+
+ char undotext[ 256 ];
+ undotext[0]=0;
+ switch( event->GetType() )
+ {
+ default:
+ Assert( 0 );
+ break;
+ case CChoreoEvent::SECTION:
+ {
+ Q_strcpy( undotext, "Delete Section Pause" );
+ }
+ break;
+ case CChoreoEvent::LOOP:
+ {
+ Q_strcpy( undotext, "Delete Loop Point" );
+ }
+ break;
+ case CChoreoEvent::STOPPOINT:
+ {
+ Q_strcpy( undotext, "Delete Fire Completion" );
+ }
+ break;
+ }
+
+ PushUndo( undotext );
+
+ DeleteSceneWidgets();
+
+ Con_Printf( "Deleting %s\n", event->GetName() );
+
+ m_pScene->DeleteReferencedObjects( event );
+
+ CreateSceneWidgets();
+
+ PushRedo( undotext );
+
+ // Redraw
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+//-----------------------------------------------------------------------------
+void CChoreoView::EditEvent( CChoreoEvent *event )
+{
+ if ( !event )
+ return;
+
+ CEventParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ strcpy( params.m_szDialogTitle, "Edit Event" );
+
+ // Copy in current even properties
+ params.m_nType = event->GetType();
+ params.m_bDisabled = !event->GetActive();
+
+ switch ( params.m_nType )
+ {
+ case CChoreoEvent::EXPRESSION:
+ case CChoreoEvent::SEQUENCE:
+ case CChoreoEvent::MOVETO:
+ case CChoreoEvent::SPEAK:
+ case CChoreoEvent::GESTURE:
+ case CChoreoEvent::INTERRUPT:
+ case CChoreoEvent::PERMIT_RESPONSES:
+ case CChoreoEvent::GENERIC:
+ V_strcpy_safe( params.m_szParameters3, event->GetParameters3() );
+ V_strcpy_safe( params.m_szParameters2, event->GetParameters2() );
+ V_strcpy_safe( params.m_szParameters, event->GetParameters() );
+ V_strcpy_safe( params.m_szName, event->GetName() );
+ break;
+ case CChoreoEvent::FACE:
+ case CChoreoEvent::LOOKAT:
+ case CChoreoEvent::FIRETRIGGER:
+ case CChoreoEvent::FLEXANIMATION:
+ case CChoreoEvent::SUBSCENE:
+ V_strcpy_safe( params.m_szParameters, event->GetParameters() );
+ V_strcpy_safe( params.m_szName, event->GetName() );
+
+ if ( params.m_nType == CChoreoEvent::LOOKAT || params.m_nType == CChoreoEvent::FACE )
+ {
+ if ( event->GetPitch() != 0 ||
+ event->GetYaw() != 0 )
+ {
+ params.usepitchyaw = true;
+ params.pitch = event->GetPitch();
+ params.yaw = event->GetYaw();
+ }
+ }
+ break;
+ default:
+ Con_Printf( "Don't know how to edit event type %s\n",
+ CChoreoEvent::NameForType( (CChoreoEvent::EVENTTYPE)params.m_nType ) );
+ return;
+ }
+
+ params.m_pScene = m_pScene;
+ params.m_pEvent = event;
+ params.m_flStartTime = event->GetStartTime();
+ params.m_flEndTime = event->GetEndTime();
+ params.m_bHasEndTime = event->HasEndTime();
+
+ params.m_bFixedLength = event->IsFixedLength();
+ params.m_bResumeCondition = event->IsResumeCondition();
+ params.m_bLockBodyFacing = event->IsLockBodyFacing();
+ params.m_flDistanceToTarget = event->GetDistanceToTarget();
+ params.m_bForceShortMovement = event->GetForceShortMovement();
+ params.m_bSyncToFollowingGesture = event->GetSyncToFollowingGesture();
+ params.m_bPlayOverScript = event->GetPlayOverScript();
+ params.m_bUsesTag = event->IsUsingRelativeTag();
+ params.m_bCloseCaptionNoAttenuate = event->IsSuppressingCaptionAttenuation();
+
+ if ( params.m_bUsesTag )
+ {
+ V_strcpy_safe( params.m_szTagName, event->GetRelativeTagName() );
+ V_strcpy_safe( params.m_szTagWav, event->GetRelativeWavName() );
+ }
+
+ while (1)
+ {
+ SetScrubTargetTime( m_flScrub );
+ FinishSimulation();
+ sound->Flush();
+
+ m_bForceProcess = true;
+ if (!EventProperties( &params ))
+ {
+ m_bForceProcess = false;
+ return;
+ }
+ m_bForceProcess = false;
+
+ if ( Q_strlen( params.m_szName ) <= 0 )
+ {
+ mxMessageBox( this, va( "Event %s must have a valid name", event->GetName() ),
+ "Edit Event", MX_MB_OK | MX_MB_ERROR );
+ continue;
+ }
+
+ if ( Q_strlen( params.m_szParameters ) <= 0 )
+ {
+ bool shouldBreak = false;
+
+ switch ( params.m_nType )
+ {
+ case CChoreoEvent::FLEXANIMATION:
+ case CChoreoEvent::INTERRUPT:
+ case CChoreoEvent::PERMIT_RESPONSES:
+ shouldBreak = true;
+ break;
+ case CChoreoEvent::GESTURE:
+ if ( !Q_stricmp( params.m_szName, "NULL" ) )
+ {
+ shouldBreak = true;
+ }
+ break;
+ default:
+ // Have to have a non-null parameters block
+ break;
+ }
+
+ if ( !shouldBreak )
+ {
+ mxMessageBox( this, va( "No parameters specified for %s\n", params.m_szName ),
+ "Edit Event", MX_MB_OK | MX_MB_ERROR );
+ continue;
+ }
+ }
+
+ break;
+ }
+
+
+ SetDirty( true );
+
+ PushUndo( "Edit Event" );
+
+ event->SetName( params.m_szName );
+ event->SetParameters( params.m_szParameters );
+ event->SetParameters2( params.m_szParameters2 );
+ event->SetParameters3( params.m_szParameters3 );
+ event->SetStartTime( params.m_flStartTime );
+ event->SetResumeCondition( params.m_bResumeCondition );
+ event->SetLockBodyFacing( params.m_bLockBodyFacing );
+ event->SetDistanceToTarget( params.m_flDistanceToTarget );
+ event->SetForceShortMovement( params.m_bForceShortMovement );
+ event->SetSyncToFollowingGesture( params.m_bSyncToFollowingGesture );
+ event->SetActive( !params.m_bDisabled );
+ event->SetPlayOverScript( params.m_bPlayOverScript );
+ if ( params.m_bUsesTag )
+ {
+ event->SetUsingRelativeTag( true, params.m_szTagName, params.m_szTagWav );
+ }
+ else
+ {
+ event->SetUsingRelativeTag( false );
+ }
+
+ if ( params.m_bHasEndTime &&
+ params.m_flEndTime != -1.0 &&
+ params.m_flEndTime > params.m_flStartTime )
+ {
+ float dt = params.m_flEndTime - event->GetEndTime();
+ float newduration = event->GetDuration() + dt;
+ RescaleRamp( event, newduration );
+ switch ( event->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ event->RescaleGestureTimes( event->GetStartTime(), event->GetEndTime() + dt, true );
+ }
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ {
+ RescaleExpressionTimes( event, event->GetStartTime(), event->GetEndTime() + dt );
+ }
+ break;
+ }
+ event->SetEndTime( params.m_flEndTime );
+ event->SnapTimes();
+ event->ResortRamp();
+ }
+ else
+ {
+ event->SetEndTime( -1.0f );
+ }
+
+ switch ( event->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::SPEAK:
+ {
+ // Try and load wav to get length
+ CAudioSource *wave = sound->LoadSound( va( "sound/%s", FacePoser_TranslateSoundName( event ) ) );
+ if ( wave )
+ {
+ event->SetEndTime( params.m_flStartTime + wave->GetRunningLength() );
+ delete wave;
+ }
+
+ event->SetSuppressingCaptionAttenuation( params.m_bCloseCaptionNoAttenuate );
+ }
+ break;
+ case CChoreoEvent::SUBSCENE:
+ {
+ // Just grab end time
+ CChoreoScene *scene = LoadScene( event->GetParameters() );
+ if ( scene )
+ {
+ event->SetEndTime( params.m_flStartTime + scene->FindStopTime() );
+ }
+ delete scene;
+ }
+ break;
+ case CChoreoEvent::SEQUENCE:
+ {
+ CheckSequenceLength( event, false );
+ }
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ CheckGestureLength( event, false );
+ AutoaddGestureKeys( event, false );
+ g_pGestureTool->redraw();
+ }
+ break;
+ case CChoreoEvent::LOOKAT:
+ case CChoreoEvent::FACE:
+ {
+ if ( params.usepitchyaw )
+ {
+ event->SetPitch( params.pitch );
+ event->SetYaw( params.yaw );
+ }
+ else
+ {
+ event->SetPitch( 0 );
+ event->SetYaw( 0 );
+ }
+ }
+ break;
+ }
+
+ event->SnapTimes();
+
+ PushRedo( "Edit Event" );
+
+ // Redraw
+ InvalidateLayout();
+}
+
+void CChoreoView::EnableSelectedEvents( bool state )
+{
+ if ( !m_pScene )
+ return;
+
+ SetDirty( true );
+
+ // If we right clicked on an unseleced event, then select it for the user.
+ if ( CountSelectedEvents() == 0 )
+ {
+ CChoreoEventWidget *event = m_pClickedEvent;
+ if ( event )
+ {
+ event->SetSelected( true );
+ }
+ }
+
+ char const *desc = state ? "Enable Events" : "Disable Events";
+
+ PushUndo( desc );
+
+ // Find the appropriate event by iterating across all actors and channels
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *a = m_SceneActors[ i ];
+ if ( !a )
+ continue;
+
+ for ( int j = 0; j < a->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *channel = a->GetChannel( j );
+ if ( !channel )
+ continue;
+
+ for ( int k = channel->GetNumEvents() - 1; k >= 0; k-- )
+ {
+ CChoreoEventWidget *event = channel->GetEvent( k );
+ if ( !event->IsSelected() )
+ continue;
+
+ event->GetEvent()->SetActive( state );
+ }
+ }
+ }
+
+ for ( int i = 0; i < m_SceneGlobalEvents.Size(); i++ )
+ {
+ CChoreoGlobalEventWidget *event = m_SceneGlobalEvents[ i ];
+ if ( !event || !event->IsSelected() )
+ continue;
+
+ event->GetEvent()->SetActive( state );
+ }
+
+ PushRedo( desc );
+
+ // Redraw
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+//-----------------------------------------------------------------------------
+void CChoreoView::DeleteSelectedEvents( void )
+{
+ if ( !m_pScene )
+ return;
+
+ SetDirty( true );
+
+ PushUndo( "Delete Events" );
+
+ int deleteCount = 0;
+
+ float oldstoptime = m_pScene->FindStopTime();
+
+ // Find the appropriate event by iterating across all actors and channels
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *a = m_SceneActors[ i ];
+ if ( !a )
+ continue;
+
+ for ( int j = 0; j < a->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *channel = a->GetChannel( j );
+ if ( !channel )
+ continue;
+
+ for ( int k = channel->GetNumEvents() - 1; k >= 0; k-- )
+ {
+ CChoreoEventWidget *event = channel->GetEvent( k );
+ if ( !event->IsSelected() )
+ continue;
+
+ channel->GetChannel()->RemoveEvent( event->GetEvent() );
+ m_pScene->DeleteReferencedObjects( event->GetEvent() );
+
+ deleteCount++;
+ }
+ }
+ }
+
+ for ( int i = 0; i < m_SceneGlobalEvents.Size(); i++ )
+ {
+ CChoreoGlobalEventWidget *event = m_SceneGlobalEvents[ i ];
+ if ( !event || !event->IsSelected() )
+ continue;
+
+ m_pScene->DeleteReferencedObjects( event->GetEvent() );
+
+ deleteCount++;
+ }
+
+ DeleteSceneWidgets();
+
+ ReportSceneClearToTools();
+
+ CreateSceneWidgets();
+
+ PushRedo( "Delete Events" );
+
+ Con_Printf( "Deleted <%i> events\n", deleteCount );
+
+ if ( m_pScene->FindStopTime() != oldstoptime )
+ {
+ // Force scroll bars to recompute
+ ForceScrollBarsToRecompute( false );
+
+ }
+ // Redraw
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : resetthumb -
+//-----------------------------------------------------------------------------
+void CChoreoView::ForceScrollBarsToRecompute( bool resetthumb )
+{
+ if ( resetthumb )
+ {
+ m_flLeftOffset = 0.0f;
+ }
+ m_nLastHPixelsNeeded = -1;
+ m_nLastVPixelsNeeded = -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *filename -
+// Output : CChoreoScene
+//-----------------------------------------------------------------------------
+CChoreoScene *CChoreoView::LoadScene( char const *filename )
+{
+ // If relative path, then make a full path
+ char pFullPathBuf[ MAX_PATH ];
+ if ( !Q_IsAbsolutePath( filename ) )
+ {
+ filesystem->RelativePathToFullPath( filename, "GAME", pFullPathBuf, sizeof( pFullPathBuf ) );
+ filename = pFullPathBuf;
+ }
+
+ if ( !filesystem->FileExists( filename ) )
+ return NULL;
+
+ LoadScriptFile( const_cast<char*>( filename ) );
+
+ CChoreoScene *scene = ChoreoLoadScene( filename, this, tokenprocessor, Con_Printf );
+ return scene;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CChoreoView::FixupSequenceDurations( CChoreoScene *scene, bool checkonly )
+{
+ bool bret = false;
+ if ( !scene )
+ return bret;
+
+ int c = scene->GetNumEvents();
+ for ( int i = 0; i < c; i++ )
+ {
+ CChoreoEvent *event = scene->GetEvent( i );
+ if ( !event )
+ continue;
+
+ switch ( event->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::SPEAK:
+ {
+ // Try and load wav to get length
+ CAudioSource *wave = sound->LoadSound( va( "sound/%s", FacePoser_TranslateSoundName( event ) ) );
+ if ( wave )
+ {
+ float endtime = event->GetStartTime() + wave->GetRunningLength();
+ if ( event->GetEndTime() != endtime )
+ {
+ event->SetEndTime( event->GetStartTime() + wave->GetRunningLength() );
+ bret = true;
+ }
+ delete wave;
+ }
+ }
+ break;
+ case CChoreoEvent::SEQUENCE:
+ {
+ if ( CheckSequenceLength( event, checkonly ) )
+ {
+ bret = true;
+ }
+ }
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ if ( CheckGestureLength( event, checkonly ) )
+ {
+ bret = true;
+ }
+ if ( AutoaddGestureKeys( event, checkonly ) )
+ {
+ bret = true;
+ }
+ }
+ break;
+ }
+ }
+
+ return bret;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: IChoreoEventCallback
+// Input : currenttime -
+// *event -
+//-----------------------------------------------------------------------------
+void CChoreoView::StartEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event )
+{
+ if ( !event || !event->GetActive() )
+ return;
+
+ CChoreoActor *actor = event->GetActor();
+ if ( actor && !actor->GetActive() )
+ {
+ return;
+ }
+
+ CChoreoChannel *channel = event->GetChannel();
+ if ( channel && !channel->GetActive() )
+ {
+ return;
+ }
+
+ // Con_Printf( "%8.4f: start %s\n", currenttime, event->GetDescription() );
+
+ switch ( event->GetType() )
+ {
+ case CChoreoEvent::SEQUENCE:
+ {
+ ProcessSequence( scene, event );
+ }
+ break;
+ case CChoreoEvent::SUBSCENE:
+ {
+ if ( !scene->IsSubScene() )
+ {
+ CChoreoScene *subscene = event->GetSubScene();
+ if ( !subscene )
+ {
+ subscene = LoadScene( event->GetParameters() );
+ subscene->SetSubScene( true );
+ event->SetSubScene( subscene );
+ }
+
+ if ( subscene )
+ {
+ subscene->ResetSimulation( m_bForward );
+ }
+ }
+ }
+ break;
+ case CChoreoEvent::SECTION:
+ {
+ ProcessPause( scene, event );
+ }
+ break;
+ case CChoreoEvent::SPEAK:
+ {
+ if ( ShouldProcessSpeak() )
+ {
+ // See if we should trigger CC
+ char soundname[ 512 ];
+ Q_strncpy( soundname, event->GetParameters(), sizeof( soundname ) );
+
+ float actualEndTime = event->GetEndTime();
+
+ if ( event->GetCloseCaptionType() == CChoreoEvent::CC_MASTER )
+ {
+ char tok[ CChoreoEvent::MAX_CCTOKEN_STRING ];
+ if ( event->GetPlaybackCloseCaptionToken( tok, sizeof( tok ) ) )
+ {
+ float duration = max( event->GetDuration(), event->GetLastSlaveEndTime() - event->GetStartTime() );
+
+ closecaptionmanager->Process( tok, duration, GetCloseCaptionLanguageId() );
+
+ // Use the token as the sound name lookup, too.
+ if ( event->IsUsingCombinedFile() &&
+ ( event->GetNumSlaves() > 0 ) )
+ {
+ Q_strncpy( soundname, tok, sizeof( soundname ) );
+ actualEndTime = max( actualEndTime, event->GetLastSlaveEndTime() );
+ }
+ }
+ }
+
+ StudioModel *model = FindAssociatedModel( scene, event->GetActor() );
+
+ CAudioMixer *mixer = event->GetMixer();
+ if ( !mixer || !sound->IsSoundPlaying( mixer ) )
+ {
+ CSoundParameters params;
+
+ float volume = VOL_NORM;
+ gender_t gender = GENDER_NONE;
+ if (model)
+ {
+ gender = soundemitter->GetActorGender( model->GetFileName() );
+ }
+
+ if ( !Q_stristr( soundname, ".wav" ) &&
+ soundemitter->GetParametersForSound( soundname, params, gender ) )
+ {
+ volume = params.volume;
+ }
+
+ sound->PlaySound(
+ model,
+ volume,
+ va( "sound/%s", FacePoser_TranslateSoundName( soundname, model ) ),
+ &mixer );
+ event->SetMixer( mixer );
+ }
+
+ if ( mixer )
+ {
+ mixer->SetDirection( m_flFrameTime >= 0.0f );
+ float starttime, endtime;
+ starttime = event->GetStartTime();
+ endtime = actualEndTime;
+
+ float soundtime = endtime - starttime;
+ if ( soundtime > 0.0f )
+ {
+ float f = ( currenttime - starttime ) / soundtime;
+ f = clamp( f, 0.0f, 1.0f );
+
+ // Compute sample
+ float numsamples = (float)mixer->GetSource()->SampleCount();
+
+ int cursample = f * numsamples;
+ cursample = clamp( cursample, 0, numsamples - 1 );
+ mixer->SetSamplePosition( cursample );
+ mixer->SetActive( true );
+ }
+ }
+ }
+ }
+ break;
+ case CChoreoEvent::EXPRESSION:
+ {
+ }
+ break;
+ case CChoreoEvent::LOOP:
+ {
+ ProcessLoop( scene, event );
+ }
+ break;
+ case CChoreoEvent::STOPPOINT:
+ {
+ // Nothing, this is a symbolic event for keeping the vcd alive for ramping out after the last true event
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : currenttime -
+// *event -
+//-----------------------------------------------------------------------------
+void CChoreoView::EndEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event )
+{
+ if ( !event || !event->GetActive() )
+ return;
+
+ CChoreoActor *actor = event->GetActor();
+ if ( actor && !actor->GetActive() )
+ {
+ return;
+ }
+
+ CChoreoChannel *channel = event->GetChannel();
+ if ( channel && !channel->GetActive() )
+ {
+ return;
+ }
+
+ switch ( event->GetType() )
+ {
+ case CChoreoEvent::SUBSCENE:
+ {
+ CChoreoScene *subscene = event->GetSubScene();
+ if ( subscene )
+ {
+ subscene->ResetSimulation();
+ }
+ }
+ break;
+ case CChoreoEvent::SPEAK:
+ {
+ CAudioMixer *mixer = event->GetMixer();
+ if ( mixer && sound->IsSoundPlaying( mixer ) )
+ {
+ sound->StopSound( mixer );
+ }
+ event->SetMixer( NULL );
+ }
+ break;
+ default:
+ break;
+ }
+
+// Con_Printf( "%8.4f: finish %s\n", currenttime, event->GetDescription() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+// mx -
+// my -
+//-----------------------------------------------------------------------------
+int CChoreoView::GetTagUnderCursorPos( CChoreoEventWidget *event, int mx, int my )
+{
+ if ( !event )
+ {
+ return -1;
+ }
+
+ for ( int i = 0; i < event->GetEvent()->GetNumRelativeTags(); i++ )
+ {
+ CEventRelativeTag *tag = event->GetEvent()->GetRelativeTag( i );
+ if ( !tag )
+ continue;
+
+ // Determine left edcge
+ RECT bounds;
+ bounds = event->getBounds();
+ int left = bounds.left + (int)( tag->GetPercentage() * (float)( bounds.right - bounds.left ) + 0.5f );
+
+ int tolerance = 3;
+
+ if ( abs( mx - left ) < tolerance )
+ {
+ if ( abs( my - bounds.top ) < tolerance )
+ {
+ return i;
+ }
+ }
+ }
+
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+// mx -
+// my -
+//-----------------------------------------------------------------------------
+CEventAbsoluteTag *CChoreoView::GetAbsoluteTagUnderCursorPos( CChoreoEventWidget *event, int mx, int my )
+{
+ if ( !event )
+ {
+ return NULL;
+ }
+
+ for ( int i = 0; i < event->GetEvent()->GetNumAbsoluteTags( CChoreoEvent::PLAYBACK ); i++ )
+ {
+ CEventAbsoluteTag *tag = event->GetEvent()->GetAbsoluteTag( CChoreoEvent::PLAYBACK, i );
+ if ( !tag )
+ continue;
+
+ // Determine left edcge
+ RECT bounds;
+ bounds = event->getBounds();
+ int left = bounds.left + (int)( tag->GetPercentage() * (float)( bounds.right - bounds.left ) + 0.5f );
+
+ int tolerance = 3;
+
+ if ( abs( mx - left ) < tolerance )
+ {
+ if ( abs( my - bounds.top ) < tolerance )
+ {
+ return tag;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+// **actor -
+// **channel -
+// **event -
+//-----------------------------------------------------------------------------
+void CChoreoView::GetObjectsUnderMouse( int mx, int my, CChoreoActorWidget **actor,
+ CChoreoChannelWidget **channel, CChoreoEventWidget **event, CChoreoGlobalEventWidget **globalevent,
+ int *clickedTag,
+ CEventAbsoluteTag **absolutetag, int *clickedCCArea )
+{
+ if ( actor )
+ {
+ *actor = GetActorUnderCursorPos( mx, my );
+ }
+ if ( channel )
+ {
+ *channel = GetChannelUnderCursorPos( mx, my );
+ if ( *channel && clickedCCArea )
+ {
+ *clickedCCArea = (*channel)->GetChannelItemUnderMouse( mx, my );
+ }
+ }
+ if ( event )
+ {
+ *event = GetEventUnderCursorPos( mx, my );
+ }
+ if ( globalevent )
+ {
+ *globalevent = GetGlobalEventUnderCursorPos( mx, my );
+ }
+ if ( clickedTag )
+ {
+ if ( event && *event )
+ {
+ *clickedTag = GetTagUnderCursorPos( *event, mx, my );
+ }
+ else
+ {
+ *clickedTag = -1;
+ }
+ }
+ if ( absolutetag )
+ {
+ if ( event && *event )
+ {
+ *absolutetag = GetAbsoluteTagUnderCursorPos( *event, mx, my );
+ }
+ else
+ {
+ *absolutetag = NULL;
+ }
+ }
+
+
+ m_nSelectedEvents = CountSelectedEvents();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+// Output : CChoreoGlobalEventWidget
+//-----------------------------------------------------------------------------
+CChoreoGlobalEventWidget *CChoreoView::GetGlobalEventUnderCursorPos( int mx, int my )
+{
+ POINT check;
+ check.x = mx;
+ check.y = my;
+
+ CChoreoGlobalEventWidget *event;
+ for ( int i = 0; i < m_SceneGlobalEvents.Size(); i++ )
+ {
+ event = m_SceneGlobalEvents[ i ];
+ if ( !event )
+ continue;
+
+ RECT bounds;
+ event->getBounds( bounds );
+
+ if ( PtInRect( &bounds, check ) )
+ {
+ return event;
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Caller must first translate mouse into screen coordinates
+// Input : mx -
+// my -
+//-----------------------------------------------------------------------------
+CChoreoActorWidget *CChoreoView::GetActorUnderCursorPos( int mx, int my )
+{
+ POINT check;
+ check.x = mx;
+ check.y = my;
+
+ CChoreoActorWidget *actor;
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ actor = m_SceneActors[ i ];
+ if ( !actor )
+ continue;
+
+ RECT bounds;
+ actor->getBounds( bounds );
+
+ if ( PtInRect( &bounds, check ) )
+ {
+ return actor;
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Caller must first translate mouse into screen coordinates
+// Input : mx -
+// my -
+// Output : CChoreoChannelWidget
+//-----------------------------------------------------------------------------
+CChoreoChannelWidget *CChoreoView::GetChannelUnderCursorPos( int mx, int my )
+{
+ CChoreoActorWidget *actor = GetActorUnderCursorPos( mx, my );
+ if ( !actor )
+ {
+ return NULL;
+ }
+
+ POINT check;
+ check.x = mx;
+ check.y = my;
+
+ CChoreoChannelWidget *channel;
+ for ( int i = 0; i < actor->GetNumChannels(); i++ )
+ {
+ channel = actor->GetChannel( i );
+ if ( !channel )
+ continue;
+
+ RECT bounds;
+ channel->getBounds( bounds );
+
+ if ( PtInRect( &bounds, check ) )
+ {
+ return channel;
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Caller must first translate mouse into screen coordinates
+// Input : mx -
+// my -
+//-----------------------------------------------------------------------------
+CChoreoEventWidget *CChoreoView::GetEventUnderCursorPos( int mx, int my )
+{
+ CChoreoChannelWidget *channel = GetChannelUnderCursorPos( mx, my );
+ if ( !channel )
+ {
+ return NULL;
+ }
+
+ POINT check;
+ check.x = mx;
+ check.y = my;
+
+ if ( mx < GetLabelWidth() )
+ return NULL;
+
+ if ( my < GetStartRow() )
+ return NULL;
+
+ if ( my >= h2() - ( m_nInfoHeight + m_nScrollbarHeight ) )
+ return NULL;
+
+ CChoreoEventWidget *event;
+ for ( int i = 0; i < channel->GetNumEvents(); i++ )
+ {
+ event = channel->GetEvent( i );
+ if ( !event )
+ continue;
+
+ RECT bounds;
+ event->getBounds( bounds );
+
+ // Events get an expanded border
+ InflateRect( &bounds, 8, 4 );
+
+ if ( PtInRect( &bounds, check ) )
+ {
+ return event;
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Select wave file for phoneme editing
+// Input : *filename -
+//-----------------------------------------------------------------------------
+void CChoreoView::SetCurrentWaveFile( const char *filename, CChoreoEvent *event )
+{
+ g_pPhonemeEditor->SetCurrentWaveFile( filename, false, event );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pfn -
+// *param1 -
+//-----------------------------------------------------------------------------
+void CChoreoView::TraverseWidgets( CVMEMBERFUNC pfn, CChoreoWidget *param1 )
+{
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *actor = m_SceneActors[ i ];
+ if ( !actor )
+ continue;
+
+ (this->*pfn)( actor, param1 );
+
+ for ( int j = 0; j < actor->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *channel = actor->GetChannel( j );
+ if ( !channel )
+ continue;
+
+ (this->*pfn)( channel, param1 );
+
+ for ( int k = 0; k < channel->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *event = channel->GetEvent( k );
+ if ( !event )
+ continue;
+
+ (this->*pfn)( event, param1 );
+ }
+ }
+ }
+
+ for ( int i = 0; i < m_SceneGlobalEvents.Size(); i++ )
+ {
+ CChoreoGlobalEventWidget *event = m_SceneGlobalEvents[ i ];
+ if ( !event )
+ continue;
+
+ (this->*pfn)( event, param1 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *widget -
+// *param1 -
+//-----------------------------------------------------------------------------
+void CChoreoView::Deselect( CChoreoWidget *widget, CChoreoWidget *param1 )
+{
+ if ( widget->IsSelected() )
+ {
+ widget->SetSelected( false );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *widget -
+// *param1 -
+//-----------------------------------------------------------------------------
+void CChoreoView::Select( CChoreoWidget *widget, CChoreoWidget *param1 )
+{
+ if ( widget != param1 )
+ return;
+
+ if ( widget->IsSelected() )
+ return;
+
+ widget->SetSelected( true );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *widget -
+// *param1 -
+//-----------------------------------------------------------------------------
+void CChoreoView::SelectAllEvents( CChoreoWidget *widget, CChoreoWidget *param1 )
+{
+ CChoreoEventWidget *ew = dynamic_cast< CChoreoEventWidget * >( widget );
+ CChoreoGlobalEventWidget *gew = dynamic_cast< CChoreoGlobalEventWidget * >( widget );
+
+ if ( ew || gew )
+ {
+ if ( widget->IsSelected() )
+ return;
+
+ widget->SetSelected( true );
+ }
+}
+
+bool CChoreoView::CreateAnimationEvent( int mx, int my, char const *animationname )
+{
+ if ( !animationname || !animationname[0] )
+ 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;
+
+ pt.x -= GetLabelWidth();
+ m_nClickedX = pt.x;
+
+ GetObjectsUnderMouse( pt.x, pt.y, &m_pClickedActor, &m_pClickedChannel, &m_pClickedEvent, &m_pClickedGlobalEvent, &m_nClickedTag, &m_pClickedAbsoluteTag, &m_nClickedChannelCloseCaptionButton );
+
+ // Find channel actor and time ( uses screen space coordinates )
+ //
+ CChoreoChannelWidget *channel = GetChannelUnderCursorPos( pt.x, pt.y );
+ if ( !channel )
+ {
+ CChoreoActorWidget *actor = GetActorUnderCursorPos( pt.x, pt.y );
+ if ( !actor )
+ return false;
+
+ // Grab first channel
+ if ( !actor->GetNumChannels() )
+ return false;
+
+ channel = actor->GetChannel( 0 );
+ }
+
+ if ( !channel )
+ return false;
+
+ CChoreoChannel *pchannel = channel->GetChannel();
+ if ( !pchannel )
+ {
+ Assert( 0 );
+ return false;
+ }
+
+ // At this point we need to ask the user what type of even to create (gesture or sequence) and just show the approprite dialog
+ CChoiceParams params;
+ strcpy( params.m_szDialogTitle, "Create Animation Event" );
+
+ params.m_bPositionDialog = false;
+ params.m_nLeft = 0;
+ params.m_nTop = 0;
+ strcpy( params.m_szPrompt, "Type of event:" );
+
+ params.m_Choices.RemoveAll();
+
+ params.m_nSelected = 0;
+ ChoiceText text;
+ strcpy( text.choice, "gesture" );
+ params.m_Choices.AddToTail( text );
+ strcpy( text.choice, "sequence" );
+ params.m_Choices.AddToTail( text );
+
+ if ( !ChoiceProperties( &params ) )
+ return false;
+
+ if ( params.m_nSelected < 0 )
+ return false;
+
+ switch ( params.m_nSelected )
+ {
+ default:
+ case 0:
+ AddEvent( CChoreoEvent::GESTURE, 0, animationname );
+ break;
+ case 1:
+ AddEvent( CChoreoEvent::SEQUENCE, 0, animationname );
+ break;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+// *cl -
+// *exp -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CChoreoView::CreateExpressionEvent( int mx, int my, CExpClass *cl, CExpression *exp )
+{
+ if ( !m_pScene )
+ 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;
+
+ // Find channel actor and time ( uses screen space coordinates )
+ //
+ CChoreoChannelWidget *channel = GetChannelUnderCursorPos( pt.x, pt.y );
+ if ( !channel )
+ {
+ CChoreoActorWidget *actor = GetActorUnderCursorPos( pt.x, pt.y );
+ if ( !actor )
+ return false;
+
+ // Grab first channel
+ if ( !actor->GetNumChannels() )
+ return false;
+
+ channel = actor->GetChannel( 0 );
+ }
+
+ if ( !channel )
+ return false;
+
+ CChoreoChannel *pchannel = channel->GetChannel();
+ if ( !pchannel )
+ {
+ Assert( 0 );
+ return false;
+ }
+
+ CChoreoEvent *event = m_pScene->AllocEvent();
+ if ( !event )
+ {
+ Assert( 0 );
+ return false;
+ }
+
+ float starttime = GetTimeValueForMouse( pt.x, false );
+
+ SetDirty( true );
+
+ PushUndo( "Create Expression" );
+
+ event->SetType( CChoreoEvent::EXPRESSION );
+ event->SetName( exp->name );
+ event->SetParameters( cl->GetName() );
+ event->SetParameters2( exp->name );
+ event->SetStartTime( starttime );
+ event->SetChannel( pchannel );
+ event->SetActor( pchannel->GetActor() );
+ event->SetEndTime( starttime + 1.0f );
+
+ event->SnapTimes();
+
+ DeleteSceneWidgets();
+
+ // Add to appropriate channel
+ pchannel->AddEvent( event );
+
+ CreateSceneWidgets();
+
+ PushRedo( "Create Expression" );
+
+ // Redraw
+ InvalidateLayout();
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CChoreoView::IsPlayingScene( void )
+{
+ return m_bSimulating;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::ResetTargetSettings( void )
+{
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *w = m_SceneActors[ i ];
+ if ( w )
+ {
+ w->ResetSettings();
+ }
+ }
+
+ models->ClearModelTargets( true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: copies the actors "settings" into the models FlexControllers
+// Input : dt -
+//-----------------------------------------------------------------------------
+void CChoreoView::UpdateCurrentSettings( void )
+{
+ StudioModel *defaultModel = models->GetActiveStudioModel();
+
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *w = m_SceneActors[ i ];
+ if ( !w )
+ continue;
+
+ if ( !w->GetActor()->GetActive() )
+ continue;
+
+ StudioModel *model = FindAssociatedModel( m_pScene, w->GetActor() );
+ if ( !model )
+ continue;
+
+ CStudioHdr *hdr = model->GetStudioHdr();
+ if ( !hdr )
+ continue;
+
+ float *current = w->GetSettings();
+
+ for ( LocalFlexController_t j = LocalFlexController_t(0); j < hdr->numflexcontrollers(); j++ )
+ {
+ int k = hdr->pFlexcontroller( j )->localToGlobal;
+
+ if (k != -1)
+ {
+ if ( defaultModel == model && g_pFlexPanel->IsEdited( k ) )
+ {
+ model->SetFlexController( j, g_pFlexPanel->GetSlider( k ) );
+ }
+ else
+ {
+ model->SetFlexController( j, current[ k ] );
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+// tagnum -
+//-----------------------------------------------------------------------------
+void CChoreoView::DeleteEventRelativeTag( CChoreoEvent *event, int tagnum )
+{
+ if ( !event )
+ return;
+
+ CEventRelativeTag *tag = event->GetRelativeTag( tagnum );
+ if ( !tag )
+ return;
+
+ SetDirty( true );
+
+ PushUndo( "Delete Event Tag" );
+
+ event->RemoveRelativeTag( tag->GetName() );
+
+ m_pScene->ReconcileTags();
+
+ PushRedo( "Delete Event Tag" );
+
+ g_pPhonemeEditor->redraw();
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+//-----------------------------------------------------------------------------
+void CChoreoView::AddEventRelativeTag( void )
+{
+ CChoreoEventWidget *ew = m_pClickedEvent;
+ if ( !ew )
+ return;
+
+ CChoreoEvent *event = ew->GetEvent();
+ if ( !event->GetEndTime() )
+ {
+ Con_ErrorPrintf( "Event Tag: Can only tag events with an end time\n" );
+ return;
+ }
+
+ CInputParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ strcpy( params.m_szDialogTitle, "Event Tag Name" );
+ strcpy( params.m_szPrompt, "Name:" );
+
+ strcpy( params.m_szInputText, "" );
+
+ if ( !InputProperties( &params ) )
+ return;
+
+ if ( strlen( params.m_szInputText ) <= 0 )
+ {
+ Con_ErrorPrintf( "Event Tag Name: No name entered!\n" );
+ return;
+ }
+
+ RECT bounds = ew->getBounds();
+
+ // Convert click to frac
+ float frac = 0.0f;
+ if ( bounds.right - bounds.left > 0 )
+ {
+ frac = (float)( m_nClickedX - bounds.left ) / (float)( bounds.right - bounds.left );
+ frac = min( 1.0f, frac );
+ frac = max( 0.0f, frac );
+ }
+
+ SetDirty( true );
+
+ PushUndo( "Add Event Tag" );
+
+ event->AddRelativeTag( params.m_szInputText, frac );
+
+ PushRedo( "Add Event Tag" );
+
+ InvalidateLayout();
+ g_pPhonemeEditor->redraw();
+ g_pExpressionTool->redraw();
+ g_pGestureTool->redraw();
+ g_pRampTool->redraw();
+ g_pSceneRampTool->redraw();
+}
+
+CChoreoChannelWidget *CChoreoView::FindChannelForEvent( CChoreoEvent *event )
+{
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *a = m_SceneActors[ i ];
+ if ( !a )
+ continue;
+
+ for ( int j = 0; j < a->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *c = a->GetChannel( j );
+ if ( !c )
+ continue;
+
+ for ( int k = 0; k < c->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *e = c->GetEvent( k );
+ if ( !e )
+ continue;
+
+ if ( e->GetEvent() != event )
+ continue;
+
+ return c;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+// Output : CChoreoEventWidget
+//-----------------------------------------------------------------------------
+CChoreoEventWidget *CChoreoView::FindWidgetForEvent( CChoreoEvent *event )
+{
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *a = m_SceneActors[ i ];
+ if ( !a )
+ continue;
+
+ for ( int j = 0; j < a->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *c = a->GetChannel( j );
+ if ( !c )
+ continue;
+
+ for ( int k = 0; k < c->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *e = c->GetEvent( k );
+ if ( !e )
+ continue;
+
+ if ( e->GetEvent() != event )
+ continue;
+
+ return e;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::SelectAll( void )
+{
+ TraverseWidgets( &CChoreoView::SelectAllEvents, NULL );
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::DeselectAll( void )
+{
+ TraverseWidgets( &CChoreoView::Deselect, NULL );
+ redraw();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+//-----------------------------------------------------------------------------
+void CChoreoView::UpdateStatusArea( int mx, int my )
+{
+ FLYOVER fo;
+
+ GetObjectsUnderMouse( mx, my, &fo.a, &fo.c,
+ &fo.e, &fo.ge, &fo.tag, &fo.at, &fo.ccbutton );
+
+ if ( fo.a )
+ {
+ m_Flyover.a = fo.a;
+ }
+ if ( fo.e )
+ {
+ m_Flyover.e = fo.e;
+ }
+ if ( fo.c )
+ {
+ m_Flyover.c = fo.c;
+ }
+ if ( fo.ge )
+ {
+ m_Flyover.ge = fo.ge;
+ }
+ if ( fo.tag != -1 )
+ {
+ m_Flyover.tag = fo.tag;
+ }
+ if ( fo.ccbutton != -1 )
+ {
+ m_Flyover.ccbutton = fo.ccbutton;
+ // m_Flyover.e = NULL;
+ }
+
+ RECT rcClip;
+ GetClientRect( (HWND)getHandle(), &rcClip );
+ rcClip.bottom -= m_nScrollbarHeight;
+ rcClip.top = rcClip.bottom - m_nInfoHeight;
+ rcClip.right -= m_nScrollbarHeight;
+
+ CChoreoWidgetDrawHelper drawHelper( this, rcClip, COLOR_CHOREO_BACKGROUND );
+
+ drawHelper.StartClipping( rcClip );
+
+ RedrawStatusArea( drawHelper, rcClip );
+
+ drawHelper.StopClipping();
+ ValidateRect( (HWND)getHandle(), &rcClip );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::ClearStatusArea( void )
+{
+ memset( &m_Flyover, 0, sizeof( m_Flyover ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+// rcStatus -
+//-----------------------------------------------------------------------------
+void CChoreoView::RedrawStatusArea( CChoreoWidgetDrawHelper& drawHelper, RECT& rcStatus )
+{
+ drawHelper.DrawFilledRect( COLOR_CHOREO_BACKGROUND, rcStatus );
+
+ drawHelper.DrawColoredLine( COLOR_INFO_BORDER, PS_SOLID, 1, rcStatus.left, rcStatus.top,
+ rcStatus.right, rcStatus.top );
+
+ RECT rcInfo = rcStatus;
+
+ rcInfo.top += 2;
+
+ if ( m_Flyover.e )
+ {
+ m_Flyover.e->redrawStatus( drawHelper, rcInfo );
+ }
+ if ( m_Flyover.c &&
+ m_Flyover.ccbutton != -1 )
+ {
+ m_Flyover.c->redrawStatus( drawHelper, rcInfo, m_Flyover.ccbutton );
+ }
+
+ if ( m_pScene )
+ {
+ char sz[ 512 ];
+
+ int fontsize = 9;
+ int fontweight = FW_NORMAL;
+
+ RECT rcText;
+ rcText = rcInfo;
+ rcText.bottom = rcText.top + fontsize + 2;
+
+ char const *mapname = m_pScene->GetMapname();
+ if ( mapname )
+ {
+ sprintf( sz, "Associated .bsp: %s", mapname[ 0 ] ? mapname : "none" );
+
+ int len = drawHelper.CalcTextWidth( "Arial", fontsize, fontweight, sz );
+ rcText.left = rcText.right - len - 10;
+
+ drawHelper.DrawColoredText( "Arial", fontsize, fontweight, COLOR_INFO_TEXT, rcText, sz );
+
+ OffsetRect( &rcText, 0, fontsize + 2 );
+ }
+
+ sprintf( sz, "Scene: %s", GetChoreoFile() );
+
+ int len = drawHelper.CalcTextWidth( "Arial", fontsize, fontweight, sz );
+ rcText.left = rcText.right - len - 10;
+
+ drawHelper.DrawColoredText( "Arial", fontsize, fontweight, COLOR_INFO_TEXT, rcText, sz );
+ }
+
+// drawHelper.DrawColoredText( "Arial", 12, 500, RGB( 0, 0, 0 ), rcInfo, m_Flyover.e ? m_Flyover.e->GetEvent()->GetName() : "" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+//-----------------------------------------------------------------------------
+void CChoreoView::MoveEventToBack( CChoreoEvent *event )
+{
+ // Now find channel widget
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *a = m_SceneActors[ i ];
+ if ( !a )
+ continue;
+
+ for ( int j = 0; j < a->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *c = a->GetChannel( j );
+ if ( !c )
+ continue;
+
+ for ( int k = 0; k < c->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *e = c->GetEvent( k );
+ if ( !e )
+ continue;
+
+ if ( event == e->GetEvent() )
+ {
+ // Move it to back of channel's list
+ c->MoveEventToTail( e );
+ InvalidateLayout();
+ return;
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoView::GetEndRow( void )
+{
+ RECT rcClient;
+ GetClientRect( (HWND)getHandle(), &rcClient );
+
+ return rcClient.bottom - ( m_nInfoHeight + m_nScrollbarHeight );
+}
+
+// Undo/Redo
+void CChoreoView::Undo( void )
+{
+ if ( m_UndoStack.Size() > 0 && m_nUndoLevel > 0 )
+ {
+ m_nUndoLevel--;
+ CVUndo *u = m_UndoStack[ m_nUndoLevel ];
+ Assert( u->undo );
+
+ DeleteSceneWidgets();
+
+ *m_pScene = *(u->undo);
+ g_MDLViewer->InitGridSettings();
+
+ CreateSceneWidgets();
+
+ ReportSceneClearToTools();
+ ClearStatusArea();
+ m_pClickedActor = NULL;
+ m_pClickedChannel = NULL;
+ m_pClickedEvent = NULL;
+ m_pClickedGlobalEvent = NULL;
+ }
+
+ InvalidateLayout();
+}
+
+void CChoreoView::Redo( void )
+{
+ if ( m_UndoStack.Size() > 0 && m_nUndoLevel <= m_UndoStack.Size() - 1 )
+ {
+ CVUndo *u = m_UndoStack[ m_nUndoLevel ];
+ Assert( u->redo );
+
+ DeleteSceneWidgets();
+
+ *m_pScene = *(u->redo);
+ g_MDLViewer->InitGridSettings();
+
+ CreateSceneWidgets();
+
+ ReportSceneClearToTools();
+ ClearStatusArea();
+ m_pClickedActor = NULL;
+ m_pClickedChannel = NULL;
+ m_pClickedEvent = NULL;
+ m_pClickedGlobalEvent = NULL;
+
+ m_nUndoLevel++;
+ }
+
+ InvalidateLayout();
+}
+
+static char *CopyString( const char *in )
+{
+ int len = strlen( in );
+ char *n = new char[ len + 1 ];
+ strcpy( n, in );
+ return n;
+}
+
+void CChoreoView::PushUndo( const char *description )
+{
+ Assert( !m_bRedoPending );
+ m_bRedoPending = true;
+ WipeRedo();
+
+ // Copy current data
+ CChoreoScene *u = new CChoreoScene( this );
+ *u = *m_pScene;
+ CVUndo *undo = new CVUndo;
+ undo->undo = u;
+ undo->redo = NULL;
+ undo->udescription = CopyString( description );
+ undo->rdescription = NULL;
+ m_UndoStack.AddToTail( undo );
+ m_nUndoLevel++;
+}
+
+void CChoreoView::PushRedo( const char *description )
+{
+ Assert( m_bRedoPending );
+ m_bRedoPending = false;
+
+ // Copy current data
+ CChoreoScene *r = new CChoreoScene( this );
+ *r = *m_pScene;
+ CVUndo *undo = m_UndoStack[ m_nUndoLevel - 1 ];
+ undo->redo = r;
+ undo->rdescription = CopyString( description );
+
+ // Always redo here to reflect that someone has made a change
+ redraw();
+}
+
+void CChoreoView::WipeUndo( void )
+{
+ while ( m_UndoStack.Size() > 0 )
+ {
+ CVUndo *u = m_UndoStack[ 0 ];
+ delete u->undo;
+ delete u->redo;
+ delete[] u->udescription;
+ delete[] u->rdescription;
+ delete u;
+ m_UndoStack.Remove( 0 );
+ }
+ m_nUndoLevel = 0;
+}
+
+void CChoreoView::WipeRedo( void )
+{
+ // Wipe everything above level
+ while ( m_UndoStack.Size() > m_nUndoLevel )
+ {
+ CVUndo *u = m_UndoStack[ m_nUndoLevel ];
+ delete u->undo;
+ delete u->redo;
+ delete[] u->udescription;
+ delete[] u->rdescription;
+ delete u;
+ m_UndoStack.Remove( m_nUndoLevel );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : const char
+//-----------------------------------------------------------------------------
+const char *CChoreoView::GetUndoDescription( void )
+{
+ if ( CanUndo() )
+ {
+ CVUndo *u = m_UndoStack[ m_nUndoLevel - 1 ];
+ return u->udescription;
+ }
+ return "???undo";
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : const char
+//-----------------------------------------------------------------------------
+const char *CChoreoView::GetRedoDescription( void )
+{
+ if ( CanRedo() )
+ {
+ CVUndo *u = m_UndoStack[ m_nUndoLevel ];
+ return u->rdescription;
+ }
+ return "???redo";
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CChoreoView::CanUndo()
+{
+ return m_nUndoLevel != 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CChoreoView::CanRedo()
+{
+ return m_nUndoLevel != m_UndoStack.Size();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::OnGestureTool( void )
+{
+ if ( m_pClickedEvent->GetEvent()->GetType() != CChoreoEvent::GESTURE )
+ return;
+
+ g_pGestureTool->SetEvent( m_pClickedEvent->GetEvent() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::OnExpressionTool( void )
+{
+ if ( m_pClickedEvent->GetEvent()->GetType() != CChoreoEvent::FLEXANIMATION )
+ return;
+
+ g_pExpressionTool->SetEvent( m_pClickedEvent->GetEvent() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : CChoreoScene
+//-----------------------------------------------------------------------------
+CChoreoScene *CChoreoView::GetScene( void )
+{
+ return m_pScene;
+}
+
+bool CChoreoView::CanPaste( void )
+{
+ char const *copyfile = COPYPASTE_FILENAME;
+
+ if ( !filesystem->FileExists( copyfile ) )
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void CChoreoView::CopyEvents( void )
+{
+ if ( !m_pScene )
+ return;
+
+ char const *copyfile = COPYPASTE_FILENAME;
+ MakeFileWriteable( copyfile );
+ ExportVCDFile( copyfile );
+}
+
+void CChoreoView::PasteEvents( void )
+{
+ if ( !m_pScene )
+ return;
+
+ if ( !CanPaste() )
+ return;
+
+ char const *copyfile = COPYPASTE_FILENAME;
+
+ ImportVCDFile( copyfile );
+}
+
+void CChoreoView::ImportEvents( void )
+{
+ if ( !m_pScene )
+ return;
+
+ if ( !m_pClickedActor || !m_pClickedChannel )
+ return;
+
+ char eventfile[ 512 ];
+ if ( !FacePoser_ShowOpenFileNameDialog( eventfile, sizeof( eventfile ), "scenes", "*.vce" ) )
+ return;
+
+ char fullpathbuf[ 512 ];
+ char *fullpath = eventfile;
+ if ( !Q_IsAbsolutePath( eventfile ) )
+ {
+ filesystem->RelativePathToFullPath( eventfile, "GAME", fullpathbuf, sizeof( fullpathbuf ) );
+ fullpath = fullpathbuf;
+ }
+
+ if ( !filesystem->FileExists( fullpath ) )
+ return;
+
+ LoadScriptFile( fullpath );
+
+ DeselectAll();
+
+ SetDirty( true );
+
+ PushUndo( "Import Events" );
+
+ m_pScene->ImportEvents( tokenprocessor, m_pClickedActor->GetActor(), m_pClickedChannel->GetChannel() );
+
+ PushRedo( "Import Events" );
+
+ CreateSceneWidgets();
+ // Redraw
+ InvalidateLayout();
+
+ Con_Printf( "Imported events from %s\n", fullpath );
+}
+
+void CChoreoView::ExportEvents( void )
+{
+ char eventfilename[ 512 ];
+ if ( !FacePoser_ShowSaveFileNameDialog( eventfilename, sizeof( eventfilename ), "scenes", "*.vce" ) )
+ return;
+
+ Q_DefaultExtension( eventfilename, ".vce", sizeof( eventfilename ) );
+
+ Con_Printf( "Exporting events to %s\n", eventfilename );
+
+ // Write to file
+ CUtlVector< CChoreoEvent * > events;
+
+ // Find selected eventss
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *a = m_SceneActors[ i ];
+ if ( !a )
+ continue;
+
+ for ( int j = 0; j < a->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *c = a->GetChannel( j );
+ if ( !c )
+ continue;
+
+ for ( int k = 0; k < c->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *e = c->GetEvent( k );
+ if ( !e )
+ continue;
+
+ if ( !e->IsSelected() )
+ continue;
+
+ CChoreoEvent *event = e->GetEvent();
+ if ( !event )
+ continue;
+
+ events.AddToTail( event );
+ }
+ }
+ }
+
+ if ( events.Size() > 0 )
+ {
+ m_pScene->ExportEvents( eventfilename, events );
+ }
+ else
+ {
+ Con_Printf( "No events selected\n" );
+ }
+}
+
+void CChoreoView::ExportVCDFile( char const *filename )
+{
+ Con_Printf( "Exporting to %s\n", filename );
+
+ // Unmark everything
+ m_pScene->MarkForSaveAll( false );
+
+ // Mark everything related to selected events
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *a = m_SceneActors[ i ];
+ if ( !a )
+ continue;
+
+ for ( int j = 0; j < a->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *c = a->GetChannel( j );
+ if ( !c )
+ continue;
+
+ for ( int k = 0; k < c->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *e = c->GetEvent( k );
+ if ( !e )
+ continue;
+
+ if ( !e->IsSelected() )
+ continue;
+
+ CChoreoEvent *event = e->GetEvent();
+ if ( !event )
+ continue;
+
+ event->SetMarkedForSave( true );
+ if ( event->GetChannel() )
+ {
+ event->GetChannel()->SetMarkedForSave( true );
+ }
+ if ( event->GetActor() )
+ {
+ event->GetActor()->SetMarkedForSave( true );
+ }
+ }
+ }
+ }
+
+ m_pScene->ExportMarkedToFile( filename );
+}
+
+void CChoreoView::ImportVCDFile( char const *filename )
+{
+ CChoreoScene *merge = LoadScene( filename );
+ if ( !merge )
+ {
+ Con_Printf( "Couldn't load from .vcd %s\n", filename );
+ return;
+ }
+
+ DeselectAll();
+
+ CUtlRBTree< CChoreoEvent *, int > oldEvents( 0, 0, DefLessFunc( CChoreoEvent * ) );
+
+ int i;
+ for ( i = 0; i < m_SceneActors.Count(); ++i )
+ {
+ CChoreoActorWidget *actor = m_SceneActors[ i ];
+ if ( !actor )
+ continue;
+
+ for ( int j = 0; j < actor->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *channel = actor->GetChannel( j );
+ if ( !channel )
+ continue;
+
+ for ( int k = 0; k < channel->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *event = channel->GetEvent( k );
+ if ( !event )
+ continue;
+
+ oldEvents.Insert( event->GetEvent() );
+ }
+ }
+ }
+
+ SetDirty( true );
+
+ PushUndo( "Merge/Import VCD" );
+
+ m_pScene->Merge( merge );
+
+ PushRedo( "Merge/Import VCD" );
+
+ DeleteSceneWidgets();
+ CreateSceneWidgets();
+
+ // Force scroll bars to recompute
+ ForceScrollBarsToRecompute( false );
+
+ // Now walk through the "new" events and select everything that wasn't already there (all of the stuff that was "added" during the merge)
+ for ( i = 0; i < m_SceneActors.Count(); ++i )
+ {
+ CChoreoActorWidget *actor = m_SceneActors[ i ];
+ if ( !actor )
+ continue;
+
+ for ( int j = 0; j < actor->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *channel = actor->GetChannel( j );
+ if ( !channel )
+ continue;
+
+ for ( int k = 0; k < channel->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *event = channel->GetEvent( k );
+ if ( !event )
+ continue;
+
+ if ( oldEvents.Find( event->GetEvent() ) == oldEvents.InvalidIndex() )
+ {
+ event->SetSelected( true );
+ }
+ }
+ }
+ }
+
+ // Redraw
+ InvalidateLayout();
+
+ Con_Printf( "Imported vcd '%s'\n", filename );
+
+ delete merge;
+
+ redraw();
+}
+
+void CChoreoView::ExportVCD()
+{
+ char scenefile[ 512 ];
+ if ( !FacePoser_ShowSaveFileNameDialog( scenefile, sizeof( scenefile ), "scenes", "*.vcd" ) )
+ {
+ return;
+ }
+
+ Q_DefaultExtension( scenefile, ".vcd", sizeof( scenefile ) );
+
+ ExportVCDFile( scenefile );
+}
+
+void CChoreoView::ImportVCD()
+{
+ if ( !m_pScene )
+ return;
+
+ if ( !m_pClickedActor || !m_pClickedChannel )
+ return;
+
+ char scenefile[ 512 ];
+ if ( !FacePoser_ShowOpenFileNameDialog( scenefile, sizeof( scenefile ), "scenes", "*.vcd" ) )
+ {
+ return;
+ }
+
+ ImportVCDFile( scenefile );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CChoreoView::IsProcessing( void )
+{
+ if ( !m_pScene )
+ return false;
+
+ if ( m_flScrub != m_flScrubTarget )
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CChoreoView::ShouldProcessSpeak( void )
+{
+ if ( !g_pControlPanel->AllToolsDriveSpeech() )
+ {
+ if ( !IsActiveTool() )
+ return false;
+ }
+
+ if ( IFacePoserToolWindow::IsAnyToolScrubbing() )
+ return true;
+
+ if ( IFacePoserToolWindow::IsAnyToolProcessing() )
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *scene -
+// *event -
+//-----------------------------------------------------------------------------
+void CChoreoView::ProcessSpeak( CChoreoScene *scene, CChoreoEvent *event )
+{
+ if ( !ShouldProcessSpeak() )
+ return;
+
+ Assert( event->GetType() == CChoreoEvent::SPEAK );
+ Assert( scene );
+
+ float t = scene->GetTime();
+
+ StudioModel *model = FindAssociatedModel( scene, event->GetActor() );
+
+ // See if we should trigger CC
+ char soundname[ 512 ];
+ Q_strncpy( soundname, event->GetParameters(), sizeof( soundname ) );
+
+ float actualEndTime = event->GetEndTime();
+
+ if ( event->GetCloseCaptionType() == CChoreoEvent::CC_MASTER )
+ {
+ char tok[ CChoreoEvent::MAX_CCTOKEN_STRING ];
+ if ( event->GetPlaybackCloseCaptionToken( tok, sizeof( tok ) ) )
+ {
+ // Use the token as the sound name lookup, too.
+ if ( event->IsUsingCombinedFile() &&
+ ( event->GetNumSlaves() > 0 ) )
+ {
+ Q_strncpy( soundname, tok, sizeof( soundname ) );
+ actualEndTime = max( actualEndTime, event->GetLastSlaveEndTime() );
+ }
+ }
+ }
+
+ CAudioMixer *mixer = event->GetMixer();
+ if ( !mixer || !sound->IsSoundPlaying( mixer ) )
+ {
+ CSoundParameters params;
+ float volume = VOL_NORM;
+
+ gender_t gender = GENDER_NONE;
+ if (model)
+ {
+ gender = soundemitter->GetActorGender( model->GetFileName() );
+ }
+
+ if ( !Q_stristr( soundname, ".wav" ) &&
+ soundemitter->GetParametersForSound( soundname, params, gender ) )
+ {
+ volume = params.volume;
+ }
+
+ sound->PlaySound(
+ model,
+ volume,
+ va( "sound/%s", FacePoser_TranslateSoundName( soundname, model ) ),
+ &mixer );
+ event->SetMixer( mixer );
+ }
+
+ mixer = event->GetMixer();
+ if ( !mixer )
+ return;
+
+ mixer->SetDirection( m_flFrameTime >= 0.0f );
+ float starttime, endtime;
+ starttime = event->GetStartTime();
+ endtime = actualEndTime;
+
+ float soundtime = endtime - starttime;
+ if ( soundtime <= 0.0f )
+ return;
+
+ float f = ( t - starttime ) / soundtime;
+ f = clamp( f, 0.0f, 1.0f );
+
+ // Compute sample
+ float numsamples = (float)mixer->GetSource()->SampleCount();
+
+ int cursample = f * numsamples;
+ cursample = clamp( cursample, 0, numsamples - 1 );
+
+ int realsample = mixer->GetSamplePosition();
+
+ int dsample = cursample - realsample;
+
+ int samplelimit = mixer->GetSource()->SampleRate() * 0.02f; // don't shift until samples are off by this much
+ if (IsScrubbing())
+ {
+ samplelimit = mixer->GetSource()->SampleRate() * 0.01f; // make it shorter tolerance when scrubbing
+ }
+
+ if ( abs( dsample ) > samplelimit )
+ {
+ mixer->SetSamplePosition( cursample, IsScrubbing() );
+ }
+ mixer->SetActive( true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+void CChoreoView::ProcessSubscene( CChoreoScene *scene, CChoreoEvent *event )
+{
+ Assert( event->GetType() == CChoreoEvent::SUBSCENE );
+
+ CChoreoScene *subscene = event->GetSubScene();
+ if ( !subscene )
+ return;
+
+ if ( subscene->SimulationFinished() )
+ return;
+
+ // Have subscenes think for appropriate time
+ subscene->Think( m_flScrub );
+}
+
+void CChoreoView::PositionControls()
+{
+ int topx = GetCaptionHeight() + SCRUBBER_HEIGHT;
+
+ int bx = 2;
+ int bw = 16;
+
+ m_btnPlay->setBounds( bx, topx + 4, 16, 16 );
+
+ bx += bw + 2;
+
+ m_btnPause->setBounds( bx, topx + 4, 16, 16 );
+ bx += bw + 2;
+ m_btnStop->setBounds( bx, topx + 4, 16, 16 );
+ bx += bw + 2;
+ m_pPlaybackRate->setBounds( bx, topx + 4, 100, 16 );
+}
+
+void CChoreoView::SetChoreoFile( char const *filename )
+{
+ strcpy( m_szChoreoFile, filename );
+ if ( m_szChoreoFile[ 0 ] )
+ {
+ char sz[ 256 ];
+ if ( IsFileWriteable( m_szChoreoFile ) )
+ {
+ Q_snprintf( sz, sizeof( sz ), " - %s", m_szChoreoFile );
+ }
+ else
+ {
+ Q_snprintf( sz, sizeof( sz ), " - %s [Read-Only]", m_szChoreoFile );
+ }
+ SetSuffix( sz );
+ }
+ else
+ {
+ SetSuffix( "" );
+ }
+}
+
+char const *CChoreoView::GetChoreoFile( void ) const
+{
+ return m_szChoreoFile;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : rcHandle -
+//-----------------------------------------------------------------------------
+void CChoreoView::GetScrubHandleRect( RECT& rcHandle, bool clipped )
+{
+ float pixel = 0.0f;
+
+ if ( m_pScene )
+ {
+ float currenttime = m_flScrub;
+ float starttime = m_flStartTime;
+ float endtime = m_flEndTime;
+
+ float screenfrac = ( currenttime - starttime ) / ( endtime - starttime );
+
+ pixel = GetLabelWidth() + screenfrac * ( w2() - GetLabelWidth() );
+
+ 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 : rcArea -
+//-----------------------------------------------------------------------------
+void CChoreoView::GetScrubAreaRect( RECT& rcArea )
+{
+ rcArea.left = 0;
+ rcArea.right = w2();
+ rcArea.top = 2 + GetCaptionHeight();
+ rcArea.bottom = rcArea.top + SCRUBBER_HEIGHT - 4;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+// rcHandle -
+//-----------------------------------------------------------------------------
+void CChoreoView::DrawScrubHandle( CChoreoWidgetDrawHelper& drawHelper )
+{
+ RECT rcHandle;
+ GetScrubHandleRect( rcHandle, true );
+
+ HBRUSH br = CreateSolidBrush( RGB( 0, 150, 100 ) );
+
+ drawHelper.DrawFilledRect( br, rcHandle );
+
+ //
+ char sz[ 32 ];
+ sprintf( sz, "%.3f", 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 CChoreoView::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;
+}
+
+bool CChoreoView::IsMouseOverScrubArea( mxEvent *event )
+{
+ RECT rcArea;
+ GetScrubAreaRect( rcArea );
+
+ InflateRect( &rcArea, 2, 2 );
+
+ POINT pt;
+ pt.x = (short)event->x;
+ pt.y = (short)event->y;
+ if ( PtInRect( &rcArea, pt ) )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+bool CChoreoView::IsScrubbing( void ) const
+{
+ bool scrubbing = ( m_nDragType == DRAGTYPE_SCRUBBER ) ? true : false;
+ return scrubbing;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : dt -
+//-----------------------------------------------------------------------------
+void CChoreoView::Think( float dt )
+{
+ bool scrubbing = IFacePoserToolWindow::IsAnyToolScrubbing();
+
+ ScrubThink( dt, scrubbing, this );
+}
+
+static int lastthinkframe = -1;
+void CChoreoView::ScrubThink( float dt, bool scrubbing, IFacePoserToolWindow *invoker )
+{
+ // Make sure we don't get called more than once per frame
+ int thisframe = g_MDLViewer->GetCurrentFrame();
+ if ( thisframe == lastthinkframe )
+ return;
+
+ lastthinkframe = thisframe;
+
+ if ( !m_pScene )
+ return;
+
+ if ( m_flScrubTarget == m_flScrub && !scrubbing )
+ {
+ // Act like it's paused
+ if ( IFacePoserToolWindow::ShouldAutoProcess() )
+ {
+ m_bSimulating = true;
+ SceneThink( m_flScrub );
+ }
+
+ if ( m_bSimulating && !m_bPaused )
+ {
+ //FinishSimulation();
+ }
+ return;
+ }
+
+ // Make sure we're solving head turns during playback
+ models->SetSolveHeadTurn( 1 );
+
+ if ( m_bPaused )
+ {
+ SceneThink( m_flScrub );
+ return;
+ }
+
+ // Make sure phonemes are loaded
+ FacePoser_EnsurePhonemesLoaded();
+
+ if ( !m_bSimulating )
+ {
+ m_bSimulating = true;
+ }
+
+ float d = m_flScrubTarget - m_flScrub;
+ int sign = d > 0.0f ? 1 : -1;
+
+ float maxmove = dt * m_flPlaybackRate;
+
+ float prevScrub = m_flScrub;
+
+ if ( sign > 0 )
+ {
+ if ( d < maxmove )
+ {
+ m_flScrub = m_flScrubTarget;
+ }
+ else
+ {
+ m_flScrub += maxmove;
+ }
+ }
+ else
+ {
+ if ( -d < maxmove )
+ {
+ m_flScrub = m_flScrubTarget;
+ }
+ else
+ {
+ m_flScrub -= maxmove;
+ }
+ }
+
+ m_flFrameTime = ( m_flScrub - prevScrub );
+
+ SceneThink( m_flScrub );
+
+ DrawScrubHandle();
+
+ if ( scrubbing )
+ {
+ g_pMatSysWindow->Frame();
+ }
+
+ if ( invoker != g_pExpressionTool )
+ {
+ g_pExpressionTool->ForceScrubPositionFromSceneTime( m_flScrub );
+ }
+ if ( invoker != g_pGestureTool )
+ {
+ g_pGestureTool->ForceScrubPositionFromSceneTime( m_flScrub );
+ }
+ if ( invoker != g_pRampTool )
+ {
+ g_pRampTool->ForceScrubPositionFromSceneTime( m_flScrub );
+ }
+ if ( invoker != g_pSceneRampTool )
+ {
+ g_pSceneRampTool->ForceScrubPositionFromSceneTime( m_flScrub );
+ }
+}
+
+void CChoreoView::DrawScrubHandle( void )
+{
+ if ( !m_bCanDraw )
+ return;
+
+ // Handle new time and
+ RECT rcArea;
+ GetScrubAreaRect( rcArea );
+
+ CChoreoWidgetDrawHelper drawHelper( this, rcArea, COLOR_CHOREO_BACKGROUND );
+ DrawScrubHandle( drawHelper );
+}
+
+void CChoreoView::SetScrubTime( float t )
+{
+ m_flScrub = t;
+
+ m_bPaused = false;
+}
+
+void CChoreoView::SetScrubTargetTime( float t )
+{
+ m_flScrubTarget = t;
+
+ m_bPaused = false;
+}
+
+void CChoreoView::ClampTimeToSelectionInterval( float& timeval )
+{
+ // FIXME hook this up later
+ return;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : show -
+//-----------------------------------------------------------------------------
+void CChoreoView::ShowButtons( bool show )
+{
+ m_btnPlay->setVisible( show );
+ m_btnPause->setVisible( show );
+ m_btnStop->setVisible( show );
+ m_pPlaybackRate->setVisible( show );
+}
+
+void CChoreoView::RememberSelectedEvents( CUtlVector< CChoreoEvent * >& list )
+{
+ GetSelectedEvents( list );
+}
+
+void CChoreoView::ReselectEvents( CUtlVector< CChoreoEvent * >& list )
+{
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *actor = m_SceneActors[ i ];
+ if ( !actor )
+ continue;
+
+ for ( int j = 0; j < actor->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *channel = actor->GetChannel( j );
+ if ( !channel )
+ continue;
+
+ for ( int k = 0; k < channel->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *event = channel->GetEvent( k );
+ if ( !event )
+ continue;
+
+ CChoreoEvent *check = event->GetEvent();
+ if ( list.Find( check ) != list.InvalidIndex() )
+ {
+ event->SetSelected( true );
+ }
+ }
+ }
+ }
+
+}
+
+void CChoreoView::OnChangeScale( void )
+{
+ CChoreoScene *scene = m_pScene;
+ if ( !scene )
+ {
+ return;
+ }
+
+ // Zoom time in / out
+ CInputParams params;
+ memset( &params, 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)GetTimeZoom( GetToolName() ) / 100.0f );
+
+ if ( !InputProperties( &params ) )
+ return;
+
+ SetTimeZoom( GetToolName(), clamp( (int)( 100.0f * atof( params.m_szInputText ) ), 1, MAX_TIME_ZOOM ), false );
+
+ // Force scroll bars to recompute
+ ForceScrollBarsToRecompute( false );
+
+ CUtlVector< CChoreoEvent * > selected;
+ RememberSelectedEvents( selected );
+
+ DeleteSceneWidgets();
+ CreateSceneWidgets();
+
+ ReselectEvents( selected );
+
+ InvalidateLayout();
+ Con_Printf( "Zoom factor %i %%\n", GetTimeZoom( GetToolName() ) );
+}
+
+void CChoreoView::OnCheckSequenceLengths( void )
+{
+ if ( !m_pScene )
+ return;
+
+ Con_Printf( "Checking sequence durations...\n" );
+
+ bool changed = FixupSequenceDurations( m_pScene, true );
+
+ if ( !changed )
+ {
+ Con_Printf( " no changes...\n" );
+ return;
+ }
+
+ SetDirty( true );
+
+ PushUndo( "Check sequence lengths" );
+
+ FixupSequenceDurations( m_pScene, false );
+
+ PushRedo( "Check sequence lengths" );
+
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *scene -
+//-----------------------------------------------------------------------------
+void CChoreoView::InvalidateTrackLookup_R( CChoreoScene *scene )
+{
+ // No need to undo since this data doesn't matter
+ int c = scene->GetNumEvents();
+ for ( int i = 0; i < c; i++ )
+ {
+ CChoreoEvent *event = scene->GetEvent( i );
+ if ( !event )
+ continue;
+
+ switch ( event->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ {
+ event->SetTrackLookupSet( false );
+ }
+ break;
+ case CChoreoEvent::SUBSCENE:
+ {
+ CChoreoScene *sub = event->GetSubScene();
+ // NOTE: Don't bother loading it now if it's not on hand
+ if ( sub )
+ {
+ InvalidateTrackLookup_R( sub );
+ }
+ }
+ break;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Model changed so we'll have to re-index flex anim tracks
+//-----------------------------------------------------------------------------
+void CChoreoView::InvalidateTrackLookup( void )
+{
+ if ( !m_pScene )
+ return;
+
+ InvalidateTrackLookup_R( m_pScene );
+}
+
+
+
+bool CChoreoView::IsRampOnly( void ) const
+{
+ return m_bRampOnly;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *scene -
+// *event -
+//-----------------------------------------------------------------------------
+void CChoreoView::ProcessInterrupt( CChoreoScene *scene, CChoreoEvent *event )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *scene -
+// *event -
+//-----------------------------------------------------------------------------
+void CChoreoView::ProcessPermitResponses( CChoreoScene *scene, CChoreoEvent *event )
+{
+}
+
+void CChoreoView::ApplyBounds( int& mx, int& my )
+{
+ if ( !m_bUseBounds )
+ return;
+
+ mx = clamp( mx, m_nMinX, m_nMaxX );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns -1 if no event found
+// Input : *channel -
+// *e -
+// forward -
+// Output : float
+//-----------------------------------------------------------------------------
+float CChoreoView::FindNextEventTime( CChoreoEvent::EVENTTYPE type, CChoreoChannel *channel, CChoreoEvent *e, bool forward )
+{
+ bool foundone = false;
+ float bestTime = -1.0f;
+ float bestGap = 999999.0f;
+
+ int c = channel->GetNumEvents();
+ for ( int i = 0; i < c; i++ )
+ {
+ CChoreoEvent *test = channel->GetEvent( i );
+ if ( test->GetType() != type )
+ continue;
+
+ if ( forward )
+ {
+ float dt = test->GetStartTime() - e->GetEndTime();
+ if ( dt <= 0.0f )
+ continue;
+
+ if ( dt < bestGap )
+ {
+ foundone = true;
+ bestGap = dt;
+ bestTime = test->GetStartTime();
+ }
+ }
+ else
+ {
+ float dt = e->GetStartTime() - test->GetEndTime();
+ if ( dt <= 0.0f )
+ continue;
+
+ if ( dt < bestGap )
+ {
+ foundone = true;
+ bestGap = dt;
+ bestTime = test->GetEndTime();
+ }
+ }
+ }
+
+ return bestTime;
+}
+
+void CChoreoView::CalcBounds( int movetype )
+{
+ m_bUseBounds = false;
+ m_nMinX = 0;
+ m_nMaxX = 0;
+
+ if ( !m_pClickedEvent )
+ return;
+
+ switch ( movetype )
+ {
+ default:
+ break;
+ case DRAGTYPE_EVENT_MOVE:
+ case DRAGTYPE_EVENT_STARTTIME:
+ case DRAGTYPE_EVENT_ENDTIME:
+ case DRAGTYPE_EVENT_STARTTIME_RESCALE:
+ case DRAGTYPE_EVENT_ENDTIME_RESCALE:
+ {
+ m_nMinX = GetPixelForTimeValue( 0 );
+ m_nMaxX = GetPixelForTimeValue( m_pScene->FindStopTime() );
+
+ CChoreoEvent *e = m_pClickedEvent->GetEvent();
+
+ m_bUseBounds = false; // e && e->GetType() == CChoreoEvent::GESTURE;
+ // FIXME: use this for finding adjacent gesture edges (kenb)
+ if ( m_bUseBounds )
+ {
+ CChoreoChannel *channel = e->GetChannel();
+ Assert( channel );
+
+ float forwardTime = FindNextEventTime( e->GetType(), channel, e, true );
+ float reverseTime = FindNextEventTime( e->GetType(), channel, e, false );
+
+ // Compute pixel for time
+ int nextPixel = forwardTime != -1 ? GetPixelForTimeValue( forwardTime ) : m_nMaxX;
+ int prevPixel = reverseTime != -1 ? GetPixelForTimeValue( reverseTime ) : m_nMinX;
+
+ int startPixel = GetPixelForTimeValue( e->GetStartTime() );
+ int endPixel = GetPixelForTimeValue( e->GetEndTime() );
+
+ switch ( movetype )
+ {
+ case DRAGTYPE_EVENT_MOVE:
+ {
+ m_nMinX = prevPixel + ( m_xStart - startPixel ) + 1;
+ m_nMaxX = nextPixel - ( endPixel - m_xStart ) - 1;
+ }
+ break;
+ case DRAGTYPE_EVENT_STARTTIME:
+ case DRAGTYPE_EVENT_STARTTIME_RESCALE:
+ {
+ m_nMinX = prevPixel + ( m_xStart - startPixel ) + 1;
+ }
+ break;
+ case DRAGTYPE_EVENT_ENDTIME:
+ case DRAGTYPE_EVENT_ENDTIME_RESCALE:
+ {
+ m_nMaxX = nextPixel - ( endPixel - m_xStart ) - 1;
+ }
+ break;
+ }
+ }
+ }
+ break;
+ }
+}
+
+bool CChoreoView::ShouldSelectEvent( SelectionParams_t &params, CChoreoEvent *event )
+{
+ if ( params.forward )
+ {
+ if ( event->GetStartTime() >= params.time )
+ return true;
+ }
+ else
+ {
+ float endtime = event->HasEndTime() ? event->GetEndTime() : event->GetStartTime();
+
+ if ( endtime <= params.time )
+ return true;
+ }
+ return false;
+}
+
+void CChoreoView::SelectEvents( SelectionParams_t& params )
+{
+ if ( !m_pScene )
+ return;
+
+ if ( !m_pClickedActor )
+ return;
+
+ if ( params.type == SelectionParams_t::SP_CHANNEL && !m_pClickedChannel )
+ return;
+
+ //CChoreoActor *actor = m_pClickedActor->GetActor();
+ CChoreoChannel *channel = m_pClickedChannel ? m_pClickedChannel->GetChannel() : NULL;
+
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *a = m_SceneActors[ i ];
+ if ( !a )
+ continue;
+
+ //if ( a->GetActor() != actor )
+ // continue;
+
+ for ( int j = 0; j < a->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *c = a->GetChannel( j );
+ if ( !c )
+ continue;
+
+ if ( params.type == SelectionParams_t::SP_CHANNEL &&
+ c->GetChannel() != channel )
+ continue;
+
+ if ( params.type == SelectionParams_t::SP_ACTIVE &&
+ !c->GetChannel()->GetActive() )
+ continue;
+
+ for ( int k = 0; k < c->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *e = c->GetEvent( k );
+ if ( !e )
+ continue;
+
+ CChoreoEvent *event = e->GetEvent();
+ if ( !event )
+ continue;
+
+ if ( !ShouldSelectEvent( params, event ) )
+ continue;
+
+ e->SetSelected( true );
+ }
+ }
+ }
+
+ // Now handle global events, too
+ for ( int i = 0; i < m_SceneGlobalEvents.Count(); i++ )
+ {
+ CChoreoGlobalEventWidget *e = m_SceneGlobalEvents[ i ];
+ if ( !e )
+ continue;
+
+ CChoreoEvent *event = e->GetEvent();
+ if ( !event )
+ continue;
+
+ if ( !ShouldSelectEvent( params, event ) )
+ continue;
+
+ e->SetSelected( true );
+ }
+
+ redraw();
+}
+
+void CChoreoView::SetTimeZoom( char const *tool, int tz, bool preserveFocus )
+{
+ if ( !m_pScene )
+ return;
+
+ // No change
+ int oldZoom = GetTimeZoom( tool );
+ if ( tz == oldZoom )
+ return;
+
+ SetDirty( true );
+
+ POINT pt;
+ ::GetCursorPos( &pt );
+ ::ScreenToClient( (HWND)getHandle(), &pt );
+
+ // Now figure out time under cursor at old zoom scale
+ float t = GetTimeValueForMouse( pt.x, true );
+
+ m_pScene->SetTimeZoom( tool, tz );
+
+ if ( preserveFocus )
+ {
+ RECT rc;
+ GetClientRect( (HWND)getHandle(), &rc );
+ RECT rcClient = rc;
+ rcClient.top += GetStartRow();
+ OffsetRect( &rcClient, 0, -m_nTopOffset );
+ m_flStartTime = m_flLeftOffset / GetPixelsPerSecond();
+ m_flEndTime = m_flStartTime + (float)( rcClient.right - GetLabelWidth() ) / GetPixelsPerSecond();
+
+ // Now figure out tie under pt.x
+ float newT = GetTimeValueForMouse( pt.x, true );
+ if ( newT != t )
+ {
+ // We need to scroll over a bit
+ float pps = GetPixelsPerSecond();
+ float movePixels = pps * ( newT - t );
+
+ m_flLeftOffset -= movePixels;
+ if ( m_flLeftOffset < 0.0f )
+ {
+ //float fixup = - m_flLeftOffset;
+ m_flLeftOffset = 0;
+ }
+
+ // float maxtime = m_pScene->FindStopTime();
+ float flApparentEndTime = max( m_pScene->FindStopTime(), 5.0f ) + 5.0f;
+ if ( m_flEndTime > flApparentEndTime )
+ {
+ movePixels = pps * ( m_flEndTime - flApparentEndTime );
+ m_flLeftOffset = max( 0.0f, m_flLeftOffset - movePixels );
+ }
+ }
+ }
+
+ // Deal with the slider
+ RepositionHSlider();
+ redraw();
+}
+
+int CChoreoView::GetTimeZoom( char const *tool )
+{
+ if ( !m_pScene )
+ return 100;
+
+ return m_pScene->GetTimeZoom( tool );
+}
+
+void CChoreoView::CheckInsertTime( CChoreoEvent *e, float dt, float starttime, float endtime )
+{
+ // Not influenced
+ float eventend = e->HasEndTime() ? e->GetEndTime() : e->GetStartTime();
+
+ if ( eventend < starttime )
+ return;
+
+ if ( e->GetStartTime() > starttime )
+ {
+ e->OffsetTime( dt );
+ e->SnapTimes();
+ }
+ else if ( !e->IsFixedLength() && e->HasEndTime() ) // the event starts before start, but ends after start time, need to insert space into the event, act like user dragged end time
+ {
+ float newduration = e->GetDuration() + dt;
+ RescaleRamp( e, newduration );
+ switch ( e->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ e->RescaleGestureTimes( e->GetStartTime(), e->GetEndTime() + dt, true );
+ }
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ {
+ RescaleExpressionTimes( e, e->GetStartTime(), e->GetEndTime() + dt );
+ }
+ break;
+ }
+ e->OffsetEndTime( dt );
+ e->SnapTimes();
+ e->ResortRamp();
+ }
+
+ switch ( e->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::SPEAK:
+ {
+ // Try and load wav to get length
+ CAudioSource *wave = sound->LoadSound( va( "sound/%s", FacePoser_TranslateSoundName( e ) ) );
+ if ( wave )
+ {
+ e->SetEndTime( e->GetStartTime() + wave->GetRunningLength() );
+ delete wave;
+ }
+ }
+ break;
+ case CChoreoEvent::SEQUENCE:
+ {
+ CheckSequenceLength( e, false );
+ }
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ CheckGestureLength( e, false );
+ }
+ break;
+ }
+}
+
+void CChoreoView::OnInsertTime()
+{
+ if ( !m_rgABPoints[ 0 ].active &&
+ !m_rgABPoints[ 1 ].active )
+ {
+ return;
+ }
+
+ Con_Printf( "OnInsertTime()\n" );
+
+ float starttime = m_rgABPoints[ 0 ].time;
+ float endtime = m_rgABPoints[ 1 ].time;
+
+ // Sort samples correctly
+ if ( starttime > endtime )
+ {
+ float temp = starttime;
+ starttime = endtime;
+ endtime = temp;
+ }
+
+ float dt = endtime - starttime;
+ if ( dt == 0.0f )
+ {
+ // Nothing to do...
+ return;
+ }
+
+ SetDirty( true );
+
+ PushUndo( "Insert Time" );
+
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *actor = m_SceneActors[ i ];
+ if ( !actor )
+ continue;
+
+ for ( int j = 0; j < actor->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *channel = actor->GetChannel( j );
+ if ( !channel )
+ continue;
+
+ for ( int k = 0; k < channel->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *event = channel->GetEvent( k );
+ if ( !event )
+ continue;
+
+ CChoreoEvent *e = event->GetEvent();
+ if ( !e )
+ continue;
+
+ CheckInsertTime( e, dt, starttime, endtime );
+ }
+ }
+ }
+
+ // Now handle global events, too
+ for ( int i = 0; i < m_SceneGlobalEvents.Count(); i++ )
+ {
+ CChoreoGlobalEventWidget *event = m_SceneGlobalEvents[ i ];
+ if ( !event )
+ continue;
+
+ CChoreoEvent *e = event->GetEvent();
+ if ( !e )
+ continue;
+
+ CheckInsertTime( e, dt, starttime, endtime );
+ }
+
+ PushRedo( "Insert Time" );
+ InvalidateLayout();
+
+ g_pExpressionTool->LayoutItems( true );
+ g_pExpressionTool->redraw();
+ g_pGestureTool->redraw();
+ g_pRampTool->redraw();
+ g_pSceneRampTool->redraw();
+}
+
+void CChoreoView::CheckDeleteTime( CChoreoEvent *e, float dt, float starttime, float endtime, bool& deleteEvent )
+{
+ deleteEvent = false;
+
+ // Not influenced
+ float eventend = e->HasEndTime() ? e->GetEndTime() : e->GetStartTime();
+
+ if ( eventend < starttime )
+ {
+ return;
+ }
+
+ // On right side of start mark, just shift left
+ if ( e->GetStartTime() > starttime )
+ {
+ // If it has no duration and it's in the bounds then kill it.
+ if ( !e->HasEndTime() && e->GetStartTime() < endtime )
+ {
+ deleteEvent = true;
+ return;
+ }
+ else
+ {
+ float shift = e->GetStartTime() - starttime;
+ float maxoffset = min( dt, shift );
+
+ e->OffsetTime( -maxoffset );
+ e->SnapTimes();
+ }
+ }
+ else if ( !e->IsFixedLength() && e->HasEndTime() ) // the event starts before start, but ends after start time, need to insert space into the event, act like user dragged end time
+ {
+ float shiftend = e->GetEndTime() - starttime;
+ float maxoffset = min( dt, shiftend );
+
+ float newduration = e->GetDuration() - maxoffset;
+ if ( newduration <= 0.0f )
+ {
+ deleteEvent = true;
+ return;
+ }
+ else
+ {
+ RescaleRamp( e, newduration );
+ switch ( e->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ e->RescaleGestureTimes( e->GetStartTime(), e->GetEndTime() - maxoffset, true );
+ }
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ {
+ RescaleExpressionTimes( e, e->GetStartTime(), e->GetEndTime() - maxoffset );
+ }
+ break;
+ }
+ e->OffsetEndTime( -maxoffset );
+ e->SnapTimes();
+ e->ResortRamp();
+ }
+ }
+
+ switch ( e->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::SPEAK:
+ {
+ // Try and load wav to get length
+ CAudioSource *wave = sound->LoadSound( va( "sound/%s", FacePoser_TranslateSoundName( e ) ) );
+ if ( wave )
+ {
+ e->SetEndTime( e->GetStartTime() + wave->GetRunningLength() );
+ delete wave;
+ }
+ }
+ break;
+ case CChoreoEvent::SEQUENCE:
+ {
+ CheckSequenceLength( e, false );
+ }
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ CheckGestureLength( e, false );
+ }
+ break;
+ }
+}
+
+void CChoreoView::OnDeleteTime()
+{
+ if ( !m_rgABPoints[ 0 ].active &&
+ !m_rgABPoints[ 1 ].active )
+ {
+ return;
+ }
+
+ Con_Printf( "OnDeleteTime()\n" );
+
+ float starttime = m_rgABPoints[ 0 ].time;
+ float endtime = m_rgABPoints[ 1 ].time;
+
+ // Sort samples correctly
+ if ( starttime > endtime )
+ {
+ float temp = starttime;
+ starttime = endtime;
+ endtime = temp;
+ }
+
+ float dt = endtime - starttime;
+ if ( dt == 0.0f )
+ {
+ // Nothing to do...
+ return;
+ }
+
+ SetDirty( true );
+
+ PushUndo( "Delete Time" );
+
+ CUtlVector< CChoreoEventWidget * > deletions;
+ CUtlVector< CChoreoGlobalEventWidget * > global_deletions;
+
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *actor = m_SceneActors[ i ];
+ if ( !actor )
+ continue;
+
+ for ( int j = 0; j < actor->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *channel = actor->GetChannel( j );
+ if ( !channel )
+ continue;
+
+ for ( int k = 0; k < channel->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *event = channel->GetEvent( k );
+ if ( !event )
+ continue;
+
+ CChoreoEvent *e = event->GetEvent();
+ if ( !e )
+ continue;
+
+ bool deleteEvent = false;
+
+ CheckDeleteTime( e, dt, starttime, endtime, deleteEvent );
+
+ if ( deleteEvent )
+ {
+ deletions.AddToTail( event );
+ }
+ }
+ }
+ }
+
+ // Now handle global events, too
+ for ( int i = 0; i < m_SceneGlobalEvents.Count(); i++ )
+ {
+ CChoreoGlobalEventWidget *event = m_SceneGlobalEvents[ i ];
+ if ( !event )
+ continue;
+
+ CChoreoEvent *e = event->GetEvent();
+ if ( !e )
+ continue;
+
+ bool deleteEvent = false;
+ CheckDeleteTime( e, dt, starttime, endtime, deleteEvent );
+
+ if ( deleteEvent )
+ {
+ global_deletions.AddToTail( event );
+ }
+ }
+
+ for ( int i = 0; i < deletions.Count(); i++ )
+ {
+ CChoreoEventWidget *w = deletions[ i ];
+
+ CChoreoEvent *e = w->GetEvent();
+ CChoreoChannel *channel = e->GetChannel();
+ if ( channel )
+ {
+ channel->RemoveEvent( e );
+ }
+ m_pScene->DeleteReferencedObjects( e );
+ }
+
+ for ( int i = 0; i < global_deletions.Count(); i++ )
+ {
+ CChoreoGlobalEventWidget *w = global_deletions[ i ];
+ CChoreoEvent *e = w->GetEvent();
+ m_pScene->DeleteReferencedObjects( e );
+ }
+
+ // Force scroll bars to recompute
+ ForceScrollBarsToRecompute( false );
+
+ if ( deletions.Count() > 0 || global_deletions.Count() > 0 )
+ {
+ DeleteSceneWidgets();
+ CreateSceneWidgets();
+ }
+
+ PushRedo( "Delete Time" );
+
+ InvalidateLayout();
+
+ g_pExpressionTool->LayoutItems( true );
+ g_pExpressionTool->redraw();
+ g_pGestureTool->redraw();
+ g_pRampTool->redraw();
+ g_pSceneRampTool->redraw();
+}
+
+void CChoreoView::OnModelChanged()
+{
+ InvalidateTrackLookup();
+ // OnCheckSequenceLengths();
+}
+
+void CChoreoView::SetShowCloseCaptionData( bool show )
+{
+ m_bShowCloseCaptionData = show;
+}
+
+bool CChoreoView::GetShowCloseCaptionData( void ) const
+{
+ return m_bShowCloseCaptionData;
+}
+
+void CChoreoView::OnToggleCloseCaptionTags()
+{
+ m_bShowCloseCaptionData = !m_bShowCloseCaptionData;
+ InvalidateLayout();
+}
+
+
+
+static bool EventStartTimeLessFunc( CChoreoEvent * const &p1, CChoreoEvent * const &p2 )
+{
+ CChoreoEvent *w1;
+ CChoreoEvent *w2;
+
+ w1 = const_cast< CChoreoEvent * >( p1 );
+ w2 = const_cast< CChoreoEvent * >( p2 );
+
+ return w1->GetStartTime() < w2->GetStartTime();
+}
+
+bool CChoreoView::GenerateCombinedFile( char const *outfilename, char const *cctoken, gender_t gender, CUtlRBTree< CChoreoEvent * >& sorted )
+{
+ CUtlVector< CombinerEntry > work;
+
+ char actualfile[ 512 ];
+ soundemitter->GenderExpandString( gender, outfilename, actualfile, sizeof( actualfile ) );
+ if ( Q_strlen( actualfile ) <= 0 )
+ {
+ return false;
+ }
+
+ int i = sorted.FirstInorder();
+ if ( i != sorted.InvalidIndex() )
+ {
+ CChoreoEvent *e = sorted[ i ];
+
+ float startoffset = e->GetStartTime();
+
+ do
+ {
+ e = sorted[ i ];
+
+ float curoffset = e->GetStartTime();
+
+ CombinerEntry ce;
+ Q_snprintf( ce.wavefile, sizeof( ce.wavefile ), "sound/%s", FacePoser_TranslateSoundNameGender( e->GetParameters(), gender ) );
+ ce.startoffset = curoffset - startoffset;
+
+ work.AddToTail( ce );
+
+ i = sorted.NextInorder( i );
+ }
+ while ( i != sorted.InvalidIndex() );
+ }
+
+ bool ok = soundcombiner->CombineSoundFiles( filesystem, actualfile, work );
+ if ( !ok )
+ {
+ Con_ErrorPrintf( "Failed to create combined sound '%s':'%s'\n", cctoken, actualfile );
+ return false;
+ }
+ Con_Printf( "Created combined sound '%s':'%s'\n", cctoken, actualfile );
+ return true;
+}
+
+bool CChoreoView::ValidateCombinedFileCheckSum( char const *outfilename, char const *cctoken, gender_t gender, CUtlRBTree< CChoreoEvent * >& sorted )
+{
+ CUtlVector< CombinerEntry > work;
+
+ char actualfile[ 512 ];
+ soundemitter->GenderExpandString( gender, outfilename, actualfile, sizeof( actualfile ) );
+ if ( Q_strlen( actualfile ) <= 0 )
+ {
+ return false;
+ }
+
+ int i = sorted.FirstInorder();
+ if ( i != sorted.InvalidIndex() )
+ {
+ CChoreoEvent *e = sorted[ i ];
+
+ float startoffset = e->GetStartTime();
+
+ do
+ {
+ e = sorted[ i ];
+
+ float curoffset = e->GetStartTime();
+
+ CombinerEntry ce;
+ Q_snprintf( ce.wavefile, sizeof( ce.wavefile ), "sound/%s", FacePoser_TranslateSoundNameGender( e->GetParameters(), gender ) );
+ ce.startoffset = curoffset - startoffset;
+
+ work.AddToTail( ce );
+
+ i = sorted.NextInorder( i );
+ }
+ while ( i != sorted.InvalidIndex() );
+ }
+
+ return soundcombiner->IsCombinedFileChecksumValid( filesystem, actualfile, work );
+}
+
+void SuggestCaption( char *dest, int destlen, CUtlVector< CChoreoEvent * >& events )
+{
+ // Walk through events and concatenate current captions, or raw wav data if have any
+ dest[ 0 ] = 0;
+
+ int c = events.Count();
+ for ( int i = 0 ; i < c; ++i )
+ {
+ CChoreoEvent *e = events[ i ];
+
+ bool found = false;
+ char tok[ CChoreoEvent::MAX_CCTOKEN_STRING ];
+ if ( e->GetPlaybackCloseCaptionToken( tok, sizeof( tok ) ) )
+ {
+ wchar_t *localized = g_pLocalize->Find( tok );
+ if ( localized )
+ {
+ found = true;
+
+ char ansi[ 1024 ];
+ g_pLocalize->ConvertUnicodeToANSI( localized, ansi, sizeof( ansi ) );
+ Q_strncat( dest, ansi, destlen, COPY_ALL_CHARACTERS );
+ }
+ }
+
+ if ( !found )
+ {
+ // See if the wav file has data...
+ CAudioSource *wave = sound->LoadSound( va( "sound/%s", FacePoser_TranslateSoundName( e ) ) );
+ if ( wave )
+ {
+ CSentence *sentence = wave->GetSentence();
+ if ( sentence )
+ {
+ Q_strncat( dest, sentence->GetText(), destlen, COPY_ALL_CHARACTERS );
+ found = true;
+ }
+ }
+ }
+
+ if ( found && Q_strlen( dest ) > 0 && i != c - 1 )
+ {
+ Q_strncat( dest, " ", destlen, COPY_ALL_CHARACTERS );
+ }
+ }
+}
+
+void CChoreoView::OnCombineSpeakEvents()
+{
+ if ( !m_pScene )
+ return;
+
+ CChoreoChannel *firstChannel = NULL;
+
+ CUtlVector< CChoreoEvent * > selected;
+ GetSelectedEvents( selected );
+
+ int c = selected.Count();
+ // Find the appropriate event by iterating across all actors and channels
+ for ( int i = c - 1; i >= 0; --i )
+ {
+ CChoreoEvent *e = selected[ i ];
+
+ if ( e->GetType() != CChoreoEvent::SPEAK )
+ {
+ Con_ErrorPrintf( "Can't combine events, all events must be SPEAK events.\n" );
+ return;
+ }
+
+ if ( !firstChannel )
+ {
+ firstChannel = e->GetChannel();
+ }
+ else if ( e->GetChannel() != firstChannel )
+ {
+ Con_ErrorPrintf( "Can't combine events, all events must reside in the same channel.\n" );
+ return;
+ }
+ }
+
+ if ( selected.Count() < 2 )
+ {
+ Con_ErrorPrintf( "Can't combine events, must have at least two events selected.\n" );
+ return;
+ }
+
+ // Let the user pick a CC phrase
+ CCloseCaptionLookupParams params;
+ Q_strncpy( params.m_szDialogTitle, "Choose Close Caption Token", sizeof( params.m_szDialogTitle ) );
+
+ params.m_bPositionDialog = false;
+ params.m_nLeft = 0;
+ params.m_nTop = 0;
+
+ char playbacktoken[ CChoreoEvent::MAX_CCTOKEN_STRING ];
+ if ( !selected[0]->GetPlaybackCloseCaptionToken( playbacktoken, sizeof( playbacktoken ) ) )
+ {
+ return;
+ }
+
+ if ( !Q_stristr( playbacktoken, "_cc" ) )
+ {
+ Q_strncpy( params.m_szCCToken, va( "%s_cc", playbacktoken ), sizeof( params.m_szCCToken ) );
+ }
+ else
+ {
+ Q_strncpy( params.m_szCCToken, va( "%s", playbacktoken ), sizeof( params.m_szCCToken ) );
+ }
+
+ // User hit okay and value actually changed?
+ if ( !CloseCaptionLookup( &params ) &&
+ params.m_szCCToken[0] != 0 )
+ {
+ return;
+ }
+
+ // See if the token exists?
+ StringIndex_t stringIndex = g_pLocalize->FindIndex( params.m_szCCToken );
+ if ( INVALID_LOCALIZE_STRING_INDEX == stringIndex )
+ {
+ // Add token to closecaption_english file.
+ // Guess at string and ask user to confirm.
+ CInputParams ip;
+ memset( &ip, 0, sizeof( ip ) );
+
+ Q_strncpy( ip.m_szDialogTitle, "Add Close Caption", sizeof( ip.m_szDialogTitle ) );
+ Q_snprintf( ip.m_szPrompt, sizeof( ip.m_szPrompt ), "Token (%s):", params.m_szCCToken );
+
+ char suggested[ 2048 ];
+
+ SuggestCaption( suggested, sizeof( suggested ), selected );
+
+ Q_snprintf( ip.m_szInputText, sizeof( ip.m_szInputText ), "%s", suggested );
+
+ if ( !InputProperties( &ip ) )
+ {
+ Con_Printf( "Combining of sound events cancelled\n" );
+ return;
+ }
+
+ if ( Q_strlen( ip.m_szInputText ) == 0 )
+ {
+ Q_snprintf( ip.m_szInputText, sizeof( ip.m_szInputText ), "!!!%s", params.m_szCCToken );
+ }
+
+ char const *captionFile = "resource/closecaption_english.txt";
+
+ if ( !filesystem->IsFileWritable( captionFile, "GAME" ) )
+ {
+ Warning( "Forcing %s to be writable!!!\n", captionFile );
+ MakeFileWriteable( captionFile );
+ }
+
+ wchar_t unicode[ 2048 ];
+ g_pLocalize->ConvertANSIToUnicode( ip.m_szInputText, unicode, sizeof( unicode ) );
+
+ g_pLocalize->AddString( params.m_szCCToken, unicode, captionFile );
+ g_pLocalize->SaveToFile( captionFile );
+ }
+
+ SetDirty( true );
+
+ PushUndo( "Combine Sound Events" );
+
+ c = selected.Count();
+ for ( int i = 0 ; i < c; ++i )
+ {
+ selected[ i ]->SetCloseCaptionToken( params.m_szCCToken );
+ }
+
+ PushRedo( "Combine Sound Events" );
+
+ // Redraw
+ InvalidateLayout();
+
+ Con_Printf( "Changed %i events to use close caption token '%s'\n", c, params.m_szCCToken );
+
+ // Sort the sounds by start time
+
+ CUtlRBTree< CChoreoEvent * > sorted( 0, 0, EventStartTimeLessFunc );
+
+ // Sort items
+ c = selected.Count();
+ bool genderwildcard = false;
+ for ( int i = 0; i < c; i++ )
+ {
+ CChoreoEvent *e = selected[ i ];
+ sorted.Insert( e );
+
+ // Get the sound entry name and use it to look up the gender info
+ // Look up the sound level from the soundemitter system
+ if ( !genderwildcard )
+ {
+ genderwildcard = soundemitter->IsUsingGenderToken( e->GetParameters() );
+ }
+ }
+
+
+ char outfilename[ 512 ];
+ Q_memset( outfilename, 0, sizeof( outfilename ) );
+
+ CChoreoEvent *e = sorted[ sorted.FirstInorder() ];
+
+ // Update whether we use the $gender token
+ e->SetCombinedUsingGenderToken( genderwildcard );
+
+ if ( !e->ComputeCombinedBaseFileName( outfilename, sizeof( outfilename ), genderwildcard ) )
+ {
+ Con_ErrorPrintf( "Unable to regenerate wav file name for combined sound\n" );
+ return;
+ }
+
+ int soundindex = soundemitter->GetSoundIndex( e->GetParameters() );
+ char const *scriptfile = soundemitter->GetSourceFileForSound( soundindex );
+ if ( !scriptfile || !scriptfile[0] )
+ {
+ Con_ErrorPrintf( "Unable to find existing script to use for new combined sound entry.\n" );
+ return;
+ }
+
+ // Create a new sound entry for this sound
+ CAddSoundParams asp;
+ Q_memset( &asp, 0, sizeof( asp ) );
+ Q_strncpy( asp.m_szDialogTitle, "Add Combined Sound Entry", sizeof( asp.m_szDialogTitle ) );
+ Q_strncpy( asp.m_szWaveFile, outfilename + Q_strlen( "sound/"), sizeof( asp.m_szWaveFile ) );
+ Q_strncpy( asp.m_szScriptName, scriptfile, sizeof( asp.m_szScriptName ) );
+ Q_strncpy( asp.m_szSoundName, params.m_szCCToken, sizeof( asp.m_szSoundName ) );
+
+ asp.m_bAllowExistingSound = true;
+ asp.m_bReadOnlySoundName = true;
+
+ if ( !AddSound( &asp, (HWND)g_MDLViewer->getHandle() ) )
+ {
+ return;
+ }
+
+ if ( genderwildcard )
+ {
+ GenerateCombinedFile( outfilename, params.m_szCCToken, GENDER_MALE, sorted );
+ GenerateCombinedFile( outfilename, params.m_szCCToken, GENDER_FEMALE, sorted );
+ }
+ else
+ {
+ GenerateCombinedFile( outfilename, params.m_szCCToken, GENDER_NONE, sorted );
+ }
+}
+
+bool CChoreoView::ValidateCombinedSoundCheckSum( CChoreoEvent *e )
+{
+ if ( !e || e->GetType() != CChoreoEvent::SPEAK )
+ return false;
+
+ bool genderwildcard = e->IsCombinedUsingGenderToken();
+ char outfilename[ 512 ];
+ Q_memset( outfilename, 0, sizeof( outfilename ) );
+ if ( !e->ComputeCombinedBaseFileName( outfilename, sizeof( outfilename ), genderwildcard ) )
+ {
+ Con_ErrorPrintf( "Unable to regenerate wav file name for combined sound (%s)\n", e->GetCloseCaptionToken() );
+ return false;
+ }
+
+ bool checksumvalid = false;
+
+ CUtlRBTree< CChoreoEvent * > eventList( 0, 0, EventStartTimeLessFunc );
+
+ if ( !e->GetChannel()->GetSortedCombinedEventList( e->GetCloseCaptionToken(), eventList ) )
+ {
+ Con_ErrorPrintf( "Unable to generated combined event list (%s)\n", e->GetCloseCaptionToken() );
+ return false;
+ }
+
+
+ if ( genderwildcard )
+ {
+ checksumvalid = ValidateCombinedFileCheckSum( outfilename, e->GetCloseCaptionToken(), GENDER_MALE, eventList );
+ checksumvalid &= ValidateCombinedFileCheckSum( outfilename, e->GetCloseCaptionToken(), GENDER_FEMALE, eventList );
+ }
+ else
+ {
+ checksumvalid = ValidateCombinedFileCheckSum( outfilename, e->GetCloseCaptionToken(), GENDER_NONE, eventList );
+ }
+
+ return checksumvalid;
+}
+
+void CChoreoView::OnRemoveSpeakEventFromGroup()
+{
+ if ( !m_pScene )
+ return;
+
+ int i, c;
+
+ CUtlVector< CChoreoEvent * > selected;
+ CUtlVector< CChoreoEvent * > processlist;
+ if ( GetSelectedEvents( selected ) > 0 )
+ {
+
+ int c = selected.Count();
+ // Find the appropriate event by iterating across all actors and channels
+ for ( i = c - 1; i >= 0; --i )
+ {
+ CChoreoEvent *e = selected[ i ];
+ if ( e->GetType() != CChoreoEvent::SPEAK )
+ {
+ selected.Remove( i );
+ continue;
+ }
+
+ if ( e->GetCloseCaptionType() == CChoreoEvent::CC_DISABLED )
+ {
+ selected.Remove( i );
+ continue;
+ }
+
+ m_pClickedChannel->GetMasterAndSlaves( e, processlist );
+ }
+ }
+ else
+ {
+ m_pClickedChannel->GetMasterAndSlaves( m_pClickedChannel->GetCaptionClickedEvent(), processlist );
+ }
+
+ if ( selected.Count() < 1 )
+ {
+ Con_ErrorPrintf( "No eligible SPEAK event selected.\n" );
+ return;
+ }
+
+ SetDirty( true );
+
+ PushUndo( "Remove speak event(s)" );
+
+ c = processlist.Count();
+ for ( i = 0 ; i < c; ++i )
+ {
+ processlist[ i ]->SetCloseCaptionToken( "" );
+ processlist[ i ]->SetCloseCaptionType( CChoreoEvent::CC_MASTER );
+ processlist[ i ]->SetUsingCombinedFile( false );
+ processlist[ i ]->SetRequiredCombinedChecksum( 0 );
+ processlist[ i ]->SetNumSlaves( 0 );
+ processlist[ i ]->SetLastSlaveEndTime( 0.0f );
+ }
+
+ PushRedo( "Remove speak event(s)" );
+
+ // Redraw
+ InvalidateLayout();
+ Con_Printf( "Reverted %i events to use default close caption token\n", c );
+}
+
+bool CChoreoView::AreSelectedEventsCombinable()
+{
+ CUtlVector< CChoreoEvent * > events;
+ if ( GetSelectedEvents( events ) <= 0 )
+ return false;
+
+ CChoreoChannel *firstChannel = NULL;
+
+ CUtlVector< CChoreoEvent * > selected;
+ GetSelectedEvents( selected );
+
+ int c = selected.Count();
+ // Find the appropriate event by iterating across all actors and channels
+ for ( int i = c - 1; i >= 0; --i )
+ {
+ CChoreoEvent *e = selected[ i ];
+
+ if ( e->GetType() != CChoreoEvent::SPEAK )
+ {
+ return false;
+ }
+
+ if ( !firstChannel )
+ {
+ firstChannel = e->GetChannel();
+ }
+ else if ( e->GetChannel() != firstChannel )
+ {
+ return false;
+ }
+ }
+ return selected.Count() >= 2 ? true : false;
+}
+
+bool CChoreoView::AreSelectedEventsInSpeakGroup()
+{
+ CUtlVector< CChoreoEvent * > selected;
+ if ( GetSelectedEvents( selected ) <= 0 )
+ {
+ if ( m_pClickedChannel )
+ {
+ CChoreoEvent *e = m_pClickedChannel->GetCaptionClickedEvent();
+ if ( e && e->GetCloseCaptionType() == CChoreoEvent::CC_MASTER &&
+ e->GetNumSlaves() >= 1 )
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ int c = selected.Count();
+ // Find the appropriate event by iterating across all actors and channels
+ for ( int i = c - 1; i >= 0; --i )
+ {
+ CChoreoEvent *e = selected[ i ];
+ if ( e->GetType() != CChoreoEvent::SPEAK )
+ {
+ selected.Remove( i );
+ continue;
+ }
+
+ if ( e->GetCloseCaptionType() == CChoreoEvent::CC_DISABLED )
+ {
+ selected.Remove( i );
+ continue;
+ }
+ }
+
+ return selected.Count() >= 1 ? true : false;
+
+}
+
+void CChoreoView::OnChangeCloseCaptionToken( CChoreoEvent *e )
+{
+ CCloseCaptionLookupParams params;
+ Q_strncpy( params.m_szDialogTitle, "Close Caption Token Lookup", sizeof( params.m_szDialogTitle ) );
+
+ params.m_bPositionDialog = false;
+ params.m_nLeft = 0;
+ params.m_nTop = 0;
+ // strcpy( params.m_szPrompt, "Choose model:" );
+
+ Q_strncpy( params.m_szCCToken, e->GetCloseCaptionToken(), sizeof( params.m_szCCToken ) );
+
+ // User hit okay and value actually changed?
+ if ( CloseCaptionLookup( &params ) &&
+ Q_stricmp( e->GetCloseCaptionToken(), params.m_szCCToken ) )
+ {
+ char oldToken[ CChoreoEvent::MAX_CCTOKEN_STRING ];
+ Q_strncpy( oldToken, e->GetCloseCaptionToken(), sizeof( oldToken ) );
+
+ CUtlVector< CChoreoEvent * > events;
+ m_pClickedChannel->GetMasterAndSlaves( e, events );
+
+ if ( events.Count() < 2 )
+ {
+ Con_ErrorPrintf( "Can't combine events, must have at least two events selected.\n" );
+ }
+
+ SetDirty( true );
+
+ PushUndo( "Change closecaption token" );
+
+ // Make the change...
+ int c = events.Count();
+ for ( int i = 0 ; i < c; ++i )
+ {
+ events[i]->SetCloseCaptionToken( params.m_szCCToken );
+ }
+
+ PushRedo( "Change closecaption token" );
+
+ InvalidateLayout();
+
+ Con_Printf( "Close Caption token for '%s' changed to '%s'\n", e->GetName(), params.m_szCCToken );
+ }
+}
+
+void CChoreoView::OnToggleCloseCaptionsForEvent()
+{
+ if ( !m_pClickedChannel )
+ {
+ return;
+ }
+
+ CChoreoEvent *e = m_pClickedChannel->GetCaptionClickedEvent();
+
+ if ( !e )
+ {
+ return;
+ }
+
+ CChoreoEvent::CLOSECAPTION newType = CChoreoEvent::CC_MASTER;
+ // Can't mess with slave
+ switch ( e->GetCloseCaptionType() )
+ {
+ default:
+ case CChoreoEvent::CC_SLAVE:
+ return;
+ case CChoreoEvent::CC_MASTER:
+ newType = CChoreoEvent::CC_DISABLED;
+ break;
+ case CChoreoEvent::CC_DISABLED:
+ newType = CChoreoEvent::CC_MASTER;
+ break;
+ }
+
+ SetDirty( true );
+
+ PushUndo( "Enable/disable captions" );
+
+ // Make the change...
+ e->SetCloseCaptionType( newType );
+
+ PushRedo( "Enable/disable captions" );
+
+ InvalidateLayout();
+
+ Con_Printf( "Close Caption type for '%s' changed to '%s'\n", e->GetName(), CChoreoEvent::NameForCCType( newType ) );
+
+}
+
+void CChoreoView::StopScene()
+{
+ SetScrubTargetTime( m_flScrub );
+ FinishSimulation();
+ sound->Flush();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+//-----------------------------------------------------------------------------
+template <class T>
+void DeleteAllAndPurge( T &tree )
+{
+ T::IndexType_t i;
+
+ for ( i = tree.FirstInorder(); i != T::InvalidIndex(); i = tree.NextInorder( i ) )
+ {
+ delete tree[i];
+ }
+
+ tree.Purge();
+}
+
+void CChoreoView::OnPlaceNextSpeakEvent()
+{
+ CUtlVector< CChoreoEvent * > list;
+ GetSelectedEvents( list );
+ if ( list.Count() != 1 )
+ {
+ Warning( "Can't place sound event, nothing selected\n" );
+ return;
+ }
+
+ CChoreoEvent *ev = list[ 0 ];
+ if ( ev->GetType() != CChoreoEvent::SPEAK )
+ {
+ Warning( "Can't place sound event, no previous sound event selected\n" );
+ return;
+ }
+
+ CChoreoChannelWidget *widget = FindChannelForEvent( ev );
+ if ( !widget )
+ {
+ Warning( "Can't place sound event, can't find channel widget for event\n" );
+ return;
+ }
+
+ CChoreoChannel *channel = widget->GetChannel();
+ if ( !channel )
+ {
+ Warning( "Can't place sound event, can't find channel for new event\n" );
+ return;
+ }
+
+ CUtlRBTree< char const *, int > m_SortedNames( 0, 0, NameLessFunc );
+
+ int c = soundemitter->GetSoundCount();
+ for ( int i = 0; i < c; i++ )
+ {
+ char const *name = soundemitter->GetSoundName( i );
+ if ( name && name[ 0 ] )
+ {
+ m_SortedNames.Insert( strdup( name ) );
+ }
+ }
+
+ int idx = m_SortedNames.Find( ev->GetParameters() );
+ if ( idx == m_SortedNames.InvalidIndex() )
+ {
+ Warning( "Can't place sound event, can't find '%s' in sound list\n", ev->GetParameters() );
+ DeleteAllAndPurge( m_SortedNames );
+ return;
+ }
+
+ int nextIdx = m_SortedNames.NextInorder( idx );
+ if ( nextIdx == m_SortedNames.InvalidIndex() )
+ {
+ Warning( "Can't place sound event, can't next sound after '%s' in sound list\n", ev->GetParameters() );
+ DeleteAllAndPurge( m_SortedNames );
+ return;
+ }
+
+ DeselectAll();
+
+ SetDirty( true );
+
+ PushUndo( "Place Next Speak Event" );
+
+ CChoreoEvent *event = m_pScene->AllocEvent();
+ Assert( event );
+ if ( event )
+ {
+ // Copy everything for source event
+ *event = *ev;
+
+ event->SetParameters( m_SortedNames[ nextIdx ] );
+ // Start it at the end time...
+ event->SetStartTime( event->GetEndTime() );
+ event->SetResumeCondition( false );
+ event->ClearAllRelativeTags();
+ event->ClearAllTimingTags();
+ event->ClearAllAbsoluteTags( CChoreoEvent::PLAYBACK );
+ event->ClearAllAbsoluteTags( CChoreoEvent::ORIGINAL );
+
+ event->SetChannel( channel );
+ event->SetActor( channel->GetActor() );
+
+ // Try and load wav to get length
+ CAudioSource *wave = sound->LoadSound( va( "sound/%s", FacePoser_TranslateSoundName( event ) ) );
+ if ( wave )
+ {
+ event->SetEndTime( event->GetStartTime() + wave->GetRunningLength() );
+ delete wave;
+ }
+
+ DeleteSceneWidgets();
+
+ // Add to appropriate channel
+ channel->AddEvent( event );
+
+ CreateSceneWidgets();
+
+ CChoreoEventWidget *eventWidget = FindWidgetForEvent( event );
+ if ( eventWidget )
+ {
+ eventWidget->SetSelected( true );
+ }
+
+ // Redraw
+ InvalidateLayout();
+ }
+
+ PushRedo( "Place Next Speak Event" );
+
+ DeleteAllAndPurge( m_SortedNames );
+}
+
+enum
+{
+ FM_LEFT = 0,
+ FM_RIGHT,
+ FM_SMALLESTWIDE,
+ FM_LARGESTWIDE
+};
+
+static int FindMetric( int type, CUtlVector< CChoreoEvent * > &list, float& value )
+{
+ float bestVal = 999999.0f;
+ int bestIndex = -1;
+ bool greater = true;
+
+ switch ( type )
+ {
+ default:
+ case FM_LEFT:
+ case FM_SMALLESTWIDE:
+ greater = false;
+ break;
+ case FM_RIGHT:
+ case FM_LARGESTWIDE:
+ bestVal = -bestVal;
+ greater = true;
+ break;
+ }
+ int c = list.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ CChoreoEvent *e = list[ i ];
+ if ( type != FM_LEFT &&
+ !e->HasEndTime() )
+ continue;
+
+ float val;
+ switch ( type )
+ {
+ default:
+ case FM_LEFT:
+ val = e->GetStartTime();
+ break;
+ case FM_RIGHT:
+ val = e->GetEndTime();
+ break;
+ case FM_SMALLESTWIDE:
+ case FM_LARGESTWIDE:
+ val = e->GetDuration();
+ break;
+ }
+
+ if ( greater )
+ {
+ if ( val <= bestVal )
+ continue;
+ }
+ else
+ {
+ if ( val >= bestVal )
+ continue;
+ }
+
+ bestVal = val;
+ bestIndex = i;
+ }
+
+ value = bestVal;
+ return bestIndex;
+}
+
+void CChoreoView::OnAlign( bool left )
+{
+ CUtlVector< CChoreoEvent * > list;
+ GetSelectedEvents( list );
+
+ if ( left )
+ {
+ for ( int i = 0; i < m_SceneGlobalEvents.Size(); i++ )
+ {
+ CChoreoGlobalEventWidget *event = m_SceneGlobalEvents[ i ];
+ if ( !event || !event->IsSelected() )
+ continue;
+
+ list.AddToTail( event->GetEvent() );
+ }
+ }
+
+ int numSel = list.Count();
+ if ( numSel < 2 )
+ {
+ Warning( "Can't align, must have at least two events selected\n" );
+ return;
+ }
+
+ float value;
+ int idx = FindMetric( left ? FM_LEFT : FM_RIGHT, list, value );
+ if ( idx == -1 )
+ {
+ return;
+ }
+
+ SetDirty( true );
+
+ char undotext[ 128 ];
+ Q_snprintf( undotext, sizeof( undotext ), "Align %s", left ? "Left" : "Right" );
+ PushUndo( undotext );
+
+ for ( int i = 0; i < numSel; ++i )
+ {
+ if ( i == idx )
+ continue;
+
+ CChoreoEvent *e = list[ i ];
+
+ float newStartTime = left ? value : ( value - e->GetDuration() );
+ float offset = newStartTime - e->GetStartTime();
+ e->OffsetTime( offset );
+ }
+
+ PushRedo( undotext );
+
+ InvalidateLayout();
+}
+
+void CChoreoView::OnMakeSameSize( bool smallest )
+{
+ CUtlVector< CChoreoEvent * > list;
+ int numSel = GetSelectedEvents( list );
+ if ( numSel < 2 )
+ {
+ Warning( "Can't align, must have at least two events selected\n" );
+ return;
+ }
+
+ float value;
+ int idx = FindMetric( smallest ? FM_SMALLESTWIDE : FM_LARGESTWIDE, list, value );
+ if ( idx == -1 )
+ {
+ return;
+ }
+
+ SetDirty( true );
+
+ char undotext[ 128 ];
+ Q_snprintf( undotext, sizeof( undotext ), "Size to %s", smallest ? "Smallest" : "Largest" );
+ PushUndo( undotext );
+
+ for ( int i = 0; i < numSel; ++i )
+ {
+ if ( i == idx )
+ continue;
+
+ list[ i ]->SetEndTime( list[ i ]->GetStartTime() + value );
+ }
+
+ PushRedo( undotext );
+
+ InvalidateLayout();
+}
+
+void CChoreoView::SelectAllEventsInActor( CChoreoActorWidget *actor )
+{
+ TraverseWidgets( &CChoreoView::SelectInActor, actor );
+ redraw();
+}
+
+void CChoreoView::SelectAllEventsInChannel( CChoreoChannelWidget *channel )
+{
+ TraverseWidgets( &CChoreoView::SelectInChannel, channel );
+ redraw();
+}
+
+void CChoreoView::SelectInActor( CChoreoWidget *widget, CChoreoWidget *param1 )
+{
+ CChoreoEventWidget *ev = dynamic_cast< CChoreoEventWidget * >( widget );
+ if ( !ev )
+ return;
+
+ if ( ev->IsSelected() )
+ return;
+
+ CChoreoChannel *ch = ev->GetEvent()->GetChannel();
+ if ( !ch )
+ return;
+ CChoreoActor *actor = ch->GetActor();
+ if ( !actor )
+ return;
+
+ CChoreoActorWidget *actorw = dynamic_cast< CChoreoActorWidget * >( param1 );
+ if ( !actorw )
+ return;
+
+ if ( actorw->GetActor() != actor )
+ return;
+
+ widget->SetSelected( true );
+}
+
+void CChoreoView::SelectInChannel( CChoreoWidget *widget, CChoreoWidget *param1 )
+{
+ CChoreoEventWidget *ev = dynamic_cast< CChoreoEventWidget * >( widget );
+ if ( !ev )
+ return;
+
+ if ( ev->IsSelected() )
+ return;
+
+ CChoreoChannel *ch = ev->GetEvent()->GetChannel();
+ if ( !ch )
+ return;
+
+ CChoreoChannelWidget *chw = dynamic_cast< CChoreoChannelWidget * >( param1 );
+ if ( !chw )
+ return;
+
+ if ( chw->GetChannel() != ch )
+ return;
+
+ widget->SetSelected( true );
+}
+