//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// // // Purpose: // // $NoKeywords: $ //===========================================================================// #include "cbase.h" #include #include #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 #include "scriplib.h" #include "WaveBrowser.h" #include "filesystem_init.h" #include "flexpanel.h" #include "tier3/choreoutils.h" #include "tier2/p4helpers.h" using namespace vgui; // 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( "" ); 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; m_bScrubSeconds = false; } //----------------------------------------------------------------------------- // 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.25f / ((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( Color( 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 ); // Draw the frame number next to the time tick char sz[48]; const int fontsize = 9; sprintf( sz, "Frame: %i", (int)(GetScene()->GetSceneFPS() * (float)scenestart) ); const int length = drawHelper.CalcTextWidth( "Arial", fontsize, FW_NORMAL, sz); rcThumb.left = markerstart - length - 10; drawHelper.DrawColoredText( "Arial", fontsize, FW_NORMAL, Color( 50, 50, 50 ), rcThumb, sz ); } 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 ); // Draw the frame number next to the time tick char sz[48]; const int fontsize = 9; sprintf( sz, "Frame: %i", (int)(GetScene()->GetSceneFPS() * (float)sceneend) ); const int length = drawHelper.CalcTextWidth( "Arial", fontsize, FW_NORMAL, sz); rcThumb.left = markerend + 10; rcThumb.right = rcThumb.left + length; drawHelper.DrawColoredText( "Arial", fontsize, FW_NORMAL, Color( 50, 50, 50 ), rcThumb, sz ); } } //----------------------------------------------------------------------------- // 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.Count(); 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( Color( 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.Count(); i++ ) { CChoreoGlobalEventWidget *event = m_SceneGlobalEvents[ i ]; if ( event ) { event->redraw( drawHelper ); } } drawHelper.StartClipping( rcClip ); for ( i = 0; i < m_SceneActors.Count(); 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.Count() > 0 ) { int length = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, "undo %i/%i", m_nUndoLevel, m_UndoStack.Count() ); 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, Color( 100, 180, 100 ), rcText, "undo %i/%i", m_nUndoLevel, m_UndoStack.Count() ); } 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 + 35; rcText.bottom = rcText.top + 10; rcText.left = GetLabelWidth() + 20; rcText.right = rcText.left + length; drawHelper.DrawColoredText( "Arial", fontsize, FW_NORMAL, Color( 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, Color( 50, 50, 50 ), rcText, sz ); } //----------------------------------------------------------------------------- // Purpose: // Input : current - // number - // Output : int //----------------------------------------------------------------------------- void CChoreoView::GetUndoLevels( int& current, int& number ) { current = m_nUndoLevel; number = m_UndoStack.Count(); } //----------------------------------------------------------------------------- // 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->add( "Camera...", IDC_ADDEVENT_CAMERA ); 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( ¶ms ) ) 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.Count(); 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.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 ( 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.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 ( event->IsSelected() ) { events.AddToTail( event->GetEvent() ); c++; } } } } return c; } int CChoreoView::CountSelectedGlobalEvents( void ) { int c = 0; for ( int i = 0; i < m_SceneGlobalEvents.Count(); 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.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 ( 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.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 ( !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.Count(); 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.Count(); 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.Count(); 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.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 ( !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_CAMERA: { AddEvent( CChoreoEvent::CAMERA ); } 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( ¶ms, 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( ¶ms, 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( ¶ms, 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( ¶ms, 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( ¶ms, 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( ¶ms, 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.Count(); i++ ) { actor = m_SceneActors[ i ]; if ( !actor ) continue; if ( actor->GetActor() == a ) break; } if ( !actor || i >= m_SceneActors.Count() ) 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.Count(); i++ ) { actor = m_SceneActors[ i ]; if ( !actor ) continue; if ( !stricmp( actor->GetActor()->GetName(), a->GetName() ) ) break; } if ( !actor || i >= m_SceneActors.Count() ) 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.Count(); 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.Count(); 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.Count(); i++ ) { w = m_SceneActors[ i ]; m_ActorExpanded[ i ].expanded = ((CChoreoActorWidget *)w)->GetShowChannels(); delete w; } m_SceneActors.RemoveAll(); for( i = 0 ; i < m_SceneGlobalEvents.Count(); 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.Count(); 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, 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( ¶ms, 0, sizeof( params ) ); strcpy( params.m_szDialogTitle, "Create Actor" ); strcpy( params.m_szName, "" ); if ( !ActorProperties( ¶ms ) ) 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( GetChoreoFile() ); // 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( scenefile )) { mxMessageBox( this, va( "Unable to write \"%s\"", scenefile ), "SaveToFile", MX_MB_OK | MX_MB_ERROR ); } g_MDLViewer->OnVCDSaved( scenefile ); 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( ¶ms, 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( ¶ms ) ) { 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( ¶ms, 0, sizeof( params ) ); strcpy( params.m_szDialogTitle, "Edit Channel" ); strcpy( params.m_szName, channel->GetName() ); if ( !ChannelProperties( ¶ms ) ) 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( ¶ms, 0, sizeof( params ) ); strcpy( params.m_szDialogTitle, "Create Actor" ); strcpy( params.m_szName, "" ); if ( !ActorProperties( ¶ms ) ) 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( ¶ms, 0, sizeof( params ) ); strcpy( params.m_szDialogTitle, "Edit Actor" ); strcpy( params.m_szName, actor->GetName() ); if ( !ActorProperties( ¶ms ) ) 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( ¶ms, 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::CAMERA: 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 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( ¶ms )) { 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( ¶ms, 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( ¶ms ) ) 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( ¶ms, 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" ); strcpy( 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( ¶ms ) ) 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( ¶ms, 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: case CChoreoEvent::CAMERA: strcpy( params.m_szParameters3, event->GetParameters3() ); strcpy( params.m_szParameters2, event->GetParameters2() ); strcpy( params.m_szParameters, event->GetParameters() ); strcpy( params.m_szName, event->GetName() ); break; case CChoreoEvent::FACE: case CChoreoEvent::LOOKAT: case CChoreoEvent::FIRETRIGGER: case CChoreoEvent::FLEXANIMATION: case CChoreoEvent::SUBSCENE: strcpy( params.m_szParameters, event->GetParameters() ); strcpy( 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 ) { strcpy( params.m_szTagName, event->GetRelativeTagName() ); strcpy( params.m_szTagWav, event->GetRelativeWavName() ); } while (1) { SetScrubTargetTime( m_flScrub ); FinishSimulation(); sound->Flush(); m_bForceProcess = true; if (!EventProperties( ¶ms )) { 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.Count(); 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.Count(); 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.Count(); 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.Count(); 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( 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.Count(); 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.Count(); 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.Count(); 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.Count(); 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( ¶ms ) ) 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.Count(); 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.Count(); 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( ¶ms, 0, sizeof( params ) ); strcpy( params.m_szDialogTitle, "Event Tag Name" ); strcpy( params.m_szPrompt, "Name:" ); strcpy( params.m_szInputText, "" ); if ( !InputProperties( ¶ms ) ) return; if ( strlen( params.m_szInputText ) <= 0 ) { Con_ErrorPrintf( "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.Count(); 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.Count(); 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, Color( 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.Count(); 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.Count() > 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.Count() > 0 && m_nUndoLevel <= m_UndoStack.Count() - 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.Count() > 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.Count() > 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.Count(); } //----------------------------------------------------------------------------- // 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.Count(); 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.Count() > 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.Count(); 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 + 14, 16, 16 ); bx += bw + 2; m_btnPause->setBounds( bx, topx + 14, 16, 16 ); bx += bw + 2; m_btnStop->setBounds( bx, topx + 14, 16, 16 ); bx += bw + 2; m_pPlaybackRate->setBounds( bx, topx + 14, 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( ColorToRGB( Color( 0, 150, 100 ) ) ); drawHelper.DrawFilledRect( br, rcHandle ); // char sz[ 48 ]; if ( m_bScrubSeconds ) { sprintf( sz, "%.3f", m_flScrub ); } else { sprintf( sz, "%i", (int) ( m_flScrub * (float)( GetScene()->GetSceneFPS() ) ) ); } 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, Color( 255, 255, 255 ), rcText, sz ); DeleteObject( br ); // // Draw the timeline // /* if ( !m_bPaused ) { // // Draw the focus rect // m_FocusRects.Purge(); RECT rcScrub; GetScrubHandleRect( rcScrub, true ); // 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 ); DrawFocusRect(); } */ } //----------------------------------------------------------------------------- // 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.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; 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( ¶ms, 0, sizeof( params ) ); strcpy( params.m_szDialogTitle, "Change Zoom" ); strcpy( params.m_szPrompt, "New scale (e.g., 2.5x):" ); Q_snprintf( params.m_szInputText, sizeof( params.m_szInputText ), "%.2f", (float)GetTimeZoom( GetToolName() ) / 100.0f ); if ( !InputProperties( ¶ms ) ) 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 ¶ms, 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.Count(); 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.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; 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.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; 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( ¶ms ) && params.m_szCCToken[0] != 0 ) { return; } // See if the token exists? StringIndex_t stringIndex = g_pLocalize->FindIndex( params.m_szCCToken ); if ( INVALID_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( ¶ms ) && 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 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.Count(); 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 ); } void CChoreoView::SetScrubUnitSeconds( bool bUseSeconds) { m_bScrubSeconds = bUseSeconds; redraw(); }