csgo-2018-source/utils/hlfaceposer/choreoview.cpp
2021-07-24 21:11:47 -07:00

11720 lines
264 KiB
C++

//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
// $NoKeywords: $
//===========================================================================//
#include "cbase.h"
#include <stdio.h>
#include <mxtk/mxWindow.h>
#include "mdlviewer.h"
#include "hlfaceposer.h"
#include "StudioModel.h"
#include "expressions.h"
#include "expclass.h"
#include "ChoreoView.h"
#include "choreoevent.h"
#include "choreoactor.h"
#include "choreochannel.h"
#include "choreoscene.h"
#include "choreowidget.h"
#include "choreoactorwidget.h"
#include "choreochannelwidget.h"
#include "choreoglobaleventwidget.h"
#include "choreowidgetdrawhelper.h"
#include "choreoeventwidget.h"
#include "viewerSettings.h"
#include "filesystem.h"
#include "choreoviewcolors.h"
#include "ActorProperties.h"
#include "ChannelProperties.h"
#include "EventProperties.h"
#include "GlobalEventProperties.h"
#include "ifaceposersound.h"
#include "snd_wave_source.h"
#include "ifaceposerworkspace.h"
#include "PhonemeEditor.h"
#include "iscenetokenprocessor.h"
#include "InputProperties.h"
#include "FileSystem.h"
#include "ExpressionTool.h"
#include "ControlPanel.h"
#include "faceposer_models.h"
#include "choiceproperties.h"
#include "MatSysWin.h"
#include "tier1/strtools.h"
#include "GestureTool.h"
#include "npcevent.h"
#include "RampTool.h"
#include "SceneRampTool.h"
#include "KeyValues.h"
#include "SoundEmitterSystem/isoundemittersystembase.h"
#include "cclookup.h"
#include "iclosecaptionmanager.h"
#include "AddSoundEntry.h"
#include "isoundcombiner.h"
#include <vgui/ILocalize.h>
#include "scriplib.h"
#include "WaveBrowser.h"
#include "filesystem_init.h"
#include "flexpanel.h"
#include "tier3/choreoutils.h"
#include "tier2/p4helpers.h"
using namespace vgui;
// 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( &params ) )
return;
if ( params.m_nSelected == oldsel )
return;
// Chose something new...
if ( params.m_nSelected >= 0 &&
params.m_nSelected < params.m_Choices.Count() )
{
AssociateModelToActor( a, params.m_nSelected );
}
else
{
// Chose "No association"
AssociateModelToActor( a, -1 );
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *actor -
// modelindex -
//-----------------------------------------------------------------------------
void CChoreoView::AssociateModelToActor( CChoreoActor *actor, int modelindex )
{
Assert( actor );
SetDirty( true );
PushUndo( "Associate model" );
// Chose something new...
if ( modelindex >= 0 &&
modelindex < models->Count() )
{
actor->SetFacePoserModelName( models->GetModelFileName( modelindex ) );
}
else
{
// Chose "No Associated Model"
actor->SetFacePoserModelName( "" );
}
RecomputeWaves();
PushRedo( "Associate model" );
}
void CChoreoView::AssociateBSP( void )
{
if ( !m_pScene )
return;
// Strip game directory and slash
char mapname[ 512 ];
if ( !FacePoser_ShowOpenFileNameDialog( mapname, sizeof( mapname ), "maps", "*.bsp" ) )
{
return;
}
m_pScene->SetMapname( mapname );
redraw();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CChoreoView::DrawFocusRect( void )
{
HDC dc = GetDC( NULL );
for ( int i = 0; i < m_FocusRects.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( &params, 0, sizeof( params ) );
params.forward = false;
params.time = GetTimeValueForMouse( m_nClickedX );
params.type = SelectionParams_t::SP_ALL;
SelectEvents( params );
}
break;
case IDC_SELECTEVENTS_ALL_AFTER:
{
SelectionParams_t params;
Q_memset( &params, 0, sizeof( params ) );
params.forward = true;
params.time = GetTimeValueForMouse( m_nClickedX );
params.type = SelectionParams_t::SP_ALL;
SelectEvents( params );
}
break;
case IDC_SELECTEVENTS_ACTIVE_BEFORE:
{
SelectionParams_t params;
Q_memset( &params, 0, sizeof( params ) );
params.forward = false;
params.time = GetTimeValueForMouse( m_nClickedX );
params.type = SelectionParams_t::SP_ACTIVE;
SelectEvents( params );
}
break;
case IDC_SELECTEVENTS_ACTIVE_AFTER:
{
SelectionParams_t params;
Q_memset( &params, 0, sizeof( params ) );
params.forward = true;
params.time = GetTimeValueForMouse( m_nClickedX );
params.type = SelectionParams_t::SP_ACTIVE;
SelectEvents( params );
}
break;
case IDC_SELECTEVENTS_CHANNEL_BEFORE:
{
SelectionParams_t params;
Q_memset( &params, 0, sizeof( params ) );
params.forward = false;
params.time = GetTimeValueForMouse( m_nClickedX );
params.type = SelectionParams_t::SP_CHANNEL;
SelectEvents( params );
}
break;
case IDC_SELECTEVENTS_CHANNEL_AFTER:
{
SelectionParams_t params;
Q_memset( &params, 0, sizeof( params ) );
params.forward = true;
params.time = GetTimeValueForMouse( m_nClickedX );
params.type = SelectionParams_t::SP_CHANNEL;
SelectEvents( params );
}
break;
case IDC_INSERT_TIME:
{
OnInsertTime();
}
break;
case IDC_DELETE_TIME:
{
OnDeleteTime();
}
break;
case IDC_CV_ALIGN_LEFT:
{
OnAlign( true );
}
break;
case IDC_CV_ALIGN_RIGHT:
{
OnAlign( false );
}
break;
case IDC_CV_SAMESIZE_SMALLEST:
{
OnMakeSameSize( true );
}
break;
case IDC_CV_SAMESIZE_LARGEST:
{
OnMakeSameSize( false );
}
break;
}
if ( iret == 1 )
{
SetActiveTool( this );
}
}
break;
}
return iret;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CChoreoView::PlayScene( bool forward )
{
m_bForward = forward;
if ( !m_pScene )
return;
sound->Flush();
// Make sure phonemes are loaded
FacePoser_EnsurePhonemesLoaded();
// Unpause
if ( m_bSimulating && m_bPaused )
{
m_bPaused = false;
return;
}
m_bSimulating = true;
m_bPaused = false;
// float soundlatency = max( sound->GetAmountofTimeAhead(), 0.0f );
// soundlatency = min( 0.5f, soundlatency );
float soundlatency = 0.0f;
float sceneendtime = m_pScene->FindStopTime();
m_pScene->SetSoundFileStartupLatency( soundlatency );
if ( m_rgABPoints[ 0 ].active ||
m_rgABPoints[ 1 ].active )
{
if ( m_rgABPoints[ 0 ].active &&
m_rgABPoints[ 1 ].active )
{
float st = m_rgABPoints[ 0 ].time;
float ed = m_rgABPoints[ 1 ].time;
m_pScene->ResetSimulation( m_bForward, st, ed );
SetScrubTime( m_bForward ? st : ed );
SetScrubTargetTime( m_bForward ? ed : st );
}
else
{
float startonly = m_rgABPoints[ 0 ].active ? m_rgABPoints[ 0 ].time : m_rgABPoints[ 1 ].time;
m_pScene->ResetSimulation( m_bForward, startonly );
SetScrubTime( m_bForward ? startonly : sceneendtime );
SetScrubTargetTime( m_bForward ? sceneendtime : startonly );
}
}
else
{
// NO start end/loop
m_pScene->ResetSimulation( m_bForward );
SetScrubTime( m_bForward ? 0 : sceneendtime );
SetScrubTargetTime( m_bForward ? sceneendtime : 0 );
}
if ( g_viewerSettings.speedScale == 0.0f )
{
m_flLastSpeedScale = g_viewerSettings.speedScale;
m_bResetSpeedScale = true;
g_viewerSettings.speedScale = 1.0f;
Con_Printf( "Resetting speed scale to 1.0\n" );
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : x -
//-----------------------------------------------------------------------------
void CChoreoView::MoveTimeSliderToPos( int x )
{
m_flLeftOffset = (float)x;
m_pHorzScrollBar->setValue( (int)m_flLeftOffset );
InvalidateRect( (HWND)m_pHorzScrollBar->getHandle(), NULL, TRUE );
InvalidateLayout();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CChoreoView::PauseScene( void )
{
if ( !m_bSimulating )
return;
m_bPaused = true;
sound->StopAll();
}
//-----------------------------------------------------------------------------
// Purpose: Apply expression to actor's face
// Input : *event -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
void CChoreoView::ProcessExpression( CChoreoScene *scene, CChoreoEvent *event )
{
Assert( event->GetType() == CChoreoEvent::EXPRESSION );
StudioModel *model = FindAssociatedModel( scene, event->GetActor() );
if ( !model )
return;
CStudioHdr *hdr = model->GetStudioHdr();
if ( !hdr )
{
return;
}
CExpClass *p = expressions->FindClass( event->GetParameters(), true );
if ( !p )
{
return;
}
CExpression *exp = p->FindExpression( event->GetParameters2() );
if ( !exp )
{
return;
}
CChoreoActor *a = event->GetActor();
if ( !a )
return;
CChoreoActorWidget *actor = NULL;
int i;
for ( i = 0; i < m_SceneActors.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( &params, 0, sizeof( params ) );
strcpy( params.m_szDialogTitle, "Create Actor" );
strcpy( params.m_szName, "" );
if ( !ActorProperties( &params ) )
return;
if ( strlen( params.m_szName ) <= 0 )
return;
SetDirty( true );
PushUndo( "Create Actor" );
Con_Printf( "Creating scene %s with actor '%s'\n", GetChoreoFile(), params.m_szName );
CChoreoActor *actor = m_pScene->AllocActor();
if ( actor )
{
actor->SetName( params.m_szName );
}
PushRedo( "Create Actor" );
CreateSceneWidgets();
// Redraw
InvalidateLayout();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CChoreoView::Save( void )
{
if ( !m_pScene )
return;
if ( !MakeFileWriteablePrompt( GetChoreoFile(), "VCD File" ) )
{
Con_Printf( "Not saving changes to %s\n", GetChoreoFile() );
return;
}
Con_Printf( "Saving changes to %s\n", GetChoreoFile() );
CP4AutoEditAddFile checkout( GetChoreoFile() );
if ( !m_pScene->SaveToFile( GetChoreoFile() ) )
{
mxMessageBox( this, va( "Unable to write \"%s\"", GetChoreoFile() ),
"SaveToFile", MX_MB_OK | MX_MB_ERROR );
}
g_MDLViewer->OnVCDSaved( 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( &params, 0, sizeof( params ) );
strcpy( params.m_szDialogTitle, "Create Channel" );
strcpy( params.m_szName, "" );
params.m_bShowActors = true;
strcpy( params.m_szSelectedActor, "" );
params.m_pScene = m_pScene;
if ( !ChannelProperties( &params ) )
{
return;
}
if ( strlen( params.m_szName ) <= 0 )
{
return;
}
CChoreoActor *actor = m_pScene->FindActor( params.m_szSelectedActor );
if ( !actor )
{
Con_Printf( "Can't add channel %s, actor %s doesn't exist\n", params.m_szName, params.m_szSelectedActor );
return;
}
SetDirty( true );
PushUndo( "Add Channel" );
DeleteSceneWidgets();
CChoreoChannel *channel = m_pScene->AllocChannel();
if ( !channel )
{
Con_Printf( "Unable to allocate channel %s!\n", params.m_szName );
}
else
{
channel->SetName( params.m_szName );
channel->SetActor( actor );
actor->AddChannel( channel );
}
CreateSceneWidgets();
PushRedo( "Add Channel" );
// Redraw
InvalidateLayout();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *channel -
//-----------------------------------------------------------------------------
void CChoreoView::MoveChannelUp( CChoreoChannel *channel )
{
SetDirty( true );
PushUndo( "Move Channel Up" );
DeleteSceneWidgets();
// Find the appropriate actor
for ( int i = 0; i < m_pScene->GetNumActors(); i++ )
{
CChoreoActor *a = m_pScene->GetActor( i );
if ( !a )
continue;
int index = a->FindChannelIndex( channel );
if ( index == -1 )
continue;
if ( index != 0 )
{
Con_Printf( "Moving %s up\n", channel->GetName() );
a->SwapChannels( index, index - 1 );
}
break;
}
CreateSceneWidgets();
PushRedo( "Move Channel Up" );
// Redraw
InvalidateLayout();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *channel -
//-----------------------------------------------------------------------------
void CChoreoView::MoveChannelDown( CChoreoChannel *channel )
{
SetDirty( true );
PushUndo( "Move Channel Down" );
DeleteSceneWidgets();
// Find the appropriate actor
for ( int i = 0; i < m_pScene->GetNumActors(); i++ )
{
CChoreoActor *a = m_pScene->GetActor( i );
if ( !a )
continue;
int index = a->FindChannelIndex( channel );
if ( index == -1 )
continue;
if ( index < a->GetNumChannels() - 1 )
{
Con_Printf( "Moving %s down\n", channel->GetName() );
a->SwapChannels( index, index + 1 );
}
break;
}
CreateSceneWidgets();
PushRedo( "Move Channel Down" );
// Redraw
InvalidateLayout();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *channel -
//-----------------------------------------------------------------------------
void CChoreoView::EditChannel( CChoreoChannel *channel )
{
if ( !channel )
return;
CChannelParams params;
memset( &params, 0, sizeof( params ) );
strcpy( params.m_szDialogTitle, "Edit Channel" );
strcpy( params.m_szName, channel->GetName() );
if ( !ChannelProperties( &params ) )
return;
if ( strlen( params.m_szName ) <= 0 )
return;
SetDirty( true );
PushUndo( "Edit Channel" );
channel->SetName( params.m_szName );
PushRedo( "Edit Channel" );
// Redraw
InvalidateLayout();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *actor -
//-----------------------------------------------------------------------------
void CChoreoView::DeleteActor( CChoreoActor *actor )
{
if ( !actor || !m_pScene )
return;
SetDirty( true );
PushUndo( "Delete Actor" );
DeleteSceneWidgets();
// Delete channel and it's children
// Find the appropriate actor
Con_Printf( "Deleting %s\n", actor->GetName() );
m_pScene->RemoveActor( actor );
m_pScene->DeleteReferencedObjects( actor );
ReportSceneClearToTools();
CreateSceneWidgets();
PushRedo( "Delete Actor" );
// Redraw
InvalidateLayout();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CChoreoView::NewActor( void )
{
if ( !m_pScene )
{
Con_ErrorPrintf( "You must load or create a scene file first\n" );
return;
}
CActorParams params;
memset( &params, 0, sizeof( params ) );
strcpy( params.m_szDialogTitle, "Create Actor" );
strcpy( params.m_szName, "" );
if ( !ActorProperties( &params ) )
return;
if ( strlen( params.m_szName ) <= 0 )
return;
SetDirty( true );
PushUndo( "Add Actor" );
DeleteSceneWidgets();
Con_Printf( "Adding new actor '%s'\n", params.m_szName );
CChoreoActor *actor = m_pScene->AllocActor();
if ( actor )
{
actor->SetName( params.m_szName );
}
CreateSceneWidgets();
PushRedo( "Add Actor" );
// Redraw
InvalidateLayout();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *actor -
//-----------------------------------------------------------------------------
void CChoreoView::MoveActorUp( CChoreoActor *actor )
{
DeleteSceneWidgets();
int index = m_pScene->FindActorIndex( actor );
// found it and it's not first
if ( index != -1 && index != 0 )
{
Con_Printf( "Moving %s up\n", actor->GetName() );
SetDirty( true );
PushUndo( "Move Actor Up" );
m_pScene->SwapActors( index, index - 1 );
PushRedo( "Move Actor Up" );
}
CreateSceneWidgets();
// Redraw
InvalidateLayout();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *actor -
//-----------------------------------------------------------------------------
void CChoreoView::MoveActorDown( CChoreoActor *actor )
{
DeleteSceneWidgets();
int index = m_pScene->FindActorIndex( actor );
// found it and it's not first
if ( index != -1 && ( index < m_pScene->GetNumActors() - 1 ) )
{
Con_Printf( "Moving %s down\n", actor->GetName() );
SetDirty( true );
PushUndo( "Move Actor Down" );
m_pScene->SwapActors( index, index + 1 );
PushRedo( "Move Actor Down" );
}
CreateSceneWidgets();
// Redraw
InvalidateLayout();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *actor -
//-----------------------------------------------------------------------------
void CChoreoView::EditActor( CChoreoActor *actor )
{
if ( !actor )
return;
CActorParams params;
memset( &params, 0, sizeof( params ) );
strcpy( params.m_szDialogTitle, "Edit Actor" );
strcpy( params.m_szName, actor->GetName() );
if ( !ActorProperties( &params ) )
return;
if ( strlen( params.m_szName ) <= 0 )
return;
SetDirty( true );
PushUndo( "Edit Actor" );
actor->SetName( params.m_szName );
PushRedo( "Edit Actor" );
// Redraw
InvalidateLayout();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : type -
//-----------------------------------------------------------------------------
void CChoreoView::AddEvent( int type, int subtype /*= 0*/, char const *defaultparameters /*= NULL*/ )
{
int mx, my;
mx = m_nClickedX;
my = m_nClickedY;
CChoreoChannelWidget *channel = m_pClickedChannel;
if ( !channel || !channel->GetChannel() )
{
CChoreoActorWidget *actor = m_pClickedActor;
if ( actor )
{
if ( actor->GetNumChannels() <= 0 )
return;
channel = actor->GetChannel( 0 );
if ( !channel || !channel->GetChannel() )
return;
}
else
{
return;
}
}
// Convert click position local to this window
POINT pt;
pt.x = mx;
pt.y = my;
CEventParams params;
memset( &params, 0, sizeof( params ) );
if ( defaultparameters )
{
Q_strncpy( params.m_szParameters, defaultparameters, sizeof( params.m_szParameters ) );
}
strcpy( params.m_szDialogTitle, "Create Event" );
params.m_nType = type;
params.m_pScene = m_pScene;
params.m_bFixedLength = false;
params.m_bResumeCondition = false;
params.m_flStartTime = GetTimeValueForMouse( pt.x );
params.m_bCloseCaptionNoAttenuate = false;
params.m_bForceShortMovement = false;
params.m_bSyncToFollowingGesture = false;
params.m_bDisabled = false;
params.m_bPlayOverScript = false;
switch ( type )
{
case CChoreoEvent::EXPRESSION:
case CChoreoEvent::FLEXANIMATION:
case CChoreoEvent::GESTURE:
case CChoreoEvent::SEQUENCE:
case CChoreoEvent::LOOKAT:
case CChoreoEvent::MOVETO:
case CChoreoEvent::FACE:
case CChoreoEvent::SUBSCENE:
case CChoreoEvent::INTERRUPT:
case CChoreoEvent::GENERIC:
case CChoreoEvent::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 <NULL> Gesture" );
strcpy( params.m_szName, "NULL" );
}
break;
case CChoreoEvent::SPEAK:
params.m_bFixedLength = true;
params.m_bHasEndTime = false;
params.m_flEndTime = -1.0f;
break;
default:
params.m_bHasEndTime = false;
params.m_flEndTime = -1.0f;
break;
}
params.m_bUsesTag = false;
while (1)
{
SetScrubTargetTime( m_flScrub );
FinishSimulation();
sound->Flush();
m_bForceProcess = true;
if (!EventProperties( &params ))
{
m_bForceProcess = false;
return;
}
m_bForceProcess = false;
if ( Q_strlen( params.m_szName ) <= 0 )
{
mxMessageBox( this, va( "Event must have a valid name" ),
"Edit Event", MX_MB_OK | MX_MB_ERROR );
continue;
}
if ( Q_strlen( params.m_szParameters ) <= 0 )
{
bool shouldBreak = false;
switch ( params.m_nType )
{
case CChoreoEvent::FLEXANIMATION:
case CChoreoEvent::INTERRUPT:
case CChoreoEvent::PERMIT_RESPONSES:
shouldBreak = true;
break;
case CChoreoEvent::GESTURE:
if ( subtype == 1 )
{
shouldBreak = true;
}
break;
default:
// Have to have a non-null parameters block
break;
}
if ( !shouldBreak )
{
mxMessageBox( this, va( "No parameters specified for %s\n", params.m_szName ),
"Edit Event", MX_MB_OK | MX_MB_ERROR );
continue;
}
}
break;
}
SetDirty( true );
PushUndo( "Add Event" );
CChoreoEvent *event = m_pScene->AllocEvent();
if ( event )
{
event->SetType( (CChoreoEvent::EVENTTYPE)type );
event->SetName( params.m_szName );
event->SetParameters( params.m_szParameters );
event->SetParameters2( params.m_szParameters2 );
event->SetParameters3( params.m_szParameters3 );
event->SetStartTime( params.m_flStartTime );
event->SetResumeCondition( params.m_bResumeCondition );
event->SetLockBodyFacing( params.m_bLockBodyFacing );
event->SetDistanceToTarget( params.m_flDistanceToTarget );
event->SetForceShortMovement( params.m_bForceShortMovement );
event->SetSyncToFollowingGesture( params.m_bSyncToFollowingGesture );
event->SetActive( !params.m_bDisabled );
event->SetPlayOverScript( params.m_bPlayOverScript );
if ( params.m_bUsesTag )
{
event->SetUsingRelativeTag( true, params.m_szTagName, params.m_szTagWav );
}
else
{
event->SetUsingRelativeTag( false );
}
CChoreoChannel *pchannel = channel->GetChannel();
event->SetChannel( pchannel );
event->SetActor( pchannel->GetActor() );
if ( params.m_bHasEndTime &&
params.m_flEndTime != -1.0 &&
params.m_flEndTime > params.m_flStartTime )
{
event->SetEndTime( params.m_flEndTime );
}
else
{
event->SetEndTime( -1.0f );
}
switch ( event->GetType() )
{
default:
break;
case CChoreoEvent::SUBSCENE:
{
// Just grab end time
CChoreoScene *scene = LoadScene( event->GetParameters() );
if ( scene )
{
event->SetEndTime( params.m_flStartTime + scene->FindStopTime() );
}
delete scene;
}
break;
case CChoreoEvent::SEQUENCE:
{
CheckSequenceLength( event, false );
// AutoaddSequenceKeys( event);
}
break;
case CChoreoEvent::GESTURE:
{
DefaultGestureLength( event, false );
AutoaddGestureKeys( event, false );
}
break;
case CChoreoEvent::LOOKAT:
case CChoreoEvent::FACE:
{
if ( params.usepitchyaw )
{
event->SetPitch( params.pitch );
event->SetYaw( params.yaw );
}
else
{
event->SetPitch( 0 );
event->SetYaw( 0 );
}
}
break;
case CChoreoEvent::SPEAK:
{
// Try and load wav to get length
CAudioSource *wave = sound->LoadSound( va( "sound/%s", FacePoser_TranslateSoundName( event ) ) );
if ( wave )
{
event->SetEndTime( params.m_flStartTime + wave->GetRunningLength() );
delete wave;
}
event->SetSuppressingCaptionAttenuation( params.m_bCloseCaptionNoAttenuate );
}
break;
}
event->SnapTimes();
DeleteSceneWidgets();
// Add to appropriate channel
pchannel->AddEvent( event );
CreateSceneWidgets();
// Redraw
InvalidateLayout();
}
PushRedo( "Add Event" );
}
//-----------------------------------------------------------------------------
// Purpose: Adds a scene "pause" event
//-----------------------------------------------------------------------------
void CChoreoView::AddGlobalEvent( CChoreoEvent::EVENTTYPE type )
{
int mx, my;
mx = m_nClickedX;
my = m_nClickedY;
// Convert click position local to this window
POINT pt;
pt.x = mx;
pt.y = my;
CGlobalEventParams params;
memset( &params, 0, sizeof( params ) );
params.m_nType = type;
switch ( type )
{
default:
Assert( 0 );
strcpy( params.m_szDialogTitle, "???" );
break;
case CChoreoEvent::SECTION:
{
strcpy( params.m_szDialogTitle, "Add Pause Point" );
}
break;
case CChoreoEvent::LOOP:
{
strcpy( params.m_szDialogTitle, "Add Loop Point" );
}
break;
case CChoreoEvent::STOPPOINT:
{
strcpy( params.m_szDialogTitle, "Add Fire Completion" );
}
break;
}
strcpy( params.m_szName, "" );
strcpy( params.m_szAction, "" );
params.m_flStartTime = GetTimeValueForMouse( pt.x );
if ( !GlobalEventProperties( &params ) )
return;
if ( strlen( params.m_szName ) <= 0 )
{
Con_Printf( "Pause section event must have a valid name\n" );
return;
}
if ( strlen( params.m_szAction ) <= 0 )
{
Con_Printf( "No action specified for section pause\n" );
return;
}
char undotext[ 256 ];
undotext[0]=0;
switch( type )
{
default:
Assert( 0 );
break;
case CChoreoEvent::SECTION:
{
Q_strcpy( undotext, "Add Section Pause" );
}
break;
case CChoreoEvent::LOOP:
{
Q_strcpy( undotext, "Add Loop Point" );
}
break;
case CChoreoEvent::STOPPOINT:
{
Q_strcpy( undotext, "Add Fire Completion" );
}
break;
}
SetDirty( true );
PushUndo( undotext );
CChoreoEvent *event = m_pScene->AllocEvent();
if ( event )
{
event->SetType( type );
event->SetName( params.m_szName );
event->SetParameters( params.m_szAction );
event->SetStartTime( params.m_flStartTime );
event->SetEndTime( -1.0f );
switch ( type )
{
default:
break;
case CChoreoEvent::LOOP:
{
event->SetLoopCount( params.m_nLoopCount );
event->SetParameters( va( "%f", params.m_flLoopTime ) );
}
break;
}
event->SnapTimes();
DeleteSceneWidgets();
CreateSceneWidgets();
// Redraw
InvalidateLayout();
}
PushRedo( undotext );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *event -
//-----------------------------------------------------------------------------
void CChoreoView::EditGlobalEvent( CChoreoEvent *event )
{
if ( !event )
return;
CGlobalEventParams params;
memset( &params, 0, sizeof( params ) );
params.m_nType = event->GetType();
switch ( event->GetType() )
{
default:
Assert( 0 );
strcpy( params.m_szDialogTitle, "???" );
break;
case CChoreoEvent::SECTION:
{
strcpy( params.m_szDialogTitle, "Edit Pause Point" );
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( &params ) )
return;
if ( strlen( params.m_szName ) <= 0 )
{
Con_Printf( "Event %s must have a valid name\n", event->GetName() );
return;
}
if ( strlen( params.m_szAction ) <= 0 )
{
Con_Printf( "No action specified for %s\n", event->GetName() );
return;
}
SetDirty( true );
char undotext[ 256 ];
undotext[0]=0;
switch( event->GetType() )
{
default:
Assert( 0 );
break;
case CChoreoEvent::SECTION:
{
Q_strcpy( undotext, "Edit Section Pause" );
}
break;
case CChoreoEvent::LOOP:
{
Q_strcpy( undotext, "Edit Loop Point" );
}
break;
case CChoreoEvent::STOPPOINT:
{
Q_strcpy( undotext, "Edit Fire Completion" );
}
break;
}
PushUndo( undotext );
event->SetName( params.m_szName );
event->SetStartTime( params.m_flStartTime );
event->SetEndTime( -1.0f );
switch ( event->GetType() )
{
default:
{
event->SetParameters( params.m_szAction );
}
break;
case CChoreoEvent::LOOP:
{
event->SetLoopCount( params.m_nLoopCount );
event->SetParameters( va( "%f", params.m_flLoopTime ) );
}
break;
}
event->SnapTimes();
PushRedo( undotext );
// Redraw
InvalidateLayout();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *event -
//-----------------------------------------------------------------------------
void CChoreoView::DeleteGlobalEvent( CChoreoEvent *event )
{
if ( !event || !m_pScene )
return;
SetDirty( true );
char undotext[ 256 ];
undotext[0]=0;
switch( event->GetType() )
{
default:
Assert( 0 );
break;
case CChoreoEvent::SECTION:
{
Q_strcpy( undotext, "Delete Section Pause" );
}
break;
case CChoreoEvent::LOOP:
{
Q_strcpy( undotext, "Delete Loop Point" );
}
break;
case CChoreoEvent::STOPPOINT:
{
Q_strcpy( undotext, "Delete Fire Completion" );
}
break;
}
PushUndo( undotext );
DeleteSceneWidgets();
Con_Printf( "Deleting %s\n", event->GetName() );
m_pScene->DeleteReferencedObjects( event );
CreateSceneWidgets();
PushRedo( undotext );
// Redraw
InvalidateLayout();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *event -
//-----------------------------------------------------------------------------
void CChoreoView::EditEvent( CChoreoEvent *event )
{
if ( !event )
return;
CEventParams params;
memset( &params, 0, sizeof( params ) );
strcpy( params.m_szDialogTitle, "Edit Event" );
// Copy in current even properties
params.m_nType = event->GetType();
params.m_bDisabled = !event->GetActive();
switch ( params.m_nType )
{
case CChoreoEvent::EXPRESSION:
case CChoreoEvent::SEQUENCE:
case CChoreoEvent::MOVETO:
case CChoreoEvent::SPEAK:
case CChoreoEvent::GESTURE:
case CChoreoEvent::INTERRUPT:
case CChoreoEvent::PERMIT_RESPONSES:
case CChoreoEvent::GENERIC:
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( &params ))
{
m_bForceProcess = false;
return;
}
m_bForceProcess = false;
if ( Q_strlen( params.m_szName ) <= 0 )
{
mxMessageBox( this, va( "Event %s must have a valid name", event->GetName() ),
"Edit Event", MX_MB_OK | MX_MB_ERROR );
continue;
}
if ( Q_strlen( params.m_szParameters ) <= 0 )
{
bool shouldBreak = false;
switch ( params.m_nType )
{
case CChoreoEvent::FLEXANIMATION:
case CChoreoEvent::INTERRUPT:
case CChoreoEvent::PERMIT_RESPONSES:
shouldBreak = true;
break;
case CChoreoEvent::GESTURE:
if ( !Q_stricmp( params.m_szName, "NULL" ) )
{
shouldBreak = true;
}
break;
default:
// Have to have a non-null parameters block
break;
}
if ( !shouldBreak )
{
mxMessageBox( this, va( "No parameters specified for %s\n", params.m_szName ),
"Edit Event", MX_MB_OK | MX_MB_ERROR );
continue;
}
}
break;
}
SetDirty( true );
PushUndo( "Edit Event" );
event->SetName( params.m_szName );
event->SetParameters( params.m_szParameters );
event->SetParameters2( params.m_szParameters2 );
event->SetParameters3( params.m_szParameters3 );
event->SetStartTime( params.m_flStartTime );
event->SetResumeCondition( params.m_bResumeCondition );
event->SetLockBodyFacing( params.m_bLockBodyFacing );
event->SetDistanceToTarget( params.m_flDistanceToTarget );
event->SetForceShortMovement( params.m_bForceShortMovement );
event->SetSyncToFollowingGesture( params.m_bSyncToFollowingGesture );
event->SetActive( !params.m_bDisabled );
event->SetPlayOverScript( params.m_bPlayOverScript );
if ( params.m_bUsesTag )
{
event->SetUsingRelativeTag( true, params.m_szTagName, params.m_szTagWav );
}
else
{
event->SetUsingRelativeTag( false );
}
if ( params.m_bHasEndTime &&
params.m_flEndTime != -1.0 &&
params.m_flEndTime > params.m_flStartTime )
{
float dt = params.m_flEndTime - event->GetEndTime();
float newduration = event->GetDuration() + dt;
RescaleRamp( event, newduration );
switch ( event->GetType() )
{
default:
break;
case CChoreoEvent::GESTURE:
{
event->RescaleGestureTimes( event->GetStartTime(), event->GetEndTime() + dt, true );
}
break;
case CChoreoEvent::FLEXANIMATION:
{
RescaleExpressionTimes( event, event->GetStartTime(), event->GetEndTime() + dt );
}
break;
}
event->SetEndTime( params.m_flEndTime );
event->SnapTimes();
event->ResortRamp();
}
else
{
event->SetEndTime( -1.0f );
}
switch ( event->GetType() )
{
default:
break;
case CChoreoEvent::SPEAK:
{
// Try and load wav to get length
CAudioSource *wave = sound->LoadSound( va( "sound/%s", FacePoser_TranslateSoundName( event ) ) );
if ( wave )
{
event->SetEndTime( params.m_flStartTime + wave->GetRunningLength() );
delete wave;
}
event->SetSuppressingCaptionAttenuation( params.m_bCloseCaptionNoAttenuate );
}
break;
case CChoreoEvent::SUBSCENE:
{
// Just grab end time
CChoreoScene *scene = LoadScene( event->GetParameters() );
if ( scene )
{
event->SetEndTime( params.m_flStartTime + scene->FindStopTime() );
}
delete scene;
}
break;
case CChoreoEvent::SEQUENCE:
{
CheckSequenceLength( event, false );
}
break;
case CChoreoEvent::GESTURE:
{
CheckGestureLength( event, false );
AutoaddGestureKeys( event, false );
g_pGestureTool->redraw();
}
break;
case CChoreoEvent::LOOKAT:
case CChoreoEvent::FACE:
{
if ( params.usepitchyaw )
{
event->SetPitch( params.pitch );
event->SetYaw( params.yaw );
}
else
{
event->SetPitch( 0 );
event->SetYaw( 0 );
}
}
break;
}
event->SnapTimes();
PushRedo( "Edit Event" );
// Redraw
InvalidateLayout();
}
void CChoreoView::EnableSelectedEvents( bool state )
{
if ( !m_pScene )
return;
SetDirty( true );
// If we right clicked on an unseleced event, then select it for the user.
if ( CountSelectedEvents() == 0 )
{
CChoreoEventWidget *event = m_pClickedEvent;
if ( event )
{
event->SetSelected( true );
}
}
char const *desc = state ? "Enable Events" : "Disable Events";
PushUndo( desc );
// Find the appropriate event by iterating across all actors and channels
for ( int i = 0; i < m_SceneActors.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<char*>( filename ) );
CChoreoScene *scene = ChoreoLoadScene( filename, this, tokenprocessor, Con_Printf );
return scene;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CChoreoView::FixupSequenceDurations( CChoreoScene *scene, bool checkonly )
{
bool bret = false;
if ( !scene )
return bret;
int c = scene->GetNumEvents();
for ( int i = 0; i < c; i++ )
{
CChoreoEvent *event = scene->GetEvent( i );
if ( !event )
continue;
switch ( event->GetType() )
{
default:
break;
case CChoreoEvent::SPEAK:
{
// Try and load wav to get length
CAudioSource *wave = sound->LoadSound( va( "sound/%s", FacePoser_TranslateSoundName( event ) ) );
if ( wave )
{
float endtime = event->GetStartTime() + wave->GetRunningLength();
if ( event->GetEndTime() != endtime )
{
event->SetEndTime( event->GetStartTime() + wave->GetRunningLength() );
bret = true;
}
delete wave;
}
}
break;
case CChoreoEvent::SEQUENCE:
{
if ( CheckSequenceLength( event, checkonly ) )
{
bret = true;
}
}
break;
case CChoreoEvent::GESTURE:
{
if ( CheckGestureLength( event, checkonly ) )
{
bret = true;
}
if ( AutoaddGestureKeys( event, checkonly ) )
{
bret = true;
}
}
break;
}
}
return bret;
}
//-----------------------------------------------------------------------------
// Purpose: IChoreoEventCallback
// Input : currenttime -
// *event -
//-----------------------------------------------------------------------------
void CChoreoView::StartEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event )
{
if ( !event || !event->GetActive() )
return;
CChoreoActor *actor = event->GetActor();
if ( actor && !actor->GetActive() )
{
return;
}
CChoreoChannel *channel = event->GetChannel();
if ( channel && !channel->GetActive() )
{
return;
}
// Con_Printf( "%8.4f: start %s\n", currenttime, event->GetDescription() );
switch ( event->GetType() )
{
case CChoreoEvent::SEQUENCE:
{
ProcessSequence( scene, event );
}
break;
case CChoreoEvent::SUBSCENE:
{
if ( !scene->IsSubScene() )
{
CChoreoScene *subscene = event->GetSubScene();
if ( !subscene )
{
subscene = LoadScene( event->GetParameters() );
subscene->SetSubScene( true );
event->SetSubScene( subscene );
}
if ( subscene )
{
subscene->ResetSimulation( m_bForward );
}
}
}
break;
case CChoreoEvent::SECTION:
{
ProcessPause( scene, event );
}
break;
case CChoreoEvent::SPEAK:
{
if ( ShouldProcessSpeak() )
{
// See if we should trigger CC
char soundname[ 512 ];
Q_strncpy( soundname, event->GetParameters(), sizeof( soundname ) );
float actualEndTime = event->GetEndTime();
if ( event->GetCloseCaptionType() == CChoreoEvent::CC_MASTER )
{
char tok[ CChoreoEvent::MAX_CCTOKEN_STRING ];
if ( event->GetPlaybackCloseCaptionToken( tok, sizeof( tok ) ) )
{
float duration = max( event->GetDuration(), event->GetLastSlaveEndTime() - event->GetStartTime() );
closecaptionmanager->Process( tok, duration, GetCloseCaptionLanguageId() );
// Use the token as the sound name lookup, too.
if ( event->IsUsingCombinedFile() &&
( event->GetNumSlaves() > 0 ) )
{
Q_strncpy( soundname, tok, sizeof( soundname ) );
actualEndTime = max( actualEndTime, event->GetLastSlaveEndTime() );
}
}
}
StudioModel *model = FindAssociatedModel( scene, event->GetActor() );
CAudioMixer *mixer = event->GetMixer();
if ( !mixer || !sound->IsSoundPlaying( mixer ) )
{
CSoundParameters params;
float volume = VOL_NORM;
gender_t gender = GENDER_NONE;
if (model)
{
gender = soundemitter->GetActorGender( model->GetFileName() );
}
if ( !Q_stristr( soundname, ".wav" ) &&
soundemitter->GetParametersForSound( soundname, params, gender ) )
{
volume = params.volume;
}
sound->PlaySound(
model,
volume,
va( "sound/%s", FacePoser_TranslateSoundName( soundname, model ) ),
&mixer );
event->SetMixer( mixer );
}
if ( mixer )
{
mixer->SetDirection( m_flFrameTime >= 0.0f );
float starttime, endtime;
starttime = event->GetStartTime();
endtime = actualEndTime;
float soundtime = endtime - starttime;
if ( soundtime > 0.0f )
{
float f = ( currenttime - starttime ) / soundtime;
f = clamp( f, 0.0f, 1.0f );
// Compute sample
float numsamples = (float)mixer->GetSource()->SampleCount();
int cursample = f * numsamples;
cursample = clamp( cursample, 0, numsamples - 1 );
mixer->SetSamplePosition( cursample );
mixer->SetActive( true );
}
}
}
}
break;
case CChoreoEvent::EXPRESSION:
{
}
break;
case CChoreoEvent::LOOP:
{
ProcessLoop( scene, event );
}
break;
case CChoreoEvent::STOPPOINT:
{
// Nothing, this is a symbolic event for keeping the vcd alive for ramping out after the last true event
}
break;
default:
break;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : currenttime -
// *event -
//-----------------------------------------------------------------------------
void CChoreoView::EndEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event )
{
if ( !event || !event->GetActive() )
return;
CChoreoActor *actor = event->GetActor();
if ( actor && !actor->GetActive() )
{
return;
}
CChoreoChannel *channel = event->GetChannel();
if ( channel && !channel->GetActive() )
{
return;
}
switch ( event->GetType() )
{
case CChoreoEvent::SUBSCENE:
{
CChoreoScene *subscene = event->GetSubScene();
if ( subscene )
{
subscene->ResetSimulation();
}
}
break;
case CChoreoEvent::SPEAK:
{
CAudioMixer *mixer = event->GetMixer();
if ( mixer && sound->IsSoundPlaying( mixer ) )
{
sound->StopSound( mixer );
}
event->SetMixer( NULL );
}
break;
default:
break;
}
// Con_Printf( "%8.4f: finish %s\n", currenttime, event->GetDescription() );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *event -
// mx -
// my -
//-----------------------------------------------------------------------------
int CChoreoView::GetTagUnderCursorPos( CChoreoEventWidget *event, int mx, int my )
{
if ( !event )
{
return -1;
}
for ( int i = 0; i < event->GetEvent()->GetNumRelativeTags(); i++ )
{
CEventRelativeTag *tag = event->GetEvent()->GetRelativeTag( i );
if ( !tag )
continue;
// Determine left edcge
RECT bounds;
bounds = event->getBounds();
int left = bounds.left + (int)( tag->GetPercentage() * (float)( bounds.right - bounds.left ) + 0.5f );
int tolerance = 3;
if ( abs( mx - left ) < tolerance )
{
if ( abs( my - bounds.top ) < tolerance )
{
return i;
}
}
}
return -1;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *event -
// mx -
// my -
//-----------------------------------------------------------------------------
CEventAbsoluteTag *CChoreoView::GetAbsoluteTagUnderCursorPos( CChoreoEventWidget *event, int mx, int my )
{
if ( !event )
{
return NULL;
}
for ( int i = 0; i < event->GetEvent()->GetNumAbsoluteTags( CChoreoEvent::PLAYBACK ); i++ )
{
CEventAbsoluteTag *tag = event->GetEvent()->GetAbsoluteTag( CChoreoEvent::PLAYBACK, i );
if ( !tag )
continue;
// Determine left edcge
RECT bounds;
bounds = event->getBounds();
int left = bounds.left + (int)( tag->GetPercentage() * (float)( bounds.right - bounds.left ) + 0.5f );
int tolerance = 3;
if ( abs( mx - left ) < tolerance )
{
if ( abs( my - bounds.top ) < tolerance )
{
return tag;
}
}
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : mx -
// my -
// **actor -
// **channel -
// **event -
//-----------------------------------------------------------------------------
void CChoreoView::GetObjectsUnderMouse( int mx, int my, CChoreoActorWidget **actor,
CChoreoChannelWidget **channel, CChoreoEventWidget **event, CChoreoGlobalEventWidget **globalevent,
int *clickedTag,
CEventAbsoluteTag **absolutetag, int *clickedCCArea )
{
if ( actor )
{
*actor = GetActorUnderCursorPos( mx, my );
}
if ( channel )
{
*channel = GetChannelUnderCursorPos( mx, my );
if ( *channel && clickedCCArea )
{
*clickedCCArea = (*channel)->GetChannelItemUnderMouse( mx, my );
}
}
if ( event )
{
*event = GetEventUnderCursorPos( mx, my );
}
if ( globalevent )
{
*globalevent = GetGlobalEventUnderCursorPos( mx, my );
}
if ( clickedTag )
{
if ( event && *event )
{
*clickedTag = GetTagUnderCursorPos( *event, mx, my );
}
else
{
*clickedTag = -1;
}
}
if ( absolutetag )
{
if ( event && *event )
{
*absolutetag = GetAbsoluteTagUnderCursorPos( *event, mx, my );
}
else
{
*absolutetag = NULL;
}
}
m_nSelectedEvents = CountSelectedEvents();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : mx -
// my -
// Output : CChoreoGlobalEventWidget
//-----------------------------------------------------------------------------
CChoreoGlobalEventWidget *CChoreoView::GetGlobalEventUnderCursorPos( int mx, int my )
{
POINT check;
check.x = mx;
check.y = my;
CChoreoGlobalEventWidget *event;
for ( int i = 0; i < m_SceneGlobalEvents.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( &params ) )
return false;
if ( params.m_nSelected < 0 )
return false;
switch ( params.m_nSelected )
{
default:
case 0:
AddEvent( CChoreoEvent::GESTURE, 0, animationname );
break;
case 1:
AddEvent( CChoreoEvent::SEQUENCE, 0, animationname );
break;
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : mx -
// my -
// *cl -
// *exp -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CChoreoView::CreateExpressionEvent( int mx, int my, CExpClass *cl, CExpression *exp )
{
if ( !m_pScene )
return false;
if ( !exp )
return false;
// Convert screen to client
POINT pt;
pt.x = mx;
pt.y = my;
ScreenToClient( (HWND)getHandle(), &pt );
if ( pt.x < 0 || pt.y < 0 )
return false;
if ( pt.x > w2() || pt.y > h2() )
return false;
// Find channel actor and time ( uses screen space coordinates )
//
CChoreoChannelWidget *channel = GetChannelUnderCursorPos( pt.x, pt.y );
if ( !channel )
{
CChoreoActorWidget *actor = GetActorUnderCursorPos( pt.x, pt.y );
if ( !actor )
return false;
// Grab first channel
if ( !actor->GetNumChannels() )
return false;
channel = actor->GetChannel( 0 );
}
if ( !channel )
return false;
CChoreoChannel *pchannel = channel->GetChannel();
if ( !pchannel )
{
Assert( 0 );
return false;
}
CChoreoEvent *event = m_pScene->AllocEvent();
if ( !event )
{
Assert( 0 );
return false;
}
float starttime = GetTimeValueForMouse( pt.x, false );
SetDirty( true );
PushUndo( "Create Expression" );
event->SetType( CChoreoEvent::EXPRESSION );
event->SetName( exp->name );
event->SetParameters( cl->GetName() );
event->SetParameters2( exp->name );
event->SetStartTime( starttime );
event->SetChannel( pchannel );
event->SetActor( pchannel->GetActor() );
event->SetEndTime( starttime + 1.0f );
event->SnapTimes();
DeleteSceneWidgets();
// Add to appropriate channel
pchannel->AddEvent( event );
CreateSceneWidgets();
PushRedo( "Create Expression" );
// Redraw
InvalidateLayout();
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CChoreoView::IsPlayingScene( void )
{
return m_bSimulating;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CChoreoView::ResetTargetSettings( void )
{
for ( int i = 0; i < m_SceneActors.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( &params, 0, sizeof( params ) );
strcpy( params.m_szDialogTitle, "Event Tag Name" );
strcpy( params.m_szPrompt, "Name:" );
strcpy( params.m_szInputText, "" );
if ( !InputProperties( &params ) )
return;
if ( strlen( params.m_szInputText ) <= 0 )
{
Con_ErrorPrintf( "Event Tag Name: No name entered!\n" );
return;
}
RECT bounds = ew->getBounds();
// Convert click to frac
float frac = 0.0f;
if ( bounds.right - bounds.left > 0 )
{
frac = (float)( m_nClickedX - bounds.left ) / (float)( bounds.right - bounds.left );
frac = min( 1.0f, frac );
frac = max( 0.0f, frac );
}
SetDirty( true );
PushUndo( "Add Event Tag" );
event->AddRelativeTag( params.m_szInputText, frac );
PushRedo( "Add Event Tag" );
InvalidateLayout();
g_pPhonemeEditor->redraw();
g_pExpressionTool->redraw();
g_pGestureTool->redraw();
g_pRampTool->redraw();
g_pSceneRampTool->redraw();
}
CChoreoChannelWidget *CChoreoView::FindChannelForEvent( CChoreoEvent *event )
{
for ( int i = 0; i < m_SceneActors.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( &params, 0, sizeof( params ) );
strcpy( params.m_szDialogTitle, "Change Zoom" );
strcpy( params.m_szPrompt, "New scale (e.g., 2.5x):" );
Q_snprintf( params.m_szInputText, sizeof( params.m_szInputText ), "%.2f", (float)GetTimeZoom( GetToolName() ) / 100.0f );
if ( !InputProperties( &params ) )
return;
SetTimeZoom( GetToolName(), clamp( (int)( 100.0f * atof( params.m_szInputText ) ), 1, MAX_TIME_ZOOM ), false );
// Force scroll bars to recompute
ForceScrollBarsToRecompute( false );
CUtlVector< CChoreoEvent * > selected;
RememberSelectedEvents( selected );
DeleteSceneWidgets();
CreateSceneWidgets();
ReselectEvents( selected );
InvalidateLayout();
Con_Printf( "Zoom factor %i %%\n", GetTimeZoom( GetToolName() ) );
}
void CChoreoView::OnCheckSequenceLengths( void )
{
if ( !m_pScene )
return;
Con_Printf( "Checking sequence durations...\n" );
bool changed = FixupSequenceDurations( m_pScene, true );
if ( !changed )
{
Con_Printf( " no changes...\n" );
return;
}
SetDirty( true );
PushUndo( "Check sequence lengths" );
FixupSequenceDurations( m_pScene, false );
PushRedo( "Check sequence lengths" );
InvalidateLayout();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *scene -
//-----------------------------------------------------------------------------
void CChoreoView::InvalidateTrackLookup_R( CChoreoScene *scene )
{
// No need to undo since this data doesn't matter
int c = scene->GetNumEvents();
for ( int i = 0; i < c; i++ )
{
CChoreoEvent *event = scene->GetEvent( i );
if ( !event )
continue;
switch ( event->GetType() )
{
default:
break;
case CChoreoEvent::FLEXANIMATION:
{
event->SetTrackLookupSet( false );
}
break;
case CChoreoEvent::SUBSCENE:
{
CChoreoScene *sub = event->GetSubScene();
// NOTE: Don't bother loading it now if it's not on hand
if ( sub )
{
InvalidateTrackLookup_R( sub );
}
}
break;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Model changed so we'll have to re-index flex anim tracks
//-----------------------------------------------------------------------------
void CChoreoView::InvalidateTrackLookup( void )
{
if ( !m_pScene )
return;
InvalidateTrackLookup_R( m_pScene );
}
bool CChoreoView::IsRampOnly( void ) const
{
return m_bRampOnly;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *scene -
// *event -
//-----------------------------------------------------------------------------
void CChoreoView::ProcessInterrupt( CChoreoScene *scene, CChoreoEvent *event )
{
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *scene -
// *event -
//-----------------------------------------------------------------------------
void CChoreoView::ProcessPermitResponses( CChoreoScene *scene, CChoreoEvent *event )
{
}
void CChoreoView::ApplyBounds( int& mx, int& my )
{
if ( !m_bUseBounds )
return;
mx = clamp( mx, m_nMinX, m_nMaxX );
}
//-----------------------------------------------------------------------------
// Purpose: Returns -1 if no event found
// Input : *channel -
// *e -
// forward -
// Output : float
//-----------------------------------------------------------------------------
float CChoreoView::FindNextEventTime( CChoreoEvent::EVENTTYPE type, CChoreoChannel *channel, CChoreoEvent *e, bool forward )
{
bool foundone = false;
float bestTime = -1.0f;
float bestGap = 999999.0f;
int c = channel->GetNumEvents();
for ( int i = 0; i < c; i++ )
{
CChoreoEvent *test = channel->GetEvent( i );
if ( test->GetType() != type )
continue;
if ( forward )
{
float dt = test->GetStartTime() - e->GetEndTime();
if ( dt <= 0.0f )
continue;
if ( dt < bestGap )
{
foundone = true;
bestGap = dt;
bestTime = test->GetStartTime();
}
}
else
{
float dt = e->GetStartTime() - test->GetEndTime();
if ( dt <= 0.0f )
continue;
if ( dt < bestGap )
{
foundone = true;
bestGap = dt;
bestTime = test->GetEndTime();
}
}
}
return bestTime;
}
void CChoreoView::CalcBounds( int movetype )
{
m_bUseBounds = false;
m_nMinX = 0;
m_nMaxX = 0;
if ( !m_pClickedEvent )
return;
switch ( movetype )
{
default:
break;
case DRAGTYPE_EVENT_MOVE:
case DRAGTYPE_EVENT_STARTTIME:
case DRAGTYPE_EVENT_ENDTIME:
case DRAGTYPE_EVENT_STARTTIME_RESCALE:
case DRAGTYPE_EVENT_ENDTIME_RESCALE:
{
m_nMinX = GetPixelForTimeValue( 0 );
m_nMaxX = GetPixelForTimeValue( m_pScene->FindStopTime() );
CChoreoEvent *e = m_pClickedEvent->GetEvent();
m_bUseBounds = false; // e && e->GetType() == CChoreoEvent::GESTURE;
// FIXME: use this for finding adjacent gesture edges (kenb)
if ( m_bUseBounds )
{
CChoreoChannel *channel = e->GetChannel();
Assert( channel );
float forwardTime = FindNextEventTime( e->GetType(), channel, e, true );
float reverseTime = FindNextEventTime( e->GetType(), channel, e, false );
// Compute pixel for time
int nextPixel = forwardTime != -1 ? GetPixelForTimeValue( forwardTime ) : m_nMaxX;
int prevPixel = reverseTime != -1 ? GetPixelForTimeValue( reverseTime ) : m_nMinX;
int startPixel = GetPixelForTimeValue( e->GetStartTime() );
int endPixel = GetPixelForTimeValue( e->GetEndTime() );
switch ( movetype )
{
case DRAGTYPE_EVENT_MOVE:
{
m_nMinX = prevPixel + ( m_xStart - startPixel ) + 1;
m_nMaxX = nextPixel - ( endPixel - m_xStart ) - 1;
}
break;
case DRAGTYPE_EVENT_STARTTIME:
case DRAGTYPE_EVENT_STARTTIME_RESCALE:
{
m_nMinX = prevPixel + ( m_xStart - startPixel ) + 1;
}
break;
case DRAGTYPE_EVENT_ENDTIME:
case DRAGTYPE_EVENT_ENDTIME_RESCALE:
{
m_nMaxX = nextPixel - ( endPixel - m_xStart ) - 1;
}
break;
}
}
}
break;
}
}
bool CChoreoView::ShouldSelectEvent( SelectionParams_t &params, CChoreoEvent *event )
{
if ( params.forward )
{
if ( event->GetStartTime() >= params.time )
return true;
}
else
{
float endtime = event->HasEndTime() ? event->GetEndTime() : event->GetStartTime();
if ( endtime <= params.time )
return true;
}
return false;
}
void CChoreoView::SelectEvents( SelectionParams_t& params )
{
if ( !m_pScene )
return;
if ( !m_pClickedActor )
return;
if ( params.type == SelectionParams_t::SP_CHANNEL && !m_pClickedChannel )
return;
//CChoreoActor *actor = m_pClickedActor->GetActor();
CChoreoChannel *channel = m_pClickedChannel ? m_pClickedChannel->GetChannel() : NULL;
for ( int i = 0; i < m_SceneActors.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( &params ) &&
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( &params ) &&
Q_stricmp( e->GetCloseCaptionToken(), params.m_szCCToken ) )
{
char oldToken[ CChoreoEvent::MAX_CCTOKEN_STRING ];
Q_strncpy( oldToken, e->GetCloseCaptionToken(), sizeof( oldToken ) );
CUtlVector< CChoreoEvent * > events;
m_pClickedChannel->GetMasterAndSlaves( e, events );
if ( events.Count() < 2 )
{
Con_ErrorPrintf( "Can't combine events, must have at least two events selected.\n" );
}
SetDirty( true );
PushUndo( "Change closecaption token" );
// Make the change...
int c = events.Count();
for ( int i = 0 ; i < c; ++i )
{
events[i]->SetCloseCaptionToken( params.m_szCCToken );
}
PushRedo( "Change closecaption token" );
InvalidateLayout();
Con_Printf( "Close Caption token for '%s' changed to '%s'\n", e->GetName(), params.m_szCCToken );
}
}
void CChoreoView::OnToggleCloseCaptionsForEvent()
{
if ( !m_pClickedChannel )
{
return;
}
CChoreoEvent *e = m_pClickedChannel->GetCaptionClickedEvent();
if ( !e )
{
return;
}
CChoreoEvent::CLOSECAPTION newType = CChoreoEvent::CC_MASTER;
// Can't mess with slave
switch ( e->GetCloseCaptionType() )
{
default:
case CChoreoEvent::CC_SLAVE:
return;
case CChoreoEvent::CC_MASTER:
newType = CChoreoEvent::CC_DISABLED;
break;
case CChoreoEvent::CC_DISABLED:
newType = CChoreoEvent::CC_MASTER;
break;
}
SetDirty( true );
PushUndo( "Enable/disable captions" );
// Make the change...
e->SetCloseCaptionType( newType );
PushRedo( "Enable/disable captions" );
InvalidateLayout();
Con_Printf( "Close Caption type for '%s' changed to '%s'\n", e->GetName(), CChoreoEvent::NameForCCType( newType ) );
}
void CChoreoView::StopScene()
{
SetScrubTargetTime( m_flScrub );
FinishSimulation();
sound->Flush();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : -
//-----------------------------------------------------------------------------
template <class T>
void DeleteAllAndPurge( T &tree )
{
T::IndexType_t i;
for ( i = tree.FirstInorder(); i != T::InvalidIndex(); i = tree.NextInorder( i ) )
{
delete tree[i];
}
tree.Purge();
}
void CChoreoView::OnPlaceNextSpeakEvent()
{
CUtlVector< CChoreoEvent * > list;
GetSelectedEvents( list );
if ( list.Count() != 1 )
{
Warning( "Can't place sound event, nothing selected\n" );
return;
}
CChoreoEvent *ev = list[ 0 ];
if ( ev->GetType() != CChoreoEvent::SPEAK )
{
Warning( "Can't place sound event, no previous sound event selected\n" );
return;
}
CChoreoChannelWidget *widget = FindChannelForEvent( ev );
if ( !widget )
{
Warning( "Can't place sound event, can't find channel widget for event\n" );
return;
}
CChoreoChannel *channel = widget->GetChannel();
if ( !channel )
{
Warning( "Can't place sound event, can't find channel for new event\n" );
return;
}
CUtlRBTree< char const *, int > m_SortedNames( 0, 0, NameLessFunc );
int c = soundemitter->GetSoundCount();
for ( int i = 0; i < c; i++ )
{
char const *name = soundemitter->GetSoundName( i );
if ( name && name[ 0 ] )
{
m_SortedNames.Insert( strdup( name ) );
}
}
int idx = m_SortedNames.Find( ev->GetParameters() );
if ( idx == m_SortedNames.InvalidIndex() )
{
Warning( "Can't place sound event, can't find '%s' in sound list\n", ev->GetParameters() );
DeleteAllAndPurge( m_SortedNames );
return;
}
int nextIdx = m_SortedNames.NextInorder( idx );
if ( nextIdx == m_SortedNames.InvalidIndex() )
{
Warning( "Can't place sound event, can't next sound after '%s' in sound list\n", ev->GetParameters() );
DeleteAllAndPurge( m_SortedNames );
return;
}
DeselectAll();
SetDirty( true );
PushUndo( "Place Next Speak Event" );
CChoreoEvent *event = m_pScene->AllocEvent();
Assert( event );
if ( event )
{
// Copy everything for source event
*event = *ev;
event->SetParameters( m_SortedNames[ nextIdx ] );
// Start it at the end time...
event->SetStartTime( event->GetEndTime() );
event->SetResumeCondition( false );
event->ClearAllRelativeTags();
event->ClearAllTimingTags();
event->ClearAllAbsoluteTags( CChoreoEvent::PLAYBACK );
event->ClearAllAbsoluteTags( CChoreoEvent::ORIGINAL );
event->SetChannel( channel );
event->SetActor( channel->GetActor() );
// Try and load wav to get length
CAudioSource *wave = sound->LoadSound( va( "sound/%s", FacePoser_TranslateSoundName( event ) ) );
if ( wave )
{
event->SetEndTime( event->GetStartTime() + wave->GetRunningLength() );
delete wave;
}
DeleteSceneWidgets();
// Add to appropriate channel
channel->AddEvent( event );
CreateSceneWidgets();
CChoreoEventWidget *eventWidget = FindWidgetForEvent( event );
if ( eventWidget )
{
eventWidget->SetSelected( true );
}
// Redraw
InvalidateLayout();
}
PushRedo( "Place Next Speak Event" );
DeleteAllAndPurge( m_SortedNames );
}
enum
{
FM_LEFT = 0,
FM_RIGHT,
FM_SMALLESTWIDE,
FM_LARGESTWIDE
};
static int FindMetric( int type, CUtlVector< CChoreoEvent * > &list, float& value )
{
float bestVal = 999999.0f;
int bestIndex = -1;
bool greater = true;
switch ( type )
{
default:
case FM_LEFT:
case FM_SMALLESTWIDE:
greater = false;
break;
case FM_RIGHT:
case FM_LARGESTWIDE:
bestVal = -bestVal;
greater = true;
break;
}
int c = list.Count();
for ( int i = 0; i < c; ++i )
{
CChoreoEvent *e = list[ i ];
if ( type != FM_LEFT &&
!e->HasEndTime() )
continue;
float val;
switch ( type )
{
default:
case FM_LEFT:
val = e->GetStartTime();
break;
case FM_RIGHT:
val = e->GetEndTime();
break;
case FM_SMALLESTWIDE:
case FM_LARGESTWIDE:
val = e->GetDuration();
break;
}
if ( greater )
{
if ( val <= bestVal )
continue;
}
else
{
if ( val >= bestVal )
continue;
}
bestVal = val;
bestIndex = i;
}
value = bestVal;
return bestIndex;
}
void CChoreoView::OnAlign( bool left )
{
CUtlVector< CChoreoEvent * > list;
GetSelectedEvents( list );
if ( left )
{
for ( int i = 0; i < m_SceneGlobalEvents.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();
}