You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
11647 lines
262 KiB
11647 lines
262 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//===========================================================================// |
|
#include "cbase.h" |
|
#include <stdio.h> |
|
#include <mxtk/mxWindow.h> |
|
#include "mdlviewer.h" |
|
#include "hlfaceposer.h" |
|
#include "StudioModel.h" |
|
#include "expressions.h" |
|
#include "expclass.h" |
|
#include "ChoreoView.h" |
|
#include "choreoevent.h" |
|
#include "choreoactor.h" |
|
#include "choreochannel.h" |
|
#include "choreoscene.h" |
|
#include "choreowidget.h" |
|
#include "choreoactorwidget.h" |
|
#include "choreochannelwidget.h" |
|
#include "choreoglobaleventwidget.h" |
|
#include "choreowidgetdrawhelper.h" |
|
#include "choreoeventwidget.h" |
|
#include "viewerSettings.h" |
|
#include "filesystem.h" |
|
#include "choreoviewcolors.h" |
|
#include "ActorProperties.h" |
|
#include "ChannelProperties.h" |
|
#include "EventProperties.h" |
|
#include "GlobalEventProperties.h" |
|
#include "ifaceposersound.h" |
|
#include "snd_wave_source.h" |
|
#include "ifaceposerworkspace.h" |
|
#include "PhonemeEditor.h" |
|
#include "iscenetokenprocessor.h" |
|
#include "InputProperties.h" |
|
#include "filesystem.h" |
|
#include "ExpressionTool.h" |
|
#include "ControlPanel.h" |
|
#include "faceposer_models.h" |
|
#include "choiceproperties.h" |
|
#include "MatSysWin.h" |
|
#include "tier1/strtools.h" |
|
#include "GestureTool.h" |
|
#include "npcevent.h" |
|
#include "RampTool.h" |
|
#include "SceneRampTool.h" |
|
#include "KeyValues.h" |
|
#include "SoundEmitterSystem/isoundemittersystembase.h" |
|
#include "cclookup.h" |
|
#include "iclosecaptionmanager.h" |
|
#include "AddSoundEntry.h" |
|
#include "isoundcombiner.h" |
|
#include <vgui/ILocalize.h> |
|
#include "scriplib.h" |
|
#include "WaveBrowser.h" |
|
#include "filesystem_init.h" |
|
#include "flexpanel.h" |
|
#include "tier3/choreoutils.h" |
|
#include "tier2/p4helpers.h" |
|
|
|
|
|
using namespace vgui; |
|
|
|
extern vgui::ILocalize *g_pLocalize; |
|
|
|
// 10x magnification |
|
#define MAX_TIME_ZOOM 1000 |
|
#define TIME_ZOOM_STEP 4 |
|
|
|
#define PHONEME_FILTER 0.08f |
|
#define PHONEME_DELAY 0.0f |
|
|
|
#define SCRUBBER_HEIGHT 15 |
|
#define TIMELINE_NUMBERS_HEIGHT 11 |
|
|
|
#define COPYPASTE_FILENAME "scenes/copydatavcd.txt" |
|
|
|
extern double realtime; |
|
extern bool NameLessFunc( const char *const& name1, const char *const& name2 ); |
|
|
|
// Try to keep shifted times at same absolute time |
|
static void RescaleExpressionTimes( CChoreoEvent *event, float newstart, float newend ) |
|
{ |
|
if ( !event || event->GetType() != CChoreoEvent::FLEXANIMATION ) |
|
return; |
|
|
|
// Did it actually change |
|
if ( newstart == event->GetStartTime() && |
|
newend == event->GetEndTime() ) |
|
{ |
|
return; |
|
} |
|
|
|
float newduration = newend - newstart; |
|
|
|
float dt = 0.0f; |
|
//If the end is moving, leave tags stay where they are (dt == 0.0f) |
|
if ( newstart != event->GetStartTime() ) |
|
{ |
|
// Otherwise, if the new start is later, then tags need to be shifted backwards |
|
dt -= ( newstart - event->GetStartTime() ); |
|
} |
|
|
|
int count = event->GetNumFlexAnimationTracks(); |
|
int i; |
|
|
|
for ( i = 0; i < count; i++ ) |
|
{ |
|
CFlexAnimationTrack *track = event->GetFlexAnimationTrack( i ); |
|
if ( !track ) |
|
continue; |
|
|
|
for ( int type = 0; type < 2; type++ ) |
|
{ |
|
int sampleCount = track->GetNumSamples( type ); |
|
for ( int sample = sampleCount - 1; sample >= 0 ; sample-- ) |
|
{ |
|
CExpressionSample *s = track->GetSample( sample, type ); |
|
if ( !s ) |
|
continue; |
|
|
|
s->time += dt; |
|
|
|
if ( s->time > newduration || s->time < 0.0f ) |
|
{ |
|
track->RemoveSample( sample, type ); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
static void RescaleRamp( CChoreoEvent *event, float newduration ) |
|
{ |
|
float oldduration = event->GetDuration(); |
|
|
|
if ( fabs( oldduration - newduration ) < 0.000001f ) |
|
return; |
|
|
|
if ( newduration <= 0.0f ) |
|
return; |
|
|
|
float midpointtime = oldduration * 0.5f; |
|
float newmidpointtime = newduration * 0.5f; |
|
|
|
int count = event->GetRampCount(); |
|
int i; |
|
|
|
for ( i = 0; i < count; i++ ) |
|
{ |
|
CExpressionSample *sample = event->GetRamp( i ); |
|
if ( !sample ) |
|
continue; |
|
|
|
float t = sample->time; |
|
if ( t < midpointtime ) |
|
continue; |
|
|
|
float timefromend = oldduration - t; |
|
|
|
// There's room to just shift it |
|
if ( timefromend <= newmidpointtime ) |
|
{ |
|
t = newduration - timefromend; |
|
} |
|
else |
|
{ |
|
// No room, rescale them instead |
|
float frac = ( t - midpointtime ) / midpointtime; |
|
t = newmidpointtime + frac * newmidpointtime; |
|
} |
|
|
|
sample->time = t; |
|
} |
|
} |
|
|
|
bool DoesAnyActorHaveAssociatedModelLoaded( CChoreoScene *scene ) |
|
{ |
|
if ( !scene ) |
|
return false; |
|
|
|
int c = scene->GetNumActors(); |
|
int i; |
|
for ( i = 0; i < c; i++ ) |
|
{ |
|
CChoreoActor *a = scene->GetActor( i ); |
|
if ( !a ) |
|
continue; |
|
|
|
char const *modelname = a->GetFacePoserModelName(); |
|
if ( !modelname ) |
|
continue; |
|
|
|
if ( !modelname[ 0 ] ) |
|
continue; |
|
|
|
char mdlname[ 256 ]; |
|
Q_strncpy( mdlname, modelname, sizeof( mdlname ) ); |
|
Q_FixSlashes( mdlname ); |
|
|
|
int idx = models->FindModelByFilename( mdlname ); |
|
if ( idx >= 0 ) |
|
{ |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *a - |
|
// Output : StudioModel |
|
//----------------------------------------------------------------------------- |
|
StudioModel *FindAssociatedModel( CChoreoScene *scene, CChoreoActor *a ) |
|
{ |
|
if ( !a || !scene ) |
|
return NULL; |
|
|
|
Assert( models->GetActiveStudioModel() ); |
|
|
|
StudioModel *model = NULL; |
|
if ( a->GetFacePoserModelName()[ 0 ] ) |
|
{ |
|
int idx = models->FindModelByFilename( a->GetFacePoserModelName() ); |
|
if ( idx >= 0 ) |
|
{ |
|
model = models->GetStudioModel( idx ); |
|
return model; |
|
} |
|
} |
|
|
|
// Is there any loaded model with the actorname in it? |
|
int c = models->Count(); |
|
for ( int i = 0; i < c; i++ ) |
|
{ |
|
char const *modelname = models->GetModelName( i ); |
|
if ( !Q_stricmp( modelname, a->GetName() ) ) |
|
{ |
|
return models->GetStudioModel( i ); |
|
} |
|
} |
|
|
|
// Does any actor have an associated model which is loaded |
|
if ( DoesAnyActorHaveAssociatedModelLoaded( scene ) ) |
|
{ |
|
// Then return NULL here so we don't override with the default an actor who has a valid model going |
|
return NULL; |
|
} |
|
|
|
// Couldn't find it and nobody else has a loaded associated model, so just use the default model |
|
if ( !model ) |
|
{ |
|
model = models->GetActiveStudioModel(); |
|
} |
|
return model; |
|
} |
|
|
|
|
|
CChoreoView *g_pChoreoView = 0; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *parent - |
|
// x - |
|
// y - |
|
// w - |
|
// h - |
|
// id - |
|
//----------------------------------------------------------------------------- |
|
CChoreoView::CChoreoView( mxWindow *parent, int x, int y, int w, int h, int id ) |
|
: IFacePoserToolWindow( "CChoreoView", "Choreography" ), mxWindow( parent, x, y, w, h ) |
|
{ |
|
m_bRampOnly = false; |
|
|
|
m_bForceProcess = false; |
|
|
|
m_bSuppressLayout = true; |
|
|
|
SetAutoProcess( true ); |
|
|
|
m_flLastMouseClickTime = -1.0f; |
|
m_bProcessSequences = true; |
|
|
|
m_flPlaybackRate = 1.0f; |
|
|
|
m_pScene = NULL; |
|
|
|
m_flScrub = 0.0f; |
|
m_flScrubTarget = 0.0f; |
|
|
|
m_bCanDraw = false; |
|
|
|
m_bRedoPending = false; |
|
m_nUndoLevel = 0; |
|
|
|
CChoreoEventWidget::LoadImages(); |
|
|
|
CChoreoWidget::m_pView = this; |
|
|
|
setId( id ); |
|
|
|
m_flLastSpeedScale = 0.0f; |
|
m_bResetSpeedScale = false; |
|
|
|
m_nTopOffset = 0; |
|
m_flLeftOffset = 0.0f; |
|
m_nLastHPixelsNeeded = -1; |
|
m_nLastVPixelsNeeded = -1; |
|
|
|
|
|
m_nStartRow = 45; |
|
m_nLabelWidth = 140; |
|
m_nRowHeight = 35; |
|
|
|
m_bSimulating = false; |
|
m_bPaused = false; |
|
|
|
m_bForward = true; |
|
|
|
m_flStartTime = 0.0f; |
|
m_flEndTime = 0.0f; |
|
m_flFrameTime = 0.0f; |
|
|
|
m_bAutomated = false; |
|
m_nAutomatedAction = SCENE_ACTION_UNKNOWN; |
|
m_flAutomationDelay = 0.0f; |
|
m_flAutomationTime = 0.0f; |
|
|
|
m_pVertScrollBar = new mxScrollbar( this, 0, 0, 18, 100, IDC_CHOREOVSCROLL, mxScrollbar::Vertical ); |
|
m_pHorzScrollBar = new mxScrollbar( this, 0, 0, 18, 100, IDC_CHOREOHSCROLL, mxScrollbar::Horizontal ); |
|
|
|
m_bLayoutIsValid = false; |
|
m_flPixelsPerSecond = 150.0f; |
|
|
|
m_btnPlay = new mxBitmapButton( this, 2, 4, 16, 16, IDC_PLAYSCENE, "gfx/hlfaceposer/play.bmp" ); |
|
m_btnPause = new mxBitmapButton( this, 18, 4, 16, 16, IDC_PAUSESCENE, "gfx/hlfaceposer/pause.bmp" ); |
|
m_btnStop = new mxBitmapButton( this, 34, 4, 16, 16, IDC_STOPSCENE, "gfx/hlfaceposer/stop.bmp" ); |
|
|
|
m_pPlaybackRate = new mxSlider( this, 0, 0, 16, 16, IDC_CHOREO_PLAYBACKRATE ); |
|
m_pPlaybackRate->setRange( 0.0, 2.0, 40 ); |
|
m_pPlaybackRate->setValue( m_flPlaybackRate ); |
|
|
|
ShowButtons( false ); |
|
|
|
m_nFontSize = 12; |
|
|
|
for ( int i = 0; i < MAX_ACTORS; i++ ) |
|
{ |
|
m_ActorExpanded[ i ].expanded = true; |
|
} |
|
|
|
SetChoreoFile( "" ); |
|
|
|
//SetFocus( (HWND)getHandle() ); |
|
if ( workspacefiles->GetNumStoredFiles( IWorkspaceFiles::CHOREODATA ) >= 1 ) |
|
{ |
|
LoadSceneFromFile( workspacefiles->GetStoredFile( IWorkspaceFiles::CHOREODATA, 0 ) ); |
|
} |
|
|
|
ClearABPoints(); |
|
|
|
m_pClickedActor = NULL; |
|
m_pClickedChannel = NULL; |
|
m_pClickedEvent = NULL; |
|
m_pClickedGlobalEvent = NULL; |
|
m_nClickedX = 0; |
|
m_nClickedY = 0; |
|
m_nSelectedEvents = 0; |
|
m_nClickedTag = -1; |
|
m_nClickedChannelCloseCaptionButton = CChoreoChannelWidget::CLOSECAPTION_NONE; |
|
|
|
// Mouse dragging |
|
m_bDragging = false; |
|
m_xStart = 0; |
|
m_yStart = 0; |
|
m_nDragType = DRAGTYPE_NONE; |
|
m_hPrevCursor = 0; |
|
|
|
m_nMinX = 0; |
|
m_nMaxX = 0; |
|
m_bUseBounds = false; |
|
|
|
m_nScrollbarHeight = 12; |
|
m_nInfoHeight = 30; |
|
|
|
ClearStatusArea(); |
|
|
|
SetDirty( false ); |
|
|
|
m_bCanDraw = true; |
|
m_bSuppressLayout = false; |
|
m_flScrubberTimeOffset = 0.0f; |
|
|
|
m_bShowCloseCaptionData = true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : closing - |
|
//----------------------------------------------------------------------------- |
|
bool CChoreoView::Close( void ) |
|
{ |
|
if ( m_pScene && m_bDirty ) |
|
{ |
|
int retval = mxMessageBox( NULL, va( "Save changes to scene '%s'?", GetChoreoFile() ), g_appTitle, MX_MB_YESNOCANCEL ); |
|
if ( retval == 2 ) |
|
{ |
|
return false; |
|
} |
|
if ( retval == 0 ) |
|
{ |
|
Save(); |
|
} |
|
} |
|
|
|
if ( m_pScene ) |
|
{ |
|
UnloadScene(); |
|
} |
|
return true; |
|
} |
|
|
|
bool CChoreoView::CanClose() |
|
{ |
|
if ( m_pScene ) |
|
{ |
|
workspacefiles->StartStoringFiles( IWorkspaceFiles::CHOREODATA ); |
|
workspacefiles->StoreFile( IWorkspaceFiles::CHOREODATA, GetChoreoFile() ); |
|
workspacefiles->FinishStoringFiles( IWorkspaceFiles::CHOREODATA ); |
|
} |
|
|
|
if ( m_pScene && m_bDirty && !Close() ) |
|
{ |
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Called just before window is destroyed |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::OnDelete() |
|
{ |
|
if ( m_pScene ) |
|
{ |
|
UnloadScene(); |
|
} |
|
|
|
CChoreoWidget::m_pView = NULL; |
|
|
|
CChoreoEventWidget::DestroyImages(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CChoreoView::~CChoreoView() |
|
{ |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::ReportSceneClearToTools( void ) |
|
{ |
|
if ( m_pScene ) |
|
{ |
|
m_pScene->ResetSimulation(); |
|
} |
|
|
|
g_pPhonemeEditor->ClearEvent(); |
|
g_pExpressionTool->LayoutItems( true ); |
|
g_pExpressionTool->redraw(); |
|
g_pGestureTool->redraw(); |
|
g_pRampTool->redraw(); |
|
g_pSceneRampTool->redraw(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Find a time that's less than input on the granularity: |
|
// e.g., 3.01 granularity 0.05 will be 3.00, 3.05 will be 3.05 |
|
// Input : input - |
|
// granularity - |
|
// Output : float |
|
//----------------------------------------------------------------------------- |
|
float SnapTime( float input, float granularity ) |
|
{ |
|
float base = (float)(int)input; |
|
float multiplier = (float)(int)( 1.0f / granularity ); |
|
float fracpart = input - (int)input; |
|
|
|
fracpart *= multiplier; |
|
|
|
fracpart = (float)(int)fracpart; |
|
fracpart *= granularity; |
|
|
|
return base + fracpart; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : drawHelper - |
|
// rc - |
|
// left - |
|
// right - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::DrawTimeLine( CChoreoWidgetDrawHelper& drawHelper, RECT& rc, float left, float right ) |
|
{ |
|
RECT rcFill = m_rcTimeLine; |
|
rcFill.bottom -= TIMELINE_NUMBERS_HEIGHT; |
|
drawHelper.DrawFilledRect( COLOR_CHOREO_DARKBACKGROUND, rcFill ); |
|
|
|
RECT rcLabel; |
|
float granularity = 0.5f / ((float)GetTimeZoom( GetToolName() ) / 100.0f); |
|
|
|
drawHelper.DrawColoredLine( COLOR_CHOREO_TIMELINE, PS_SOLID, 1, rc.left, GetStartRow() - 1, rc.right, GetStartRow() - 1 ); |
|
|
|
float f = SnapTime( left, granularity ); |
|
while ( f < right ) |
|
{ |
|
float frac = ( f - left ) / ( right - left ); |
|
if ( frac >= 0.0f && frac <= 1.0f ) |
|
{ |
|
rcLabel.left = GetLabelWidth() + (int)( frac * ( rc.right - GetLabelWidth() ) ); |
|
rcLabel.bottom = GetStartRow() - 1; |
|
rcLabel.top = rcLabel.bottom - 10; |
|
|
|
if ( f != left ) |
|
{ |
|
drawHelper.DrawColoredLine( RGB( 220, 220, 240 ), PS_DOT, 1, |
|
rcLabel.left, GetStartRow(), rcLabel.left, h2() ); |
|
} |
|
|
|
char sz[ 32 ]; |
|
sprintf( sz, "%.2f", f ); |
|
|
|
int textWidth = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, sz ); |
|
|
|
rcLabel.right = rcLabel.left + textWidth; |
|
|
|
OffsetRect( &rcLabel, -textWidth / 2, 0 ); |
|
|
|
drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, COLOR_CHOREO_TEXT, rcLabel, sz ); |
|
|
|
} |
|
f += granularity; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CChoreoView::PaintBackground( void ) |
|
{ |
|
redraw(); |
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : drawHelper - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::DrawSceneABTicks( CChoreoWidgetDrawHelper& drawHelper ) |
|
{ |
|
RECT rcThumb; |
|
|
|
float scenestart = m_rgABPoints[ 0 ].active ? m_rgABPoints[ 0 ].time : 0.0f; |
|
float sceneend = m_rgABPoints[ 1 ].active ? m_rgABPoints[ 1 ].time : 0.0f; |
|
|
|
if ( scenestart ) |
|
{ |
|
int markerstart = GetPixelForTimeValue( scenestart ); |
|
|
|
rcThumb.left = markerstart - 4; |
|
rcThumb.right = markerstart + 4; |
|
rcThumb.top = 2 + GetCaptionHeight() + SCRUBBER_HEIGHT; |
|
rcThumb.bottom = rcThumb.top + 8; |
|
|
|
drawHelper.DrawTriangleMarker( rcThumb, COLOR_CHOREO_TICKAB ); |
|
} |
|
|
|
if ( sceneend ) |
|
{ |
|
int markerend = GetPixelForTimeValue( sceneend ); |
|
|
|
rcThumb.left = markerend - 4; |
|
rcThumb.right = markerend + 4; |
|
rcThumb.top = 2 + GetCaptionHeight() + SCRUBBER_HEIGHT; |
|
rcThumb.bottom = rcThumb.top + 8; |
|
|
|
drawHelper.DrawTriangleMarker( rcThumb, COLOR_CHOREO_TICKAB ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : drawHelper - |
|
// rc - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::DrawRelativeTagLines( CChoreoWidgetDrawHelper& drawHelper, RECT& rc ) |
|
{ |
|
if ( !m_pScene ) |
|
return; |
|
|
|
RECT rcClip; |
|
GetClientRect( (HWND)getHandle(), &rcClip ); |
|
rcClip.top = GetStartRow(); |
|
rcClip.bottom -= ( m_nInfoHeight + m_nScrollbarHeight ); |
|
rcClip.right -= m_nScrollbarHeight; |
|
|
|
drawHelper.StartClipping( rcClip ); |
|
|
|
for ( int i = 0; i < m_SceneActors.Size(); i++ ) |
|
{ |
|
CChoreoActorWidget *a = m_SceneActors[ i ]; |
|
if ( !a ) |
|
continue; |
|
|
|
for ( int j = 0; j < a->GetNumChannels(); j++ ) |
|
{ |
|
CChoreoChannelWidget *c = a->GetChannel( j ); |
|
if ( !c ) |
|
continue; |
|
|
|
for ( int k = 0; k < c->GetNumEvents(); k++ ) |
|
{ |
|
CChoreoEventWidget *e = c->GetEvent( k ); |
|
if ( !e ) |
|
continue; |
|
|
|
CChoreoEvent *event = e->GetEvent(); |
|
if ( !event ) |
|
continue; |
|
|
|
if ( !event->IsUsingRelativeTag() ) |
|
continue; |
|
|
|
// Using it, find the tag and figure out the time for it |
|
CEventRelativeTag *tag = m_pScene->FindTagByName( |
|
event->GetRelativeWavName(), |
|
event->GetRelativeTagName() ); |
|
|
|
if ( !tag ) |
|
continue; |
|
|
|
// Found it, draw a vertical line |
|
// |
|
float tagtime = tag->GetStartTime(); |
|
|
|
// Convert to pixel value |
|
bool clipped = false; |
|
int pixel = GetPixelForTimeValue( tagtime, &clipped ); |
|
if ( clipped ) |
|
continue; |
|
|
|
drawHelper.DrawColoredLine( RGB( 180, 180, 220 ), PS_SOLID, 1, |
|
pixel, rcClip.top, pixel, rcClip.bottom ); |
|
} |
|
} |
|
} |
|
|
|
drawHelper.StopClipping(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Draw the background markings (actor names, etc) |
|
// Input : drawHelper - |
|
// rc - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::DrawBackground( CChoreoWidgetDrawHelper& drawHelper, RECT& rc ) |
|
{ |
|
RECT rcClip; |
|
GetClientRect( (HWND)getHandle(), &rcClip ); |
|
rcClip.top = GetStartRow(); |
|
rcClip.bottom -= ( m_nInfoHeight + m_nScrollbarHeight ); |
|
rcClip.right -= m_nScrollbarHeight; |
|
|
|
int i; |
|
|
|
for ( i = 0; i < m_SceneGlobalEvents.Size(); i++ ) |
|
{ |
|
CChoreoGlobalEventWidget *event = m_SceneGlobalEvents[ i ]; |
|
if ( event ) |
|
{ |
|
event->redraw( drawHelper ); |
|
} |
|
} |
|
|
|
drawHelper.StartClipping( rcClip ); |
|
|
|
for ( i = 0; i < m_SceneActors.Size(); i++ ) |
|
{ |
|
CChoreoActorWidget *actorW = m_SceneActors[ i ]; |
|
if ( !actorW ) |
|
continue; |
|
|
|
actorW->redraw( drawHelper ); |
|
} |
|
|
|
drawHelper.StopClipping(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::redraw() |
|
{ |
|
if ( !ToolCanDraw() ) |
|
return; |
|
|
|
if ( m_bSuppressLayout ) |
|
return; |
|
|
|
LayoutScene(); |
|
|
|
CChoreoWidgetDrawHelper drawHelper( this, COLOR_CHOREO_BACKGROUND ); |
|
HandleToolRedraw( drawHelper ); |
|
|
|
if ( !m_bCanDraw ) |
|
return; |
|
|
|
RECT rc; |
|
rc.left = 0; |
|
rc.top = GetCaptionHeight(); |
|
rc.right = drawHelper.GetWidth(); |
|
rc.bottom = drawHelper.GetHeight(); |
|
|
|
RECT rcInfo; |
|
rcInfo.left = rc.left; |
|
rcInfo.right = rc.right - m_nScrollbarHeight; |
|
rcInfo.bottom = rc.bottom - m_nScrollbarHeight; |
|
rcInfo.top = rcInfo.bottom - m_nInfoHeight; |
|
|
|
drawHelper.StartClipping( rcInfo ); |
|
|
|
RedrawStatusArea( drawHelper, rcInfo ); |
|
|
|
drawHelper.StopClipping(); |
|
|
|
RECT rcClip = rc; |
|
rcClip.bottom -= ( m_nInfoHeight + m_nScrollbarHeight ); |
|
|
|
drawHelper.StartClipping( rcClip ); |
|
|
|
if ( !m_pScene ) |
|
{ |
|
char sz[ 256 ]; |
|
sprintf( sz, "No choreography scene file (.vcd) loaded" ); |
|
|
|
int pointsize = 18; |
|
int textlen = drawHelper.CalcTextWidth( "Arial", pointsize, FW_NORMAL, sz ); |
|
|
|
RECT rcText; |
|
rcText.top = ( rc.bottom - rc.top ) / 2 - pointsize / 2; |
|
rcText.bottom = rcText.top + pointsize + 10; |
|
rcText.left = rc.right / 2 - textlen / 2; |
|
rcText.right = rcText.left + textlen; |
|
|
|
drawHelper.DrawColoredText( "Arial", pointsize, FW_NORMAL, COLOR_CHOREO_LIGHTTEXT, rcText, sz ); |
|
|
|
drawHelper.StopClipping(); |
|
return; |
|
} |
|
|
|
DrawTimeLine( drawHelper, rc, m_flStartTime, m_flEndTime ); |
|
|
|
bool clipped = false; |
|
int finishx = GetPixelForTimeValue( m_pScene->FindStopTime(), &clipped ); |
|
if ( !clipped ) |
|
{ |
|
drawHelper.DrawColoredLine( COLOR_CHOREO_ENDTIME, PS_DOT, 1, finishx, rc.top + GetStartRow(), finishx, rc.bottom ); |
|
} |
|
|
|
DrawRelativeTagLines( drawHelper, rc ); |
|
DrawBackground( drawHelper, rc ); |
|
|
|
DrawSceneABTicks( drawHelper ); |
|
|
|
drawHelper.StopClipping(); |
|
|
|
if ( m_UndoStack.Size() > 0 ) |
|
{ |
|
int length = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, |
|
"undo %i/%i", m_nUndoLevel, m_UndoStack.Size() ); |
|
RECT rcText = rc; |
|
rcText.top = rc.top + 48; |
|
rcText.bottom = rcText.top + 10; |
|
rcText.left = GetLabelWidth() - length - 20; |
|
rcText.right = rcText.left + length; |
|
|
|
drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 100, 180, 100 ), rcText, |
|
"undo %i/%i", m_nUndoLevel, m_UndoStack.Size() ); |
|
} |
|
|
|
DrawScrubHandle( drawHelper ); |
|
|
|
char sz[ 48 ]; |
|
sprintf( sz, "Speed: %.2fx", m_flPlaybackRate ); |
|
|
|
int fontsize = 9; |
|
|
|
int length = drawHelper.CalcTextWidth( "Arial", fontsize, FW_NORMAL, sz); |
|
|
|
RECT rcText = rc; |
|
rcText.top = rc.top + 25; |
|
rcText.bottom = rcText.top + 10; |
|
rcText.left = GetLabelWidth() + 20; |
|
rcText.right = rcText.left + length; |
|
|
|
drawHelper.DrawColoredText( "Arial", fontsize, FW_NORMAL, |
|
RGB( 50, 50, 50 ), rcText, sz ); |
|
|
|
sprintf( sz, "Zoom: %.2fx", (float)GetTimeZoom( GetToolName() ) / 100.0f ); |
|
|
|
length = drawHelper.CalcTextWidth( "Arial", fontsize, FW_NORMAL, sz); |
|
|
|
rcText = rc; |
|
rcText.left = 5; |
|
rcText.top = rc.top + 48; |
|
rcText.bottom = rcText.top + 10; |
|
rcText.right = rcText.left + length; |
|
|
|
drawHelper.DrawColoredText( "Arial", fontsize, FW_NORMAL, |
|
RGB( 50, 50, 50 ), rcText, sz ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : current - |
|
// number - |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::GetUndoLevels( int& current, int& number ) |
|
{ |
|
current = m_nUndoLevel; |
|
number = m_UndoStack.Size(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : time - |
|
// *clipped - |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
int CChoreoView::GetPixelForTimeValue( float time, bool *clipped /*=NULL*/ ) |
|
{ |
|
if ( clipped ) |
|
{ |
|
*clipped = false; |
|
} |
|
|
|
float frac = ( time - m_flStartTime ) / ( m_flEndTime - m_flStartTime ); |
|
if ( frac < 0.0 || frac > 1.0 ) |
|
{ |
|
if ( clipped ) |
|
{ |
|
*clipped = true; |
|
} |
|
} |
|
|
|
int pixel = GetLabelWidth() + (int)( frac * ( w2() - GetLabelWidth() ) ); |
|
return pixel; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : mx - |
|
// clip - |
|
// Output : float |
|
//----------------------------------------------------------------------------- |
|
float CChoreoView::GetTimeValueForMouse( int mx, bool clip /*=false*/) |
|
{ |
|
RECT rc = m_rcTimeLine; |
|
rc.left = GetLabelWidth(); |
|
|
|
if ( clip ) |
|
{ |
|
if ( mx < rc.left ) |
|
{ |
|
return m_flStartTime; |
|
} |
|
if ( mx > rc.right ) |
|
{ |
|
return m_flEndTime; |
|
} |
|
} |
|
|
|
float frac = (float)( mx - rc.left ) / (float)( rc.right - rc.left ); |
|
|
|
return m_flStartTime + frac * ( m_flEndTime - m_flStartTime ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : time - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::SetStartTime( float time ) |
|
{ |
|
m_flStartTime = time; |
|
InvalidateLayout(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : float |
|
//----------------------------------------------------------------------------- |
|
float CChoreoView::GetStartTime( void ) |
|
{ |
|
return m_flStartTime; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : float |
|
//----------------------------------------------------------------------------- |
|
float CChoreoView::GetEndTime( void ) |
|
{ |
|
return m_flEndTime; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : float |
|
//----------------------------------------------------------------------------- |
|
float CChoreoView::GetPixelsPerSecond( void ) |
|
{ |
|
return m_flPixelsPerSecond * (float)GetTimeZoom( GetToolName() ) / 100.0f; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : mx - |
|
// origmx - |
|
// Output : float |
|
//----------------------------------------------------------------------------- |
|
float CChoreoView::GetTimeDeltaForMouseDelta( int mx, int origmx ) |
|
{ |
|
float t1, t2; |
|
|
|
t2 = GetTimeValueForMouse( mx ); |
|
t1 = GetTimeValueForMouse( origmx ); |
|
|
|
return t2 - t1; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : mx - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::PlaceABPoint( int mx ) |
|
{ |
|
m_rgABPoints[ ( m_nCurrentABPoint) & 0x01 ].time = GetTimeValueForMouse( mx ); |
|
m_rgABPoints[ ( m_nCurrentABPoint) & 0x01 ].active = true; |
|
m_nCurrentABPoint++; |
|
|
|
if ( m_rgABPoints[ 0 ].active && m_rgABPoints [ 1 ].active && |
|
m_rgABPoints[ 0 ].time > m_rgABPoints[ 1 ].time ) |
|
{ |
|
float temp = m_rgABPoints[ 0 ].time; |
|
m_rgABPoints[ 0 ].time = m_rgABPoints[ 1 ].time; |
|
m_rgABPoints[ 1 ].time = temp; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::ClearABPoints( void ) |
|
{ |
|
memset( m_rgABPoints, 0, sizeof( m_rgABPoints ) ); |
|
m_nCurrentABPoint = 0; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : mx - |
|
// my - |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CChoreoView::IsMouseOverTimeline( int mx, int my ) |
|
{ |
|
POINT pt; |
|
pt.x = mx; |
|
pt.y = my; |
|
|
|
RECT rcCheck = m_rcTimeLine; |
|
rcCheck.bottom -= TIMELINE_NUMBERS_HEIGHT; |
|
|
|
if ( PtInRect( &rcCheck, pt ) ) |
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : mx - |
|
// my - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::ShowContextMenu( int mx, int my ) |
|
{ |
|
CChoreoActorWidget *a = NULL; |
|
CChoreoChannelWidget *c = NULL; |
|
CChoreoEventWidget *e = NULL; |
|
CChoreoGlobalEventWidget *ge = NULL; |
|
int ct = -1; |
|
CEventAbsoluteTag *at = NULL; |
|
int clickedCloseCaptionButton = CChoreoChannelWidget::CLOSECAPTION_NONE; |
|
|
|
GetObjectsUnderMouse( mx, my, &a, &c, &e, &ge, &ct, &at, &clickedCloseCaptionButton ); |
|
|
|
m_pClickedActor = a; |
|
m_pClickedChannel = c; |
|
m_pClickedEvent = e; |
|
m_pClickedGlobalEvent = ge; |
|
m_nClickedX = mx; |
|
m_nClickedY = my; |
|
m_nClickedTag = ct; |
|
m_pClickedAbsoluteTag = at; |
|
m_nClickedChannelCloseCaptionButton = clickedCloseCaptionButton; |
|
|
|
// Construct main |
|
mxPopupMenu *pop = new mxPopupMenu(); |
|
|
|
if ( a && c ) |
|
{ |
|
if (!e) |
|
{ |
|
pop->add( "Expression...", IDC_ADDEVENT_EXPRESSION ); |
|
pop->add( "WAV File...", IDC_ADDEVENT_SPEAK ); |
|
pop->add( "Gesture...", IDC_ADDEVENT_GESTURE ); |
|
pop->add( "NULL Gesture...", IDC_ADDEVENT_NULLGESTURE ); |
|
pop->add( "Look at actor...", IDC_ADDEVENT_LOOKAT ); |
|
pop->add( "Move to actor...", IDC_ADDEVENT_MOVETO ); |
|
pop->add( "Face actor...", IDC_ADDEVENT_FACE ); |
|
pop->add( "Fire Trigger...", IDC_ADDEVENT_FIRETRIGGER ); |
|
pop->add( "Generic(AI)...", IDC_ADDEVENT_GENERIC ); |
|
pop->add( "Sequence...", IDC_ADDEVENT_SEQUENCE ); |
|
pop->add( "Flex animation...", IDC_ADDEVENT_FLEXANIMATION ); |
|
pop->add( "Sub-scene...", IDC_ADDEVENT_SUBSCENE ); |
|
pop->add( "Interrupt...", IDC_ADDEVENT_INTERRUPT ); |
|
pop->add( "Permit Responses...", IDC_ADDEVENT_PERMITRESPONSES ); |
|
|
|
pop->addSeparator(); |
|
} |
|
else |
|
{ |
|
pop->add( va( "Edit Event '%s'...", e->GetEvent()->GetName() ), IDC_EDITEVENT ); |
|
switch ( e->GetEvent()->GetType() ) |
|
{ |
|
default: |
|
break; |
|
case CChoreoEvent::FLEXANIMATION: |
|
{ |
|
pop->add( va( "Edit Event '%s' in expression tool", e->GetEvent()->GetName() ), IDC_EXPRESSIONTOOL ); |
|
} |
|
break; |
|
case CChoreoEvent::GESTURE: |
|
{ |
|
pop->add( va( "Edit Event '%s' in gesture tool", e->GetEvent()->GetName() ), IDC_GESTURETOOL ); |
|
} |
|
break; |
|
} |
|
|
|
if ( e->GetEvent()->HasEndTime() ) |
|
{ |
|
pop->add( "Timing Tag...", IDC_ADDTIMINGTAG ); |
|
} |
|
|
|
pop->addSeparator(); |
|
} |
|
} |
|
|
|
// Construct "New..." |
|
mxPopupMenu *newMenu = new mxPopupMenu(); |
|
{ |
|
newMenu->add( "Actor...", IDC_ADDACTOR ); |
|
if ( a ) |
|
{ |
|
newMenu->add( "Channel...", IDC_ADDCHANNEL ); |
|
} |
|
newMenu->add( "Section Pause...", IDC_ADDEVENT_PAUSE ); |
|
newMenu->add( "Loop...", IDC_ADDEVENT_LOOP ); |
|
newMenu->add( "Fire Completion...", IDC_ADDEVENT_STOPPOINT ); |
|
} |
|
pop->addMenu( "New", newMenu ); |
|
|
|
// Now construct "Edit..." |
|
if ( a || c || e || ge ) |
|
{ |
|
mxPopupMenu *editMenu = new mxPopupMenu(); |
|
{ |
|
if ( a ) |
|
{ |
|
editMenu->add( va( "Actor '%s'...", a->GetActor()->GetName() ), IDC_EDITACTOR ); |
|
} |
|
if ( c ) |
|
{ |
|
editMenu->add( va( "Channel '%s'...", c->GetChannel()->GetName() ), IDC_EDITCHANNEL ); |
|
} |
|
if ( ge ) |
|
{ |
|
switch ( ge->GetEvent()->GetType() ) |
|
{ |
|
default: |
|
break; |
|
case CChoreoEvent::SECTION: |
|
{ |
|
editMenu->add( va( "Section Pause '%s'...", ge->GetEvent()->GetName() ), IDC_EDITGLOBALEVENT ); |
|
} |
|
break; |
|
case CChoreoEvent::LOOP: |
|
{ |
|
editMenu->add( va( "Loop Point '%s'...", ge->GetEvent()->GetName() ), IDC_EDITGLOBALEVENT ); |
|
} |
|
break; |
|
case CChoreoEvent::STOPPOINT: |
|
{ |
|
editMenu->add( va( "Fire Completion '%s'...", ge->GetEvent()->GetName() ), IDC_EDITGLOBALEVENT ); |
|
} |
|
break; |
|
} |
|
} |
|
} |
|
|
|
pop->addMenu( "Edit", editMenu ); |
|
|
|
} |
|
|
|
// Move up/down |
|
if ( a || c ) |
|
{ |
|
mxPopupMenu *moveUpMenu = new mxPopupMenu(); |
|
mxPopupMenu *moveDownMenu = new mxPopupMenu(); |
|
|
|
if ( a ) |
|
{ |
|
moveUpMenu->add( va( "Move '%s' up", a->GetActor()->GetName() ), IDC_MOVEACTORUP ); |
|
moveDownMenu->add( va( "Move '%s' down", a->GetActor()->GetName() ), IDC_MOVEACTORDOWN ); |
|
} |
|
if ( c ) |
|
{ |
|
moveUpMenu->add( va( "Move '%s' up", c->GetChannel()->GetName() ), IDC_MOVECHANNELUP ); |
|
moveDownMenu->add( va( "Move '%s' down", c->GetChannel()->GetName() ), IDC_MOVECHANNELDOWN ); |
|
} |
|
|
|
pop->addMenu( "Move Up", moveUpMenu ); |
|
pop->addMenu( "Move Down", moveDownMenu ); |
|
} |
|
|
|
// Delete |
|
if ( a || c || e || ge || (ct != -1) ) |
|
{ |
|
mxPopupMenu *deleteMenu = new mxPopupMenu(); |
|
if ( a ) |
|
{ |
|
deleteMenu->add( va( "Actor '%s'", a->GetActor()->GetName() ), IDC_DELETEACTOR ); |
|
} |
|
if ( c ) |
|
{ |
|
deleteMenu->add( va( "Channel '%s'", c->GetChannel()->GetName() ), IDC_DELETECHANNEL ); |
|
} |
|
if ( e ) |
|
{ |
|
deleteMenu->add( va( "Event '%s'", e->GetEvent()->GetName() ), IDC_DELETEEVENT ); |
|
} |
|
if ( ge ) |
|
{ |
|
switch ( ge->GetEvent()->GetType() ) |
|
{ |
|
default: |
|
break; |
|
case CChoreoEvent::SECTION: |
|
{ |
|
deleteMenu->add( va( "Section Pause '%s'...", ge->GetEvent()->GetName() ), IDC_DELETEGLOBALEVENT ); |
|
} |
|
break; |
|
case CChoreoEvent::LOOP: |
|
{ |
|
deleteMenu->add( va( "Loop Point '%s'...", ge->GetEvent()->GetName() ), IDC_DELETEGLOBALEVENT ); |
|
} |
|
break; |
|
case CChoreoEvent::STOPPOINT: |
|
{ |
|
deleteMenu->add( va( "Fire Completion '%s'...", ge->GetEvent()->GetName() ), IDC_DELETEGLOBALEVENT ); |
|
} |
|
break; |
|
} |
|
} |
|
if ( e && ct != -1 ) |
|
{ |
|
CEventRelativeTag *tag = e->GetEvent()->GetRelativeTag( ct ); |
|
if ( tag ) |
|
{ |
|
deleteMenu->add( va( "Relative Tag '%s'...", tag->GetName() ), IDC_DELETERELATIVETAG ); |
|
} |
|
} |
|
pop->addMenu( "Delete", deleteMenu ); |
|
} |
|
|
|
// Select |
|
{ |
|
mxPopupMenu *selectMenu = new mxPopupMenu(); |
|
selectMenu->add( "Select All", IDC_SELECTALL ); |
|
selectMenu->add( "Deselect All", IDC_DESELECTALL ); |
|
selectMenu->addSeparator(); |
|
|
|
selectMenu->add( "All events before", IDC_SELECTEVENTS_ALL_BEFORE ); |
|
selectMenu->add( "All events after", IDC_SELECTEVENTS_ALL_AFTER ); |
|
selectMenu->add( "Active events before", IDC_SELECTEVENTS_ACTIVE_BEFORE ); |
|
selectMenu->add( "Active events after", IDC_SELECTEVENTS_ACTIVE_AFTER ); |
|
selectMenu->add( "Channel events before", IDC_SELECTEVENTS_CHANNEL_BEFORE ); |
|
selectMenu->add( "Channel events after", IDC_SELECTEVENTS_CHANNEL_AFTER ); |
|
|
|
if ( a || c ) |
|
{ |
|
selectMenu->addSeparator(); |
|
if ( a ) |
|
{ |
|
selectMenu->add( va( "All events in actor '%s'", a->GetActor()->GetName() ), IDC_CV_ALLEVENTS_ACTOR ); |
|
} |
|
if ( c ) |
|
{ |
|
selectMenu->add( va( "All events in channel '%s'", c->GetChannel()->GetName() ), IDC_CV_ALLEVENTS_CHANNEL ); |
|
} |
|
} |
|
pop->addMenu( "Select/Deselect", selectMenu ); |
|
} |
|
|
|
// Quick delete for events |
|
if ( e ) |
|
{ |
|
pop->addSeparator(); |
|
|
|
switch ( e->GetEvent()->GetType() ) |
|
{ |
|
default: |
|
break; |
|
case CChoreoEvent::FLEXANIMATION: |
|
{ |
|
pop->add( va( "Edit event '%s' in expression tool", e->GetEvent()->GetName() ), IDC_EXPRESSIONTOOL ); |
|
} |
|
break; |
|
case CChoreoEvent::GESTURE: |
|
{ |
|
pop->add( va( "Edit event '%s' in gesture tool", e->GetEvent()->GetName() ), IDC_GESTURETOOL ); |
|
} |
|
break; |
|
} |
|
|
|
pop->add( va( "Move event '%s' to back", e->GetEvent()->GetName() ), IDC_MOVETOBACK ); |
|
if ( CountSelectedEvents() > 1 ) |
|
{ |
|
pop->add( va( "Delete events" ), IDC_DELETEEVENT ); |
|
pop->addSeparator(); |
|
pop->add( "Enable events", IDC_CV_ENABLEEVENTS ); |
|
pop->add( "Disable events", IDC_CV_DISABLEEVENTS ); |
|
} |
|
else |
|
{ |
|
pop->add( va( "Delete event '%s'", e->GetEvent()->GetName() ), IDC_DELETEEVENT ); |
|
pop->addSeparator(); |
|
if ( e->GetEvent()->GetActive() ) |
|
{ |
|
pop->add( va( "Disable event '%s'", e->GetEvent()->GetName() ), IDC_CV_DISABLEEVENTS ); |
|
} |
|
else |
|
{ |
|
pop->add( va( "Enable event '%s'", e->GetEvent()->GetName() ), IDC_CV_ENABLEEVENTS ); |
|
} |
|
} |
|
} |
|
|
|
if ( m_rgABPoints[ 0 ].active && m_rgABPoints[ 1 ].active ) |
|
{ |
|
pop->addSeparator(); |
|
mxPopupMenu *timeMenu = new mxPopupMenu(); |
|
timeMenu->add( "Insert empty space between marks (shifts events right)", IDC_INSERT_TIME ); |
|
timeMenu->add( "Delete events between marks (shifts remaining events left)", IDC_DELETE_TIME ); |
|
pop->addMenu( "Time Marks", timeMenu ); |
|
} |
|
|
|
|
|
// Copy/paste |
|
if ( CanPaste() || e ) |
|
{ |
|
pop->addSeparator(); |
|
|
|
if ( CountSelectedEvents() > 1 ) |
|
{ |
|
pop->add( va( "Copy events to clipboard" ), IDC_COPYEVENTS ); |
|
} |
|
else if ( e ) |
|
{ |
|
pop->add( va( "Copy event '%s' to clipboard", e->GetEvent()->GetName() ), IDC_COPYEVENTS ); |
|
} |
|
|
|
if ( CanPaste() ) |
|
{ |
|
pop->add( va( "Paste events" ), IDC_PASTEEVENTS ); |
|
} |
|
} |
|
|
|
// Export / import |
|
pop->addSeparator(); |
|
|
|
if ( e ) |
|
{ |
|
mxPopupMenu *exportMenu = new mxPopupMenu(); |
|
if ( CountSelectedEvents() > 1 ) |
|
{ |
|
exportMenu->add( va( "Export events to .vce..." ), IDC_EXPORTEVENTS ); |
|
} |
|
else if ( e ) |
|
{ |
|
exportMenu->add( va( "Export event '%s' to .vce...", e->GetEvent()->GetName() ), IDC_EXPORTEVENTS ); |
|
} |
|
exportMenu->add( va( "Export as .vcd..." ), IDC_EXPORT_VCD ); |
|
pop->addMenu( "Export", exportMenu ); |
|
} |
|
|
|
mxPopupMenu *importMenu = new mxPopupMenu(); |
|
importMenu->add( va( "Import events from .vce..." ), IDC_IMPORTEVENTS ); |
|
importMenu->add( va( "Merge from .vcd..." ), IDC_IMPORT_VCD ); |
|
pop->addMenu( "Import", importMenu ); |
|
|
|
bool bShowAlignLeft = ( CountSelectedEvents() + CountSelectedGlobalEvents() ) > 1 ? true : false; |
|
|
|
if ( e && ( ( CountSelectedEvents() > 1 ) || bShowAlignLeft ) ) |
|
{ |
|
pop->addSeparator(); |
|
|
|
mxPopupMenu *alignMenu = new mxPopupMenu(); |
|
alignMenu->add( "Align Left", IDC_CV_ALIGN_LEFT ); |
|
if ( CountSelectedEvents() > 1 ) |
|
{ |
|
alignMenu->add( "Align Right", IDC_CV_ALIGN_RIGHT ); |
|
alignMenu->add( "Size to Smallest", IDC_CV_SAMESIZE_SMALLEST ); |
|
alignMenu->add( "Size to Largest", IDC_CV_SAMESIZE_LARGEST ); |
|
} |
|
pop->addMenu( "Align", alignMenu ); |
|
} |
|
|
|
// Misc. |
|
pop->addSeparator(); |
|
pop->add( va( "Change scale..." ), IDC_CV_CHANGESCALE ); |
|
pop->add( va( "Check sequences" ), IDC_CV_CHECKSEQLENGTHS ); |
|
pop->add( va( "Process sequences" ), IDC_CV_PROCESSSEQUENCES ); |
|
pop->add( va( m_bRampOnly ? "Ramp normal" : "Ramp only" ), IDC_CV_TOGGLERAMPONLY ); |
|
pop->setChecked( IDC_CV_PROCESSSEQUENCES, m_bProcessSequences ); |
|
|
|
bool onmaster= ( m_pClickedChannel && |
|
m_pClickedChannel->GetCaptionClickedEvent() && |
|
m_pClickedChannel->GetCaptionClickedEvent()->GetCloseCaptionType() == CChoreoEvent::CC_MASTER ) ? true : false; |
|
bool ondisabled = ( m_pClickedChannel && |
|
m_pClickedChannel->GetCaptionClickedEvent() && |
|
m_pClickedChannel->GetCaptionClickedEvent()->GetCloseCaptionType() == CChoreoEvent::CC_DISABLED ) ? true : false; |
|
|
|
// The close captioning menu |
|
if ( m_bShowCloseCaptionData && ( AreSelectedEventsCombinable() || AreSelectedEventsInSpeakGroup() || onmaster || ondisabled ) ) |
|
{ |
|
pop->addSeparator(); |
|
if ( AreSelectedEventsCombinable() ) |
|
{ |
|
pop->add( "Combine Speak Events", IDC_CV_COMBINESPEAKEVENTS ); |
|
} |
|
if ( AreSelectedEventsInSpeakGroup() ) |
|
{ |
|
pop->add( "Uncombine Speak Events", IDC_CV_REMOVESPEAKEVENTFROMGROUP ); |
|
} |
|
if ( onmaster ) |
|
{ |
|
// Can only change tokens for "combined" files |
|
if ( m_pClickedChannel->GetCaptionClickedEvent()->GetNumSlaves() >= 1 ) |
|
{ |
|
pop->add( "Change Token", IDC_CV_CHANGECLOSECAPTIONTOKEN ); |
|
} |
|
pop->add( "Disable captions", IDC_CV_TOGGLECLOSECAPTIONS ); |
|
} |
|
if ( ondisabled ) |
|
{ |
|
pop->add( "Enable captions", IDC_CV_TOGGLECLOSECAPTIONS ); |
|
} |
|
} |
|
|
|
// Undo/redo |
|
if ( CanUndo() || CanRedo() ) |
|
{ |
|
|
|
pop->addSeparator(); |
|
|
|
if ( CanUndo() ) |
|
{ |
|
pop->add( va( "Undo %s", GetUndoDescription() ), IDC_CVUNDO ); |
|
} |
|
if ( CanRedo() ) |
|
{ |
|
pop->add( va( "Redo %s", GetRedoDescription() ), IDC_CVREDO ); |
|
} |
|
} |
|
|
|
if ( m_pScene ) |
|
{ |
|
// Associate map file |
|
pop->addSeparator(); |
|
pop->add( va( "Associate .bsp (%s)", m_pScene->GetMapname() ), IDC_ASSOCIATEBSP ); |
|
if ( a ) |
|
{ |
|
if ( a->GetActor() && a->GetActor()->GetFacePoserModelName()[0] ) |
|
{ |
|
pop->add( va( "Change .mdl for %s", a->GetActor()->GetName() ), IDC_ASSOCIATEMODEL ); |
|
} |
|
else |
|
{ |
|
pop->add( va( "Associate .mdl with %s", a->GetActor()->GetName() ), IDC_ASSOCIATEMODEL ); |
|
} |
|
} |
|
} |
|
|
|
pop->popup( this, mx, my ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::AssociateModel( void ) |
|
{ |
|
if ( !m_pScene ) |
|
return; |
|
|
|
CChoreoActorWidget *actor = m_pClickedActor; |
|
if ( !actor ) |
|
return; |
|
|
|
CChoreoActor *a = actor->GetActor(); |
|
if ( !a ) |
|
return; |
|
|
|
CChoiceParams params; |
|
strcpy( params.m_szDialogTitle, "Associate Model" ); |
|
|
|
params.m_bPositionDialog = false; |
|
params.m_nLeft = 0; |
|
params.m_nTop = 0; |
|
strcpy( params.m_szPrompt, "Choose model:" ); |
|
|
|
params.m_Choices.RemoveAll(); |
|
|
|
params.m_nSelected = -1; |
|
int oldsel = -1; |
|
|
|
int c = models->Count(); |
|
ChoiceText text; |
|
for ( int i = 0; i < c; i++ ) |
|
{ |
|
char const *modelname = models->GetModelName( i ); |
|
|
|
strcpy( text.choice, modelname ); |
|
|
|
if ( !stricmp( a->GetName(), modelname ) ) |
|
{ |
|
params.m_nSelected = i; |
|
oldsel = -1; |
|
} |
|
|
|
params.m_Choices.AddToTail( text ); |
|
} |
|
|
|
// Add an extra entry which is "No association" |
|
strcpy( text.choice, "No Associated Model" ); |
|
params.m_Choices.AddToTail( text ); |
|
|
|
if ( !ChoiceProperties( ¶ms ) ) |
|
return; |
|
|
|
if ( params.m_nSelected == oldsel ) |
|
return; |
|
|
|
// Chose something new... |
|
if ( params.m_nSelected >= 0 && |
|
params.m_nSelected < params.m_Choices.Count() ) |
|
{ |
|
AssociateModelToActor( a, params.m_nSelected ); |
|
} |
|
else |
|
{ |
|
// Chose "No association" |
|
AssociateModelToActor( a, -1 ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *actor - |
|
// modelindex - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::AssociateModelToActor( CChoreoActor *actor, int modelindex ) |
|
{ |
|
Assert( actor ); |
|
|
|
SetDirty( true ); |
|
|
|
PushUndo( "Associate model" ); |
|
|
|
// Chose something new... |
|
if ( modelindex >= 0 && |
|
modelindex < models->Count() ) |
|
{ |
|
actor->SetFacePoserModelName( models->GetModelFileName( modelindex ) ); |
|
} |
|
else |
|
{ |
|
// Chose "No Associated Model" |
|
actor->SetFacePoserModelName( "" ); |
|
} |
|
|
|
RecomputeWaves(); |
|
|
|
PushRedo( "Associate model" ); |
|
} |
|
|
|
void CChoreoView::AssociateBSP( void ) |
|
{ |
|
if ( !m_pScene ) |
|
return; |
|
|
|
// Strip game directory and slash |
|
char mapname[ 512 ]; |
|
if ( !FacePoser_ShowOpenFileNameDialog( mapname, sizeof( mapname ), "maps", "*.bsp" ) ) |
|
{ |
|
return; |
|
} |
|
|
|
m_pScene->SetMapname( mapname ); |
|
redraw(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::DrawFocusRect( void ) |
|
{ |
|
HDC dc = GetDC( NULL ); |
|
|
|
for ( int i = 0; i < m_FocusRects.Size(); i++ ) |
|
{ |
|
RECT rc = m_FocusRects[ i ].m_rcFocus; |
|
|
|
::DrawFocusRect( dc, &rc ); |
|
} |
|
|
|
ReleaseDC( NULL, dc ); |
|
} |
|
|
|
int CChoreoView::GetSelectedEventWidgets( CUtlVector< CChoreoEventWidget * >& events ) |
|
{ |
|
events.RemoveAll(); |
|
|
|
int c = 0; |
|
|
|
for ( int i = 0; i < m_SceneActors.Size(); i++ ) |
|
{ |
|
CChoreoActorWidget *actor = m_SceneActors[ i ]; |
|
if ( !actor ) |
|
continue; |
|
|
|
for ( int j = 0; j < actor->GetNumChannels(); j++ ) |
|
{ |
|
CChoreoChannelWidget *channel = actor->GetChannel( j ); |
|
if ( !channel ) |
|
continue; |
|
|
|
for ( int k = 0; k < channel->GetNumEvents(); k++ ) |
|
{ |
|
CChoreoEventWidget *event = channel->GetEvent( k ); |
|
if ( !event ) |
|
continue; |
|
|
|
if ( event->IsSelected() ) |
|
{ |
|
events.AddToTail( event ); |
|
c++; |
|
} |
|
} |
|
} |
|
} |
|
|
|
return c; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : events - |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
int CChoreoView::GetSelectedEvents( CUtlVector< CChoreoEvent * >& events ) |
|
{ |
|
events.RemoveAll(); |
|
|
|
int c = 0; |
|
|
|
for ( int i = 0; i < m_SceneActors.Size(); i++ ) |
|
{ |
|
CChoreoActorWidget *actor = m_SceneActors[ i ]; |
|
if ( !actor ) |
|
continue; |
|
|
|
for ( int j = 0; j < actor->GetNumChannels(); j++ ) |
|
{ |
|
CChoreoChannelWidget *channel = actor->GetChannel( j ); |
|
if ( !channel ) |
|
continue; |
|
|
|
for ( int k = 0; k < channel->GetNumEvents(); k++ ) |
|
{ |
|
CChoreoEventWidget *event = channel->GetEvent( k ); |
|
if ( !event ) |
|
continue; |
|
|
|
if ( event->IsSelected() ) |
|
{ |
|
events.AddToTail( event->GetEvent() ); |
|
c++; |
|
} |
|
} |
|
} |
|
} |
|
|
|
return c; |
|
} |
|
|
|
int CChoreoView::CountSelectedGlobalEvents( void ) |
|
{ |
|
int c = 0; |
|
for ( int i = 0; i < m_SceneGlobalEvents.Size(); i++ ) |
|
{ |
|
CChoreoGlobalEventWidget *event = m_SceneGlobalEvents[ i ]; |
|
if ( !event || !event->IsSelected() ) |
|
continue; |
|
|
|
++c; |
|
} |
|
return c; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
int CChoreoView::CountSelectedEvents( void ) |
|
{ |
|
int c = 0; |
|
|
|
for ( int i = 0; i < m_SceneActors.Size(); i++ ) |
|
{ |
|
CChoreoActorWidget *actor = m_SceneActors[ i ]; |
|
if ( !actor ) |
|
continue; |
|
|
|
for ( int j = 0; j < actor->GetNumChannels(); j++ ) |
|
{ |
|
CChoreoChannelWidget *channel = actor->GetChannel( j ); |
|
if ( !channel ) |
|
continue; |
|
|
|
for ( int k = 0; k < channel->GetNumEvents(); k++ ) |
|
{ |
|
CChoreoEventWidget *event = channel->GetEvent( k ); |
|
if ( !event ) |
|
continue; |
|
|
|
if ( event->IsSelected() ) |
|
c++; |
|
|
|
} |
|
} |
|
} |
|
|
|
return c; |
|
} |
|
|
|
bool CChoreoView::IsMouseOverEvent( CChoreoEventWidget *ew, int mx, int my ) |
|
{ |
|
int tolerance = DRAG_EVENT_EDGE_TOLERANCE; |
|
|
|
RECT bounds = ew->getBounds(); |
|
mx -= bounds.left; |
|
my -= bounds.top; |
|
|
|
if ( mx <= -tolerance ) |
|
{ |
|
return false; |
|
} |
|
|
|
CChoreoEvent *event = ew->GetEvent(); |
|
if ( event ) |
|
{ |
|
if ( event->HasEndTime() ) |
|
{ |
|
int rightside = ew->GetDurationRightEdge() ? ew->GetDurationRightEdge() : ew->w(); |
|
|
|
if ( mx > rightside + tolerance ) |
|
{ |
|
return false; |
|
} |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
bool CChoreoView::IsMouseOverEventEdge( CChoreoEventWidget *ew, bool bLeftEdge, int mx, int my ) |
|
{ |
|
int tolerance = DRAG_EVENT_EDGE_TOLERANCE; |
|
|
|
RECT bounds = ew->getBounds(); |
|
mx -= bounds.left; |
|
my -= bounds.top; |
|
|
|
CChoreoEvent *event = ew->GetEvent(); |
|
if ( event && event->HasEndTime() ) |
|
{ |
|
if ( mx > -tolerance && mx <= tolerance ) |
|
{ |
|
return bLeftEdge; |
|
} |
|
|
|
int rightside = ew->GetDurationRightEdge() ? ew->GetDurationRightEdge() : ew->w(); |
|
|
|
if ( mx >= rightside - tolerance ) |
|
{ |
|
if ( mx > rightside + tolerance ) |
|
{ |
|
return false; |
|
} |
|
else |
|
{ |
|
return !bLeftEdge; |
|
} |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
int CChoreoView::GetEarliestEventIndex( CUtlVector< CChoreoEventWidget * >& events ) |
|
{ |
|
int best = -1; |
|
float minTime = FLT_MAX; |
|
|
|
int c = events.Count(); |
|
for ( int i = 0; i < c; ++i ) |
|
{ |
|
CChoreoEvent *e = events[ i ]->GetEvent(); |
|
float t = e->GetStartTime(); |
|
if ( t < minTime ) |
|
{ |
|
minTime = t; |
|
best = i; |
|
} |
|
} |
|
|
|
return best; |
|
} |
|
|
|
int CChoreoView::GetLatestEventIndex( CUtlVector< CChoreoEventWidget * >& events ) |
|
{ |
|
int best = -1; |
|
float maxTime = FLT_MIN; |
|
|
|
int c = events.Count(); |
|
for ( int i = 0; i < c; ++i ) |
|
{ |
|
CChoreoEvent *e = events[ i ]->GetEvent(); |
|
float t = e->GetEndTime(); |
|
if ( t > maxTime ) |
|
{ |
|
maxTime = t; |
|
best = i; |
|
} |
|
} |
|
|
|
return best; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : mx - |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
int CChoreoView::ComputeEventDragType( int mx, int my ) |
|
{ |
|
int tolerance = DRAG_EVENT_EDGE_TOLERANCE; |
|
|
|
// Iterate the events and see who's closest |
|
CChoreoEventWidget *ew = GetEventUnderCursorPos( mx, my ); |
|
if ( !ew ) |
|
{ |
|
return DRAGTYPE_NONE; |
|
} |
|
|
|
// Deal with small windows by lowering tolerance |
|
if ( ew->w() < 4 * tolerance ) |
|
{ |
|
tolerance = 2; |
|
} |
|
|
|
int tagnum = GetTagUnderCursorPos( ew, mx, my ); |
|
if ( tagnum != -1 && CountSelectedEvents() <= 1 ) |
|
{ |
|
return DRAGTYPE_EVENTTAG_MOVE; |
|
} |
|
|
|
CEventAbsoluteTag *tag = GetAbsoluteTagUnderCursorPos( ew, mx, my ); |
|
if ( tag != NULL && CountSelectedEvents() <= 1 ) |
|
{ |
|
return DRAGTYPE_EVENTABSTAG_MOVE; |
|
} |
|
|
|
if ( CountSelectedEvents() > 1 ) |
|
{ |
|
CUtlVector< CChoreoEventWidget * > events; |
|
GetSelectedEventWidgets( events ); |
|
|
|
int iStart, iEnd; |
|
iStart = GetEarliestEventIndex( events ); |
|
iEnd = GetLatestEventIndex( events ); |
|
|
|
if ( events.IsValidIndex( iStart ) ) |
|
{ |
|
// See if mouse is over left edge of starting event |
|
if ( IsMouseOverEventEdge( events[ iStart ], true, mx, my ) ) |
|
{ |
|
return DRAGTYPE_RESCALELEFT; |
|
} |
|
} |
|
if ( events.IsValidIndex( iEnd ) ) |
|
{ |
|
if ( IsMouseOverEventEdge( events[ iEnd ], false, mx, my ) ) |
|
{ |
|
return DRAGTYPE_RESCALERIGHT; |
|
} |
|
} |
|
|
|
return DRAGTYPE_EVENT_MOVE; |
|
} |
|
|
|
CChoreoEvent *event = ew->GetEvent(); |
|
if ( event ) |
|
{ |
|
if ( event->IsFixedLength() || !event->HasEndTime() ) |
|
{ |
|
return DRAGTYPE_EVENT_MOVE; |
|
} |
|
} |
|
|
|
if ( IsMouseOverEventEdge( ew, true, mx, my ) ) |
|
{ |
|
if ( GetAsyncKeyState( VK_SHIFT ) ) |
|
return DRAGTYPE_EVENT_STARTTIME_RESCALE; |
|
return DRAGTYPE_EVENT_STARTTIME; |
|
} |
|
|
|
if ( IsMouseOverEventEdge( ew, false, mx, my ) ) |
|
{ |
|
if ( GetAsyncKeyState( VK_SHIFT ) ) |
|
return DRAGTYPE_EVENT_ENDTIME_RESCALE; |
|
return DRAGTYPE_EVENT_ENDTIME; |
|
} |
|
|
|
if ( IsMouseOverEvent( ew, mx, my ) ) |
|
{ |
|
return DRAGTYPE_EVENT_MOVE; |
|
} |
|
|
|
return DRAGTYPE_NONE; |
|
} |
|
|
|
void CChoreoView::StartDraggingSceneEndTime( int mx, int my ) |
|
{ |
|
m_nDragType = DRAGTYPE_SCENE_ENDTIME; |
|
|
|
m_FocusRects.Purge(); |
|
|
|
RECT rcFocus; |
|
rcFocus.left = mx; |
|
rcFocus.top = 0; |
|
rcFocus.bottom = h2(); |
|
rcFocus.right = rcFocus.left + 2; |
|
|
|
POINT offset; |
|
offset.x = 0; |
|
offset.y = 0; |
|
ClientToScreen( (HWND)getHandle(), &offset ); |
|
OffsetRect( &rcFocus, offset.x, offset.y ); |
|
|
|
CFocusRect fr; |
|
fr.m_rcFocus = rcFocus; |
|
fr.m_rcOrig = rcFocus; |
|
|
|
// Relative tag events don't move |
|
m_FocusRects.AddToTail( fr ); |
|
|
|
m_xStart = mx; |
|
m_yStart = my; |
|
m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) ); |
|
|
|
DrawFocusRect(); |
|
|
|
m_bDragging = true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::StartDraggingEvent( int mx, int my ) |
|
{ |
|
m_nDragType = ComputeEventDragType( mx, my ); |
|
if ( m_nDragType == DRAGTYPE_NONE ) |
|
{ |
|
if( m_pClickedGlobalEvent ) |
|
{ |
|
m_nDragType = DRAGTYPE_EVENT_MOVE; |
|
} |
|
else |
|
{ |
|
return; |
|
} |
|
} |
|
|
|
m_FocusRects.Purge(); |
|
|
|
// Go through all selected events |
|
RECT rcFocus; |
|
for ( int i = 0; i < m_SceneActors.Size(); i++ ) |
|
{ |
|
CChoreoActorWidget *actor = m_SceneActors[ i ]; |
|
if ( !actor ) |
|
continue; |
|
|
|
for ( int j = 0; j < actor->GetNumChannels(); j++ ) |
|
{ |
|
CChoreoChannelWidget *channel = actor->GetChannel( j ); |
|
if ( !channel ) |
|
continue; |
|
|
|
for ( int k = 0; k < channel->GetNumEvents(); k++ ) |
|
{ |
|
CChoreoEventWidget *event = channel->GetEvent( k ); |
|
if ( !event ) |
|
continue; |
|
|
|
if ( !event->IsSelected() ) |
|
continue; |
|
|
|
if ( event == m_pClickedEvent && |
|
( m_nClickedTag != -1 || m_pClickedAbsoluteTag ) ) |
|
{ |
|
int leftEdge = 0; |
|
int tagWidth = 1; |
|
if ( !m_pClickedAbsoluteTag ) |
|
{ |
|
CEventRelativeTag *tag = event->GetEvent()->GetRelativeTag( m_nClickedTag ); |
|
if ( tag ) |
|
{ |
|
// Determine left edcge |
|
RECT bounds; |
|
bounds = event->getBounds(); |
|
if ( bounds.right - bounds.left > 0 ) |
|
{ |
|
leftEdge = (int)( tag->GetPercentage() * (float)( bounds.right - bounds.left ) + 0.5f ); |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
// Determine left edcge |
|
RECT bounds; |
|
bounds = event->getBounds(); |
|
if ( bounds.right - bounds.left > 0 ) |
|
{ |
|
leftEdge = (int)( m_pClickedAbsoluteTag->GetPercentage() * (float)( bounds.right - bounds.left ) + 0.5f ); |
|
} |
|
} |
|
|
|
rcFocus.left = event->x() + leftEdge - tagWidth; |
|
rcFocus.top = event->y() - tagWidth; |
|
rcFocus.right = rcFocus.left + 2 * tagWidth; |
|
rcFocus.bottom = event->y() + event->h(); |
|
} |
|
else |
|
{ |
|
rcFocus.left = event->x(); |
|
rcFocus.top = event->y(); |
|
if ( event->GetDurationRightEdge() ) |
|
{ |
|
rcFocus.right = event->x() + event->GetDurationRightEdge(); |
|
} |
|
else |
|
{ |
|
rcFocus.right = rcFocus.left + event->w(); |
|
} |
|
rcFocus.bottom = rcFocus.top + event->h(); |
|
} |
|
|
|
POINT offset; |
|
offset.x = 0; |
|
offset.y = 0; |
|
ClientToScreen( (HWND)getHandle(), &offset ); |
|
OffsetRect( &rcFocus, offset.x, offset.y ); |
|
|
|
CFocusRect fr; |
|
fr.m_rcFocus = rcFocus; |
|
fr.m_rcOrig = rcFocus; |
|
|
|
// Relative tag events don't move |
|
m_FocusRects.AddToTail( fr ); |
|
} |
|
} |
|
} |
|
|
|
for ( int i = 0; i < m_SceneGlobalEvents.Count(); i++ ) |
|
{ |
|
CChoreoGlobalEventWidget *gew = m_SceneGlobalEvents[ i ]; |
|
if ( !gew ) |
|
continue; |
|
|
|
if ( !gew->IsSelected() ) |
|
continue; |
|
|
|
rcFocus.left = gew->x() + gew->w() / 2; |
|
rcFocus.top = 0; |
|
rcFocus.right = rcFocus.left + 2; |
|
rcFocus.bottom = h2(); |
|
|
|
POINT offset; |
|
offset.x = 0; |
|
offset.y = 0; |
|
ClientToScreen( (HWND)getHandle(), &offset ); |
|
OffsetRect( &rcFocus, offset.x, offset.y ); |
|
|
|
CFocusRect fr; |
|
fr.m_rcFocus = rcFocus; |
|
fr.m_rcOrig = rcFocus; |
|
|
|
m_FocusRects.AddToTail( fr ); |
|
} |
|
|
|
m_xStart = mx; |
|
m_yStart = my; |
|
m_hPrevCursor = NULL; |
|
switch ( m_nDragType ) |
|
{ |
|
default: |
|
break; |
|
case DRAGTYPE_EVENTTAG_MOVE: |
|
m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) ); |
|
break; |
|
case DRAGTYPE_EVENTABSTAG_MOVE: |
|
m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_IBEAM ) ); |
|
break; |
|
case DRAGTYPE_EVENT_MOVE: |
|
m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEALL ) ); |
|
break; |
|
case DRAGTYPE_EVENT_STARTTIME: |
|
case DRAGTYPE_EVENT_STARTTIME_RESCALE: |
|
m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) ); |
|
break; |
|
case DRAGTYPE_EVENT_ENDTIME: |
|
case DRAGTYPE_EVENT_ENDTIME_RESCALE: |
|
m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) ); |
|
break; |
|
case DRAGTYPE_RESCALELEFT: |
|
case DRAGTYPE_RESCALERIGHT: |
|
m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) ); |
|
break; |
|
} |
|
|
|
DrawFocusRect(); |
|
|
|
m_bDragging = true; |
|
} |
|
|
|
bool CChoreoView::IsMouseOverSceneEndTime( int mx ) |
|
{ |
|
// See if mouse if over scene end time instead |
|
if ( m_pScene ) |
|
{ |
|
float endtime = m_pScene->FindStopTime(); |
|
|
|
bool clip = false; |
|
int lastpixel = GetPixelForTimeValue( endtime, &clip ); |
|
if ( !clip ) |
|
{ |
|
if ( abs( mx - lastpixel ) < DRAG_EVENT_EDGE_TOLERANCE ) |
|
{ |
|
return true; |
|
} |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : mx - |
|
// my - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::MouseStartDrag( mxEvent *event, int mx, int my ) |
|
{ |
|
bool isrightbutton = event->buttons & mxEvent::MouseRightButton ? true : false; |
|
|
|
if ( m_bDragging ) |
|
{ |
|
return; |
|
} |
|
|
|
GetObjectsUnderMouse( mx, my, &m_pClickedActor, &m_pClickedChannel, &m_pClickedEvent, &m_pClickedGlobalEvent, &m_nClickedTag, &m_pClickedAbsoluteTag, &m_nClickedChannelCloseCaptionButton ); |
|
|
|
if ( m_pClickedEvent ) |
|
{ |
|
CChoreoEvent *e = m_pClickedEvent->GetEvent(); |
|
Assert( e ); |
|
|
|
int dtPreview = ComputeEventDragType( mx, my ); |
|
// Shift clicking on exact edge shouldn't toggle selection state |
|
bool bIsEdgeRescale = ( dtPreview == DRAGTYPE_EVENT_ENDTIME_RESCALE || dtPreview == DRAGTYPE_EVENT_STARTTIME_RESCALE ); |
|
|
|
if ( !( event->modifiers & ( mxEvent::KeyCtrl | mxEvent::KeyShift ) ) ) |
|
{ |
|
if ( !m_pClickedEvent->IsSelected() ) |
|
{ |
|
DeselectAll(); |
|
} |
|
TraverseWidgets( &CChoreoView::Select, m_pClickedEvent ); |
|
} |
|
else if ( !bIsEdgeRescale ) |
|
{ |
|
m_pClickedEvent->SetSelected( !m_pClickedEvent->IsSelected() ); |
|
} |
|
|
|
switch ( m_pClickedEvent->GetEvent()->GetType() ) |
|
{ |
|
default: |
|
break; |
|
case CChoreoEvent::FLEXANIMATION: |
|
{ |
|
g_pExpressionTool->SetEvent( e ); |
|
g_pFlexPanel->SetEvent( e ); |
|
} |
|
break; |
|
case CChoreoEvent::GESTURE: |
|
{ |
|
g_pGestureTool->SetEvent( e ); |
|
} |
|
break; |
|
case CChoreoEvent::SPEAK: |
|
{ |
|
g_pWaveBrowser->SetEvent( e ); |
|
} |
|
break; |
|
} |
|
|
|
if ( e->HasEndTime() ) |
|
{ |
|
g_pRampTool->SetEvent( e ); |
|
} |
|
|
|
redraw(); |
|
StartDraggingEvent( mx, my ); |
|
} |
|
else if ( m_pClickedGlobalEvent ) |
|
{ |
|
if ( !( event->modifiers & ( mxEvent::KeyCtrl | mxEvent::KeyShift ) ) ) |
|
{ |
|
if ( !m_pClickedGlobalEvent->IsSelected() ) |
|
{ |
|
DeselectAll(); |
|
} |
|
TraverseWidgets( &CChoreoView::Select, m_pClickedGlobalEvent ); |
|
} |
|
else |
|
{ |
|
m_pClickedGlobalEvent->SetSelected( !m_pClickedGlobalEvent->IsSelected() ); |
|
} |
|
|
|
redraw(); |
|
StartDraggingEvent( mx, my ); |
|
} |
|
else if ( IsMouseOverScrubArea( event ) ) |
|
{ |
|
if ( IsMouseOverScrubHandle( event ) ) |
|
{ |
|
m_nDragType = DRAGTYPE_SCRUBBER; |
|
|
|
m_bDragging = true; |
|
|
|
float t = GetTimeValueForMouse( (short)event->x ); |
|
m_flScrubberTimeOffset = m_flScrub - t; |
|
float maxoffset = 0.5f * (float)SCRUBBER_HANDLE_WIDTH / GetPixelsPerSecond(); |
|
m_flScrubberTimeOffset = clamp( m_flScrubberTimeOffset, -maxoffset, maxoffset ); |
|
t += m_flScrubberTimeOffset; |
|
|
|
ClampTimeToSelectionInterval( t ); |
|
|
|
SetScrubTime( t ); |
|
SetScrubTargetTime( t ); |
|
|
|
redraw(); |
|
|
|
RECT rcScrub; |
|
GetScrubHandleRect( rcScrub, true ); |
|
|
|
m_FocusRects.Purge(); |
|
|
|
// Go through all selected events |
|
RECT rcFocus; |
|
|
|
rcFocus.top = GetStartRow(); |
|
rcFocus.bottom = h2() - m_nScrollbarHeight - m_nInfoHeight; |
|
rcFocus.left = ( rcScrub.left + rcScrub.right ) / 2; |
|
rcFocus.right = rcFocus.left; |
|
|
|
POINT pt; |
|
pt.x = pt.y = 0; |
|
ClientToScreen( (HWND)getHandle(), &pt ); |
|
|
|
OffsetRect( &rcFocus, pt.x, pt.y ); |
|
|
|
CFocusRect fr; |
|
fr.m_rcFocus = rcFocus; |
|
fr.m_rcOrig = rcFocus; |
|
|
|
m_FocusRects.AddToTail( fr ); |
|
|
|
m_xStart = mx; |
|
m_yStart = my; |
|
|
|
m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) ); |
|
|
|
DrawFocusRect(); |
|
} |
|
else |
|
{ |
|
float t = GetTimeValueForMouse( mx ); |
|
|
|
ClampTimeToSelectionInterval( t ); |
|
|
|
SetScrubTargetTime( t ); |
|
|
|
// Unpause the scene |
|
m_bPaused = false; |
|
redraw(); |
|
} |
|
} |
|
else if ( IsMouseOverSceneEndTime( mx ) ) |
|
{ |
|
redraw(); |
|
StartDraggingSceneEndTime( mx, my ); |
|
} |
|
else if ( m_pClickedChannel && |
|
m_nClickedChannelCloseCaptionButton != CChoreoChannelWidget::CLOSECAPTION_NONE && |
|
m_nClickedChannelCloseCaptionButton != CChoreoChannelWidget::CLOSECAPTION_CAPTION ) |
|
{ |
|
switch ( m_nClickedChannelCloseCaptionButton ) |
|
{ |
|
default: |
|
case CChoreoChannelWidget::CLOSECAPTION_EXPANDCOLLAPSE: |
|
{ |
|
OnToggleCloseCaptionTags(); |
|
} |
|
break; |
|
case CChoreoChannelWidget::CLOSECAPTION_PREVLANGUAGE: |
|
{ |
|
// Change language |
|
int id = GetCloseCaptionLanguageId(); |
|
--id; |
|
if ( id < 0 ) |
|
{ |
|
id = CC_NUM_LANGUAGES - 1; |
|
Assert( id >= 0 ); |
|
} |
|
SetCloseCaptionLanguageId( id ); |
|
redraw(); |
|
} |
|
break; |
|
case CChoreoChannelWidget::CLOSECAPTION_NEXTLANGUAGE: |
|
{ |
|
int id = GetCloseCaptionLanguageId(); |
|
++id; |
|
if ( id >= CC_NUM_LANGUAGES ) |
|
{ |
|
id = 0; |
|
} |
|
SetCloseCaptionLanguageId( id ); |
|
redraw(); |
|
} |
|
break; |
|
case CChoreoChannelWidget::CLOSECAPTION_SELECTOR: |
|
{ |
|
SetDirty( true ); |
|
|
|
PushUndo( "Change selector" ); |
|
|
|
m_pClickedChannel->HandleSelectorClicked(); |
|
|
|
PushRedo( "Change selector" ); |
|
|
|
redraw(); |
|
} |
|
break; |
|
} |
|
} |
|
else |
|
{ |
|
if ( !( event->modifiers & ( mxEvent::KeyCtrl | mxEvent::KeyShift ) ) ) |
|
{ |
|
DeselectAll(); |
|
|
|
if ( !isrightbutton ) |
|
{ |
|
if ( realtime - m_flLastMouseClickTime < 0.3f ) |
|
{ |
|
OnDoubleClicked(); |
|
m_flLastMouseClickTime = -1.0f; |
|
} |
|
else |
|
{ |
|
m_flLastMouseClickTime = realtime; |
|
} |
|
} |
|
|
|
redraw(); |
|
} |
|
} |
|
|
|
CalcBounds( m_nDragType ); |
|
} |
|
|
|
void CChoreoView::OnDoubleClicked() |
|
{ |
|
if ( m_pClickedChannel ) |
|
{ |
|
switch (m_nClickedChannelCloseCaptionButton ) |
|
{ |
|
default: |
|
break; |
|
case CChoreoChannelWidget::CLOSECAPTION_NONE: |
|
{ |
|
SetDirty( true ); |
|
PushUndo( "Enable/disable Channel" ); |
|
|
|
m_pClickedChannel->GetChannel()->SetActive( !m_pClickedChannel->GetChannel()->GetActive() ); |
|
|
|
PushRedo( "Enable/disable Channel" ); |
|
} |
|
break; |
|
case CChoreoChannelWidget::CLOSECAPTION_CAPTION: |
|
{ |
|
CChoreoEvent *e = m_pClickedChannel->GetCaptionClickedEvent(); |
|
if ( e && e->GetNumSlaves() >= 1 ) |
|
{ |
|
OnChangeCloseCaptionToken( e ); |
|
} |
|
} |
|
break; |
|
} |
|
|
|
return; |
|
} |
|
|
|
if ( m_pClickedActor ) |
|
{ |
|
SetDirty( true ); |
|
PushUndo( "Enable/disable Actor" ); |
|
|
|
m_pClickedActor->GetActor()->SetActive( !m_pClickedActor->GetActor()->GetActive() ); |
|
|
|
PushRedo( "Enable/disable Actor" ); |
|
return; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : mx - |
|
// my - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::MouseContinueDrag( mxEvent *event, int mx, int my ) |
|
{ |
|
if ( !m_bDragging ) |
|
return; |
|
|
|
DrawFocusRect(); |
|
|
|
ApplyBounds( mx, my ); |
|
|
|
for ( int i = 0; i < m_FocusRects.Size(); i++ ) |
|
{ |
|
CFocusRect *f = &m_FocusRects[ i ]; |
|
f->m_rcFocus = f->m_rcOrig; |
|
|
|
switch ( m_nDragType ) |
|
{ |
|
default: |
|
case DRAGTYPE_SCRUBBER: |
|
{ |
|
float t = GetTimeValueForMouse( mx ); |
|
t += m_flScrubberTimeOffset; |
|
|
|
ClampTimeToSelectionInterval( t ); |
|
|
|
float dt = t - m_flScrub; |
|
|
|
SetScrubTargetTime( t ); |
|
|
|
m_bSimulating = true; |
|
ScrubThink( dt, true, this ); |
|
|
|
SetScrubTime( t ); |
|
|
|
OffsetRect( &f->m_rcFocus, ( mx - m_xStart ), 0 ); |
|
} |
|
break; |
|
case DRAGTYPE_EVENT_MOVE: |
|
case DRAGTYPE_EVENTTAG_MOVE: |
|
case DRAGTYPE_EVENTABSTAG_MOVE: |
|
{ |
|
int dx = mx - m_xStart; |
|
int dy = my - m_yStart; |
|
if ( m_pClickedEvent ) |
|
{ |
|
bool shiftdown = ( event->modifiers & mxEvent::KeyShift ) ? true : false; |
|
|
|
|
|
// Only allow jumping channels if shift is down |
|
if ( !shiftdown ) |
|
{ |
|
dy = 0; |
|
} |
|
if ( abs( dy ) < m_pClickedEvent->GetItemHeight() ) |
|
{ |
|
dy = 0; |
|
} |
|
if ( m_nSelectedEvents > 1 ) |
|
{ |
|
dy = 0; |
|
} |
|
if ( m_nDragType == DRAGTYPE_EVENTTAG_MOVE || m_nDragType == DRAGTYPE_EVENTABSTAG_MOVE ) |
|
{ |
|
dy = 0; |
|
} |
|
|
|
if ( m_pClickedEvent->GetEvent()->IsUsingRelativeTag() ) |
|
{ |
|
dx = 0; |
|
} |
|
} |
|
else |
|
{ |
|
dy = 0; |
|
} |
|
OffsetRect( &f->m_rcFocus, dx, dy ); |
|
} |
|
break; |
|
case DRAGTYPE_EVENT_STARTTIME: |
|
case DRAGTYPE_EVENT_STARTTIME_RESCALE: |
|
f->m_rcFocus.left += ( mx - m_xStart ); |
|
break; |
|
case DRAGTYPE_EVENT_ENDTIME: |
|
case DRAGTYPE_EVENT_ENDTIME_RESCALE: |
|
f->m_rcFocus.right += ( mx - m_xStart ); |
|
break; |
|
case DRAGTYPE_SCENE_ENDTIME: |
|
OffsetRect( &f->m_rcFocus, ( mx - m_xStart ), 0 ); |
|
break; |
|
case DRAGTYPE_RESCALELEFT: |
|
case DRAGTYPE_RESCALERIGHT: |
|
//f->m_rcFocus.right += ( mx - m_xStart ); |
|
break; |
|
} |
|
} |
|
|
|
if ( m_nDragType == DRAGTYPE_RESCALELEFT || |
|
m_nDragType == DRAGTYPE_RESCALERIGHT ) |
|
{ |
|
int c = m_FocusRects.Count(); |
|
int m_nStart = INT_MAX; |
|
int m_nEnd = INT_MIN; |
|
|
|
for ( int i = 0; i < c; ++i ) |
|
{ |
|
CFocusRect *f = &m_FocusRects[ i ]; |
|
if ( f->m_rcFocus.left < m_nStart ) |
|
{ |
|
m_nStart = f->m_rcFocus.left; |
|
} |
|
if ( f->m_rcFocus.right > m_nEnd ) |
|
{ |
|
m_nEnd = f->m_rcFocus.right; |
|
} |
|
} |
|
|
|
// Now figure out rescaling logic |
|
int dxPixels = mx - m_xStart; |
|
|
|
int oldSize = m_nEnd - m_nStart; |
|
if ( oldSize > 0 ) |
|
{ |
|
float rescale = 1.0f; |
|
if ( m_nDragType == DRAGTYPE_RESCALERIGHT ) |
|
{ |
|
rescale = (float)( oldSize + dxPixels )/(float)oldSize; |
|
} |
|
else |
|
{ |
|
rescale = (float)( oldSize - dxPixels )/(float)oldSize; |
|
} |
|
|
|
for ( int i = 0; i < c; ++i ) |
|
{ |
|
CFocusRect *f = &m_FocusRects[ i ]; |
|
int w = f->m_rcFocus.right - f->m_rcFocus.left; |
|
if ( m_nDragType == DRAGTYPE_RESCALERIGHT ) |
|
{ |
|
f->m_rcFocus.left = m_nStart + ( int )( rescale * (float)( f->m_rcFocus.left - m_nStart ) + 0.5f ); |
|
f->m_rcFocus.right = f->m_rcFocus.left + ( int )( rescale * (float)w + 0.5f ); |
|
} |
|
else |
|
{ |
|
f->m_rcFocus.right = m_nEnd - ( int )( rescale * (float)( m_nEnd - f->m_rcFocus.right ) + 0.5f ); |
|
f->m_rcFocus.left = f->m_rcFocus.right - ( int )( rescale * (float)w + 0.5f ); |
|
} |
|
} |
|
} |
|
} |
|
DrawFocusRect(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : mx - |
|
// my - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::MouseMove( int mx, int my ) |
|
{ |
|
if ( m_bDragging ) |
|
return; |
|
|
|
int dragtype = ComputeEventDragType( mx, my ); |
|
if ( dragtype == DRAGTYPE_NONE ) |
|
{ |
|
CChoreoGlobalEventWidget *ge = NULL; |
|
GetObjectsUnderMouse( mx, my, NULL, NULL, NULL, &ge, NULL, NULL, NULL ); |
|
if ( ge ) |
|
{ |
|
dragtype = DRAGTYPE_EVENT_MOVE; |
|
} |
|
|
|
if ( dragtype == DRAGTYPE_NONE ) |
|
{ |
|
if ( IsMouseOverSceneEndTime( mx ) ) |
|
{ |
|
dragtype = DRAGTYPE_SCENE_ENDTIME; |
|
} |
|
} |
|
} |
|
|
|
if ( m_hPrevCursor ) |
|
{ |
|
SetCursor( m_hPrevCursor ); |
|
m_hPrevCursor = NULL; |
|
} |
|
switch ( dragtype ) |
|
{ |
|
default: |
|
break; |
|
case DRAGTYPE_EVENTTAG_MOVE: |
|
m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) ); |
|
break; |
|
case DRAGTYPE_EVENTABSTAG_MOVE: |
|
m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_IBEAM ) ); |
|
break; |
|
case DRAGTYPE_EVENT_MOVE: |
|
m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEALL ) ); |
|
break; |
|
case DRAGTYPE_EVENT_STARTTIME: |
|
case DRAGTYPE_EVENT_STARTTIME_RESCALE: |
|
case DRAGTYPE_EVENT_ENDTIME: |
|
case DRAGTYPE_EVENT_ENDTIME_RESCALE: |
|
case DRAGTYPE_SCENE_ENDTIME: |
|
case DRAGTYPE_RESCALELEFT: |
|
case DRAGTYPE_RESCALERIGHT: |
|
m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) ); |
|
break; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *e - |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CChoreoView::CheckGestureLength( CChoreoEvent *e, bool bCheckOnly ) |
|
{ |
|
Assert( e ); |
|
if ( !e ) |
|
return false; |
|
|
|
if ( e->GetType() != CChoreoEvent::GESTURE ) |
|
{ |
|
Con_Printf( "CheckGestureLength: called on non-GESTURE event %s\n", e->GetName() ); |
|
return false; |
|
} |
|
|
|
StudioModel *model = FindAssociatedModel( e->GetScene(), e->GetActor() ); |
|
if ( !model ) |
|
return false; |
|
|
|
CStudioHdr *pStudioHdr = model->GetStudioHdr(); |
|
if ( !pStudioHdr ) |
|
return false; |
|
|
|
return UpdateGestureLength( e, pStudioHdr, model->GetPoseParameters(), bCheckOnly ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *e - |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CChoreoView::DefaultGestureLength( CChoreoEvent *e, bool bCheckOnly ) |
|
{ |
|
Assert( e ); |
|
if ( !e ) |
|
return false; |
|
|
|
if ( e->GetType() != CChoreoEvent::GESTURE ) |
|
{ |
|
Con_Printf( "DefaultGestureLength: called on non-GESTURE event %s\n", e->GetName() ); |
|
return false; |
|
} |
|
|
|
StudioModel *model = FindAssociatedModel( e->GetScene(), e->GetActor() ); |
|
if ( !model ) |
|
return false; |
|
|
|
if ( !model->GetStudioHdr() ) |
|
return false; |
|
|
|
int iSequence = model->LookupSequence( e->GetParameters() ); |
|
if ( iSequence < 0 ) |
|
return false; |
|
|
|
bool bret = false; |
|
|
|
float seqduration = model->GetDuration( iSequence ); |
|
if ( seqduration != 0.0f ) |
|
{ |
|
bret = true; |
|
if ( !bCheckOnly ) |
|
{ |
|
e->SetEndTime( e->GetStartTime() + seqduration ); |
|
} |
|
} |
|
|
|
return bret; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *e - |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CChoreoView::AutoaddGestureKeys( CChoreoEvent *e, bool bCheckOnly ) |
|
{ |
|
if ( !e ) |
|
return false; |
|
|
|
StudioModel *model = FindAssociatedModel( e->GetScene(), e->GetActor() ); |
|
if ( !model ) |
|
return false; |
|
|
|
CStudioHdr *pStudioHdr = model->GetStudioHdr(); |
|
if ( !pStudioHdr ) |
|
return false; |
|
|
|
return AutoAddGestureKeys( e, pStudioHdr, model->GetPoseParameters(), bCheckOnly ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CChoreoView::CheckSequenceLength( CChoreoEvent *e, bool bCheckOnly ) |
|
{ |
|
Assert( e ); |
|
if ( !e ) |
|
return false; |
|
|
|
if ( e->GetType() != CChoreoEvent::SEQUENCE ) |
|
{ |
|
Con_Printf( "CheckSequenceLength: called on non-SEQUENCE event %s\n", e->GetName() ); |
|
return false; |
|
} |
|
|
|
StudioModel *model = FindAssociatedModel( e->GetScene(), e->GetActor() ); |
|
if ( !model ) |
|
return false; |
|
|
|
CStudioHdr *pStudioHdr = model->GetStudioHdr(); |
|
if ( !pStudioHdr ) |
|
return false; |
|
|
|
return UpdateSequenceLength( e, pStudioHdr, model->GetPoseParameters(), bCheckOnly, true ); |
|
} |
|
|
|
void CChoreoView::FinishDraggingSceneEndTime( mxEvent *event, int mx, int my ) |
|
{ |
|
DrawFocusRect(); |
|
|
|
m_FocusRects.Purge(); |
|
|
|
m_bDragging = false; |
|
|
|
float mouse_dt = GetTimeDeltaForMouseDelta( mx, m_xStart ); |
|
if ( !mouse_dt ) |
|
{ |
|
return; |
|
} |
|
|
|
SetDirty( true ); |
|
|
|
const char *desc = "Change Scene Duration"; |
|
|
|
PushUndo( desc ); |
|
|
|
float newendtime = GetTimeValueForMouse( mx ); |
|
float oldendtime = m_pScene->FindStopTime(); |
|
|
|
float scene_dt = newendtime - oldendtime; |
|
|
|
for ( int i = 0; i < m_SceneActors.Size(); i++ ) |
|
{ |
|
CChoreoActorWidget *a = m_SceneActors[ i ]; |
|
if ( !a ) |
|
continue; |
|
|
|
for ( int j = 0; j < a->GetNumChannels(); j++ ) |
|
{ |
|
CChoreoChannelWidget *channel = a->GetChannel( j ); |
|
if ( !channel ) |
|
continue; |
|
|
|
int k; |
|
|
|
CChoreoEvent *finalGesture = NULL; |
|
for ( k = channel->GetNumEvents() - 1; k >= 0; k-- ) |
|
{ |
|
CChoreoEventWidget *event = channel->GetEvent( k ); |
|
CChoreoEvent *e = event->GetEvent(); |
|
if ( e->GetType() != CChoreoEvent::GESTURE ) |
|
continue; |
|
|
|
if ( !finalGesture ) |
|
{ |
|
finalGesture = e; |
|
} |
|
else |
|
{ |
|
if ( e->GetStartTime() > finalGesture->GetStartTime() ) |
|
{ |
|
finalGesture = e; |
|
} |
|
} |
|
} |
|
|
|
|
|
for ( k = channel->GetNumEvents() - 1; k >= 0; k-- ) |
|
{ |
|
CChoreoEventWidget *event = channel->GetEvent( k ); |
|
CChoreoEvent *e = event->GetEvent(); |
|
|
|
// Event starts after new end time, kill it |
|
if ( e->GetStartTime() > newendtime ) |
|
{ |
|
channel->GetChannel()->RemoveEvent( e ); |
|
m_pScene->DeleteReferencedObjects( e ); |
|
continue; |
|
} |
|
|
|
// No change to normal events that end earlier than new time (but do change gestures) |
|
if ( e->GetEndTime() < newendtime && |
|
e != finalGesture ) |
|
{ |
|
continue; |
|
} |
|
|
|
float dt = scene_dt; |
|
if ( e->GetType() == CChoreoEvent::GESTURE ) |
|
{ |
|
if ( e->GetEndTime() < newendtime ) |
|
{ |
|
dt = newendtime - e->GetEndTime(); |
|
} |
|
} |
|
|
|
float newduration = e->GetDuration() + dt; |
|
RescaleRamp( e, newduration ); |
|
switch ( e->GetType() ) |
|
{ |
|
default: |
|
break; |
|
case CChoreoEvent::GESTURE: |
|
{ |
|
e->RescaleGestureTimes( e->GetStartTime(), e->GetEndTime() + dt, true ); |
|
} |
|
break; |
|
case CChoreoEvent::FLEXANIMATION: |
|
{ |
|
RescaleExpressionTimes( e, e->GetStartTime(), e->GetEndTime() + dt ); |
|
} |
|
break; |
|
} |
|
e->OffsetEndTime( dt ); |
|
e->SnapTimes(); |
|
e->ResortRamp(); |
|
} |
|
} |
|
} |
|
|
|
// Remove event and move to new object |
|
DeleteSceneWidgets(); |
|
|
|
m_nDragType = DRAGTYPE_NONE; |
|
|
|
if ( m_hPrevCursor ) |
|
{ |
|
SetCursor( m_hPrevCursor ); |
|
m_hPrevCursor = 0; |
|
} |
|
|
|
PushRedo( desc ); |
|
|
|
CreateSceneWidgets(); |
|
|
|
InvalidateLayout(); |
|
|
|
g_pExpressionTool->LayoutItems( true ); |
|
g_pExpressionTool->redraw(); |
|
g_pGestureTool->redraw(); |
|
g_pRampTool->redraw(); |
|
g_pSceneRampTool->redraw(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Called after association changes to reset .wav file images |
|
// Input : - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::RecomputeWaves() |
|
{ |
|
for ( int i = 0; i < m_SceneActors.Size(); i++ ) |
|
{ |
|
CChoreoActorWidget *a = m_SceneActors[ i ]; |
|
if ( !a ) |
|
continue; |
|
|
|
for ( int j = 0; j < a->GetNumChannels(); j++ ) |
|
{ |
|
CChoreoChannelWidget *c = a->GetChannel( j ); |
|
if ( !c ) |
|
continue; |
|
|
|
for ( int k = 0; k < c->GetNumEvents(); k++ ) |
|
{ |
|
CChoreoEventWidget *e = c->GetEvent( k ); |
|
if ( !e ) |
|
continue; |
|
|
|
e->RecomputeWave(); |
|
} |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : mx - |
|
// my - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::FinishDraggingEvent( mxEvent *event, int mx, int my ) |
|
{ |
|
DrawFocusRect(); |
|
|
|
m_FocusRects.Purge(); |
|
|
|
m_bDragging = false; |
|
|
|
float dt = GetTimeDeltaForMouseDelta( mx, m_xStart ); |
|
if ( !dt ) |
|
{ |
|
if ( m_pScene && m_pClickedEvent && m_pClickedEvent->GetEvent()->GetType() == CChoreoEvent::SPEAK ) |
|
{ |
|
// Show phone wav in wav viewer |
|
char sndname[ 512 ]; |
|
Q_strncpy( sndname, FacePoser_TranslateSoundName( m_pClickedEvent->GetEvent() ), sizeof( sndname ) ); |
|
if ( sndname[ 0 ] ) |
|
{ |
|
SetCurrentWaveFile( va( "sound/%s", sndname ), m_pClickedEvent->GetEvent() ); |
|
} |
|
else |
|
{ |
|
Warning( "Unable to resolve sound name for '%s', check actor associations\n", m_pClickedEvent->GetEvent()->GetName() ); |
|
} |
|
} |
|
return; |
|
} |
|
|
|
SetDirty( true ); |
|
|
|
char const *desc = ""; |
|
|
|
switch ( m_nDragType ) |
|
{ |
|
default: |
|
case DRAGTYPE_EVENT_MOVE: |
|
desc = "Event Move"; |
|
break; |
|
case DRAGTYPE_EVENT_STARTTIME: |
|
case DRAGTYPE_EVENT_STARTTIME_RESCALE: |
|
desc = "Change Start Time"; |
|
break; |
|
case DRAGTYPE_EVENT_ENDTIME: |
|
case DRAGTYPE_EVENT_ENDTIME_RESCALE: |
|
desc = "Change End Time"; |
|
break; |
|
case DRAGTYPE_EVENTTAG_MOVE: |
|
desc = "Move Event Tag"; |
|
break; |
|
case DRAGTYPE_EVENTABSTAG_MOVE: |
|
desc = "Move Abs Event Tag"; |
|
break; |
|
case DRAGTYPE_RESCALELEFT: |
|
case DRAGTYPE_RESCALERIGHT: |
|
desc = "Rescale Time"; |
|
break; |
|
} |
|
PushUndo( desc ); |
|
|
|
CUtlVector< CChoreoEvent * > rescaleHelper; |
|
|
|
for ( int i = 0; i < m_SceneActors.Size(); i++ ) |
|
{ |
|
CChoreoActorWidget *actor = m_SceneActors[ i ]; |
|
if ( !actor ) |
|
continue; |
|
|
|
for ( int j = 0; j < actor->GetNumChannels(); j++ ) |
|
{ |
|
CChoreoChannelWidget *channel = actor->GetChannel( j ); |
|
if ( !channel ) |
|
continue; |
|
|
|
for ( int k = 0; k < channel->GetNumEvents(); k++ ) |
|
{ |
|
CChoreoEventWidget *event = channel->GetEvent( k ); |
|
if ( !event ) |
|
continue; |
|
|
|
if ( !event->IsSelected() ) |
|
continue; |
|
|
|
// Figure out true dt |
|
CChoreoEvent *e = event->GetEvent(); |
|
if ( e ) |
|
{ |
|
switch ( m_nDragType ) |
|
{ |
|
default: |
|
case DRAGTYPE_EVENT_MOVE: |
|
e->OffsetTime( dt ); |
|
e->SnapTimes(); |
|
break; |
|
case DRAGTYPE_EVENT_STARTTIME: |
|
case DRAGTYPE_EVENT_STARTTIME_RESCALE: |
|
{ |
|
float newduration = e->GetDuration() - dt; |
|
RescaleRamp( e, newduration ); |
|
switch ( e->GetType() ) |
|
{ |
|
default: |
|
break; |
|
case CChoreoEvent::GESTURE: |
|
{ |
|
e->RescaleGestureTimes( e->GetStartTime() + dt, e->GetEndTime(), m_nDragType == DRAGTYPE_EVENT_STARTTIME ); |
|
} |
|
break; |
|
case CChoreoEvent::FLEXANIMATION: |
|
{ |
|
RescaleExpressionTimes( e, e->GetStartTime() + dt, e->GetEndTime() ); |
|
} |
|
break; |
|
} |
|
e->OffsetStartTime( dt ); |
|
e->SnapTimes(); |
|
e->ResortRamp(); |
|
} |
|
break; |
|
case DRAGTYPE_EVENT_ENDTIME: |
|
case DRAGTYPE_EVENT_ENDTIME_RESCALE: |
|
{ |
|
float newduration = e->GetDuration() + dt; |
|
RescaleRamp( e, newduration ); |
|
switch ( e->GetType() ) |
|
{ |
|
default: |
|
break; |
|
case CChoreoEvent::GESTURE: |
|
{ |
|
e->RescaleGestureTimes( e->GetStartTime(), e->GetEndTime() + dt, m_nDragType == DRAGTYPE_EVENT_ENDTIME ); |
|
} |
|
break; |
|
case CChoreoEvent::FLEXANIMATION: |
|
{ |
|
RescaleExpressionTimes( e, e->GetStartTime(), e->GetEndTime() + dt ); |
|
} |
|
break; |
|
} |
|
e->OffsetEndTime( dt ); |
|
e->SnapTimes(); |
|
e->ResortRamp(); |
|
} |
|
break; |
|
case DRAGTYPE_RESCALELEFT: |
|
case DRAGTYPE_RESCALERIGHT: |
|
{ |
|
rescaleHelper.AddToTail( e ); |
|
} |
|
break; |
|
case DRAGTYPE_EVENTTAG_MOVE: |
|
{ |
|
// Get current x position |
|
if ( m_nClickedTag != -1 ) |
|
{ |
|
CEventRelativeTag *tag = e->GetRelativeTag( m_nClickedTag ); |
|
if ( tag ) |
|
{ |
|
float dx = mx - m_xStart; |
|
// Determine left edcge |
|
RECT bounds; |
|
bounds = event->getBounds(); |
|
if ( bounds.right - bounds.left > 0 ) |
|
{ |
|
int left = bounds.left + (int)( tag->GetPercentage() * (float)( bounds.right - bounds.left ) + 0.5f ); |
|
|
|
left += dx; |
|
|
|
if ( left < bounds.left ) |
|
{ |
|
left = bounds.left; |
|
} |
|
else if ( left >= bounds.right ) |
|
{ |
|
left = bounds.right - 1; |
|
} |
|
|
|
// Now convert back to a percentage |
|
float frac = (float)( left - bounds.left ) / (float)( bounds.right - bounds.left ); |
|
|
|
tag->SetPercentage( frac ); |
|
} |
|
} |
|
} |
|
} |
|
break; |
|
case DRAGTYPE_EVENTABSTAG_MOVE: |
|
{ |
|
// Get current x position |
|
if ( m_pClickedAbsoluteTag != NULL ) |
|
{ |
|
CEventAbsoluteTag *tag = m_pClickedAbsoluteTag; |
|
if ( tag ) |
|
{ |
|
float dx = mx - m_xStart; |
|
// Determine left edcge |
|
RECT bounds; |
|
bounds = event->getBounds(); |
|
if ( bounds.right - bounds.left > 0 ) |
|
{ |
|
int left = bounds.left + (int)( tag->GetPercentage() * (float)( bounds.right - bounds.left ) + 0.5f ); |
|
|
|
left += dx; |
|
|
|
if ( left < bounds.left ) |
|
{ |
|
left = bounds.left; |
|
} |
|
else if ( left >= bounds.right ) |
|
{ |
|
left = bounds.right - 1; |
|
} |
|
|
|
// Now convert back to a percentage |
|
float frac = (float)( left - bounds.left ) / (float)( bounds.right - bounds.left ); |
|
|
|
tag->SetPercentage( frac ); |
|
} |
|
} |
|
} |
|
} |
|
break; |
|
} |
|
} |
|
|
|
switch ( e->GetType() ) |
|
{ |
|
default: |
|
break; |
|
case CChoreoEvent::SPEAK: |
|
{ |
|
// Try and load wav to get length |
|
CAudioSource *wave = sound->LoadSound( va( "sound/%s", FacePoser_TranslateSoundName( e ) ) ); |
|
if ( wave ) |
|
{ |
|
e->SetEndTime( e->GetStartTime() + wave->GetRunningLength() ); |
|
delete wave; |
|
} |
|
} |
|
break; |
|
case CChoreoEvent::SEQUENCE: |
|
{ |
|
CheckSequenceLength( e, false ); |
|
} |
|
break; |
|
case CChoreoEvent::GESTURE: |
|
{ |
|
CheckGestureLength( e, false ); |
|
} |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
|
|
if ( rescaleHelper.Count() > 0 ) |
|
{ |
|
int i; |
|
// Determine start and end times for existing "selection" |
|
float flStart = FLT_MAX; |
|
float flEnd = FLT_MIN; |
|
for ( i = 0; i < rescaleHelper.Count(); ++i ) |
|
{ |
|
CChoreoEvent *e = rescaleHelper[ i ]; |
|
float st = e->GetStartTime(); |
|
float ed = e->GetEndTime(); |
|
|
|
if ( st < flStart ) |
|
{ |
|
flStart = st; |
|
} |
|
if ( ed > flEnd ) |
|
{ |
|
flEnd = ed; |
|
} |
|
} |
|
|
|
float flSelectionDuration = flEnd - flStart; |
|
if ( flSelectionDuration > 0.0f ) |
|
{ |
|
float flNewDuration = 0.0f; |
|
if ( m_nDragType == DRAGTYPE_RESCALELEFT ) |
|
{ |
|
flNewDuration = max( 0.1f, flSelectionDuration - dt ); |
|
} |
|
else |
|
{ |
|
flNewDuration = max( 0.1f, flSelectionDuration + dt ); |
|
} |
|
float flScale = flNewDuration / flSelectionDuration; |
|
|
|
for ( i = 0; i < rescaleHelper.Count(); ++i ) |
|
{ |
|
CChoreoEvent *e = rescaleHelper[ i ]; |
|
float st = e->GetStartTime(); |
|
float et = e->HasEndTime() ? e->GetEndTime() : e->GetStartTime(); |
|
float flTimeFromStart = st - flStart; |
|
float flTimeFromEnd = flEnd - et; |
|
float flDuration = e->GetDuration(); |
|
|
|
float flNewStartTime = 0.0f; |
|
float flNewDuration = 0.0f; |
|
|
|
if ( m_nDragType == DRAGTYPE_RESCALELEFT ) |
|
{ |
|
float flNewEndTime = flEnd - flTimeFromEnd * flScale; |
|
if ( !e->HasEndTime() || e->IsFixedLength() ) |
|
{ |
|
e->OffsetTime( flNewEndTime - flDuration - st ); |
|
continue; |
|
} |
|
flNewDuration = flDuration * flScale; |
|
flNewStartTime = flNewEndTime - flNewDuration; |
|
} |
|
else |
|
{ |
|
flNewStartTime = flTimeFromStart * flScale + flStart; |
|
if ( !e->HasEndTime() || e->IsFixedLength() ) |
|
{ |
|
e->OffsetTime( flNewStartTime - st ); |
|
continue; |
|
} |
|
flNewDuration = flDuration * flScale; |
|
} |
|
|
|
RescaleRamp( e, flNewDuration ); |
|
switch ( e->GetType() ) |
|
{ |
|
default: |
|
break; |
|
case CChoreoEvent::GESTURE: |
|
{ |
|
e->RescaleGestureTimes( flNewStartTime, flNewStartTime + flNewDuration, m_nDragType == DRAGTYPE_EVENT_STARTTIME || m_nDragType == DRAGTYPE_EVENT_ENDTIME ); |
|
} |
|
break; |
|
case CChoreoEvent::FLEXANIMATION: |
|
{ |
|
RescaleExpressionTimes( e, flNewStartTime, flNewStartTime + flNewDuration ); |
|
} |
|
break; |
|
} |
|
|
|
e->SetStartTime( flNewStartTime ); |
|
Assert( e->HasEndTime() ); |
|
e->SetEndTime( flNewStartTime + flNewDuration ); |
|
} |
|
} |
|
} |
|
|
|
for ( int i = 0; i < m_SceneGlobalEvents.Count(); i++ ) |
|
{ |
|
CChoreoGlobalEventWidget *gew = m_SceneGlobalEvents[ i ]; |
|
if ( !gew || !gew->IsSelected() ) |
|
continue; |
|
|
|
CChoreoEvent *e = gew->GetEvent(); |
|
if ( !e ) |
|
continue; |
|
|
|
e->OffsetTime( dt ); |
|
e->SnapTimes(); |
|
} |
|
|
|
m_nDragType = DRAGTYPE_NONE; |
|
|
|
if ( m_hPrevCursor ) |
|
{ |
|
SetCursor( m_hPrevCursor ); |
|
m_hPrevCursor = 0; |
|
} |
|
|
|
CChoreoEvent *e = m_pClickedEvent ? m_pClickedEvent->GetEvent() : NULL; |
|
|
|
if ( e ) |
|
{ |
|
// See if event is moving to a new owner |
|
CChoreoChannelWidget *chOrig, *chNew; |
|
|
|
int dy = my - m_yStart; |
|
bool shiftdown = ( event->modifiers & mxEvent::KeyShift ) ? true : false; |
|
if ( !shiftdown ) |
|
{ |
|
dy = 0; |
|
} |
|
|
|
if ( abs( dy ) < m_pClickedEvent->GetItemHeight() ) |
|
{ |
|
my = m_yStart; |
|
} |
|
|
|
chNew = GetChannelUnderCursorPos( mx, my ); |
|
|
|
InvalidateLayout(); |
|
|
|
mx = m_xStart; |
|
my = m_yStart; |
|
|
|
chOrig = m_pClickedChannel; |
|
|
|
if ( chOrig && chNew && chOrig != chNew ) |
|
{ |
|
// Swap underlying objects |
|
CChoreoChannel *pOrigChannel, *pNewChannel; |
|
|
|
pOrigChannel = chOrig->GetChannel(); |
|
pNewChannel = chNew->GetChannel(); |
|
|
|
Assert( pOrigChannel && pNewChannel ); |
|
|
|
// Remove event and move to new object |
|
DeleteSceneWidgets(); |
|
|
|
pOrigChannel->RemoveEvent( e ); |
|
pNewChannel->AddEvent( e ); |
|
|
|
e->SetChannel( pNewChannel ); |
|
e->SetActor( pNewChannel->GetActor() ); |
|
|
|
CreateSceneWidgets(); |
|
} |
|
else |
|
{ |
|
if ( e && e->GetType() == CChoreoEvent::SPEAK ) |
|
{ |
|
// Show phone wav in wav viewer |
|
SetCurrentWaveFile( va( "sound/%s", FacePoser_TranslateSoundName( e ) ), e ); |
|
} |
|
} |
|
} |
|
|
|
PushRedo( desc ); |
|
InvalidateLayout(); |
|
|
|
if ( e ) |
|
{ |
|
switch ( e->GetType() ) |
|
{ |
|
default: |
|
break; |
|
case CChoreoEvent::FLEXANIMATION: |
|
{ |
|
g_pExpressionTool->SetEvent( e ); |
|
g_pFlexPanel->SetEvent( e ); |
|
} |
|
break; |
|
case CChoreoEvent::GESTURE: |
|
{ |
|
g_pGestureTool->SetEvent( e ); |
|
} |
|
break; |
|
} |
|
|
|
if ( e->HasEndTime() ) |
|
{ |
|
g_pRampTool->SetEvent( e ); |
|
} |
|
} |
|
g_pExpressionTool->LayoutItems( true ); |
|
g_pExpressionTool->redraw(); |
|
g_pGestureTool->redraw(); |
|
g_pRampTool->redraw(); |
|
g_pSceneRampTool->redraw(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : mx - |
|
// my - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::MouseFinishDrag( mxEvent *event, int mx, int my ) |
|
{ |
|
if ( !m_bDragging ) |
|
return; |
|
|
|
ApplyBounds( mx, my ); |
|
|
|
switch ( m_nDragType ) |
|
{ |
|
case DRAGTYPE_SCRUBBER: |
|
{ |
|
DrawFocusRect(); |
|
|
|
m_FocusRects.Purge(); |
|
|
|
float t = GetTimeValueForMouse( mx ); |
|
t += m_flScrubberTimeOffset; |
|
m_flScrubberTimeOffset = 0.0f; |
|
|
|
ClampTimeToSelectionInterval( t ); |
|
|
|
SetScrubTime( t ); |
|
SetScrubTargetTime( t ); |
|
|
|
m_bDragging = false; |
|
m_nDragType = DRAGTYPE_NONE; |
|
|
|
redraw(); |
|
} |
|
break; |
|
case DRAGTYPE_EVENT_MOVE: |
|
case DRAGTYPE_EVENT_STARTTIME: |
|
case DRAGTYPE_EVENT_STARTTIME_RESCALE: |
|
case DRAGTYPE_EVENT_ENDTIME: |
|
case DRAGTYPE_EVENT_ENDTIME_RESCALE: |
|
case DRAGTYPE_EVENTTAG_MOVE: |
|
case DRAGTYPE_EVENTABSTAG_MOVE: |
|
case DRAGTYPE_RESCALELEFT: |
|
case DRAGTYPE_RESCALERIGHT: |
|
FinishDraggingEvent( event, mx, my ); |
|
break; |
|
case DRAGTYPE_SCENE_ENDTIME: |
|
FinishDraggingSceneEndTime( event, mx, my ); |
|
break; |
|
default: |
|
break; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *event - |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
int CChoreoView::handleEvent( mxEvent *event ) |
|
{ |
|
MDLCACHE_CRITICAL_SECTION_( g_pMDLCache ); |
|
|
|
int iret = 0; |
|
|
|
if ( HandleToolEvent( event ) ) |
|
{ |
|
return iret; |
|
} |
|
|
|
switch ( event->event ) |
|
{ |
|
case mxEvent::MouseWheeled: |
|
{ |
|
CChoreoScene *scene = GetScene(); |
|
if ( scene ) |
|
{ |
|
int tz = GetTimeZoom( GetToolName() ); |
|
bool shiftdown = ( event->modifiers & mxEvent::KeyShift ) ? true : false; |
|
int stepMultipiler = shiftdown ? 5 : 1; |
|
|
|
// Zoom time in / out |
|
if ( event->height > 0 ) |
|
{ |
|
tz = min( tz + TIME_ZOOM_STEP * stepMultipiler, MAX_TIME_ZOOM ); |
|
} |
|
else |
|
{ |
|
tz = max( tz - TIME_ZOOM_STEP * stepMultipiler, TIME_ZOOM_STEP ); |
|
} |
|
|
|
SetTimeZoom( GetToolName(), tz, true ); |
|
|
|
CUtlVector< CChoreoEvent * > selected; |
|
RememberSelectedEvents( selected ); |
|
|
|
DeleteSceneWidgets(); |
|
CreateSceneWidgets(); |
|
|
|
ReselectEvents( selected ); |
|
|
|
InvalidateLayout(); |
|
Con_Printf( "Zoom factor %i %%\n", GetTimeZoom( GetToolName() ) ); |
|
} |
|
iret = 1; |
|
} |
|
break; |
|
case mxEvent::Size: |
|
{ |
|
// Force scroll bars to recompute |
|
ForceScrollBarsToRecompute( false ); |
|
|
|
InvalidateLayout(); |
|
PositionControls(); |
|
iret = 1; |
|
} |
|
break; |
|
case mxEvent::MouseDown: |
|
{ |
|
if ( !m_bDragging ) |
|
{ |
|
if ( event->buttons & mxEvent::MouseRightButton ) |
|
{ |
|
if ( IsMouseOverTimeline( (short)event->x, (short)event->y ) ) |
|
{ |
|
PlaceABPoint( (short)event->x ); |
|
redraw(); |
|
} |
|
else if ( IsMouseOverScrubArea( event ) ) |
|
{ |
|
float t = GetTimeValueForMouse( (short)event->x ); |
|
|
|
ClampTimeToSelectionInterval( t ); |
|
|
|
SetScrubTime( t ); |
|
SetScrubTargetTime( t ); |
|
|
|
sound->Flush(); |
|
|
|
// Unpause the scene |
|
m_bPaused = false; |
|
|
|
redraw(); |
|
} |
|
else |
|
{ |
|
// Show right click menu |
|
ShowContextMenu( (short)event->x, (short)event->y ); |
|
} |
|
} |
|
else |
|
{ |
|
if ( IsMouseOverTimeline( (short)event->x, (short)event->y ) ) |
|
{ |
|
ClearABPoints(); |
|
redraw(); |
|
} |
|
else |
|
{ |
|
// Handle mouse dragging here |
|
MouseStartDrag( event, (short)event->x, (short)event->y ); |
|
} |
|
} |
|
} |
|
iret = 1; |
|
} |
|
break; |
|
case mxEvent::MouseDrag: |
|
{ |
|
MouseContinueDrag( event, (short)event->x, (short)event->y ); |
|
iret = 1; |
|
} |
|
break; |
|
case mxEvent::MouseUp: |
|
{ |
|
MouseFinishDrag( event, (short)event->x, (short)event->y ); |
|
iret = 1; |
|
} |
|
break; |
|
case mxEvent::MouseMove: |
|
{ |
|
MouseMove( (short)event->x, (short)event->y ); |
|
UpdateStatusArea( (short)event->x, (short)event->y ); |
|
iret = 1; |
|
} |
|
break; |
|
case mxEvent::KeyDown: |
|
{ |
|
iret = 1; |
|
|
|
switch ( event->key ) |
|
{ |
|
default: |
|
iret = 0; |
|
break; |
|
case 'E': |
|
if ( GetAsyncKeyState( VK_CONTROL ) ) |
|
{ |
|
OnPlaceNextSpeakEvent(); |
|
} |
|
break; |
|
case VK_ESCAPE: |
|
DeselectAll(); |
|
break; |
|
case 'C': |
|
CopyEvents(); |
|
iret = 1; |
|
break; |
|
case 'V': |
|
PasteEvents(); |
|
redraw(); |
|
break; |
|
case VK_DELETE: |
|
{ |
|
if ( IsActiveTool() ) |
|
{ |
|
DeleteSelectedEvents(); |
|
} |
|
} |
|
break; |
|
case VK_RETURN: |
|
{ |
|
CUtlVector< CChoreoEvent * > events; |
|
GetSelectedEvents( events ); |
|
if ( events.Count() == 1 ) |
|
{ |
|
if ( GetAsyncKeyState( VK_MENU ) ) |
|
{ |
|
EditEvent( events[ 0 ] ); |
|
redraw(); |
|
iret = 1; |
|
} |
|
} |
|
} |
|
break; |
|
case 'Z': // Undo/Redo |
|
{ |
|
if ( GetAsyncKeyState( VK_CONTROL ) ) |
|
{ |
|
if ( GetAsyncKeyState( VK_SHIFT ) ) |
|
{ |
|
if ( CanRedo() ) |
|
{ |
|
Con_Printf( "Redo %s\n", GetRedoDescription() ); |
|
Redo(); |
|
iret = 1; |
|
} |
|
} |
|
else |
|
{ |
|
if ( CanUndo() ) |
|
{ |
|
Con_Printf( "Undo %s\n", GetUndoDescription() ); |
|
Undo(); |
|
iret = 1; |
|
} |
|
} |
|
} |
|
} |
|
break; |
|
|
|
case VK_SPACE: |
|
{ |
|
if ( IsPlayingScene() ) |
|
{ |
|
StopScene(); |
|
} |
|
} |
|
break; |
|
case 188: // VK_OEM_COMMA: |
|
{ |
|
SetScrubTargetTime( 0.0f ); |
|
} |
|
break; |
|
case 190: // VK_OEM_PERIOD: |
|
{ |
|
CChoreoScene *scene = GetScene(); |
|
if ( scene ) |
|
{ |
|
SetScrubTargetTime( scene->FindStopTime() ); |
|
} |
|
} |
|
break; |
|
case VK_LEFT: |
|
{ |
|
CChoreoScene *scene = GetScene(); |
|
if ( scene && scene->GetSceneFPS() > 0 ) |
|
{ |
|
float curscrub = m_flScrub; |
|
curscrub -= ( 1.0f / (float)scene->GetSceneFPS() ); |
|
curscrub = max( curscrub, 0.0f ); |
|
SetScrubTargetTime( curscrub ); |
|
} |
|
} |
|
break; |
|
case VK_RIGHT: |
|
{ |
|
CChoreoScene *scene = GetScene(); |
|
if ( scene && scene->GetSceneFPS() > 0 ) |
|
{ |
|
float curscrub = m_flScrub; |
|
curscrub += ( 1.0f / (float)scene->GetSceneFPS() ); |
|
curscrub = min( curscrub, scene->FindStopTime() ); |
|
SetScrubTargetTime( curscrub ); |
|
} |
|
} |
|
break; |
|
case VK_HOME: |
|
{ |
|
MoveTimeSliderToPos( 0 ); |
|
} |
|
break; |
|
case VK_END: |
|
{ |
|
float maxtime = m_pScene->FindStopTime() - 1.0f; |
|
int pixels = (int)( maxtime * GetPixelsPerSecond() ); |
|
MoveTimeSliderToPos( pixels - 1 ); |
|
} |
|
break; |
|
case VK_PRIOR: // PgUp |
|
{ |
|
int window = w2() - GetLabelWidth(); |
|
m_flLeftOffset = max( m_flLeftOffset - (float)window, 0.0f ); |
|
MoveTimeSliderToPos( (int)m_flLeftOffset ); |
|
} |
|
break; |
|
case VK_NEXT: // PgDown |
|
{ |
|
int window = w2() - GetLabelWidth(); |
|
int pixels = ComputeHPixelsNeeded(); |
|
m_flLeftOffset = min( m_flLeftOffset + (float)window, (float)pixels ); |
|
MoveTimeSliderToPos( (int)m_flLeftOffset ); |
|
} |
|
break; |
|
} |
|
} |
|
break; |
|
case mxEvent::Action: |
|
{ |
|
iret = 1; |
|
switch ( event->action ) |
|
{ |
|
default: |
|
{ |
|
iret = 0; |
|
int lang_index = event->action - IDC_CV_CC_LANGUAGESTART; |
|
if ( lang_index >= 0 && lang_index < CC_NUM_LANGUAGES ) |
|
{ |
|
iret = 1; |
|
SetCloseCaptionLanguageId( lang_index ); |
|
} |
|
} |
|
break; |
|
case IDC_CV_TOGGLECLOSECAPTIONS: |
|
{ |
|
OnToggleCloseCaptionsForEvent(); |
|
} |
|
break; |
|
case IDC_CV_CHANGECLOSECAPTIONTOKEN: |
|
{ |
|
if ( m_pClickedChannel ) |
|
{ |
|
CChoreoEvent *e = m_pClickedChannel->GetCaptionClickedEvent(); |
|
if ( e && e->GetNumSlaves() >= 1 ) |
|
{ |
|
OnChangeCloseCaptionToken( e ); |
|
} |
|
} |
|
} |
|
break; |
|
case IDC_CV_REMOVESPEAKEVENTFROMGROUP: |
|
{ |
|
OnRemoveSpeakEventFromGroup(); |
|
} |
|
break; |
|
case IDC_CV_COMBINESPEAKEVENTS: |
|
{ |
|
OnCombineSpeakEvents(); |
|
} |
|
break; |
|
case IDC_CV_CC_SHOW: |
|
{ |
|
OnToggleCloseCaptionTags(); |
|
} |
|
break; |
|
case IDC_CV_TOGGLERAMPONLY: |
|
{ |
|
m_bRampOnly = !m_bRampOnly; |
|
redraw(); |
|
} |
|
break; |
|
case IDC_CV_PROCESSSEQUENCES: |
|
{ |
|
m_bProcessSequences = !m_bProcessSequences; |
|
} |
|
break; |
|
case IDC_CV_CHECKSEQLENGTHS: |
|
{ |
|
OnCheckSequenceLengths(); |
|
} |
|
break; |
|
case IDC_CV_CHANGESCALE: |
|
{ |
|
OnChangeScale(); |
|
} |
|
break; |
|
case IDC_CHOREO_PLAYBACKRATE: |
|
{ |
|
m_flPlaybackRate = m_pPlaybackRate->getValue(); |
|
redraw(); |
|
} |
|
break; |
|
case IDC_COPYEVENTS: |
|
CopyEvents(); |
|
break; |
|
case IDC_PASTEEVENTS: |
|
PasteEvents(); |
|
redraw(); |
|
break; |
|
case IDC_IMPORTEVENTS: |
|
ImportEvents(); |
|
redraw(); |
|
break; |
|
case IDC_EXPORTEVENTS: |
|
ExportEvents(); |
|
redraw(); |
|
break; |
|
case IDC_EXPORT_VCD: |
|
ExportVCD(); |
|
redraw(); |
|
break; |
|
case IDC_IMPORT_VCD: |
|
ImportVCD(); |
|
redraw(); |
|
break; |
|
case IDC_EXPRESSIONTOOL: |
|
OnExpressionTool(); |
|
break; |
|
case IDC_GESTURETOOL: |
|
OnGestureTool(); |
|
break; |
|
case IDC_ASSOCIATEBSP: |
|
AssociateBSP(); |
|
break; |
|
case IDC_ASSOCIATEMODEL: |
|
AssociateModel(); |
|
break; |
|
case IDC_CVUNDO: |
|
Undo(); |
|
break; |
|
case IDC_CVREDO: |
|
Redo(); |
|
break; |
|
case IDC_SELECTALL: |
|
SelectAll(); |
|
break; |
|
case IDC_DESELECTALL: |
|
DeselectAll(); |
|
break; |
|
case IDC_PLAYSCENE: |
|
Con_Printf( "Commencing playback\n" ); |
|
PlayScene( true ); |
|
break; |
|
case IDC_PAUSESCENE: |
|
Con_Printf( "Pausing playback\n" ); |
|
PauseScene(); |
|
break; |
|
case IDC_STOPSCENE: |
|
Con_Printf( "Canceling playback\n" ); |
|
StopScene(); |
|
break; |
|
case IDC_CHOREOVSCROLL: |
|
{ |
|
int offset = 0; |
|
bool processed = true; |
|
|
|
switch ( event->modifiers ) |
|
{ |
|
case SB_THUMBTRACK: |
|
offset = event->height; |
|
break; |
|
case SB_PAGEUP: |
|
offset = m_pVertScrollBar->getValue(); |
|
offset -= 20; |
|
offset = max( offset, m_pVertScrollBar->getMinValue() ); |
|
break; |
|
case SB_PAGEDOWN: |
|
offset = m_pVertScrollBar->getValue(); |
|
offset += 20; |
|
offset = min( offset, m_pVertScrollBar->getMaxValue() ); |
|
break; |
|
case SB_LINEDOWN: |
|
offset = m_pVertScrollBar->getValue(); |
|
offset += 10; |
|
offset = min( offset, m_pVertScrollBar->getMaxValue() ); |
|
break; |
|
case SB_LINEUP: |
|
offset = m_pVertScrollBar->getValue(); |
|
offset -= 10; |
|
offset = max( offset, m_pVertScrollBar->getMinValue() ); |
|
break; |
|
default: |
|
processed = false; |
|
break; |
|
} |
|
|
|
if ( processed ) |
|
{ |
|
m_pVertScrollBar->setValue( offset ); |
|
InvalidateRect( (HWND)m_pVertScrollBar->getHandle(), NULL, TRUE ); |
|
m_nTopOffset = offset; |
|
InvalidateLayout(); |
|
} |
|
} |
|
break; |
|
case IDC_CHOREOHSCROLL: |
|
{ |
|
int offset = 0; |
|
bool processed = true; |
|
|
|
switch ( event->modifiers ) |
|
{ |
|
case SB_THUMBTRACK: |
|
offset = event->height; |
|
break; |
|
case SB_PAGEUP: |
|
offset = m_pHorzScrollBar->getValue(); |
|
offset -= 20; |
|
offset = max( offset, m_pHorzScrollBar->getMinValue() ); |
|
break; |
|
case SB_PAGEDOWN: |
|
offset = m_pHorzScrollBar->getValue(); |
|
offset += 20; |
|
offset = min( offset, m_pHorzScrollBar->getMaxValue() ); |
|
break; |
|
case SB_LINEUP: |
|
offset = m_pHorzScrollBar->getValue(); |
|
offset -= 10; |
|
offset = max( offset, m_pHorzScrollBar->getMinValue() ); |
|
break; |
|
case SB_LINEDOWN: |
|
offset = m_pHorzScrollBar->getValue(); |
|
offset += 10; |
|
offset = min( offset, m_pHorzScrollBar->getMaxValue() ); |
|
break; |
|
default: |
|
processed = false; |
|
break; |
|
} |
|
|
|
if ( processed ) |
|
{ |
|
MoveTimeSliderToPos( offset ); |
|
} |
|
} |
|
break; |
|
case IDC_ADDACTOR: |
|
{ |
|
NewActor(); |
|
} |
|
break; |
|
case IDC_EDITACTOR: |
|
{ |
|
CChoreoActorWidget *actor = m_pClickedActor; |
|
if ( actor ) |
|
{ |
|
EditActor( actor->GetActor() ); |
|
} |
|
} |
|
break; |
|
case IDC_DELETEACTOR: |
|
{ |
|
CChoreoActorWidget *actor = m_pClickedActor; |
|
if ( actor ) |
|
{ |
|
DeleteActor( actor->GetActor() ); |
|
} |
|
} |
|
break; |
|
case IDC_MOVEACTORUP: |
|
{ |
|
CChoreoActorWidget *actor = m_pClickedActor; |
|
if ( actor ) |
|
{ |
|
MoveActorUp( actor->GetActor() ); |
|
} |
|
} |
|
break; |
|
case IDC_MOVEACTORDOWN: |
|
{ |
|
CChoreoActorWidget *actor = m_pClickedActor; |
|
if ( actor ) |
|
{ |
|
MoveActorDown( actor->GetActor() ); |
|
} |
|
} |
|
break; |
|
case IDC_CHANNELOPEN: |
|
{ |
|
CActorBitmapButton *btn = static_cast< CActorBitmapButton * >( event->widget ); |
|
if ( btn ) |
|
{ |
|
CChoreoActorWidget *a = btn->GetActor(); |
|
if ( a ) |
|
{ |
|
a->ShowChannels( true ); |
|
} |
|
} |
|
} |
|
break; |
|
case IDC_CHANNELCLOSE: |
|
{ |
|
CActorBitmapButton *btn = static_cast< CActorBitmapButton * >( event->widget ); |
|
if ( btn ) |
|
{ |
|
CChoreoActorWidget *a = btn->GetActor(); |
|
if ( a ) |
|
{ |
|
a->ShowChannels( false ); |
|
} |
|
} |
|
} |
|
break; |
|
case IDC_ADDEVENT_INTERRUPT: |
|
{ |
|
AddEvent( CChoreoEvent::INTERRUPT ); |
|
} |
|
break; |
|
case IDC_ADDEVENT_PERMITRESPONSES: |
|
{ |
|
AddEvent( CChoreoEvent::PERMIT_RESPONSES ); |
|
} |
|
break; |
|
case IDC_ADDEVENT_EXPRESSION: |
|
{ |
|
AddEvent( CChoreoEvent::EXPRESSION ); |
|
} |
|
break; |
|
case IDC_ADDEVENT_FLEXANIMATION: |
|
{ |
|
AddEvent( CChoreoEvent::FLEXANIMATION ); |
|
} |
|
break; |
|
case IDC_ADDEVENT_GESTURE: |
|
{ |
|
AddEvent( CChoreoEvent::GESTURE ); |
|
} |
|
break; |
|
case IDC_ADDEVENT_NULLGESTURE: |
|
{ |
|
AddEvent( CChoreoEvent::GESTURE, 1 ); |
|
} |
|
break; |
|
case IDC_ADDEVENT_LOOKAT: |
|
{ |
|
AddEvent( CChoreoEvent::LOOKAT ); |
|
} |
|
break; |
|
case IDC_ADDEVENT_MOVETO: |
|
{ |
|
AddEvent( CChoreoEvent::MOVETO ); |
|
} |
|
break; |
|
case IDC_ADDEVENT_FACE: |
|
{ |
|
AddEvent( CChoreoEvent::FACE ); |
|
} |
|
break; |
|
case IDC_ADDEVENT_SPEAK: |
|
{ |
|
AddEvent( CChoreoEvent::SPEAK ); |
|
} |
|
break; |
|
case IDC_ADDEVENT_FIRETRIGGER: |
|
{ |
|
AddEvent( CChoreoEvent::FIRETRIGGER ); |
|
} |
|
break; |
|
case IDC_ADDEVENT_GENERIC: |
|
{ |
|
AddEvent( CChoreoEvent::GENERIC ); |
|
} |
|
break; |
|
case IDC_ADDEVENT_SUBSCENE: |
|
{ |
|
AddEvent( CChoreoEvent::SUBSCENE ); |
|
} |
|
break; |
|
case IDC_ADDEVENT_SEQUENCE: |
|
{ |
|
AddEvent( CChoreoEvent::SEQUENCE ); |
|
} |
|
break; |
|
case IDC_EDITEVENT: |
|
{ |
|
CChoreoEventWidget *event = m_pClickedEvent; |
|
if ( event ) |
|
{ |
|
EditEvent( event->GetEvent() ); |
|
redraw(); |
|
} |
|
} |
|
break; |
|
case IDC_DELETEEVENT: |
|
{ |
|
DeleteSelectedEvents(); |
|
} |
|
break; |
|
case IDC_CV_ENABLEEVENTS: |
|
{ |
|
EnableSelectedEvents( true ); |
|
} |
|
break; |
|
case IDC_CV_DISABLEEVENTS: |
|
{ |
|
EnableSelectedEvents( false ); |
|
} |
|
break; |
|
case IDC_MOVETOBACK: |
|
{ |
|
CChoreoEventWidget *event = m_pClickedEvent; |
|
if ( event ) |
|
{ |
|
MoveEventToBack( event->GetEvent() ); |
|
} |
|
} |
|
break; |
|
case IDC_DELETERELATIVETAG: |
|
{ |
|
CChoreoEventWidget *event = m_pClickedEvent; |
|
if ( event && m_nClickedTag >= 0 ) |
|
{ |
|
DeleteEventRelativeTag( event->GetEvent(), m_nClickedTag ); |
|
} |
|
} |
|
break; |
|
case IDC_ADDTIMINGTAG: |
|
{ |
|
AddEventRelativeTag(); |
|
} |
|
break; |
|
case IDC_ADDEVENT_PAUSE: |
|
{ |
|
AddGlobalEvent( CChoreoEvent::SECTION ); |
|
} |
|
break; |
|
case IDC_ADDEVENT_LOOP: |
|
{ |
|
AddGlobalEvent( CChoreoEvent::LOOP ); |
|
} |
|
break; |
|
case IDC_ADDEVENT_STOPPOINT: |
|
{ |
|
AddGlobalEvent( CChoreoEvent::STOPPOINT ); |
|
} |
|
break; |
|
case IDC_EDITGLOBALEVENT: |
|
{ |
|
CChoreoGlobalEventWidget *event = m_pClickedGlobalEvent; |
|
if ( event ) |
|
{ |
|
EditGlobalEvent( event->GetEvent() ); |
|
redraw(); |
|
} |
|
} |
|
break; |
|
case IDC_DELETEGLOBALEVENT: |
|
{ |
|
CChoreoGlobalEventWidget *event = m_pClickedGlobalEvent; |
|
if ( event ) |
|
{ |
|
DeleteGlobalEvent( event->GetEvent() ); |
|
} |
|
} |
|
break; |
|
case IDC_ADDCHANNEL: |
|
{ |
|
NewChannel(); |
|
} |
|
break; |
|
case IDC_EDITCHANNEL: |
|
{ |
|
CChoreoChannelWidget *channel = m_pClickedChannel; |
|
if ( channel ) |
|
{ |
|
EditChannel( channel->GetChannel() ); |
|
} |
|
} |
|
break; |
|
case IDC_DELETECHANNEL: |
|
{ |
|
CChoreoChannelWidget *channel = m_pClickedChannel; |
|
if ( channel ) |
|
{ |
|
DeleteChannel( channel->GetChannel() ); |
|
} |
|
} |
|
break; |
|
case IDC_MOVECHANNELUP: |
|
{ |
|
CChoreoChannelWidget *channel = m_pClickedChannel; |
|
if ( channel ) |
|
{ |
|
MoveChannelUp( channel->GetChannel() ); |
|
} |
|
} |
|
break; |
|
case IDC_MOVECHANNELDOWN: |
|
{ |
|
CChoreoChannelWidget *channel = m_pClickedChannel; |
|
if ( channel ) |
|
{ |
|
MoveChannelDown( channel->GetChannel() ); |
|
} |
|
} |
|
break; |
|
case IDC_CV_ALLEVENTS_CHANNEL: |
|
{ |
|
CChoreoChannelWidget *channel = m_pClickedChannel; |
|
if ( channel ) |
|
{ |
|
SelectAllEventsInChannel( channel ); |
|
} |
|
} |
|
break; |
|
case IDC_CV_ALLEVENTS_ACTOR: |
|
{ |
|
CChoreoActorWidget *actor = m_pClickedActor; |
|
if ( actor ) |
|
{ |
|
SelectAllEventsInActor( actor ); |
|
} |
|
} |
|
break; |
|
case IDC_SELECTEVENTS_ALL_BEFORE: |
|
{ |
|
SelectionParams_t params; |
|
Q_memset( ¶ms, 0, sizeof( params ) ); |
|
params.forward = false; |
|
params.time = GetTimeValueForMouse( m_nClickedX ); |
|
params.type = SelectionParams_t::SP_ALL; |
|
|
|
SelectEvents( params ); |
|
} |
|
break; |
|
case IDC_SELECTEVENTS_ALL_AFTER: |
|
{ |
|
SelectionParams_t params; |
|
Q_memset( ¶ms, 0, sizeof( params ) ); |
|
params.forward = true; |
|
params.time = GetTimeValueForMouse( m_nClickedX ); |
|
params.type = SelectionParams_t::SP_ALL; |
|
|
|
SelectEvents( params ); |
|
} |
|
break; |
|
case IDC_SELECTEVENTS_ACTIVE_BEFORE: |
|
{ |
|
SelectionParams_t params; |
|
Q_memset( ¶ms, 0, sizeof( params ) ); |
|
params.forward = false; |
|
params.time = GetTimeValueForMouse( m_nClickedX ); |
|
params.type = SelectionParams_t::SP_ACTIVE; |
|
|
|
SelectEvents( params ); |
|
} |
|
break; |
|
case IDC_SELECTEVENTS_ACTIVE_AFTER: |
|
{ |
|
SelectionParams_t params; |
|
Q_memset( ¶ms, 0, sizeof( params ) ); |
|
params.forward = true; |
|
params.time = GetTimeValueForMouse( m_nClickedX ); |
|
params.type = SelectionParams_t::SP_ACTIVE; |
|
|
|
SelectEvents( params ); |
|
} |
|
break; |
|
case IDC_SELECTEVENTS_CHANNEL_BEFORE: |
|
{ |
|
SelectionParams_t params; |
|
Q_memset( ¶ms, 0, sizeof( params ) ); |
|
params.forward = false; |
|
params.time = GetTimeValueForMouse( m_nClickedX ); |
|
params.type = SelectionParams_t::SP_CHANNEL; |
|
|
|
SelectEvents( params ); |
|
} |
|
break; |
|
case IDC_SELECTEVENTS_CHANNEL_AFTER: |
|
{ |
|
SelectionParams_t params; |
|
Q_memset( ¶ms, 0, sizeof( params ) ); |
|
params.forward = true; |
|
params.time = GetTimeValueForMouse( m_nClickedX ); |
|
params.type = SelectionParams_t::SP_CHANNEL; |
|
|
|
SelectEvents( params ); |
|
} |
|
break; |
|
case IDC_INSERT_TIME: |
|
{ |
|
OnInsertTime(); |
|
} |
|
break; |
|
case IDC_DELETE_TIME: |
|
{ |
|
OnDeleteTime(); |
|
} |
|
break; |
|
case IDC_CV_ALIGN_LEFT: |
|
{ |
|
OnAlign( true ); |
|
} |
|
break; |
|
case IDC_CV_ALIGN_RIGHT: |
|
{ |
|
OnAlign( false ); |
|
} |
|
break; |
|
case IDC_CV_SAMESIZE_SMALLEST: |
|
{ |
|
OnMakeSameSize( true ); |
|
} |
|
break; |
|
case IDC_CV_SAMESIZE_LARGEST: |
|
{ |
|
OnMakeSameSize( false ); |
|
} |
|
break; |
|
} |
|
|
|
if ( iret == 1 ) |
|
{ |
|
SetActiveTool( this ); |
|
} |
|
} |
|
break; |
|
} |
|
return iret; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::PlayScene( bool forward ) |
|
{ |
|
m_bForward = forward; |
|
if ( !m_pScene ) |
|
return; |
|
|
|
sound->Flush(); |
|
|
|
// Make sure phonemes are loaded |
|
FacePoser_EnsurePhonemesLoaded(); |
|
|
|
// Unpause |
|
if ( m_bSimulating && m_bPaused ) |
|
{ |
|
m_bPaused = false; |
|
return; |
|
} |
|
|
|
m_bSimulating = true; |
|
m_bPaused = false; |
|
|
|
// float soundlatency = max( sound->GetAmountofTimeAhead(), 0.0f ); |
|
// soundlatency = min( 0.5f, soundlatency ); |
|
|
|
float soundlatency = 0.0f; |
|
|
|
float sceneendtime = m_pScene->FindStopTime(); |
|
|
|
m_pScene->SetSoundFileStartupLatency( soundlatency ); |
|
|
|
if ( m_rgABPoints[ 0 ].active || |
|
m_rgABPoints[ 1 ].active ) |
|
{ |
|
if ( m_rgABPoints[ 0 ].active && |
|
m_rgABPoints[ 1 ].active ) |
|
{ |
|
float st = m_rgABPoints[ 0 ].time; |
|
float ed = m_rgABPoints[ 1 ].time; |
|
|
|
m_pScene->ResetSimulation( m_bForward, st, ed ); |
|
|
|
SetScrubTime( m_bForward ? st : ed ); |
|
SetScrubTargetTime( m_bForward ? ed : st ); |
|
} |
|
else |
|
{ |
|
float startonly = m_rgABPoints[ 0 ].active ? m_rgABPoints[ 0 ].time : m_rgABPoints[ 1 ].time; |
|
|
|
m_pScene->ResetSimulation( m_bForward, startonly ); |
|
|
|
SetScrubTime( m_bForward ? startonly : sceneendtime ); |
|
SetScrubTargetTime( m_bForward ? sceneendtime : startonly ); |
|
} |
|
} |
|
else |
|
{ |
|
// NO start end/loop |
|
m_pScene->ResetSimulation( m_bForward ); |
|
|
|
SetScrubTime( m_bForward ? 0 : sceneendtime ); |
|
SetScrubTargetTime( m_bForward ? sceneendtime : 0 ); |
|
} |
|
|
|
if ( g_viewerSettings.speedScale == 0.0f ) |
|
{ |
|
m_flLastSpeedScale = g_viewerSettings.speedScale; |
|
m_bResetSpeedScale = true; |
|
|
|
g_viewerSettings.speedScale = 1.0f; |
|
|
|
Con_Printf( "Resetting speed scale to 1.0\n" ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : x - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::MoveTimeSliderToPos( int x ) |
|
{ |
|
m_flLeftOffset = (float)x; |
|
m_pHorzScrollBar->setValue( (int)m_flLeftOffset ); |
|
InvalidateRect( (HWND)m_pHorzScrollBar->getHandle(), NULL, TRUE ); |
|
InvalidateLayout(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::PauseScene( void ) |
|
{ |
|
if ( !m_bSimulating ) |
|
return; |
|
|
|
m_bPaused = true; |
|
sound->StopAll(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Apply expression to actor's face |
|
// Input : *event - |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::ProcessExpression( CChoreoScene *scene, CChoreoEvent *event ) |
|
{ |
|
Assert( event->GetType() == CChoreoEvent::EXPRESSION ); |
|
|
|
StudioModel *model = FindAssociatedModel( scene, event->GetActor() ); |
|
if ( !model ) |
|
return; |
|
|
|
CStudioHdr *hdr = model->GetStudioHdr(); |
|
if ( !hdr ) |
|
{ |
|
return; |
|
} |
|
|
|
CExpClass *p = expressions->FindClass( event->GetParameters(), true ); |
|
if ( !p ) |
|
{ |
|
return; |
|
} |
|
|
|
CExpression *exp = p->FindExpression( event->GetParameters2() ); |
|
if ( !exp ) |
|
{ |
|
return; |
|
} |
|
|
|
CChoreoActor *a = event->GetActor(); |
|
if ( !a ) |
|
return; |
|
|
|
CChoreoActorWidget *actor = NULL; |
|
|
|
int i; |
|
for ( i = 0; i < m_SceneActors.Size(); i++ ) |
|
{ |
|
actor = m_SceneActors[ i ]; |
|
if ( !actor ) |
|
continue; |
|
|
|
if ( actor->GetActor() == a ) |
|
break; |
|
} |
|
|
|
if ( !actor || i >= m_SceneActors.Size() ) |
|
return; |
|
|
|
float *settings = exp->GetSettings(); |
|
Assert( settings ); |
|
float *weights = exp->GetWeights(); |
|
Assert( weights ); |
|
float *current = actor->GetSettings(); |
|
Assert( current ); |
|
|
|
float flIntensity = event->GetIntensity( scene->GetTime() ); |
|
|
|
// blend in target values for correct actor |
|
for ( LocalFlexController_t i = (LocalFlexController_t)0; i < hdr->numflexcontrollers(); i++ ) |
|
{ |
|
mstudioflexcontroller_t *pFlex = hdr->pFlexcontroller( i ); |
|
int j = pFlex->localToGlobal; |
|
if ( j < 0 ) |
|
continue; |
|
float s = clamp( weights[j] * flIntensity, 0.0, 1.0 ); |
|
current[ j ] = current[j] * (1.0f - s) + settings[ j ] * s; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *hdr - |
|
// *event - |
|
//----------------------------------------------------------------------------- |
|
void SetupFlexControllerTracks( CStudioHdr *hdr, CChoreoEvent *event ) |
|
{ |
|
Assert( hdr ); |
|
Assert( event ); |
|
|
|
if ( !hdr ) |
|
return; |
|
|
|
if ( !event ) |
|
return; |
|
|
|
// Already done |
|
if ( event->GetTrackLookupSet() ) |
|
return; |
|
|
|
/* |
|
// FIXME: Brian hooked this stuff up for some took work, but at this point the .mdl files don't look like they've been updated to include the remapping data yet... |
|
int c = hdr->numflexcontrollerremaps(); |
|
for ( i = 0; i < c; ++i ) |
|
{ |
|
mstudioflexcontrollerremap_t *remap = hdr->pFlexcontrollerRemap( i ); |
|
Msg( "remap %s\n", remap->pszName() ); |
|
Msg( " type %d\n", remap->remaptype ); |
|
Msg( " num remaps %d (stereo %s)\n", remap->numremaps, remap->stereo ? "true" : "false" ); |
|
for ( int j = 0 ; j < remap->numremaps; ++j ) |
|
{ |
|
int index = remap->pRemapControlIndex( j ); |
|
Msg( " %d: maps to %d (%s) with %s\n", j, index, hdr->pFlexcontroller( index )->pszName(), remap->pRemapControl( j ) ); |
|
} |
|
} |
|
*/ |
|
|
|
// Unlink stuff in case it doesn't exist |
|
int nTrackCount = event->GetNumFlexAnimationTracks(); |
|
for ( int i = 0; i < nTrackCount; ++i ) |
|
{ |
|
CFlexAnimationTrack *pTrack = event->GetFlexAnimationTrack( i ); |
|
pTrack->SetFlexControllerIndex( LocalFlexController_t(-1), -1, 0 ); |
|
pTrack->SetFlexControllerIndex( LocalFlexController_t(-1), -1, 1 ); |
|
} |
|
|
|
for ( LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); ++i ) |
|
{ |
|
int j = hdr->pFlexcontroller( i )->localToGlobal; |
|
|
|
char const *name = hdr->pFlexcontroller( i )->pszName(); |
|
if ( !name ) |
|
continue; |
|
|
|
bool combo = false; |
|
// Look up or create all necessary tracks |
|
if ( strncmp( "right_", name, 6 ) == 0 ) |
|
{ |
|
combo = true; |
|
name = &name[6]; |
|
} |
|
|
|
CFlexAnimationTrack *track = event->FindTrack( name ); |
|
if ( !track ) |
|
{ |
|
track = event->AddTrack( name ); |
|
Assert( track ); |
|
} |
|
|
|
track->SetFlexControllerIndex( i, j, 0 ); |
|
if ( combo ) |
|
{ |
|
track->SetFlexControllerIndex( LocalFlexController_t(i + 1), hdr->pFlexcontroller( LocalFlexController_t(i + 1) )->localToGlobal, 1 ); |
|
track->SetComboType( true ); |
|
} |
|
|
|
float orig_min = track->GetMin( ); |
|
float orig_max = track->GetMax( ); |
|
|
|
// set range |
|
if (hdr->pFlexcontroller( i )->min == 0.0f || hdr->pFlexcontroller( i )->max == 1.0f) |
|
{ |
|
track->SetInverted( false ); |
|
track->SetMin( hdr->pFlexcontroller( i )->min ); |
|
track->SetMax( hdr->pFlexcontroller( i )->max ); |
|
} |
|
else |
|
{ |
|
// invert ranges for wide ranged, makes sense considering flexcontroller names... |
|
track->SetInverted( true ); |
|
track->SetMin( hdr->pFlexcontroller( i )->max ); |
|
track->SetMax( hdr->pFlexcontroller( i )->min ); |
|
} |
|
|
|
// resample track based on this models dynamic range |
|
if (track->GetNumSamples( 0 ) > 0) |
|
{ |
|
float range = track->GetMax( ) - track->GetMin( ); |
|
|
|
for (int i = 0; i < track->GetNumSamples( 0 ); i++) |
|
{ |
|
CExpressionSample *sample = track->GetSample( i, 0 ); |
|
float rangedValue = orig_min * (1 - sample->value) + orig_max * sample->value; |
|
sample->value = clamp( (rangedValue - track->GetMin( )) / range, 0.0, 1.0 ); |
|
} |
|
} |
|
|
|
// skip next flex since we've already assigned it |
|
if ( combo ) |
|
{ |
|
i++; |
|
} |
|
} |
|
|
|
event->SetTrackLookupSet( true ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Apply flexanimation to actor's face |
|
// Input : *event - |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::ProcessFlexAnimation( CChoreoScene *scene, CChoreoEvent *event ) |
|
{ |
|
Assert( event->GetType() == CChoreoEvent::FLEXANIMATION ); |
|
|
|
StudioModel *model = FindAssociatedModel( scene, event->GetActor() ); |
|
if ( !model ) |
|
return; |
|
|
|
CStudioHdr *hdr = model->GetStudioHdr(); |
|
if ( !hdr ) |
|
{ |
|
return; |
|
} |
|
|
|
CChoreoActor *a = event->GetActor(); |
|
|
|
CChoreoActorWidget *actor = NULL; |
|
|
|
int i; |
|
for ( i = 0; i < m_SceneActors.Size(); i++ ) |
|
{ |
|
actor = m_SceneActors[ i ]; |
|
if ( !actor ) |
|
continue; |
|
|
|
if ( !stricmp( actor->GetActor()->GetName(), a->GetName() ) ) |
|
break; |
|
} |
|
|
|
if ( !actor || i >= m_SceneActors.Size() ) |
|
return; |
|
|
|
float *current = actor->GetSettings(); |
|
Assert( current ); |
|
|
|
if ( !event->GetTrackLookupSet() ) |
|
{ |
|
SetupFlexControllerTracks( hdr, event ); |
|
} |
|
|
|
float weight = event->GetIntensity( scene->GetTime() ); |
|
|
|
CChoreoEventWidget *eventwidget = FindWidgetForEvent( event ); |
|
bool bUpdateSliders = (eventwidget && eventwidget->IsSelected() && model == models->GetActiveStudioModel() ); |
|
|
|
// Iterate animation tracks |
|
for ( i = 0; i < event->GetNumFlexAnimationTracks(); i++ ) |
|
{ |
|
CFlexAnimationTrack *track = event->GetFlexAnimationTrack( i ); |
|
if ( !track ) |
|
continue; |
|
|
|
// Disabled |
|
if ( !track->IsTrackActive() ) |
|
{ |
|
if ( bUpdateSliders ) |
|
{ |
|
for ( int side = 0; side < 1 + track->IsComboType(); side++ ) |
|
{ |
|
int controller = track->GetFlexControllerIndex( side ); |
|
if ( controller != -1 && !g_pFlexPanel->IsEdited( controller )) |
|
{ |
|
g_pFlexPanel->SetSlider( controller, 0.0 ); |
|
g_pFlexPanel->SetInfluence( controller, 0.0f ); |
|
} |
|
} |
|
} |
|
continue; |
|
} |
|
|
|
// Map track flex controller to global name |
|
if ( track->IsComboType() ) |
|
{ |
|
for ( int side = 0; side < 2; side++ ) |
|
{ |
|
int controller = track->GetFlexControllerIndex( side ); |
|
if ( controller != -1 ) |
|
{ |
|
// Get spline intensity for controller |
|
float flIntensity = track->GetIntensity( scene->GetTime(), side ); |
|
|
|
if (bUpdateSliders && !g_pFlexPanel->IsEdited( controller ) ) |
|
{ |
|
g_pFlexPanel->SetSlider( controller, flIntensity ); |
|
g_pFlexPanel->SetInfluence( controller, 1.0f ); |
|
} |
|
|
|
flIntensity = current[ controller ] * (1 - weight) + flIntensity * weight; |
|
current[ controller ] = flIntensity; |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
int controller = track->GetFlexControllerIndex( 0 ); |
|
if ( controller != -1 ) |
|
{ |
|
// Get spline intensity for controller |
|
float flIntensity = track->GetIntensity( scene->GetTime(), 0 ); |
|
|
|
if (bUpdateSliders && !g_pFlexPanel->IsEdited( controller ) ) |
|
{ |
|
g_pFlexPanel->SetSlider( controller, flIntensity ); |
|
g_pFlexPanel->SetInfluence( controller, 1.0f ); |
|
} |
|
|
|
flIntensity = current[ controller ] * (1 - weight) + flIntensity * weight; |
|
current[ controller ] = flIntensity; |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
#include "mapentities.h" |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Apply lookat target |
|
// Input : *event - |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::ProcessLookat( CChoreoScene *scene, CChoreoEvent *event ) |
|
{ |
|
Assert( event->GetType() == CChoreoEvent::LOOKAT ); |
|
|
|
if ( !event->GetActor() ) |
|
return; |
|
|
|
CChoreoActor *a = event->GetActor(); |
|
|
|
Assert( a ); |
|
|
|
StudioModel *model = FindAssociatedModel( scene, a ); |
|
if ( !model ) |
|
{ |
|
return; |
|
} |
|
|
|
float flIntensity = event->GetIntensity( scene->GetTime() ); |
|
|
|
// clamp in-ramp to 0.3 seconds |
|
float flDuration = scene->GetTime() - event->GetStartTime(); |
|
float flMaxIntensity = flDuration < 0.3f ? SimpleSpline( flDuration / 0.3f ) : 1.0f; |
|
flDuration = event->GetEndTime() - scene->GetTime(); |
|
flMaxIntensity = min( flMaxIntensity, flDuration < 0.3f ? SimpleSpline( flDuration / 0.3f ) : 1.0f ); |
|
flIntensity = clamp( flIntensity, 0.0f, flMaxIntensity ); |
|
|
|
if (!stricmp( event->GetParameters(), a->GetName() ) || !stricmp( event->GetParameters(), "!self" )) |
|
{ |
|
model->AddLookTargetSelf( flIntensity ); |
|
} |
|
else if ( !stricmp( event->GetParameters(), "player" ) || |
|
!stricmp( event->GetParameters(), "!player" ) ) |
|
{ |
|
Vector vecTarget = model->m_origin; |
|
vecTarget.z = 0; |
|
|
|
model->AddLookTarget( vecTarget, flIntensity ); |
|
} |
|
else |
|
{ |
|
mapentities->CheckUpdateMap( scene->GetMapname() ); |
|
|
|
Vector orgActor; |
|
Vector orgTarget; |
|
QAngle anglesActor; |
|
QAngle anglesDummy; |
|
|
|
if ( event->GetPitch() != 0 || |
|
event->GetYaw() != 0 ) |
|
{ |
|
QAngle angles( -(float)event->GetPitch(), |
|
(float)event->GetYaw(), |
|
0 ); |
|
|
|
matrix3x4_t matrix; |
|
|
|
AngleMatrix( model->m_angles, matrix ); |
|
|
|
Vector vecForward; |
|
AngleVectors( angles, &vecForward ); |
|
|
|
Vector eyeTarget; |
|
VectorRotate( vecForward, matrix, eyeTarget ); |
|
VectorScale( eyeTarget, 75, eyeTarget ); |
|
|
|
model->AddLookTarget( eyeTarget, flIntensity ); |
|
} |
|
else |
|
{ |
|
if ( mapentities->LookupOrigin( a->GetName(), orgActor, anglesActor ) ) |
|
{ |
|
if ( mapentities->LookupOrigin( event->GetParameters(), orgTarget, anglesDummy ) ) |
|
{ |
|
Vector delta = orgTarget - orgActor; |
|
|
|
matrix3x4_t matrix; |
|
Vector lookTarget; |
|
|
|
// Rotate around actor's placed forward direction since we look straight down x in faceposer/hlmv |
|
AngleMatrix( anglesActor, matrix ); |
|
VectorIRotate( delta, matrix, lookTarget ); |
|
|
|
model->AddLookTarget( lookTarget, flIntensity ); |
|
return; |
|
} |
|
} |
|
// hack up something based on the name. |
|
{ |
|
const char *cp = event->GetParameters(); |
|
float value = 0.0; |
|
while (*cp) |
|
{ |
|
value += *cp++; |
|
} |
|
value = cos( value ); |
|
value = acos( value ); |
|
QAngle angles( 0.0, value * 45 / M_PI, 0.0 ); |
|
|
|
matrix3x4_t matrix; |
|
AngleMatrix( model->m_angles, matrix ); |
|
|
|
Vector vecForward; |
|
AngleVectors( angles, &vecForward ); |
|
|
|
Vector eyeTarget; |
|
VectorRotate( vecForward, matrix, eyeTarget ); |
|
VectorScale( eyeTarget, 75, eyeTarget ); |
|
|
|
model->AddLookTarget( eyeTarget, flIntensity ); |
|
} |
|
|
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns a target for Faceing |
|
// Input : *event - |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CChoreoView::GetTarget( CChoreoScene *scene, CChoreoEvent *event, Vector &vecTarget, QAngle &vecAngle ) |
|
{ |
|
if ( !event->GetActor() ) |
|
return false; |
|
|
|
CChoreoActor *a = event->GetActor(); |
|
|
|
Assert( a ); |
|
|
|
StudioModel *model = FindAssociatedModel( scene, a ); |
|
if ( !model ) |
|
{ |
|
return false; |
|
} |
|
|
|
if (!stricmp( event->GetParameters(), a->GetName() )) |
|
{ |
|
vecTarget = vec3_origin; |
|
return true; |
|
} |
|
else if ( !stricmp( event->GetParameters(), "player" ) || |
|
!stricmp( event->GetParameters(), "!player" ) ) |
|
{ |
|
vecTarget = model->m_origin; |
|
vecTarget.z = 0; |
|
vecAngle = model->m_angles; |
|
|
|
return true; |
|
} |
|
else |
|
{ |
|
mapentities->CheckUpdateMap( scene->GetMapname() ); |
|
|
|
Vector orgActor; |
|
Vector orgTarget; |
|
QAngle anglesActor; |
|
QAngle anglesDummy; |
|
|
|
if ( event->GetPitch() != 0 || |
|
event->GetYaw() != 0 ) |
|
{ |
|
QAngle angles( -(float)event->GetPitch(), |
|
(float)event->GetYaw(), |
|
0 ); |
|
|
|
matrix3x4_t matrix; |
|
|
|
AngleMatrix( model->m_angles, matrix ); |
|
|
|
QAngle angles2 = angles; |
|
angles2.x *= 0.6f; |
|
angles2.y *= 0.8f; |
|
|
|
Vector vecForward, vecForward2; |
|
AngleVectors( angles, &vecForward ); |
|
AngleVectors( angles2, &vecForward2 ); |
|
|
|
VectorNormalize( vecForward ); |
|
VectorNormalize( vecForward2 ); |
|
|
|
Vector eyeTarget, headTarget; |
|
|
|
VectorRotate( vecForward, matrix, eyeTarget ); |
|
VectorRotate( vecForward2, matrix, headTarget ); |
|
|
|
VectorScale( eyeTarget, 150, eyeTarget ); |
|
|
|
VectorScale( headTarget, 150, vecTarget ); |
|
return true; |
|
|
|
} |
|
else |
|
{ |
|
if ( mapentities->LookupOrigin( a->GetName(), orgActor, anglesActor ) ) |
|
{ |
|
if ( mapentities->LookupOrigin( event->GetParameters(), orgTarget, anglesDummy ) ) |
|
{ |
|
Vector delta = orgTarget - orgActor; |
|
|
|
matrix3x4_t matrix; |
|
Vector lookTarget; |
|
|
|
// Rotate around actor's placed forward direction since we look straight down x in faceposer/hlmv |
|
AngleMatrix( anglesActor, matrix ); |
|
VectorIRotate( delta, matrix, vecTarget ); |
|
|
|
return true; |
|
} |
|
} |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Apply lookat target |
|
// Input : *event - |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::ProcessFace( CChoreoScene *scene, CChoreoEvent *event ) |
|
{ |
|
Assert( event->GetType() == CChoreoEvent::FACE ); |
|
|
|
if ( !event->GetActor() ) |
|
return; |
|
|
|
CChoreoActor *a = event->GetActor(); |
|
|
|
Assert( a ); |
|
|
|
StudioModel *model = FindAssociatedModel( scene, a ); |
|
if ( !model ) |
|
{ |
|
return; |
|
} |
|
|
|
Vector vecTarget; |
|
QAngle vecAngle; |
|
|
|
if (!GetTarget( scene, event, vecTarget, vecAngle )) |
|
{ |
|
return; |
|
} |
|
|
|
/* |
|
// FIXME: this is broke |
|
float goalYaw = -(vecAngle.y > 180 ? 360 - vecAngle.y : vecAngle.y ); |
|
|
|
float intensity = event->GetIntensity( scene->GetTime() ); |
|
|
|
float diff = goalYaw * intensity; |
|
float dir = 1.0; |
|
|
|
if (diff < 0) |
|
{ |
|
diff = -diff; |
|
dir = -1; |
|
} |
|
|
|
float spineintensity = 0 * max( 0.0, (intensity - 0.5) / 0.5 ); |
|
float goalSpineYaw = min( diff * (1.0 - spineintensity), 30 ); |
|
//float idealYaw = info->m_flInitialYaw + (diff - m_goalBodyYaw * dir - m_goalSpineYaw * dir) * dir; |
|
// float idealYaw = UTIL_AngleMod( info->m_flInitialYaw + diff * intensity ); |
|
|
|
// FIXME: this is broke |
|
// model->SetSpineYaw( goalSpineYaw * dir); |
|
// model->SetBodyYaw( goalBodyYaw * dir ); |
|
|
|
// Msg("yaw %.1f : %.1f (%.1f)\n", info->m_flInitialYaw, idealYaw, intensity ); |
|
*/ |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *scene - |
|
// *event - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::ProcessLoop( CChoreoScene *scene, CChoreoEvent *event ) |
|
{ |
|
Assert( event->GetType() == CChoreoEvent::LOOP ); |
|
|
|
// Don't loop when dragging scrubber! |
|
if ( IsScrubbing() ) |
|
return; |
|
|
|
float backtime = (float)atof( event->GetParameters() ); |
|
|
|
bool process = true; |
|
int counter = event->GetLoopCount(); |
|
if ( counter != -1 ) |
|
{ |
|
int remaining = event->GetNumLoopsRemaining(); |
|
if ( remaining <= 0 ) |
|
{ |
|
process = false; |
|
} |
|
else |
|
{ |
|
event->SetNumLoopsRemaining( --remaining ); |
|
} |
|
} |
|
|
|
if ( !process ) |
|
return; |
|
|
|
scene->LoopToTime( backtime ); |
|
SetScrubTime( backtime ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Add a gesture layer |
|
// Input : *event - |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::ProcessGesture( CChoreoScene *scene, CChoreoEvent *event ) |
|
{ |
|
Assert( event->GetType() == CChoreoEvent::GESTURE ); |
|
|
|
// NULL event is just a placeholder |
|
if ( !Q_stricmp( event->GetName(), "NULL" ) ) |
|
{ |
|
return; |
|
} |
|
|
|
StudioModel *model = FindAssociatedModel( scene, event->GetActor() ); |
|
if ( !model ) |
|
return; |
|
|
|
if ( !event->GetActor() ) |
|
return; |
|
|
|
CChoreoActor *a = event->GetActor(); |
|
|
|
Assert( a ); |
|
|
|
int iSequence = model->LookupSequence( event->GetParameters() ); |
|
if (iSequence < 0) |
|
return; |
|
|
|
// Get spline intensity for controller |
|
float eventlocaltime = scene->GetTime() - event->GetStartTime(); |
|
|
|
float referencetime = event->GetOriginalPercentageFromPlaybackPercentage( eventlocaltime / event->GetDuration() ) * event->GetDuration(); |
|
|
|
float resampledtime = event->GetStartTime() + referencetime; |
|
|
|
float cycle = event->GetCompletion( resampledtime ); |
|
|
|
int iLayer = model->GetNewAnimationLayer( a->FindChannelIndex( event->GetChannel() ) ); |
|
|
|
model->SetOverlaySequence( iLayer, iSequence, event->GetIntensity( scene->GetTime() ) ); |
|
model->SetOverlayRate( iLayer, cycle, 0.0 ); |
|
} |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Apply a sequence |
|
// Input : *event - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::ProcessSequence( CChoreoScene *scene, CChoreoEvent *event ) |
|
{ |
|
Assert( event->GetType() == CChoreoEvent::SEQUENCE ); |
|
|
|
if ( !m_bProcessSequences ) |
|
{ |
|
return; |
|
} |
|
|
|
StudioModel *model = FindAssociatedModel( scene, event->GetActor() ); |
|
if ( !model ) |
|
return; |
|
|
|
if ( !event->GetActor() ) |
|
return; |
|
|
|
CChoreoActor *a = event->GetActor(); |
|
|
|
Assert( a ); |
|
|
|
int iSequence = model->LookupSequence( event->GetParameters() ); |
|
if (iSequence < 0) |
|
return; |
|
|
|
float flFrameRate; |
|
float flGroundSpeed; |
|
model->GetSequenceInfo( iSequence, &flFrameRate, &flGroundSpeed ); |
|
|
|
float cycle; |
|
bool looping = model->GetSequenceLoops( iSequence ); |
|
if (looping) |
|
{ |
|
float dt = scene->GetTime() - event->m_flPrevTime; |
|
event->m_flPrevTime = scene->GetTime(); |
|
dt = clamp( dt, 0.0, 0.1 ); |
|
cycle = event->m_flPrevCycle + flFrameRate * dt; |
|
cycle = cycle - (int)cycle; |
|
event->m_flPrevCycle = cycle; |
|
} |
|
else |
|
{ |
|
float dt = scene->GetTime() - event->GetStartTime(); |
|
cycle = flFrameRate * dt; |
|
cycle = cycle - (int)(cycle); |
|
} |
|
|
|
// FIXME: shouldn't sequences always be lower priority than gestures? |
|
int iLayer = model->GetNewAnimationLayer( a->FindChannelIndex( event->GetChannel() ) ); |
|
model->SetOverlaySequence( iLayer, iSequence, event->GetIntensity( scene->GetTime() ) ); |
|
model->SetOverlayRate( iLayer, cycle, 0.0 ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Apply a walking animation |
|
// Input : *event - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::ProcessMoveto( CChoreoScene *scene, CChoreoEvent *event ) |
|
{ |
|
Assert( event->GetType() == CChoreoEvent::MOVETO ); |
|
|
|
if ( !m_bProcessSequences ) |
|
{ |
|
return; |
|
} |
|
|
|
StudioModel *model = FindAssociatedModel( scene, event->GetActor() ); |
|
if ( !model ) |
|
return; |
|
|
|
if ( !event->GetActor() ) |
|
return; |
|
|
|
int iSequence = GetMovetoSequence( scene, event, model ); |
|
if (iSequence < 0) |
|
return; |
|
|
|
float flFrameRate; |
|
float flGroundSpeed; |
|
model->GetSequenceInfo( iSequence, &flFrameRate, &flGroundSpeed ); |
|
|
|
float dt = scene->GetTime() - event->GetStartTime(); |
|
float cycle = flFrameRate * dt; |
|
cycle = cycle - (int)(cycle); |
|
|
|
float idealAccel = 100; |
|
|
|
// accel to ideal |
|
float t1 = flGroundSpeed / idealAccel; |
|
|
|
float intensity = 1.0; |
|
|
|
if (dt < t1) |
|
{ |
|
intensity = dt / t1; |
|
} |
|
else if (event->GetDuration() - dt < t1) |
|
{ |
|
intensity = (event->GetDuration() - dt) / t1; |
|
} |
|
|
|
// movement should always be higher priority than postures, but not gestures....grrr, any way to tell them apart? |
|
int iLayer = model->GetNewAnimationLayer( 0 /* a->FindChannelIndex( event->GetChannel() ) */ ); |
|
model->SetOverlaySequence( iLayer, iSequence, intensity ); |
|
model->SetOverlayRate( iLayer, cycle, 0.0 ); |
|
} |
|
|
|
|
|
|
|
int CChoreoView::GetMovetoSequence( CChoreoScene *scene, CChoreoEvent *event, StudioModel *model ) |
|
{ |
|
// FIXME: needs to pull from event (activity or sequence?) |
|
if ( !event->GetParameters2() || !event->GetParameters2()[0] ) |
|
return model->LookupSequence( "walk_all" ); |
|
|
|
// Custom distance styles are appended to param2 with a space as a separator |
|
const char *pszAct = Q_strstr( event->GetParameters2(), " " ); |
|
if ( pszAct ) |
|
{ |
|
char szActName[256]; |
|
Q_strncpy( szActName, event->GetParameters2(), sizeof(szActName) ); |
|
szActName[ (pszAct-event->GetParameters2()) ] = '\0'; |
|
pszAct = szActName; |
|
} |
|
else |
|
{ |
|
pszAct = event->GetParameters2(); |
|
} |
|
|
|
if ( !Q_strcmp( pszAct, "Walk" ) ) |
|
{ |
|
pszAct = "ACT_WALK"; |
|
} |
|
else if ( !Q_strcmp( pszAct, "Run" ) ) |
|
{ |
|
pszAct = "ACT_RUN"; |
|
} |
|
else if ( !Q_strcmp( pszAct, "CrouchWalk" ) ) |
|
{ |
|
pszAct = "ACT_WALK_CROUCH"; |
|
} |
|
|
|
int iSequence = model->LookupActivity( pszAct ); |
|
|
|
if (iSequence == -1) |
|
{ |
|
return model->LookupSequence( "walk_all" ); |
|
} |
|
return iSequence; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Process a pause event |
|
// Input : *event - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::ProcessPause( CChoreoScene *scene, CChoreoEvent *event ) |
|
{ |
|
Assert( event->GetType() == CChoreoEvent::SECTION ); |
|
|
|
// Don't pause if scrubbing |
|
bool scrubbing = ( m_nDragType == DRAGTYPE_SCRUBBER ) ? true : false; |
|
if ( scrubbing ) |
|
return; |
|
|
|
PauseScene(); |
|
|
|
m_bAutomated = false; |
|
m_nAutomatedAction = SCENE_ACTION_UNKNOWN; |
|
m_flAutomationDelay = 0.0f; |
|
m_flAutomationTime = 0.0f; |
|
|
|
// Check for auto resume/cancel |
|
ParseFromMemory( (char *)event->GetParameters(), strlen( event->GetParameters() ) ); |
|
if ( tokenprocessor->TokenAvailable() ) |
|
{ |
|
tokenprocessor->GetToken( false ); |
|
if ( !stricmp( tokenprocessor->CurrentToken(), "automate" ) ) |
|
{ |
|
if ( tokenprocessor->TokenAvailable() ) |
|
{ |
|
tokenprocessor->GetToken( false ); |
|
if ( !stricmp( tokenprocessor->CurrentToken(), "Cancel" ) ) |
|
{ |
|
m_nAutomatedAction = SCENE_ACTION_CANCEL; |
|
} |
|
else if ( !stricmp( tokenprocessor->CurrentToken(), "Resume" ) ) |
|
{ |
|
m_nAutomatedAction = SCENE_ACTION_RESUME; |
|
} |
|
|
|
if ( tokenprocessor->TokenAvailable() && |
|
m_nAutomatedAction != SCENE_ACTION_UNKNOWN ) |
|
{ |
|
tokenprocessor->GetToken( false ); |
|
m_flAutomationDelay = (float)atof( tokenprocessor->CurrentToken() ); |
|
|
|
if ( m_flAutomationDelay > 0.0f ) |
|
{ |
|
// Success |
|
m_bAutomated = true; |
|
m_flAutomationTime = 0.0f; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Main event processor |
|
// Input : *event - |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::ProcessEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event ) |
|
{ |
|
if ( !event || !event->GetActive() ) |
|
return; |
|
|
|
CChoreoActor *actor = event->GetActor(); |
|
if ( actor && !actor->GetActive() ) |
|
{ |
|
return; |
|
} |
|
|
|
CChoreoChannel *channel = event->GetChannel(); |
|
if ( channel && !channel->GetActive() ) |
|
{ |
|
return; |
|
} |
|
|
|
switch( event->GetType() ) |
|
{ |
|
case CChoreoEvent::EXPRESSION: |
|
ProcessExpression( scene, event ); |
|
break; |
|
case CChoreoEvent::FLEXANIMATION: |
|
ProcessFlexAnimation( scene, event ); |
|
break; |
|
case CChoreoEvent::LOOKAT: |
|
ProcessLookat( scene, event ); |
|
break; |
|
case CChoreoEvent::FACE: |
|
ProcessFace( scene, event ); |
|
break; |
|
case CChoreoEvent::GESTURE: |
|
ProcessGesture( scene, event ); |
|
break; |
|
case CChoreoEvent::SEQUENCE: |
|
ProcessSequence( scene, event ); |
|
break; |
|
case CChoreoEvent::SUBSCENE: |
|
ProcessSubscene( scene, event ); |
|
break; |
|
case CChoreoEvent::SPEAK: |
|
ProcessSpeak( scene, event ); |
|
break; |
|
case CChoreoEvent::MOVETO: |
|
ProcessMoveto( scene, event ); |
|
break; |
|
case CChoreoEvent::STOPPOINT: |
|
// Nothing |
|
break; |
|
case CChoreoEvent::INTERRUPT: |
|
ProcessInterrupt( scene, event ); |
|
break; |
|
case CChoreoEvent::PERMIT_RESPONSES: |
|
ProcessPermitResponses( scene, event ); |
|
break; |
|
default: |
|
break; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Main event completion checker |
|
// Input : *event - |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CChoreoView::CheckEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event ) |
|
{ |
|
if ( !event || !event->GetActive() ) |
|
return true; |
|
|
|
CChoreoActor *actor = event->GetActor(); |
|
if ( actor && !actor->GetActive() ) |
|
{ |
|
return true; |
|
} |
|
|
|
CChoreoChannel *channel = event->GetChannel(); |
|
if ( channel && !channel->GetActive() ) |
|
{ |
|
return true; |
|
} |
|
|
|
switch( event->GetType() ) |
|
{ |
|
case CChoreoEvent::EXPRESSION: |
|
break; |
|
case CChoreoEvent::FLEXANIMATION: |
|
break; |
|
case CChoreoEvent::LOOKAT: |
|
break; |
|
case CChoreoEvent::GESTURE: |
|
break; |
|
case CChoreoEvent::SEQUENCE: |
|
break; |
|
case CChoreoEvent::SUBSCENE: |
|
break; |
|
case CChoreoEvent::SPEAK: |
|
break; |
|
case CChoreoEvent::MOVETO: |
|
break; |
|
case CChoreoEvent::INTERRUPT: |
|
break; |
|
case CChoreoEvent::PERMIT_RESPONSES: |
|
break; |
|
default: |
|
break; |
|
} |
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::PauseThink( void ) |
|
{ |
|
// FIXME: Game code would check for conditions being met |
|
|
|
if ( !m_bAutomated ) |
|
return; |
|
|
|
m_flAutomationTime += fabs( m_flFrameTime ); |
|
|
|
RECT rcPauseRect; |
|
rcPauseRect.left = 0; |
|
rcPauseRect.right = w2(); |
|
rcPauseRect.top = GetCaptionHeight() + SCRUBBER_HEIGHT; |
|
rcPauseRect.bottom = rcPauseRect.top + 10; |
|
|
|
CChoreoWidgetDrawHelper drawHelper( this, |
|
rcPauseRect, |
|
COLOR_CHOREO_BACKGROUND ); |
|
|
|
DrawSceneABTicks( drawHelper ); |
|
|
|
if ( m_flAutomationDelay > 0.0f && |
|
m_flAutomationTime < m_flAutomationDelay ) |
|
{ |
|
char sz[ 256 ]; |
|
sprintf( sz, "Pause %.2f/%.2f", m_flAutomationTime, m_flAutomationDelay ); |
|
|
|
int textlen = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, sz ); |
|
|
|
RECT rcText; |
|
GetScrubHandleRect( rcText, true ); |
|
|
|
rcText.left = ( rcText.left + rcText.right ) / 2; |
|
rcText.left -= ( textlen * 0.5f ); |
|
rcText.right = rcText.left + textlen + 1; |
|
|
|
rcText.top = rcPauseRect.top; |
|
rcText.bottom = rcPauseRect.bottom; |
|
|
|
drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, COLOR_CHOREO_PLAYBACKTICKTEXT, rcText, sz ); |
|
|
|
return; |
|
} |
|
|
|
// Time to act |
|
m_bAutomated = false; |
|
|
|
switch ( m_nAutomatedAction ) |
|
{ |
|
case SCENE_ACTION_RESUME: |
|
m_bPaused = false; |
|
sound->StopAll(); |
|
break; |
|
case SCENE_ACTION_CANCEL: |
|
FinishSimulation(); |
|
break; |
|
default: |
|
break; |
|
} |
|
|
|
m_nAutomatedAction = SCENE_ACTION_UNKNOWN; |
|
m_flAutomationTime = 0.0f; |
|
m_flAutomationDelay = 0.0f; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Conclude simulation |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::FinishSimulation( void ) |
|
{ |
|
if ( !m_bSimulating ) |
|
return; |
|
|
|
// m_pScene->ResetSimulation(); |
|
|
|
m_bSimulating = false; |
|
m_bPaused = false; |
|
|
|
sound->StopAll(); |
|
|
|
if ( m_bResetSpeedScale ) |
|
{ |
|
m_bResetSpeedScale = false; |
|
g_viewerSettings.speedScale = m_flLastSpeedScale; |
|
m_flLastSpeedScale = 0.0f; |
|
|
|
Con_Printf( "Resetting speed scale to %f\n", m_flLastSpeedScale ); |
|
} |
|
|
|
models->ClearOverlaysSequences(); |
|
|
|
// redraw(); |
|
} |
|
|
|
void CChoreoView::SceneThink( float time ) |
|
{ |
|
if ( !m_pScene ) |
|
return; |
|
|
|
if ( m_bSimulating ) |
|
{ |
|
if ( m_bPaused ) |
|
{ |
|
PauseThink(); |
|
} |
|
else |
|
{ |
|
m_pScene->SetSoundFileStartupLatency( 0.0f ); |
|
|
|
models->CheckResetFlexes(); |
|
|
|
ResetTargetSettings(); |
|
|
|
models->ClearOverlaysSequences(); |
|
|
|
// Tell scene to go |
|
m_pScene->Think( time ); |
|
|
|
// Move flexes toward their targets |
|
UpdateCurrentSettings(); |
|
} |
|
} |
|
else |
|
{ |
|
FinishSimulation(); |
|
} |
|
|
|
if ( !ShouldProcessSpeak() ) |
|
{ |
|
bool autoprocess = ShouldAutoProcess(); |
|
bool anyscrub = IsAnyToolScrubbing() ; |
|
bool anyprocessing = IsAnyToolProcessing(); |
|
|
|
//Con_Printf( "autoprocess %i anyscrub %i anyprocessing %i\n", |
|
// autoprocess ? 1 : 0, |
|
// anyscrub ? 1 : 0, |
|
// anyprocessing ? 1 : 0 ); |
|
|
|
if ( !anyscrub && |
|
!anyprocessing && |
|
autoprocess && |
|
!m_bForceProcess ) |
|
{ |
|
sound->StopAll(); |
|
|
|
// why clear lookat? |
|
//models->ClearModelTargets( false ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::LayoutScene( void ) |
|
{ |
|
if ( !m_pScene ) |
|
return; |
|
|
|
if ( m_bLayoutIsValid ) |
|
return; |
|
|
|
m_pScene->ReconcileTags(); |
|
|
|
RECT rc; |
|
GetClientRect( (HWND)getHandle(), &rc ); |
|
|
|
RECT rcClient = rc; |
|
rcClient.top += GetStartRow(); |
|
OffsetRect( &rcClient, 0, -m_nTopOffset ); |
|
|
|
m_flStartTime = m_flLeftOffset / GetPixelsPerSecond(); |
|
|
|
m_flEndTime = m_flStartTime + (float)( rcClient.right - GetLabelWidth() ) / GetPixelsPerSecond(); |
|
|
|
m_rcTimeLine = rcClient; |
|
m_rcTimeLine.top = GetCaptionHeight() + SCRUBBER_HEIGHT; |
|
m_rcTimeLine.bottom = m_rcTimeLine.top + 44; |
|
|
|
int currentRow = rcClient.top + 2; |
|
int itemHeight; |
|
|
|
// Draw actors |
|
int i; |
|
for ( i = 0; i < m_SceneActors.Size(); i++ ) |
|
{ |
|
CChoreoActorWidget *a = m_SceneActors[ i ]; |
|
Assert( a ); |
|
if ( !a ) |
|
{ |
|
continue; |
|
} |
|
|
|
// Figure out rectangle |
|
itemHeight = a->GetItemHeight(); |
|
|
|
RECT rcActor = rcClient; |
|
rcActor.top = currentRow; |
|
rcActor.bottom = currentRow + itemHeight; |
|
|
|
a->Layout( rcActor ); |
|
|
|
currentRow += itemHeight; |
|
} |
|
|
|
// Draw section tabs |
|
for ( i = 0; i < m_SceneGlobalEvents.Size(); i++ ) |
|
{ |
|
CChoreoGlobalEventWidget *e = m_SceneGlobalEvents[ i ]; |
|
if ( !e ) |
|
continue; |
|
|
|
RECT rcEvent; |
|
rcEvent = m_rcTimeLine; |
|
|
|
float frac = ( e->GetEvent()->GetStartTime() - m_flStartTime ) / ( m_flEndTime - m_flStartTime ); |
|
|
|
rcEvent.left = GetLabelWidth() + rcEvent.left + (int)( frac * ( m_rcTimeLine.right - m_rcTimeLine.left - GetLabelWidth() ) ); |
|
rcEvent.left -= 4; |
|
rcEvent.right = rcEvent.left + 8; |
|
rcEvent.bottom += 0; |
|
rcEvent.top = rcEvent.bottom - 8; |
|
|
|
if ( rcEvent.left + 10 < GetLabelWidth() ) |
|
{ |
|
e->setVisible( false ); |
|
} |
|
else |
|
{ |
|
e->setVisible( true ); |
|
} |
|
|
|
// OffsetRect( &rcEvent, GetLabelWidth(), 0 ); |
|
|
|
e->Layout( rcEvent ); |
|
} |
|
|
|
m_bLayoutIsValid = true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::DeleteSceneWidgets( void ) |
|
{ |
|
bool oldcandraw = m_bCanDraw; |
|
|
|
m_bCanDraw = false; |
|
|
|
int i; |
|
CChoreoWidget *w; |
|
|
|
ClearStatusArea(); |
|
|
|
for( i = 0 ; i < m_SceneActors.Size(); i++ ) |
|
{ |
|
w = m_SceneActors[ i ]; |
|
m_ActorExpanded[ i ].expanded = ((CChoreoActorWidget *)w)->GetShowChannels(); |
|
delete w; |
|
} |
|
|
|
m_SceneActors.RemoveAll(); |
|
|
|
for( i = 0 ; i < m_SceneGlobalEvents.Size(); i++ ) |
|
{ |
|
w = m_SceneGlobalEvents[ i ]; |
|
delete w; |
|
} |
|
|
|
m_SceneGlobalEvents.RemoveAll(); |
|
|
|
m_bCanDraw = oldcandraw; |
|
|
|
// Make sure nobody is still pointing at us |
|
m_pClickedActor = NULL; |
|
m_pClickedChannel = NULL; |
|
m_pClickedEvent = NULL; |
|
m_pClickedGlobalEvent = NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::InvalidateLayout( void ) |
|
{ |
|
if ( m_bSuppressLayout ) |
|
return; |
|
|
|
if ( ComputeHPixelsNeeded() != m_nLastHPixelsNeeded ) |
|
{ |
|
RepositionHSlider(); |
|
} |
|
|
|
if ( ComputeVPixelsNeeded() != m_nLastVPixelsNeeded ) |
|
{ |
|
RepositionVSlider(); |
|
} |
|
|
|
// Recheck gesture start/end times |
|
if ( m_pScene ) |
|
{ |
|
m_pScene->ReconcileGestureTimes(); |
|
m_pScene->ReconcileCloseCaption(); |
|
} |
|
|
|
m_bLayoutIsValid = false; |
|
redraw(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::CreateSceneWidgets( void ) |
|
{ |
|
DeleteSceneWidgets(); |
|
|
|
m_bSuppressLayout = true; |
|
|
|
int i; |
|
for ( i = 0; i < m_pScene->GetNumActors(); i++ ) |
|
{ |
|
CChoreoActor *a = m_pScene->GetActor( i ); |
|
Assert( a ); |
|
if ( !a ) |
|
continue; |
|
|
|
CChoreoActorWidget *actorWidget = new CChoreoActorWidget( NULL ); |
|
Assert( actorWidget ); |
|
|
|
actorWidget->SetActor( a ); |
|
actorWidget->Create(); |
|
|
|
m_SceneActors.AddToTail( actorWidget ); |
|
|
|
actorWidget->ShowChannels( m_ActorExpanded[ i ].expanded ); |
|
} |
|
|
|
// Find global events |
|
for ( i = 0; i < m_pScene->GetNumEvents(); i++ ) |
|
{ |
|
CChoreoEvent *e = m_pScene->GetEvent( i ); |
|
if ( !e || e->GetActor() ) |
|
continue; |
|
|
|
CChoreoGlobalEventWidget *eventWidget = new CChoreoGlobalEventWidget( NULL ); |
|
Assert( eventWidget ); |
|
|
|
eventWidget->SetEvent( e ); |
|
eventWidget->Create(); |
|
|
|
m_SceneGlobalEvents.AddToTail( eventWidget ); |
|
} |
|
|
|
m_bSuppressLayout = false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
int CChoreoView::GetLabelWidth( void ) |
|
{ |
|
return m_nLabelWidth; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
int CChoreoView::GetStartRow( void ) |
|
{ |
|
return m_nStartRow + GetCaptionHeight() + SCRUBBER_HEIGHT; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
int CChoreoView::GetRowHeight( void ) |
|
{ |
|
return m_nRowHeight; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
int CChoreoView::GetFontSize( void ) |
|
{ |
|
return m_nFontSize; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
int CChoreoView::ComputeVPixelsNeeded( void ) |
|
{ |
|
int pixels = 0; |
|
for ( int i = 0; i < m_SceneActors.Size(); i++ ) |
|
{ |
|
CChoreoActorWidget *actor = m_SceneActors[ i ]; |
|
if ( !actor ) |
|
continue; |
|
|
|
pixels += actor->GetItemHeight() + 2; |
|
} |
|
|
|
pixels += GetStartRow() + 15; |
|
|
|
// pixels += m_nInfoHeight; |
|
|
|
//pixels += 30; |
|
return pixels; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
int CChoreoView::ComputeHPixelsNeeded( void ) |
|
{ |
|
if ( !m_pScene ) |
|
{ |
|
return 0; |
|
} |
|
|
|
int pixels = 0; |
|
float maxtime = m_pScene->FindStopTime(); |
|
if ( maxtime < 5.0 ) |
|
{ |
|
maxtime = 5.0f; |
|
} |
|
pixels = (int)( ( maxtime + 5.0 ) * GetPixelsPerSecond() ); |
|
|
|
return pixels; |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::RepositionVSlider( void ) |
|
{ |
|
int pixelsneeded = ComputeVPixelsNeeded(); |
|
|
|
if ( pixelsneeded <= ( h2() - GetStartRow() )) |
|
{ |
|
m_pVertScrollBar->setVisible( false ); |
|
m_nTopOffset = 0; |
|
} |
|
else |
|
{ |
|
m_pVertScrollBar->setVisible( true ); |
|
} |
|
|
|
m_pVertScrollBar->setBounds( w2() - m_nScrollbarHeight, GetStartRow(), m_nScrollbarHeight, h2() - m_nScrollbarHeight - GetStartRow() ); |
|
|
|
//int visiblepixels = h2() - m_nScrollbarHeight - GetStartRow(); |
|
//m_nTopOffset = min( pixelsneeded - visiblepixels, m_nTopOffset ); |
|
m_nTopOffset = max( 0, m_nTopOffset ); |
|
m_nTopOffset = min( pixelsneeded, m_nTopOffset ); |
|
|
|
m_pVertScrollBar->setRange( 0, pixelsneeded ); |
|
m_pVertScrollBar->setValue( m_nTopOffset ); |
|
m_pVertScrollBar->setPagesize( h2() - GetStartRow() ); |
|
|
|
m_nLastVPixelsNeeded = pixelsneeded; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::RepositionHSlider( void ) |
|
{ |
|
int pixelsneeded = ComputeHPixelsNeeded(); |
|
|
|
int w = w2(); |
|
int lw = GetLabelWidth(); |
|
|
|
if ( pixelsneeded <= ( w - lw ) ) |
|
{ |
|
m_pHorzScrollBar->setVisible( false ); |
|
} |
|
else |
|
{ |
|
m_pHorzScrollBar->setVisible( true ); |
|
} |
|
m_pHorzScrollBar->setBounds( 0, h2() - m_nScrollbarHeight, w - m_nScrollbarHeight, m_nScrollbarHeight ); |
|
|
|
m_flLeftOffset = max( 0.f, m_flLeftOffset ); |
|
m_flLeftOffset = min( (float)pixelsneeded, m_flLeftOffset ); |
|
|
|
m_pHorzScrollBar->setRange( 0, pixelsneeded ); |
|
m_pHorzScrollBar->setValue( (int)m_flLeftOffset ); |
|
m_pHorzScrollBar->setPagesize(w - lw ); |
|
|
|
m_nLastHPixelsNeeded = pixelsneeded; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : dirty - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::SetDirty( bool dirty, bool clearundo /*=true*/ ) |
|
{ |
|
bool changed = dirty != m_bDirty; |
|
|
|
m_bDirty = dirty; |
|
|
|
if ( !dirty && clearundo ) |
|
{ |
|
WipeUndo(); |
|
redraw(); |
|
} |
|
|
|
if ( changed ) |
|
{ |
|
SetPrefix( m_bDirty ? "* " : "" ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::New( void ) |
|
{ |
|
if ( m_pScene ) |
|
{ |
|
Close( ); |
|
if ( m_pScene ) |
|
{ |
|
return; |
|
} |
|
} |
|
|
|
char scenefile[ 512 ]; |
|
if ( FacePoser_ShowSaveFileNameDialog( scenefile, sizeof( scenefile ), "scenes", "*.vcd" ) ) |
|
{ |
|
Q_DefaultExtension( scenefile, ".vcd", sizeof( scenefile ) ); |
|
|
|
m_pScene = new CChoreoScene( this ); |
|
g_MDLViewer->InitGridSettings(); |
|
SetChoreoFile( scenefile ); |
|
m_pScene->SetPrintFunc( Con_Printf ); |
|
|
|
ShowButtons( true ); |
|
|
|
SetDirty( false ); |
|
} |
|
|
|
if ( !m_pScene ) |
|
return; |
|
|
|
// Get first actor name |
|
CActorParams params; |
|
memset( ¶ms, 0, sizeof( params ) ); |
|
|
|
strcpy( params.m_szDialogTitle, "Create Actor" ); |
|
strcpy( params.m_szName, "" ); |
|
|
|
if ( !ActorProperties( ¶ms ) ) |
|
return; |
|
|
|
if ( strlen( params.m_szName ) <= 0 ) |
|
return; |
|
|
|
SetDirty( true ); |
|
|
|
PushUndo( "Create Actor" ); |
|
|
|
Con_Printf( "Creating scene %s with actor '%s'\n", GetChoreoFile(), params.m_szName ); |
|
|
|
CChoreoActor *actor = m_pScene->AllocActor(); |
|
if ( actor ) |
|
{ |
|
actor->SetName( params.m_szName ); |
|
} |
|
|
|
PushRedo( "Create Actor" ); |
|
|
|
CreateSceneWidgets(); |
|
// Redraw |
|
InvalidateLayout(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::Save( void ) |
|
{ |
|
if ( !m_pScene ) |
|
return; |
|
|
|
if ( !MakeFileWriteablePrompt( GetChoreoFile(), "VCD File" ) ) |
|
{ |
|
Con_Printf( "Not saving changes to %s\n", GetChoreoFile() ); |
|
return; |
|
} |
|
|
|
Con_Printf( "Saving changes to %s\n", GetChoreoFile() ); |
|
|
|
CP4AutoEditAddFile checkout( GetChoreoFile() ); |
|
if ( !m_pScene->SaveToFile( GetChoreoFile() ) ) |
|
{ |
|
mxMessageBox( this, va( "Unable to write \"%s\"", GetChoreoFile() ), |
|
"SaveToFile", MX_MB_OK | MX_MB_ERROR ); |
|
} |
|
|
|
g_MDLViewer->OnVCDSaved(); |
|
|
|
// Refresh the suffix |
|
SetChoreoFile( GetChoreoFile() ); |
|
|
|
SetDirty( false, false ); |
|
redraw(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::SaveAs( void ) |
|
{ |
|
if ( !m_pScene ) |
|
return; |
|
|
|
char scenefile[ 512 ]; |
|
if ( !FacePoser_ShowSaveFileNameDialog( scenefile, sizeof( scenefile ), "scenes", "*.vcd" ) ) |
|
return; |
|
|
|
Q_DefaultExtension( scenefile, ".vcd", sizeof( scenefile ) ); |
|
|
|
Con_Printf( "Saving %s\n", scenefile ); |
|
|
|
MakeFileWriteable( scenefile ); |
|
|
|
// Change filename |
|
SetChoreoFile( scenefile ); |
|
|
|
// Write it out baby |
|
CP4AutoEditAddFile checkout( scenefile ); |
|
if (!m_pScene->SaveToFile( GetChoreoFile() )) |
|
{ |
|
mxMessageBox( this, va( "Unable to write \"%s\"", GetChoreoFile() ), |
|
"SaveToFile", MX_MB_OK | MX_MB_ERROR ); |
|
} |
|
|
|
g_MDLViewer->OnVCDSaved(); |
|
|
|
SetDirty( false, false ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::Load( void ) |
|
{ |
|
char scenefile[ 512 ]; |
|
if ( !FacePoser_ShowOpenFileNameDialog( scenefile, sizeof( scenefile ), "scenes", "*.vcd" ) ) |
|
{ |
|
return; |
|
} |
|
|
|
Q_DefaultExtension( scenefile, ".vcd", sizeof( scenefile ) ); |
|
|
|
LoadSceneFromFile( scenefile ); |
|
|
|
m_nextFileList.RemoveAll(); |
|
} |
|
|
|
void CChoreoView::LoadNext( void ) |
|
{ |
|
if (GetChoreoFile() == NULL) |
|
return; |
|
|
|
char fixedupFile[ 512 ]; |
|
V_FixupPathName( fixedupFile, sizeof( fixedupFile ), GetChoreoFile() ); |
|
|
|
char relativeFile[ 512 ]; |
|
filesystem->FullPathToRelativePath( fixedupFile, relativeFile, sizeof( relativeFile ) ); |
|
|
|
char relativePath[ 512 ]; |
|
Q_ExtractFilePath( relativeFile, relativePath, sizeof( relativePath ) ); |
|
|
|
if (m_nextFileList.Count() == 0) |
|
{ |
|
// iterate files in the local directory |
|
char path[ 512 ]; |
|
strcpy( path, relativePath ); |
|
strcat( path, "/*.vcd" ); |
|
|
|
FileFindHandle_t hFindFile; |
|
char const *fn = filesystem->FindFirstEx( path, "MOD", &hFindFile ); |
|
if ( fn ) |
|
{ |
|
while ( fn ) |
|
{ |
|
// Don't do anything with directories |
|
if ( !filesystem->FindIsDirectory( hFindFile ) ) |
|
{ |
|
CUtlString s = fn; |
|
m_nextFileList.AddToTail( s ); |
|
} |
|
|
|
fn = filesystem->FindNext( hFindFile ); |
|
} |
|
|
|
filesystem->FindClose( hFindFile ); |
|
} |
|
} |
|
|
|
// look for a match, then pick the next in the list |
|
const char *fileBase; |
|
fileBase = V_UnqualifiedFileName( fixedupFile ); |
|
|
|
for (int i = 0; i < m_nextFileList.Count(); i++) |
|
{ |
|
if (!stricmp( fileBase, m_nextFileList[i] )) |
|
{ |
|
char fileName[512]; |
|
strcpy( fileName, relativePath ); |
|
if (i < m_nextFileList.Count() - 1) |
|
{ |
|
strcat( fileName, m_nextFileList[i+1] ); |
|
} |
|
else |
|
{ |
|
strcat( fileName, m_nextFileList[0] ); |
|
} |
|
|
|
LoadSceneFromFile( fileName ); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *filename - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::LoadSceneFromFile( const char *filename ) |
|
{ |
|
if ( filename[ 0 ] == '/' || |
|
filename[ 0 ] == '\\' ) |
|
{ |
|
++filename; |
|
} |
|
|
|
char fn[ 512 ]; |
|
Q_strncpy( fn, filename, sizeof( fn ) ); |
|
if ( m_pScene ) |
|
{ |
|
Close(); |
|
if ( m_pScene ) |
|
{ |
|
return; |
|
} |
|
} |
|
|
|
m_pScene = LoadScene( fn ); |
|
g_MDLViewer->InitGridSettings(); |
|
if ( !m_pScene ) |
|
return; |
|
|
|
g_MDLViewer->OnFileLoaded( fn ); |
|
|
|
ShowButtons( true ); |
|
|
|
CChoreoWidget::m_pScene = m_pScene; |
|
SetChoreoFile( fn ); |
|
|
|
bool cleaned = FixupSequenceDurations( m_pScene, false ); |
|
|
|
SetDirty( cleaned ); |
|
|
|
DeleteSceneWidgets(); |
|
CreateSceneWidgets(); |
|
|
|
// Force scroll bars to recompute |
|
ForceScrollBarsToRecompute( false ); |
|
|
|
InvalidateLayout(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : closing - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::UnloadScene( void ) |
|
{ |
|
InvalidateLayout(); |
|
ReportSceneClearToTools(); |
|
|
|
ClearStatusArea(); |
|
|
|
delete m_pScene; |
|
m_pScene = NULL; |
|
SetDirty( false ); |
|
SetChoreoFile( "" ); |
|
g_MDLViewer->InitGridSettings(); |
|
CChoreoWidget::m_pScene = NULL; |
|
|
|
DeleteSceneWidgets(); |
|
|
|
m_pVertScrollBar->setVisible( false ); |
|
m_pHorzScrollBar->setVisible( false ); |
|
|
|
ShowButtons( false ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *channel - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::DeleteChannel( CChoreoChannel *channel ) |
|
{ |
|
if ( !channel || !m_pScene ) |
|
return; |
|
|
|
SetDirty( true ); |
|
|
|
PushUndo( "Delete Channel" ); |
|
|
|
DeleteSceneWidgets(); |
|
|
|
// Delete channel and it's children |
|
// Find the appropriate actor |
|
for ( int i = 0; i < m_pScene->GetNumActors(); i++ ) |
|
{ |
|
CChoreoActor *a = m_pScene->GetActor( i ); |
|
if ( !a ) |
|
continue; |
|
|
|
if ( a->FindChannelIndex( channel ) == -1 ) |
|
continue; |
|
|
|
Con_Printf( "Deleting %s\n", channel->GetName() ); |
|
a->RemoveChannel( channel ); |
|
|
|
m_pScene->DeleteReferencedObjects( channel ); |
|
break; |
|
} |
|
|
|
ReportSceneClearToTools(); |
|
|
|
CreateSceneWidgets(); |
|
|
|
PushRedo( "Delete Channel" ); |
|
|
|
// Redraw |
|
InvalidateLayout(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::NewChannel( void ) |
|
{ |
|
if ( !m_pScene ) |
|
return; |
|
|
|
if ( !m_pScene->GetNumActors() ) |
|
{ |
|
Con_Printf( "You must create an actor before you can add a channel\n" ); |
|
return; |
|
} |
|
|
|
CChannelParams params; |
|
memset( ¶ms, 0, sizeof( params ) ); |
|
|
|
strcpy( params.m_szDialogTitle, "Create Channel" ); |
|
strcpy( params.m_szName, "" ); |
|
params.m_bShowActors = true; |
|
strcpy( params.m_szSelectedActor, "" ); |
|
params.m_pScene = m_pScene; |
|
|
|
if ( !ChannelProperties( ¶ms ) ) |
|
{ |
|
return; |
|
} |
|
|
|
if ( strlen( params.m_szName ) <= 0 ) |
|
{ |
|
return; |
|
} |
|
|
|
CChoreoActor *actor = m_pScene->FindActor( params.m_szSelectedActor ); |
|
if ( !actor ) |
|
{ |
|
Con_Printf( "Can't add channel %s, actor %s doesn't exist\n", params.m_szName, params.m_szSelectedActor ); |
|
return; |
|
} |
|
|
|
SetDirty( true ); |
|
|
|
PushUndo( "Add Channel" ); |
|
|
|
DeleteSceneWidgets(); |
|
|
|
CChoreoChannel *channel = m_pScene->AllocChannel(); |
|
if ( !channel ) |
|
{ |
|
Con_Printf( "Unable to allocate channel %s!\n", params.m_szName ); |
|
} |
|
else |
|
{ |
|
channel->SetName( params.m_szName ); |
|
channel->SetActor( actor ); |
|
actor->AddChannel( channel ); |
|
} |
|
|
|
CreateSceneWidgets(); |
|
|
|
PushRedo( "Add Channel" ); |
|
|
|
// Redraw |
|
InvalidateLayout(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *channel - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::MoveChannelUp( CChoreoChannel *channel ) |
|
{ |
|
SetDirty( true ); |
|
|
|
PushUndo( "Move Channel Up" ); |
|
|
|
DeleteSceneWidgets(); |
|
|
|
// Find the appropriate actor |
|
for ( int i = 0; i < m_pScene->GetNumActors(); i++ ) |
|
{ |
|
CChoreoActor *a = m_pScene->GetActor( i ); |
|
if ( !a ) |
|
continue; |
|
|
|
int index = a->FindChannelIndex( channel ); |
|
if ( index == -1 ) |
|
continue; |
|
|
|
if ( index != 0 ) |
|
{ |
|
Con_Printf( "Moving %s up\n", channel->GetName() ); |
|
a->SwapChannels( index, index - 1 ); |
|
} |
|
break; |
|
} |
|
|
|
CreateSceneWidgets(); |
|
|
|
PushRedo( "Move Channel Up" ); |
|
|
|
// Redraw |
|
InvalidateLayout(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *channel - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::MoveChannelDown( CChoreoChannel *channel ) |
|
{ |
|
SetDirty( true ); |
|
|
|
PushUndo( "Move Channel Down" ); |
|
|
|
DeleteSceneWidgets(); |
|
|
|
// Find the appropriate actor |
|
for ( int i = 0; i < m_pScene->GetNumActors(); i++ ) |
|
{ |
|
CChoreoActor *a = m_pScene->GetActor( i ); |
|
if ( !a ) |
|
continue; |
|
|
|
int index = a->FindChannelIndex( channel ); |
|
if ( index == -1 ) |
|
continue; |
|
|
|
if ( index < a->GetNumChannels() - 1 ) |
|
{ |
|
Con_Printf( "Moving %s down\n", channel->GetName() ); |
|
a->SwapChannels( index, index + 1 ); |
|
} |
|
break; |
|
} |
|
|
|
CreateSceneWidgets(); |
|
|
|
PushRedo( "Move Channel Down" ); |
|
|
|
// Redraw |
|
InvalidateLayout(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *channel - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::EditChannel( CChoreoChannel *channel ) |
|
{ |
|
if ( !channel ) |
|
return; |
|
|
|
CChannelParams params; |
|
memset( ¶ms, 0, sizeof( params ) ); |
|
|
|
strcpy( params.m_szDialogTitle, "Edit Channel" ); |
|
V_strcpy_safe( params.m_szName, channel->GetName() ); |
|
|
|
if ( !ChannelProperties( ¶ms ) ) |
|
return; |
|
|
|
if ( strlen( params.m_szName ) <= 0 ) |
|
return; |
|
|
|
SetDirty( true ); |
|
|
|
PushUndo( "Edit Channel" ); |
|
|
|
channel->SetName( params.m_szName ); |
|
|
|
PushRedo( "Edit Channel" ); |
|
|
|
// Redraw |
|
InvalidateLayout(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *actor - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::DeleteActor( CChoreoActor *actor ) |
|
{ |
|
if ( !actor || !m_pScene ) |
|
return; |
|
|
|
SetDirty( true ); |
|
|
|
PushUndo( "Delete Actor" ); |
|
|
|
DeleteSceneWidgets(); |
|
|
|
// Delete channel and it's children |
|
// Find the appropriate actor |
|
Con_Printf( "Deleting %s\n", actor->GetName() ); |
|
m_pScene->RemoveActor( actor ); |
|
|
|
m_pScene->DeleteReferencedObjects( actor ); |
|
|
|
ReportSceneClearToTools(); |
|
|
|
CreateSceneWidgets(); |
|
|
|
PushRedo( "Delete Actor" ); |
|
|
|
// Redraw |
|
InvalidateLayout(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::NewActor( void ) |
|
{ |
|
if ( !m_pScene ) |
|
{ |
|
Con_ErrorPrintf( "You must load or create a scene file first\n" ); |
|
return; |
|
} |
|
|
|
CActorParams params; |
|
memset( ¶ms, 0, sizeof( params ) ); |
|
|
|
strcpy( params.m_szDialogTitle, "Create Actor" ); |
|
strcpy( params.m_szName, "" ); |
|
|
|
if ( !ActorProperties( ¶ms ) ) |
|
return; |
|
|
|
if ( strlen( params.m_szName ) <= 0 ) |
|
return; |
|
|
|
SetDirty( true ); |
|
|
|
PushUndo( "Add Actor" ); |
|
|
|
DeleteSceneWidgets(); |
|
|
|
Con_Printf( "Adding new actor '%s'\n", params.m_szName ); |
|
|
|
CChoreoActor *actor = m_pScene->AllocActor(); |
|
if ( actor ) |
|
{ |
|
actor->SetName( params.m_szName ); |
|
} |
|
|
|
CreateSceneWidgets(); |
|
|
|
PushRedo( "Add Actor" ); |
|
|
|
// Redraw |
|
InvalidateLayout(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *actor - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::MoveActorUp( CChoreoActor *actor ) |
|
{ |
|
DeleteSceneWidgets(); |
|
|
|
int index = m_pScene->FindActorIndex( actor ); |
|
// found it and it's not first |
|
if ( index != -1 && index != 0 ) |
|
{ |
|
Con_Printf( "Moving %s up\n", actor->GetName() ); |
|
|
|
SetDirty( true ); |
|
|
|
PushUndo( "Move Actor Up" ); |
|
|
|
m_pScene->SwapActors( index, index - 1 ); |
|
|
|
PushRedo( "Move Actor Up" ); |
|
} |
|
|
|
CreateSceneWidgets(); |
|
// Redraw |
|
InvalidateLayout(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *actor - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::MoveActorDown( CChoreoActor *actor ) |
|
{ |
|
DeleteSceneWidgets(); |
|
|
|
int index = m_pScene->FindActorIndex( actor ); |
|
// found it and it's not first |
|
if ( index != -1 && ( index < m_pScene->GetNumActors() - 1 ) ) |
|
{ |
|
Con_Printf( "Moving %s down\n", actor->GetName() ); |
|
|
|
SetDirty( true ); |
|
|
|
PushUndo( "Move Actor Down" ); |
|
|
|
m_pScene->SwapActors( index, index + 1 ); |
|
|
|
PushRedo( "Move Actor Down" ); |
|
} |
|
|
|
CreateSceneWidgets(); |
|
// Redraw |
|
InvalidateLayout(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *actor - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::EditActor( CChoreoActor *actor ) |
|
{ |
|
if ( !actor ) |
|
return; |
|
|
|
CActorParams params; |
|
memset( ¶ms, 0, sizeof( params ) ); |
|
|
|
strcpy( params.m_szDialogTitle, "Edit Actor" ); |
|
V_strcpy_safe( params.m_szName, actor->GetName() ); |
|
|
|
if ( !ActorProperties( ¶ms ) ) |
|
return; |
|
|
|
if ( strlen( params.m_szName ) <= 0 ) |
|
return; |
|
|
|
SetDirty( true ); |
|
|
|
PushUndo( "Edit Actor" ); |
|
|
|
actor->SetName( params.m_szName ); |
|
|
|
PushRedo( "Edit Actor" ); |
|
|
|
// Redraw |
|
InvalidateLayout(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : type - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::AddEvent( int type, int subtype /*= 0*/, char const *defaultparameters /*= NULL*/ ) |
|
{ |
|
int mx, my; |
|
mx = m_nClickedX; |
|
my = m_nClickedY; |
|
CChoreoChannelWidget *channel = m_pClickedChannel; |
|
if ( !channel || !channel->GetChannel() ) |
|
{ |
|
CChoreoActorWidget *actor = m_pClickedActor; |
|
if ( actor ) |
|
{ |
|
if ( actor->GetNumChannels() <= 0 ) |
|
return; |
|
|
|
channel = actor->GetChannel( 0 ); |
|
if ( !channel || !channel->GetChannel() ) |
|
return; |
|
} |
|
else |
|
{ |
|
return; |
|
} |
|
} |
|
|
|
// Convert click position local to this window |
|
POINT pt; |
|
pt.x = mx; |
|
pt.y = my; |
|
|
|
CEventParams params; |
|
memset( ¶ms, 0, sizeof( params ) ); |
|
|
|
if ( defaultparameters ) |
|
{ |
|
Q_strncpy( params.m_szParameters, defaultparameters, sizeof( params.m_szParameters ) ); |
|
} |
|
|
|
strcpy( params.m_szDialogTitle, "Create Event" ); |
|
|
|
params.m_nType = type; |
|
params.m_pScene = m_pScene; |
|
|
|
params.m_bFixedLength = false; |
|
params.m_bResumeCondition = false; |
|
params.m_flStartTime = GetTimeValueForMouse( pt.x ); |
|
params.m_bCloseCaptionNoAttenuate = false; |
|
params.m_bForceShortMovement = false; |
|
params.m_bSyncToFollowingGesture = false; |
|
params.m_bDisabled = false; |
|
params.m_bPlayOverScript = false; |
|
|
|
switch ( type ) |
|
{ |
|
case CChoreoEvent::EXPRESSION: |
|
case CChoreoEvent::FLEXANIMATION: |
|
case CChoreoEvent::GESTURE: |
|
case CChoreoEvent::SEQUENCE: |
|
case CChoreoEvent::LOOKAT: |
|
case CChoreoEvent::MOVETO: |
|
case CChoreoEvent::FACE: |
|
case CChoreoEvent::SUBSCENE: |
|
case CChoreoEvent::INTERRUPT: |
|
case CChoreoEvent::GENERIC: |
|
case CChoreoEvent::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( ¶ms )) |
|
{ |
|
m_bForceProcess = false; |
|
return; |
|
} |
|
m_bForceProcess = false; |
|
|
|
if ( Q_strlen( params.m_szName ) <= 0 ) |
|
{ |
|
mxMessageBox( this, va( "Event must have a valid name" ), |
|
"Edit Event", MX_MB_OK | MX_MB_ERROR ); |
|
continue; |
|
} |
|
|
|
if ( Q_strlen( params.m_szParameters ) <= 0 ) |
|
{ |
|
bool shouldBreak = false; |
|
|
|
switch ( params.m_nType ) |
|
{ |
|
case CChoreoEvent::FLEXANIMATION: |
|
case CChoreoEvent::INTERRUPT: |
|
case CChoreoEvent::PERMIT_RESPONSES: |
|
shouldBreak = true; |
|
break; |
|
case CChoreoEvent::GESTURE: |
|
if ( subtype == 1 ) |
|
{ |
|
shouldBreak = true; |
|
} |
|
break; |
|
default: |
|
// Have to have a non-null parameters block |
|
break; |
|
} |
|
|
|
if ( !shouldBreak ) |
|
{ |
|
mxMessageBox( this, va( "No parameters specified for %s\n", params.m_szName ), |
|
"Edit Event", MX_MB_OK | MX_MB_ERROR ); |
|
continue; |
|
} |
|
} |
|
|
|
break; |
|
} |
|
|
|
SetDirty( true ); |
|
|
|
PushUndo( "Add Event" ); |
|
|
|
CChoreoEvent *event = m_pScene->AllocEvent(); |
|
if ( event ) |
|
{ |
|
event->SetType( (CChoreoEvent::EVENTTYPE)type ); |
|
event->SetName( params.m_szName ); |
|
event->SetParameters( params.m_szParameters ); |
|
event->SetParameters2( params.m_szParameters2 ); |
|
event->SetParameters3( params.m_szParameters3 ); |
|
event->SetStartTime( params.m_flStartTime ); |
|
|
|
event->SetResumeCondition( params.m_bResumeCondition ); |
|
event->SetLockBodyFacing( params.m_bLockBodyFacing ); |
|
event->SetDistanceToTarget( params.m_flDistanceToTarget ); |
|
event->SetForceShortMovement( params.m_bForceShortMovement ); |
|
event->SetSyncToFollowingGesture( params.m_bSyncToFollowingGesture ); |
|
event->SetActive( !params.m_bDisabled ); |
|
event->SetPlayOverScript( params.m_bPlayOverScript ); |
|
|
|
if ( params.m_bUsesTag ) |
|
{ |
|
event->SetUsingRelativeTag( true, params.m_szTagName, params.m_szTagWav ); |
|
} |
|
else |
|
{ |
|
event->SetUsingRelativeTag( false ); |
|
} |
|
CChoreoChannel *pchannel = channel->GetChannel(); |
|
|
|
event->SetChannel( pchannel ); |
|
event->SetActor( pchannel->GetActor() ); |
|
|
|
if ( params.m_bHasEndTime && |
|
params.m_flEndTime != -1.0 && |
|
params.m_flEndTime > params.m_flStartTime ) |
|
{ |
|
event->SetEndTime( params.m_flEndTime ); |
|
} |
|
else |
|
{ |
|
event->SetEndTime( -1.0f ); |
|
} |
|
|
|
switch ( event->GetType() ) |
|
{ |
|
default: |
|
break; |
|
case CChoreoEvent::SUBSCENE: |
|
{ |
|
// Just grab end time |
|
CChoreoScene *scene = LoadScene( event->GetParameters() ); |
|
if ( scene ) |
|
{ |
|
event->SetEndTime( params.m_flStartTime + scene->FindStopTime() ); |
|
} |
|
delete scene; |
|
} |
|
break; |
|
case CChoreoEvent::SEQUENCE: |
|
{ |
|
CheckSequenceLength( event, false ); |
|
// AutoaddSequenceKeys( event); |
|
} |
|
break; |
|
case CChoreoEvent::GESTURE: |
|
{ |
|
DefaultGestureLength( event, false ); |
|
AutoaddGestureKeys( event, false ); |
|
} |
|
break; |
|
case CChoreoEvent::LOOKAT: |
|
case CChoreoEvent::FACE: |
|
{ |
|
if ( params.usepitchyaw ) |
|
{ |
|
event->SetPitch( params.pitch ); |
|
event->SetYaw( params.yaw ); |
|
} |
|
else |
|
{ |
|
event->SetPitch( 0 ); |
|
event->SetYaw( 0 ); |
|
} |
|
} |
|
break; |
|
case CChoreoEvent::SPEAK: |
|
{ |
|
// Try and load wav to get length |
|
CAudioSource *wave = sound->LoadSound( va( "sound/%s", FacePoser_TranslateSoundName( event ) ) ); |
|
if ( wave ) |
|
{ |
|
event->SetEndTime( params.m_flStartTime + wave->GetRunningLength() ); |
|
delete wave; |
|
} |
|
|
|
event->SetSuppressingCaptionAttenuation( params.m_bCloseCaptionNoAttenuate ); |
|
} |
|
break; |
|
} |
|
event->SnapTimes(); |
|
|
|
DeleteSceneWidgets(); |
|
|
|
// Add to appropriate channel |
|
pchannel->AddEvent( event ); |
|
|
|
CreateSceneWidgets(); |
|
|
|
// Redraw |
|
InvalidateLayout(); |
|
} |
|
|
|
PushRedo( "Add Event" ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Adds a scene "pause" event |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::AddGlobalEvent( CChoreoEvent::EVENTTYPE type ) |
|
{ |
|
int mx, my; |
|
mx = m_nClickedX; |
|
my = m_nClickedY; |
|
|
|
// Convert click position local to this window |
|
POINT pt; |
|
pt.x = mx; |
|
pt.y = my; |
|
|
|
CGlobalEventParams params; |
|
memset( ¶ms, 0, sizeof( params ) ); |
|
|
|
params.m_nType = type; |
|
|
|
switch ( type ) |
|
{ |
|
default: |
|
Assert( 0 ); |
|
strcpy( params.m_szDialogTitle, "???" ); |
|
break; |
|
case CChoreoEvent::SECTION: |
|
{ |
|
strcpy( params.m_szDialogTitle, "Add Pause Point" ); |
|
} |
|
break; |
|
case CChoreoEvent::LOOP: |
|
{ |
|
strcpy( params.m_szDialogTitle, "Add Loop Point" ); |
|
} |
|
break; |
|
case CChoreoEvent::STOPPOINT: |
|
{ |
|
strcpy( params.m_szDialogTitle, "Add Fire Completion" ); |
|
} |
|
break; |
|
} |
|
strcpy( params.m_szName, "" ); |
|
strcpy( params.m_szAction, "" ); |
|
|
|
params.m_flStartTime = GetTimeValueForMouse( pt.x ); |
|
|
|
if ( !GlobalEventProperties( ¶ms ) ) |
|
return; |
|
|
|
if ( strlen( params.m_szName ) <= 0 ) |
|
{ |
|
Con_Printf( "Pause section event must have a valid name\n" ); |
|
return; |
|
} |
|
|
|
if ( strlen( params.m_szAction ) <= 0 ) |
|
{ |
|
Con_Printf( "No action specified for section pause\n" ); |
|
return; |
|
} |
|
|
|
char undotext[ 256 ]; |
|
undotext[0]=0; |
|
switch( type ) |
|
{ |
|
default: |
|
Assert( 0 ); |
|
break; |
|
case CChoreoEvent::SECTION: |
|
{ |
|
Q_strcpy( undotext, "Add Section Pause" ); |
|
} |
|
break; |
|
case CChoreoEvent::LOOP: |
|
{ |
|
Q_strcpy( undotext, "Add Loop Point" ); |
|
} |
|
break; |
|
case CChoreoEvent::STOPPOINT: |
|
{ |
|
Q_strcpy( undotext, "Add Fire Completion" ); |
|
} |
|
break; |
|
} |
|
|
|
SetDirty( true ); |
|
|
|
PushUndo( undotext ); |
|
|
|
CChoreoEvent *event = m_pScene->AllocEvent(); |
|
if ( event ) |
|
{ |
|
event->SetType( type ); |
|
event->SetName( params.m_szName ); |
|
event->SetParameters( params.m_szAction ); |
|
event->SetStartTime( params.m_flStartTime ); |
|
event->SetEndTime( -1.0f ); |
|
|
|
switch ( type ) |
|
{ |
|
default: |
|
break; |
|
case CChoreoEvent::LOOP: |
|
{ |
|
event->SetLoopCount( params.m_nLoopCount ); |
|
event->SetParameters( va( "%f", params.m_flLoopTime ) ); |
|
} |
|
break; |
|
} |
|
|
|
event->SnapTimes(); |
|
|
|
DeleteSceneWidgets(); |
|
|
|
CreateSceneWidgets(); |
|
|
|
// Redraw |
|
InvalidateLayout(); |
|
} |
|
|
|
PushRedo( undotext ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *event - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::EditGlobalEvent( CChoreoEvent *event ) |
|
{ |
|
if ( !event ) |
|
return; |
|
|
|
CGlobalEventParams params; |
|
memset( ¶ms, 0, sizeof( params ) ); |
|
|
|
params.m_nType = event->GetType(); |
|
|
|
switch ( event->GetType() ) |
|
{ |
|
default: |
|
Assert( 0 ); |
|
strcpy( params.m_szDialogTitle, "???" ); |
|
break; |
|
case CChoreoEvent::SECTION: |
|
{ |
|
strcpy( params.m_szDialogTitle, "Edit Pause Point" ); |
|
V_strcpy_safe( params.m_szAction, event->GetParameters() ); |
|
} |
|
break; |
|
case CChoreoEvent::LOOP: |
|
{ |
|
strcpy( params.m_szDialogTitle, "Edit Loop Point" ); |
|
strcpy( params.m_szAction, "" ); |
|
params.m_flLoopTime = (float)atof( event->GetParameters() ); |
|
params.m_nLoopCount = event->GetLoopCount(); |
|
} |
|
break; |
|
case CChoreoEvent::STOPPOINT: |
|
{ |
|
strcpy( params.m_szDialogTitle, "Edit Fire Completion" ); |
|
strcpy( params.m_szAction, "" ); |
|
} |
|
break; |
|
} |
|
|
|
strcpy( params.m_szName, event->GetName() ); |
|
|
|
params.m_flStartTime = event->GetStartTime(); |
|
|
|
if ( !GlobalEventProperties( ¶ms ) ) |
|
return; |
|
|
|
if ( strlen( params.m_szName ) <= 0 ) |
|
{ |
|
Con_Printf( "Event %s must have a valid name\n", event->GetName() ); |
|
return; |
|
} |
|
|
|
if ( strlen( params.m_szAction ) <= 0 ) |
|
{ |
|
Con_Printf( "No action specified for %s\n", event->GetName() ); |
|
return; |
|
} |
|
|
|
SetDirty( true ); |
|
|
|
char undotext[ 256 ]; |
|
undotext[0]=0; |
|
switch( event->GetType() ) |
|
{ |
|
default: |
|
Assert( 0 ); |
|
break; |
|
case CChoreoEvent::SECTION: |
|
{ |
|
Q_strcpy( undotext, "Edit Section Pause" ); |
|
} |
|
break; |
|
case CChoreoEvent::LOOP: |
|
{ |
|
Q_strcpy( undotext, "Edit Loop Point" ); |
|
} |
|
break; |
|
case CChoreoEvent::STOPPOINT: |
|
{ |
|
Q_strcpy( undotext, "Edit Fire Completion" ); |
|
} |
|
break; |
|
} |
|
|
|
PushUndo( undotext ); |
|
|
|
event->SetName( params.m_szName ); |
|
event->SetStartTime( params.m_flStartTime ); |
|
event->SetEndTime( -1.0f ); |
|
|
|
switch ( event->GetType() ) |
|
{ |
|
default: |
|
{ |
|
event->SetParameters( params.m_szAction ); |
|
} |
|
break; |
|
case CChoreoEvent::LOOP: |
|
{ |
|
event->SetLoopCount( params.m_nLoopCount ); |
|
event->SetParameters( va( "%f", params.m_flLoopTime ) ); |
|
} |
|
break; |
|
} |
|
|
|
event->SnapTimes(); |
|
|
|
PushRedo( undotext ); |
|
|
|
// Redraw |
|
InvalidateLayout(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *event - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::DeleteGlobalEvent( CChoreoEvent *event ) |
|
{ |
|
if ( !event || !m_pScene ) |
|
return; |
|
|
|
SetDirty( true ); |
|
|
|
|
|
char undotext[ 256 ]; |
|
undotext[0]=0; |
|
switch( event->GetType() ) |
|
{ |
|
default: |
|
Assert( 0 ); |
|
break; |
|
case CChoreoEvent::SECTION: |
|
{ |
|
Q_strcpy( undotext, "Delete Section Pause" ); |
|
} |
|
break; |
|
case CChoreoEvent::LOOP: |
|
{ |
|
Q_strcpy( undotext, "Delete Loop Point" ); |
|
} |
|
break; |
|
case CChoreoEvent::STOPPOINT: |
|
{ |
|
Q_strcpy( undotext, "Delete Fire Completion" ); |
|
} |
|
break; |
|
} |
|
|
|
PushUndo( undotext ); |
|
|
|
DeleteSceneWidgets(); |
|
|
|
Con_Printf( "Deleting %s\n", event->GetName() ); |
|
|
|
m_pScene->DeleteReferencedObjects( event ); |
|
|
|
CreateSceneWidgets(); |
|
|
|
PushRedo( undotext ); |
|
|
|
// Redraw |
|
InvalidateLayout(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *event - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::EditEvent( CChoreoEvent *event ) |
|
{ |
|
if ( !event ) |
|
return; |
|
|
|
CEventParams params; |
|
memset( ¶ms, 0, sizeof( params ) ); |
|
|
|
strcpy( params.m_szDialogTitle, "Edit Event" ); |
|
|
|
// Copy in current even properties |
|
params.m_nType = event->GetType(); |
|
params.m_bDisabled = !event->GetActive(); |
|
|
|
switch ( params.m_nType ) |
|
{ |
|
case CChoreoEvent::EXPRESSION: |
|
case CChoreoEvent::SEQUENCE: |
|
case CChoreoEvent::MOVETO: |
|
case CChoreoEvent::SPEAK: |
|
case CChoreoEvent::GESTURE: |
|
case CChoreoEvent::INTERRUPT: |
|
case CChoreoEvent::PERMIT_RESPONSES: |
|
case CChoreoEvent::GENERIC: |
|
V_strcpy_safe( params.m_szParameters3, event->GetParameters3() ); |
|
V_strcpy_safe( params.m_szParameters2, event->GetParameters2() ); |
|
V_strcpy_safe( params.m_szParameters, event->GetParameters() ); |
|
V_strcpy_safe( params.m_szName, event->GetName() ); |
|
break; |
|
case CChoreoEvent::FACE: |
|
case CChoreoEvent::LOOKAT: |
|
case CChoreoEvent::FIRETRIGGER: |
|
case CChoreoEvent::FLEXANIMATION: |
|
case CChoreoEvent::SUBSCENE: |
|
V_strcpy_safe( params.m_szParameters, event->GetParameters() ); |
|
V_strcpy_safe( params.m_szName, event->GetName() ); |
|
|
|
if ( params.m_nType == CChoreoEvent::LOOKAT || params.m_nType == CChoreoEvent::FACE ) |
|
{ |
|
if ( event->GetPitch() != 0 || |
|
event->GetYaw() != 0 ) |
|
{ |
|
params.usepitchyaw = true; |
|
params.pitch = event->GetPitch(); |
|
params.yaw = event->GetYaw(); |
|
} |
|
} |
|
break; |
|
default: |
|
Con_Printf( "Don't know how to edit event type %s\n", |
|
CChoreoEvent::NameForType( (CChoreoEvent::EVENTTYPE)params.m_nType ) ); |
|
return; |
|
} |
|
|
|
params.m_pScene = m_pScene; |
|
params.m_pEvent = event; |
|
params.m_flStartTime = event->GetStartTime(); |
|
params.m_flEndTime = event->GetEndTime(); |
|
params.m_bHasEndTime = event->HasEndTime(); |
|
|
|
params.m_bFixedLength = event->IsFixedLength(); |
|
params.m_bResumeCondition = event->IsResumeCondition(); |
|
params.m_bLockBodyFacing = event->IsLockBodyFacing(); |
|
params.m_flDistanceToTarget = event->GetDistanceToTarget(); |
|
params.m_bForceShortMovement = event->GetForceShortMovement(); |
|
params.m_bSyncToFollowingGesture = event->GetSyncToFollowingGesture(); |
|
params.m_bPlayOverScript = event->GetPlayOverScript(); |
|
params.m_bUsesTag = event->IsUsingRelativeTag(); |
|
params.m_bCloseCaptionNoAttenuate = event->IsSuppressingCaptionAttenuation(); |
|
|
|
if ( params.m_bUsesTag ) |
|
{ |
|
V_strcpy_safe( params.m_szTagName, event->GetRelativeTagName() ); |
|
V_strcpy_safe( params.m_szTagWav, event->GetRelativeWavName() ); |
|
} |
|
|
|
while (1) |
|
{ |
|
SetScrubTargetTime( m_flScrub ); |
|
FinishSimulation(); |
|
sound->Flush(); |
|
|
|
m_bForceProcess = true; |
|
if (!EventProperties( ¶ms )) |
|
{ |
|
m_bForceProcess = false; |
|
return; |
|
} |
|
m_bForceProcess = false; |
|
|
|
if ( Q_strlen( params.m_szName ) <= 0 ) |
|
{ |
|
mxMessageBox( this, va( "Event %s must have a valid name", event->GetName() ), |
|
"Edit Event", MX_MB_OK | MX_MB_ERROR ); |
|
continue; |
|
} |
|
|
|
if ( Q_strlen( params.m_szParameters ) <= 0 ) |
|
{ |
|
bool shouldBreak = false; |
|
|
|
switch ( params.m_nType ) |
|
{ |
|
case CChoreoEvent::FLEXANIMATION: |
|
case CChoreoEvent::INTERRUPT: |
|
case CChoreoEvent::PERMIT_RESPONSES: |
|
shouldBreak = true; |
|
break; |
|
case CChoreoEvent::GESTURE: |
|
if ( !Q_stricmp( params.m_szName, "NULL" ) ) |
|
{ |
|
shouldBreak = true; |
|
} |
|
break; |
|
default: |
|
// Have to have a non-null parameters block |
|
break; |
|
} |
|
|
|
if ( !shouldBreak ) |
|
{ |
|
mxMessageBox( this, va( "No parameters specified for %s\n", params.m_szName ), |
|
"Edit Event", MX_MB_OK | MX_MB_ERROR ); |
|
continue; |
|
} |
|
} |
|
|
|
break; |
|
} |
|
|
|
|
|
SetDirty( true ); |
|
|
|
PushUndo( "Edit Event" ); |
|
|
|
event->SetName( params.m_szName ); |
|
event->SetParameters( params.m_szParameters ); |
|
event->SetParameters2( params.m_szParameters2 ); |
|
event->SetParameters3( params.m_szParameters3 ); |
|
event->SetStartTime( params.m_flStartTime ); |
|
event->SetResumeCondition( params.m_bResumeCondition ); |
|
event->SetLockBodyFacing( params.m_bLockBodyFacing ); |
|
event->SetDistanceToTarget( params.m_flDistanceToTarget ); |
|
event->SetForceShortMovement( params.m_bForceShortMovement ); |
|
event->SetSyncToFollowingGesture( params.m_bSyncToFollowingGesture ); |
|
event->SetActive( !params.m_bDisabled ); |
|
event->SetPlayOverScript( params.m_bPlayOverScript ); |
|
if ( params.m_bUsesTag ) |
|
{ |
|
event->SetUsingRelativeTag( true, params.m_szTagName, params.m_szTagWav ); |
|
} |
|
else |
|
{ |
|
event->SetUsingRelativeTag( false ); |
|
} |
|
|
|
if ( params.m_bHasEndTime && |
|
params.m_flEndTime != -1.0 && |
|
params.m_flEndTime > params.m_flStartTime ) |
|
{ |
|
float dt = params.m_flEndTime - event->GetEndTime(); |
|
float newduration = event->GetDuration() + dt; |
|
RescaleRamp( event, newduration ); |
|
switch ( event->GetType() ) |
|
{ |
|
default: |
|
break; |
|
case CChoreoEvent::GESTURE: |
|
{ |
|
event->RescaleGestureTimes( event->GetStartTime(), event->GetEndTime() + dt, true ); |
|
} |
|
break; |
|
case CChoreoEvent::FLEXANIMATION: |
|
{ |
|
RescaleExpressionTimes( event, event->GetStartTime(), event->GetEndTime() + dt ); |
|
} |
|
break; |
|
} |
|
event->SetEndTime( params.m_flEndTime ); |
|
event->SnapTimes(); |
|
event->ResortRamp(); |
|
} |
|
else |
|
{ |
|
event->SetEndTime( -1.0f ); |
|
} |
|
|
|
switch ( event->GetType() ) |
|
{ |
|
default: |
|
break; |
|
case CChoreoEvent::SPEAK: |
|
{ |
|
// Try and load wav to get length |
|
CAudioSource *wave = sound->LoadSound( va( "sound/%s", FacePoser_TranslateSoundName( event ) ) ); |
|
if ( wave ) |
|
{ |
|
event->SetEndTime( params.m_flStartTime + wave->GetRunningLength() ); |
|
delete wave; |
|
} |
|
|
|
event->SetSuppressingCaptionAttenuation( params.m_bCloseCaptionNoAttenuate ); |
|
} |
|
break; |
|
case CChoreoEvent::SUBSCENE: |
|
{ |
|
// Just grab end time |
|
CChoreoScene *scene = LoadScene( event->GetParameters() ); |
|
if ( scene ) |
|
{ |
|
event->SetEndTime( params.m_flStartTime + scene->FindStopTime() ); |
|
} |
|
delete scene; |
|
} |
|
break; |
|
case CChoreoEvent::SEQUENCE: |
|
{ |
|
CheckSequenceLength( event, false ); |
|
} |
|
break; |
|
case CChoreoEvent::GESTURE: |
|
{ |
|
CheckGestureLength( event, false ); |
|
AutoaddGestureKeys( event, false ); |
|
g_pGestureTool->redraw(); |
|
} |
|
break; |
|
case CChoreoEvent::LOOKAT: |
|
case CChoreoEvent::FACE: |
|
{ |
|
if ( params.usepitchyaw ) |
|
{ |
|
event->SetPitch( params.pitch ); |
|
event->SetYaw( params.yaw ); |
|
} |
|
else |
|
{ |
|
event->SetPitch( 0 ); |
|
event->SetYaw( 0 ); |
|
} |
|
} |
|
break; |
|
} |
|
|
|
event->SnapTimes(); |
|
|
|
PushRedo( "Edit Event" ); |
|
|
|
// Redraw |
|
InvalidateLayout(); |
|
} |
|
|
|
void CChoreoView::EnableSelectedEvents( bool state ) |
|
{ |
|
if ( !m_pScene ) |
|
return; |
|
|
|
SetDirty( true ); |
|
|
|
// If we right clicked on an unseleced event, then select it for the user. |
|
if ( CountSelectedEvents() == 0 ) |
|
{ |
|
CChoreoEventWidget *event = m_pClickedEvent; |
|
if ( event ) |
|
{ |
|
event->SetSelected( true ); |
|
} |
|
} |
|
|
|
char const *desc = state ? "Enable Events" : "Disable Events"; |
|
|
|
PushUndo( desc ); |
|
|
|
// Find the appropriate event by iterating across all actors and channels |
|
for ( int i = 0; i < m_SceneActors.Size(); i++ ) |
|
{ |
|
CChoreoActorWidget *a = m_SceneActors[ i ]; |
|
if ( !a ) |
|
continue; |
|
|
|
for ( int j = 0; j < a->GetNumChannels(); j++ ) |
|
{ |
|
CChoreoChannelWidget *channel = a->GetChannel( j ); |
|
if ( !channel ) |
|
continue; |
|
|
|
for ( int k = channel->GetNumEvents() - 1; k >= 0; k-- ) |
|
{ |
|
CChoreoEventWidget *event = channel->GetEvent( k ); |
|
if ( !event->IsSelected() ) |
|
continue; |
|
|
|
event->GetEvent()->SetActive( state ); |
|
} |
|
} |
|
} |
|
|
|
for ( int i = 0; i < m_SceneGlobalEvents.Size(); i++ ) |
|
{ |
|
CChoreoGlobalEventWidget *event = m_SceneGlobalEvents[ i ]; |
|
if ( !event || !event->IsSelected() ) |
|
continue; |
|
|
|
event->GetEvent()->SetActive( state ); |
|
} |
|
|
|
PushRedo( desc ); |
|
|
|
// Redraw |
|
InvalidateLayout(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *event - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::DeleteSelectedEvents( void ) |
|
{ |
|
if ( !m_pScene ) |
|
return; |
|
|
|
SetDirty( true ); |
|
|
|
PushUndo( "Delete Events" ); |
|
|
|
int deleteCount = 0; |
|
|
|
float oldstoptime = m_pScene->FindStopTime(); |
|
|
|
// Find the appropriate event by iterating across all actors and channels |
|
for ( int i = 0; i < m_SceneActors.Size(); i++ ) |
|
{ |
|
CChoreoActorWidget *a = m_SceneActors[ i ]; |
|
if ( !a ) |
|
continue; |
|
|
|
for ( int j = 0; j < a->GetNumChannels(); j++ ) |
|
{ |
|
CChoreoChannelWidget *channel = a->GetChannel( j ); |
|
if ( !channel ) |
|
continue; |
|
|
|
for ( int k = channel->GetNumEvents() - 1; k >= 0; k-- ) |
|
{ |
|
CChoreoEventWidget *event = channel->GetEvent( k ); |
|
if ( !event->IsSelected() ) |
|
continue; |
|
|
|
channel->GetChannel()->RemoveEvent( event->GetEvent() ); |
|
m_pScene->DeleteReferencedObjects( event->GetEvent() ); |
|
|
|
deleteCount++; |
|
} |
|
} |
|
} |
|
|
|
for ( int i = 0; i < m_SceneGlobalEvents.Size(); i++ ) |
|
{ |
|
CChoreoGlobalEventWidget *event = m_SceneGlobalEvents[ i ]; |
|
if ( !event || !event->IsSelected() ) |
|
continue; |
|
|
|
m_pScene->DeleteReferencedObjects( event->GetEvent() ); |
|
|
|
deleteCount++; |
|
} |
|
|
|
DeleteSceneWidgets(); |
|
|
|
ReportSceneClearToTools(); |
|
|
|
CreateSceneWidgets(); |
|
|
|
PushRedo( "Delete Events" ); |
|
|
|
Con_Printf( "Deleted <%i> events\n", deleteCount ); |
|
|
|
if ( m_pScene->FindStopTime() != oldstoptime ) |
|
{ |
|
// Force scroll bars to recompute |
|
ForceScrollBarsToRecompute( false ); |
|
|
|
} |
|
// Redraw |
|
InvalidateLayout(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : resetthumb - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::ForceScrollBarsToRecompute( bool resetthumb ) |
|
{ |
|
if ( resetthumb ) |
|
{ |
|
m_flLeftOffset = 0.0f; |
|
} |
|
m_nLastHPixelsNeeded = -1; |
|
m_nLastVPixelsNeeded = -1; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *filename - |
|
// Output : CChoreoScene |
|
//----------------------------------------------------------------------------- |
|
CChoreoScene *CChoreoView::LoadScene( char const *filename ) |
|
{ |
|
// If relative path, then make a full path |
|
char pFullPathBuf[ MAX_PATH ]; |
|
if ( !Q_IsAbsolutePath( filename ) ) |
|
{ |
|
filesystem->RelativePathToFullPath( filename, "GAME", pFullPathBuf, sizeof( pFullPathBuf ) ); |
|
filename = pFullPathBuf; |
|
} |
|
|
|
if ( !filesystem->FileExists( filename ) ) |
|
return NULL; |
|
|
|
LoadScriptFile( const_cast<char*>( filename ) ); |
|
|
|
CChoreoScene *scene = ChoreoLoadScene( filename, this, tokenprocessor, Con_Printf ); |
|
return scene; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CChoreoView::FixupSequenceDurations( CChoreoScene *scene, bool checkonly ) |
|
{ |
|
bool bret = false; |
|
if ( !scene ) |
|
return bret; |
|
|
|
int c = scene->GetNumEvents(); |
|
for ( int i = 0; i < c; i++ ) |
|
{ |
|
CChoreoEvent *event = scene->GetEvent( i ); |
|
if ( !event ) |
|
continue; |
|
|
|
switch ( event->GetType() ) |
|
{ |
|
default: |
|
break; |
|
case CChoreoEvent::SPEAK: |
|
{ |
|
// Try and load wav to get length |
|
CAudioSource *wave = sound->LoadSound( va( "sound/%s", FacePoser_TranslateSoundName( event ) ) ); |
|
if ( wave ) |
|
{ |
|
float endtime = event->GetStartTime() + wave->GetRunningLength(); |
|
if ( event->GetEndTime() != endtime ) |
|
{ |
|
event->SetEndTime( event->GetStartTime() + wave->GetRunningLength() ); |
|
bret = true; |
|
} |
|
delete wave; |
|
} |
|
} |
|
break; |
|
case CChoreoEvent::SEQUENCE: |
|
{ |
|
if ( CheckSequenceLength( event, checkonly ) ) |
|
{ |
|
bret = true; |
|
} |
|
} |
|
break; |
|
case CChoreoEvent::GESTURE: |
|
{ |
|
if ( CheckGestureLength( event, checkonly ) ) |
|
{ |
|
bret = true; |
|
} |
|
if ( AutoaddGestureKeys( event, checkonly ) ) |
|
{ |
|
bret = true; |
|
} |
|
} |
|
break; |
|
} |
|
} |
|
|
|
return bret; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: IChoreoEventCallback |
|
// Input : currenttime - |
|
// *event - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::StartEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event ) |
|
{ |
|
if ( !event || !event->GetActive() ) |
|
return; |
|
|
|
CChoreoActor *actor = event->GetActor(); |
|
if ( actor && !actor->GetActive() ) |
|
{ |
|
return; |
|
} |
|
|
|
CChoreoChannel *channel = event->GetChannel(); |
|
if ( channel && !channel->GetActive() ) |
|
{ |
|
return; |
|
} |
|
|
|
// Con_Printf( "%8.4f: start %s\n", currenttime, event->GetDescription() ); |
|
|
|
switch ( event->GetType() ) |
|
{ |
|
case CChoreoEvent::SEQUENCE: |
|
{ |
|
ProcessSequence( scene, event ); |
|
} |
|
break; |
|
case CChoreoEvent::SUBSCENE: |
|
{ |
|
if ( !scene->IsSubScene() ) |
|
{ |
|
CChoreoScene *subscene = event->GetSubScene(); |
|
if ( !subscene ) |
|
{ |
|
subscene = LoadScene( event->GetParameters() ); |
|
subscene->SetSubScene( true ); |
|
event->SetSubScene( subscene ); |
|
} |
|
|
|
if ( subscene ) |
|
{ |
|
subscene->ResetSimulation( m_bForward ); |
|
} |
|
} |
|
} |
|
break; |
|
case CChoreoEvent::SECTION: |
|
{ |
|
ProcessPause( scene, event ); |
|
} |
|
break; |
|
case CChoreoEvent::SPEAK: |
|
{ |
|
if ( ShouldProcessSpeak() ) |
|
{ |
|
// See if we should trigger CC |
|
char soundname[ 512 ]; |
|
Q_strncpy( soundname, event->GetParameters(), sizeof( soundname ) ); |
|
|
|
float actualEndTime = event->GetEndTime(); |
|
|
|
if ( event->GetCloseCaptionType() == CChoreoEvent::CC_MASTER ) |
|
{ |
|
char tok[ CChoreoEvent::MAX_CCTOKEN_STRING ]; |
|
if ( event->GetPlaybackCloseCaptionToken( tok, sizeof( tok ) ) ) |
|
{ |
|
float duration = max( event->GetDuration(), event->GetLastSlaveEndTime() - event->GetStartTime() ); |
|
|
|
closecaptionmanager->Process( tok, duration, GetCloseCaptionLanguageId() ); |
|
|
|
// Use the token as the sound name lookup, too. |
|
if ( event->IsUsingCombinedFile() && |
|
( event->GetNumSlaves() > 0 ) ) |
|
{ |
|
Q_strncpy( soundname, tok, sizeof( soundname ) ); |
|
actualEndTime = max( actualEndTime, event->GetLastSlaveEndTime() ); |
|
} |
|
} |
|
} |
|
|
|
StudioModel *model = FindAssociatedModel( scene, event->GetActor() ); |
|
|
|
CAudioMixer *mixer = event->GetMixer(); |
|
if ( !mixer || !sound->IsSoundPlaying( mixer ) ) |
|
{ |
|
CSoundParameters params; |
|
|
|
float volume = VOL_NORM; |
|
gender_t gender = GENDER_NONE; |
|
if (model) |
|
{ |
|
gender = soundemitter->GetActorGender( model->GetFileName() ); |
|
} |
|
|
|
if ( !Q_stristr( soundname, ".wav" ) && |
|
soundemitter->GetParametersForSound( soundname, params, gender ) ) |
|
{ |
|
volume = params.volume; |
|
} |
|
|
|
sound->PlaySound( |
|
model, |
|
volume, |
|
va( "sound/%s", FacePoser_TranslateSoundName( soundname, model ) ), |
|
&mixer ); |
|
event->SetMixer( mixer ); |
|
} |
|
|
|
if ( mixer ) |
|
{ |
|
mixer->SetDirection( m_flFrameTime >= 0.0f ); |
|
float starttime, endtime; |
|
starttime = event->GetStartTime(); |
|
endtime = actualEndTime; |
|
|
|
float soundtime = endtime - starttime; |
|
if ( soundtime > 0.0f ) |
|
{ |
|
float f = ( currenttime - starttime ) / soundtime; |
|
f = clamp( f, 0.0f, 1.0f ); |
|
|
|
// Compute sample |
|
float numsamples = (float)mixer->GetSource()->SampleCount(); |
|
|
|
int cursample = f * numsamples; |
|
cursample = clamp( cursample, 0, numsamples - 1 ); |
|
mixer->SetSamplePosition( cursample ); |
|
mixer->SetActive( true ); |
|
} |
|
} |
|
} |
|
} |
|
break; |
|
case CChoreoEvent::EXPRESSION: |
|
{ |
|
} |
|
break; |
|
case CChoreoEvent::LOOP: |
|
{ |
|
ProcessLoop( scene, event ); |
|
} |
|
break; |
|
case CChoreoEvent::STOPPOINT: |
|
{ |
|
// Nothing, this is a symbolic event for keeping the vcd alive for ramping out after the last true event |
|
} |
|
break; |
|
default: |
|
break; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : currenttime - |
|
// *event - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::EndEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event ) |
|
{ |
|
if ( !event || !event->GetActive() ) |
|
return; |
|
|
|
CChoreoActor *actor = event->GetActor(); |
|
if ( actor && !actor->GetActive() ) |
|
{ |
|
return; |
|
} |
|
|
|
CChoreoChannel *channel = event->GetChannel(); |
|
if ( channel && !channel->GetActive() ) |
|
{ |
|
return; |
|
} |
|
|
|
switch ( event->GetType() ) |
|
{ |
|
case CChoreoEvent::SUBSCENE: |
|
{ |
|
CChoreoScene *subscene = event->GetSubScene(); |
|
if ( subscene ) |
|
{ |
|
subscene->ResetSimulation(); |
|
} |
|
} |
|
break; |
|
case CChoreoEvent::SPEAK: |
|
{ |
|
CAudioMixer *mixer = event->GetMixer(); |
|
if ( mixer && sound->IsSoundPlaying( mixer ) ) |
|
{ |
|
sound->StopSound( mixer ); |
|
} |
|
event->SetMixer( NULL ); |
|
} |
|
break; |
|
default: |
|
break; |
|
} |
|
|
|
// Con_Printf( "%8.4f: finish %s\n", currenttime, event->GetDescription() ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *event - |
|
// mx - |
|
// my - |
|
//----------------------------------------------------------------------------- |
|
int CChoreoView::GetTagUnderCursorPos( CChoreoEventWidget *event, int mx, int my ) |
|
{ |
|
if ( !event ) |
|
{ |
|
return -1; |
|
} |
|
|
|
for ( int i = 0; i < event->GetEvent()->GetNumRelativeTags(); i++ ) |
|
{ |
|
CEventRelativeTag *tag = event->GetEvent()->GetRelativeTag( i ); |
|
if ( !tag ) |
|
continue; |
|
|
|
// Determine left edcge |
|
RECT bounds; |
|
bounds = event->getBounds(); |
|
int left = bounds.left + (int)( tag->GetPercentage() * (float)( bounds.right - bounds.left ) + 0.5f ); |
|
|
|
int tolerance = 3; |
|
|
|
if ( abs( mx - left ) < tolerance ) |
|
{ |
|
if ( abs( my - bounds.top ) < tolerance ) |
|
{ |
|
return i; |
|
} |
|
} |
|
} |
|
|
|
return -1; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *event - |
|
// mx - |
|
// my - |
|
//----------------------------------------------------------------------------- |
|
CEventAbsoluteTag *CChoreoView::GetAbsoluteTagUnderCursorPos( CChoreoEventWidget *event, int mx, int my ) |
|
{ |
|
if ( !event ) |
|
{ |
|
return NULL; |
|
} |
|
|
|
for ( int i = 0; i < event->GetEvent()->GetNumAbsoluteTags( CChoreoEvent::PLAYBACK ); i++ ) |
|
{ |
|
CEventAbsoluteTag *tag = event->GetEvent()->GetAbsoluteTag( CChoreoEvent::PLAYBACK, i ); |
|
if ( !tag ) |
|
continue; |
|
|
|
// Determine left edcge |
|
RECT bounds; |
|
bounds = event->getBounds(); |
|
int left = bounds.left + (int)( tag->GetPercentage() * (float)( bounds.right - bounds.left ) + 0.5f ); |
|
|
|
int tolerance = 3; |
|
|
|
if ( abs( mx - left ) < tolerance ) |
|
{ |
|
if ( abs( my - bounds.top ) < tolerance ) |
|
{ |
|
return tag; |
|
} |
|
} |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : mx - |
|
// my - |
|
// **actor - |
|
// **channel - |
|
// **event - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::GetObjectsUnderMouse( int mx, int my, CChoreoActorWidget **actor, |
|
CChoreoChannelWidget **channel, CChoreoEventWidget **event, CChoreoGlobalEventWidget **globalevent, |
|
int *clickedTag, |
|
CEventAbsoluteTag **absolutetag, int *clickedCCArea ) |
|
{ |
|
if ( actor ) |
|
{ |
|
*actor = GetActorUnderCursorPos( mx, my ); |
|
} |
|
if ( channel ) |
|
{ |
|
*channel = GetChannelUnderCursorPos( mx, my ); |
|
if ( *channel && clickedCCArea ) |
|
{ |
|
*clickedCCArea = (*channel)->GetChannelItemUnderMouse( mx, my ); |
|
} |
|
} |
|
if ( event ) |
|
{ |
|
*event = GetEventUnderCursorPos( mx, my ); |
|
} |
|
if ( globalevent ) |
|
{ |
|
*globalevent = GetGlobalEventUnderCursorPos( mx, my ); |
|
} |
|
if ( clickedTag ) |
|
{ |
|
if ( event && *event ) |
|
{ |
|
*clickedTag = GetTagUnderCursorPos( *event, mx, my ); |
|
} |
|
else |
|
{ |
|
*clickedTag = -1; |
|
} |
|
} |
|
if ( absolutetag ) |
|
{ |
|
if ( event && *event ) |
|
{ |
|
*absolutetag = GetAbsoluteTagUnderCursorPos( *event, mx, my ); |
|
} |
|
else |
|
{ |
|
*absolutetag = NULL; |
|
} |
|
} |
|
|
|
|
|
m_nSelectedEvents = CountSelectedEvents(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : mx - |
|
// my - |
|
// Output : CChoreoGlobalEventWidget |
|
//----------------------------------------------------------------------------- |
|
CChoreoGlobalEventWidget *CChoreoView::GetGlobalEventUnderCursorPos( int mx, int my ) |
|
{ |
|
POINT check; |
|
check.x = mx; |
|
check.y = my; |
|
|
|
CChoreoGlobalEventWidget *event; |
|
for ( int i = 0; i < m_SceneGlobalEvents.Size(); i++ ) |
|
{ |
|
event = m_SceneGlobalEvents[ i ]; |
|
if ( !event ) |
|
continue; |
|
|
|
RECT bounds; |
|
event->getBounds( bounds ); |
|
|
|
if ( PtInRect( &bounds, check ) ) |
|
{ |
|
return event; |
|
} |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Caller must first translate mouse into screen coordinates |
|
// Input : mx - |
|
// my - |
|
//----------------------------------------------------------------------------- |
|
CChoreoActorWidget *CChoreoView::GetActorUnderCursorPos( int mx, int my ) |
|
{ |
|
POINT check; |
|
check.x = mx; |
|
check.y = my; |
|
|
|
CChoreoActorWidget *actor; |
|
for ( int i = 0; i < m_SceneActors.Size(); i++ ) |
|
{ |
|
actor = m_SceneActors[ i ]; |
|
if ( !actor ) |
|
continue; |
|
|
|
RECT bounds; |
|
actor->getBounds( bounds ); |
|
|
|
if ( PtInRect( &bounds, check ) ) |
|
{ |
|
return actor; |
|
} |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Caller must first translate mouse into screen coordinates |
|
// Input : mx - |
|
// my - |
|
// Output : CChoreoChannelWidget |
|
//----------------------------------------------------------------------------- |
|
CChoreoChannelWidget *CChoreoView::GetChannelUnderCursorPos( int mx, int my ) |
|
{ |
|
CChoreoActorWidget *actor = GetActorUnderCursorPos( mx, my ); |
|
if ( !actor ) |
|
{ |
|
return NULL; |
|
} |
|
|
|
POINT check; |
|
check.x = mx; |
|
check.y = my; |
|
|
|
CChoreoChannelWidget *channel; |
|
for ( int i = 0; i < actor->GetNumChannels(); i++ ) |
|
{ |
|
channel = actor->GetChannel( i ); |
|
if ( !channel ) |
|
continue; |
|
|
|
RECT bounds; |
|
channel->getBounds( bounds ); |
|
|
|
if ( PtInRect( &bounds, check ) ) |
|
{ |
|
return channel; |
|
} |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Caller must first translate mouse into screen coordinates |
|
// Input : mx - |
|
// my - |
|
//----------------------------------------------------------------------------- |
|
CChoreoEventWidget *CChoreoView::GetEventUnderCursorPos( int mx, int my ) |
|
{ |
|
CChoreoChannelWidget *channel = GetChannelUnderCursorPos( mx, my ); |
|
if ( !channel ) |
|
{ |
|
return NULL; |
|
} |
|
|
|
POINT check; |
|
check.x = mx; |
|
check.y = my; |
|
|
|
if ( mx < GetLabelWidth() ) |
|
return NULL; |
|
|
|
if ( my < GetStartRow() ) |
|
return NULL; |
|
|
|
if ( my >= h2() - ( m_nInfoHeight + m_nScrollbarHeight ) ) |
|
return NULL; |
|
|
|
CChoreoEventWidget *event; |
|
for ( int i = 0; i < channel->GetNumEvents(); i++ ) |
|
{ |
|
event = channel->GetEvent( i ); |
|
if ( !event ) |
|
continue; |
|
|
|
RECT bounds; |
|
event->getBounds( bounds ); |
|
|
|
// Events get an expanded border |
|
InflateRect( &bounds, 8, 4 ); |
|
|
|
if ( PtInRect( &bounds, check ) ) |
|
{ |
|
return event; |
|
} |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Select wave file for phoneme editing |
|
// Input : *filename - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::SetCurrentWaveFile( const char *filename, CChoreoEvent *event ) |
|
{ |
|
g_pPhonemeEditor->SetCurrentWaveFile( filename, false, event ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : pfn - |
|
// *param1 - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::TraverseWidgets( CVMEMBERFUNC pfn, CChoreoWidget *param1 ) |
|
{ |
|
for ( int i = 0; i < m_SceneActors.Size(); i++ ) |
|
{ |
|
CChoreoActorWidget *actor = m_SceneActors[ i ]; |
|
if ( !actor ) |
|
continue; |
|
|
|
(this->*pfn)( actor, param1 ); |
|
|
|
for ( int j = 0; j < actor->GetNumChannels(); j++ ) |
|
{ |
|
CChoreoChannelWidget *channel = actor->GetChannel( j ); |
|
if ( !channel ) |
|
continue; |
|
|
|
(this->*pfn)( channel, param1 ); |
|
|
|
for ( int k = 0; k < channel->GetNumEvents(); k++ ) |
|
{ |
|
CChoreoEventWidget *event = channel->GetEvent( k ); |
|
if ( !event ) |
|
continue; |
|
|
|
(this->*pfn)( event, param1 ); |
|
} |
|
} |
|
} |
|
|
|
for ( int i = 0; i < m_SceneGlobalEvents.Size(); i++ ) |
|
{ |
|
CChoreoGlobalEventWidget *event = m_SceneGlobalEvents[ i ]; |
|
if ( !event ) |
|
continue; |
|
|
|
(this->*pfn)( event, param1 ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *widget - |
|
// *param1 - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::Deselect( CChoreoWidget *widget, CChoreoWidget *param1 ) |
|
{ |
|
if ( widget->IsSelected() ) |
|
{ |
|
widget->SetSelected( false ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *widget - |
|
// *param1 - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::Select( CChoreoWidget *widget, CChoreoWidget *param1 ) |
|
{ |
|
if ( widget != param1 ) |
|
return; |
|
|
|
if ( widget->IsSelected() ) |
|
return; |
|
|
|
widget->SetSelected( true ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *widget - |
|
// *param1 - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::SelectAllEvents( CChoreoWidget *widget, CChoreoWidget *param1 ) |
|
{ |
|
CChoreoEventWidget *ew = dynamic_cast< CChoreoEventWidget * >( widget ); |
|
CChoreoGlobalEventWidget *gew = dynamic_cast< CChoreoGlobalEventWidget * >( widget ); |
|
|
|
if ( ew || gew ) |
|
{ |
|
if ( widget->IsSelected() ) |
|
return; |
|
|
|
widget->SetSelected( true ); |
|
} |
|
} |
|
|
|
bool CChoreoView::CreateAnimationEvent( int mx, int my, char const *animationname ) |
|
{ |
|
if ( !animationname || !animationname[0] ) |
|
return false; |
|
|
|
// Convert screen to client |
|
POINT pt; |
|
pt.x = mx; |
|
pt.y = my; |
|
|
|
ScreenToClient( (HWND)getHandle(), &pt ); |
|
|
|
if ( pt.x < 0 || pt.y < 0 ) |
|
return false; |
|
|
|
if ( pt.x > w2() || pt.y > h2() ) |
|
return false; |
|
|
|
pt.x -= GetLabelWidth(); |
|
m_nClickedX = pt.x; |
|
|
|
GetObjectsUnderMouse( pt.x, pt.y, &m_pClickedActor, &m_pClickedChannel, &m_pClickedEvent, &m_pClickedGlobalEvent, &m_nClickedTag, &m_pClickedAbsoluteTag, &m_nClickedChannelCloseCaptionButton ); |
|
|
|
// Find channel actor and time ( uses screen space coordinates ) |
|
// |
|
CChoreoChannelWidget *channel = GetChannelUnderCursorPos( pt.x, pt.y ); |
|
if ( !channel ) |
|
{ |
|
CChoreoActorWidget *actor = GetActorUnderCursorPos( pt.x, pt.y ); |
|
if ( !actor ) |
|
return false; |
|
|
|
// Grab first channel |
|
if ( !actor->GetNumChannels() ) |
|
return false; |
|
|
|
channel = actor->GetChannel( 0 ); |
|
} |
|
|
|
if ( !channel ) |
|
return false; |
|
|
|
CChoreoChannel *pchannel = channel->GetChannel(); |
|
if ( !pchannel ) |
|
{ |
|
Assert( 0 ); |
|
return false; |
|
} |
|
|
|
// At this point we need to ask the user what type of even to create (gesture or sequence) and just show the approprite dialog |
|
CChoiceParams params; |
|
strcpy( params.m_szDialogTitle, "Create Animation Event" ); |
|
|
|
params.m_bPositionDialog = false; |
|
params.m_nLeft = 0; |
|
params.m_nTop = 0; |
|
strcpy( params.m_szPrompt, "Type of event:" ); |
|
|
|
params.m_Choices.RemoveAll(); |
|
|
|
params.m_nSelected = 0; |
|
ChoiceText text; |
|
strcpy( text.choice, "gesture" ); |
|
params.m_Choices.AddToTail( text ); |
|
strcpy( text.choice, "sequence" ); |
|
params.m_Choices.AddToTail( text ); |
|
|
|
if ( !ChoiceProperties( ¶ms ) ) |
|
return false; |
|
|
|
if ( params.m_nSelected < 0 ) |
|
return false; |
|
|
|
switch ( params.m_nSelected ) |
|
{ |
|
default: |
|
case 0: |
|
AddEvent( CChoreoEvent::GESTURE, 0, animationname ); |
|
break; |
|
case 1: |
|
AddEvent( CChoreoEvent::SEQUENCE, 0, animationname ); |
|
break; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : mx - |
|
// my - |
|
// *cl - |
|
// *exp - |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CChoreoView::CreateExpressionEvent( int mx, int my, CExpClass *cl, CExpression *exp ) |
|
{ |
|
if ( !m_pScene ) |
|
return false; |
|
|
|
if ( !exp ) |
|
return false; |
|
|
|
// Convert screen to client |
|
POINT pt; |
|
pt.x = mx; |
|
pt.y = my; |
|
|
|
ScreenToClient( (HWND)getHandle(), &pt ); |
|
|
|
if ( pt.x < 0 || pt.y < 0 ) |
|
return false; |
|
|
|
if ( pt.x > w2() || pt.y > h2() ) |
|
return false; |
|
|
|
// Find channel actor and time ( uses screen space coordinates ) |
|
// |
|
CChoreoChannelWidget *channel = GetChannelUnderCursorPos( pt.x, pt.y ); |
|
if ( !channel ) |
|
{ |
|
CChoreoActorWidget *actor = GetActorUnderCursorPos( pt.x, pt.y ); |
|
if ( !actor ) |
|
return false; |
|
|
|
// Grab first channel |
|
if ( !actor->GetNumChannels() ) |
|
return false; |
|
|
|
channel = actor->GetChannel( 0 ); |
|
} |
|
|
|
if ( !channel ) |
|
return false; |
|
|
|
CChoreoChannel *pchannel = channel->GetChannel(); |
|
if ( !pchannel ) |
|
{ |
|
Assert( 0 ); |
|
return false; |
|
} |
|
|
|
CChoreoEvent *event = m_pScene->AllocEvent(); |
|
if ( !event ) |
|
{ |
|
Assert( 0 ); |
|
return false; |
|
} |
|
|
|
float starttime = GetTimeValueForMouse( pt.x, false ); |
|
|
|
SetDirty( true ); |
|
|
|
PushUndo( "Create Expression" ); |
|
|
|
event->SetType( CChoreoEvent::EXPRESSION ); |
|
event->SetName( exp->name ); |
|
event->SetParameters( cl->GetName() ); |
|
event->SetParameters2( exp->name ); |
|
event->SetStartTime( starttime ); |
|
event->SetChannel( pchannel ); |
|
event->SetActor( pchannel->GetActor() ); |
|
event->SetEndTime( starttime + 1.0f ); |
|
|
|
event->SnapTimes(); |
|
|
|
DeleteSceneWidgets(); |
|
|
|
// Add to appropriate channel |
|
pchannel->AddEvent( event ); |
|
|
|
CreateSceneWidgets(); |
|
|
|
PushRedo( "Create Expression" ); |
|
|
|
// Redraw |
|
InvalidateLayout(); |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CChoreoView::IsPlayingScene( void ) |
|
{ |
|
return m_bSimulating; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::ResetTargetSettings( void ) |
|
{ |
|
for ( int i = 0; i < m_SceneActors.Size(); i++ ) |
|
{ |
|
CChoreoActorWidget *w = m_SceneActors[ i ]; |
|
if ( w ) |
|
{ |
|
w->ResetSettings(); |
|
} |
|
} |
|
|
|
models->ClearModelTargets( true ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: copies the actors "settings" into the models FlexControllers |
|
// Input : dt - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::UpdateCurrentSettings( void ) |
|
{ |
|
StudioModel *defaultModel = models->GetActiveStudioModel(); |
|
|
|
for ( int i = 0; i < m_SceneActors.Size(); i++ ) |
|
{ |
|
CChoreoActorWidget *w = m_SceneActors[ i ]; |
|
if ( !w ) |
|
continue; |
|
|
|
if ( !w->GetActor()->GetActive() ) |
|
continue; |
|
|
|
StudioModel *model = FindAssociatedModel( m_pScene, w->GetActor() ); |
|
if ( !model ) |
|
continue; |
|
|
|
CStudioHdr *hdr = model->GetStudioHdr(); |
|
if ( !hdr ) |
|
continue; |
|
|
|
float *current = w->GetSettings(); |
|
|
|
for ( LocalFlexController_t j = LocalFlexController_t(0); j < hdr->numflexcontrollers(); j++ ) |
|
{ |
|
int k = hdr->pFlexcontroller( j )->localToGlobal; |
|
|
|
if (k != -1) |
|
{ |
|
if ( defaultModel == model && g_pFlexPanel->IsEdited( k ) ) |
|
{ |
|
model->SetFlexController( j, g_pFlexPanel->GetSlider( k ) ); |
|
} |
|
else |
|
{ |
|
model->SetFlexController( j, current[ k ] ); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *event - |
|
// tagnum - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::DeleteEventRelativeTag( CChoreoEvent *event, int tagnum ) |
|
{ |
|
if ( !event ) |
|
return; |
|
|
|
CEventRelativeTag *tag = event->GetRelativeTag( tagnum ); |
|
if ( !tag ) |
|
return; |
|
|
|
SetDirty( true ); |
|
|
|
PushUndo( "Delete Event Tag" ); |
|
|
|
event->RemoveRelativeTag( tag->GetName() ); |
|
|
|
m_pScene->ReconcileTags(); |
|
|
|
PushRedo( "Delete Event Tag" ); |
|
|
|
g_pPhonemeEditor->redraw(); |
|
InvalidateLayout(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *event - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::AddEventRelativeTag( void ) |
|
{ |
|
CChoreoEventWidget *ew = m_pClickedEvent; |
|
if ( !ew ) |
|
return; |
|
|
|
CChoreoEvent *event = ew->GetEvent(); |
|
if ( !event->GetEndTime() ) |
|
{ |
|
Con_ErrorPrintf( "Event Tag: Can only tag events with an end time\n" ); |
|
return; |
|
} |
|
|
|
CInputParams params; |
|
memset( ¶ms, 0, sizeof( params ) ); |
|
|
|
strcpy( params.m_szDialogTitle, "Event Tag Name" ); |
|
strcpy( params.m_szPrompt, "Name:" ); |
|
|
|
strcpy( params.m_szInputText, "" ); |
|
|
|
if ( !InputProperties( ¶ms ) ) |
|
return; |
|
|
|
if ( strlen( params.m_szInputText ) <= 0 ) |
|
{ |
|
Con_ErrorPrintf( "Event Tag Name: No name entered!\n" ); |
|
return; |
|
} |
|
|
|
RECT bounds = ew->getBounds(); |
|
|
|
// Convert click to frac |
|
float frac = 0.0f; |
|
if ( bounds.right - bounds.left > 0 ) |
|
{ |
|
frac = (float)( m_nClickedX - bounds.left ) / (float)( bounds.right - bounds.left ); |
|
frac = min( 1.0f, frac ); |
|
frac = max( 0.0f, frac ); |
|
} |
|
|
|
SetDirty( true ); |
|
|
|
PushUndo( "Add Event Tag" ); |
|
|
|
event->AddRelativeTag( params.m_szInputText, frac ); |
|
|
|
PushRedo( "Add Event Tag" ); |
|
|
|
InvalidateLayout(); |
|
g_pPhonemeEditor->redraw(); |
|
g_pExpressionTool->redraw(); |
|
g_pGestureTool->redraw(); |
|
g_pRampTool->redraw(); |
|
g_pSceneRampTool->redraw(); |
|
} |
|
|
|
CChoreoChannelWidget *CChoreoView::FindChannelForEvent( CChoreoEvent *event ) |
|
{ |
|
for ( int i = 0; i < m_SceneActors.Size(); i++ ) |
|
{ |
|
CChoreoActorWidget *a = m_SceneActors[ i ]; |
|
if ( !a ) |
|
continue; |
|
|
|
for ( int j = 0; j < a->GetNumChannels(); j++ ) |
|
{ |
|
CChoreoChannelWidget *c = a->GetChannel( j ); |
|
if ( !c ) |
|
continue; |
|
|
|
for ( int k = 0; k < c->GetNumEvents(); k++ ) |
|
{ |
|
CChoreoEventWidget *e = c->GetEvent( k ); |
|
if ( !e ) |
|
continue; |
|
|
|
if ( e->GetEvent() != event ) |
|
continue; |
|
|
|
return c; |
|
} |
|
} |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *event - |
|
// Output : CChoreoEventWidget |
|
//----------------------------------------------------------------------------- |
|
CChoreoEventWidget *CChoreoView::FindWidgetForEvent( CChoreoEvent *event ) |
|
{ |
|
for ( int i = 0; i < m_SceneActors.Size(); i++ ) |
|
{ |
|
CChoreoActorWidget *a = m_SceneActors[ i ]; |
|
if ( !a ) |
|
continue; |
|
|
|
for ( int j = 0; j < a->GetNumChannels(); j++ ) |
|
{ |
|
CChoreoChannelWidget *c = a->GetChannel( j ); |
|
if ( !c ) |
|
continue; |
|
|
|
for ( int k = 0; k < c->GetNumEvents(); k++ ) |
|
{ |
|
CChoreoEventWidget *e = c->GetEvent( k ); |
|
if ( !e ) |
|
continue; |
|
|
|
if ( e->GetEvent() != event ) |
|
continue; |
|
|
|
return e; |
|
} |
|
} |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::SelectAll( void ) |
|
{ |
|
TraverseWidgets( &CChoreoView::SelectAllEvents, NULL ); |
|
redraw(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::DeselectAll( void ) |
|
{ |
|
TraverseWidgets( &CChoreoView::Deselect, NULL ); |
|
redraw(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : mx - |
|
// my - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::UpdateStatusArea( int mx, int my ) |
|
{ |
|
FLYOVER fo; |
|
|
|
GetObjectsUnderMouse( mx, my, &fo.a, &fo.c, |
|
&fo.e, &fo.ge, &fo.tag, &fo.at, &fo.ccbutton ); |
|
|
|
if ( fo.a ) |
|
{ |
|
m_Flyover.a = fo.a; |
|
} |
|
if ( fo.e ) |
|
{ |
|
m_Flyover.e = fo.e; |
|
} |
|
if ( fo.c ) |
|
{ |
|
m_Flyover.c = fo.c; |
|
} |
|
if ( fo.ge ) |
|
{ |
|
m_Flyover.ge = fo.ge; |
|
} |
|
if ( fo.tag != -1 ) |
|
{ |
|
m_Flyover.tag = fo.tag; |
|
} |
|
if ( fo.ccbutton != -1 ) |
|
{ |
|
m_Flyover.ccbutton = fo.ccbutton; |
|
// m_Flyover.e = NULL; |
|
} |
|
|
|
RECT rcClip; |
|
GetClientRect( (HWND)getHandle(), &rcClip ); |
|
rcClip.bottom -= m_nScrollbarHeight; |
|
rcClip.top = rcClip.bottom - m_nInfoHeight; |
|
rcClip.right -= m_nScrollbarHeight; |
|
|
|
CChoreoWidgetDrawHelper drawHelper( this, rcClip, COLOR_CHOREO_BACKGROUND ); |
|
|
|
drawHelper.StartClipping( rcClip ); |
|
|
|
RedrawStatusArea( drawHelper, rcClip ); |
|
|
|
drawHelper.StopClipping(); |
|
ValidateRect( (HWND)getHandle(), &rcClip ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::ClearStatusArea( void ) |
|
{ |
|
memset( &m_Flyover, 0, sizeof( m_Flyover ) ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : drawHelper - |
|
// rcStatus - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::RedrawStatusArea( CChoreoWidgetDrawHelper& drawHelper, RECT& rcStatus ) |
|
{ |
|
drawHelper.DrawFilledRect( COLOR_CHOREO_BACKGROUND, rcStatus ); |
|
|
|
drawHelper.DrawColoredLine( COLOR_INFO_BORDER, PS_SOLID, 1, rcStatus.left, rcStatus.top, |
|
rcStatus.right, rcStatus.top ); |
|
|
|
RECT rcInfo = rcStatus; |
|
|
|
rcInfo.top += 2; |
|
|
|
if ( m_Flyover.e ) |
|
{ |
|
m_Flyover.e->redrawStatus( drawHelper, rcInfo ); |
|
} |
|
if ( m_Flyover.c && |
|
m_Flyover.ccbutton != -1 ) |
|
{ |
|
m_Flyover.c->redrawStatus( drawHelper, rcInfo, m_Flyover.ccbutton ); |
|
} |
|
|
|
if ( m_pScene ) |
|
{ |
|
char sz[ 512 ]; |
|
|
|
int fontsize = 9; |
|
int fontweight = FW_NORMAL; |
|
|
|
RECT rcText; |
|
rcText = rcInfo; |
|
rcText.bottom = rcText.top + fontsize + 2; |
|
|
|
char const *mapname = m_pScene->GetMapname(); |
|
if ( mapname ) |
|
{ |
|
sprintf( sz, "Associated .bsp: %s", mapname[ 0 ] ? mapname : "none" ); |
|
|
|
int len = drawHelper.CalcTextWidth( "Arial", fontsize, fontweight, sz ); |
|
rcText.left = rcText.right - len - 10; |
|
|
|
drawHelper.DrawColoredText( "Arial", fontsize, fontweight, COLOR_INFO_TEXT, rcText, sz ); |
|
|
|
OffsetRect( &rcText, 0, fontsize + 2 ); |
|
} |
|
|
|
sprintf( sz, "Scene: %s", GetChoreoFile() ); |
|
|
|
int len = drawHelper.CalcTextWidth( "Arial", fontsize, fontweight, sz ); |
|
rcText.left = rcText.right - len - 10; |
|
|
|
drawHelper.DrawColoredText( "Arial", fontsize, fontweight, COLOR_INFO_TEXT, rcText, sz ); |
|
} |
|
|
|
// drawHelper.DrawColoredText( "Arial", 12, 500, RGB( 0, 0, 0 ), rcInfo, m_Flyover.e ? m_Flyover.e->GetEvent()->GetName() : "" ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *event - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::MoveEventToBack( CChoreoEvent *event ) |
|
{ |
|
// Now find channel widget |
|
for ( int i = 0; i < m_SceneActors.Size(); i++ ) |
|
{ |
|
CChoreoActorWidget *a = m_SceneActors[ i ]; |
|
if ( !a ) |
|
continue; |
|
|
|
for ( int j = 0; j < a->GetNumChannels(); j++ ) |
|
{ |
|
CChoreoChannelWidget *c = a->GetChannel( j ); |
|
if ( !c ) |
|
continue; |
|
|
|
for ( int k = 0; k < c->GetNumEvents(); k++ ) |
|
{ |
|
CChoreoEventWidget *e = c->GetEvent( k ); |
|
if ( !e ) |
|
continue; |
|
|
|
if ( event == e->GetEvent() ) |
|
{ |
|
// Move it to back of channel's list |
|
c->MoveEventToTail( e ); |
|
InvalidateLayout(); |
|
return; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
int CChoreoView::GetEndRow( void ) |
|
{ |
|
RECT rcClient; |
|
GetClientRect( (HWND)getHandle(), &rcClient ); |
|
|
|
return rcClient.bottom - ( m_nInfoHeight + m_nScrollbarHeight ); |
|
} |
|
|
|
// Undo/Redo |
|
void CChoreoView::Undo( void ) |
|
{ |
|
if ( m_UndoStack.Size() > 0 && m_nUndoLevel > 0 ) |
|
{ |
|
m_nUndoLevel--; |
|
CVUndo *u = m_UndoStack[ m_nUndoLevel ]; |
|
Assert( u->undo ); |
|
|
|
DeleteSceneWidgets(); |
|
|
|
*m_pScene = *(u->undo); |
|
g_MDLViewer->InitGridSettings(); |
|
|
|
CreateSceneWidgets(); |
|
|
|
ReportSceneClearToTools(); |
|
ClearStatusArea(); |
|
m_pClickedActor = NULL; |
|
m_pClickedChannel = NULL; |
|
m_pClickedEvent = NULL; |
|
m_pClickedGlobalEvent = NULL; |
|
} |
|
|
|
InvalidateLayout(); |
|
} |
|
|
|
void CChoreoView::Redo( void ) |
|
{ |
|
if ( m_UndoStack.Size() > 0 && m_nUndoLevel <= m_UndoStack.Size() - 1 ) |
|
{ |
|
CVUndo *u = m_UndoStack[ m_nUndoLevel ]; |
|
Assert( u->redo ); |
|
|
|
DeleteSceneWidgets(); |
|
|
|
*m_pScene = *(u->redo); |
|
g_MDLViewer->InitGridSettings(); |
|
|
|
CreateSceneWidgets(); |
|
|
|
ReportSceneClearToTools(); |
|
ClearStatusArea(); |
|
m_pClickedActor = NULL; |
|
m_pClickedChannel = NULL; |
|
m_pClickedEvent = NULL; |
|
m_pClickedGlobalEvent = NULL; |
|
|
|
m_nUndoLevel++; |
|
} |
|
|
|
InvalidateLayout(); |
|
} |
|
|
|
static char *CopyString( const char *in ) |
|
{ |
|
int len = strlen( in ); |
|
char *n = new char[ len + 1 ]; |
|
strcpy( n, in ); |
|
return n; |
|
} |
|
|
|
void CChoreoView::PushUndo( const char *description ) |
|
{ |
|
Assert( !m_bRedoPending ); |
|
m_bRedoPending = true; |
|
WipeRedo(); |
|
|
|
// Copy current data |
|
CChoreoScene *u = new CChoreoScene( this ); |
|
*u = *m_pScene; |
|
CVUndo *undo = new CVUndo; |
|
undo->undo = u; |
|
undo->redo = NULL; |
|
undo->udescription = CopyString( description ); |
|
undo->rdescription = NULL; |
|
m_UndoStack.AddToTail( undo ); |
|
m_nUndoLevel++; |
|
} |
|
|
|
void CChoreoView::PushRedo( const char *description ) |
|
{ |
|
Assert( m_bRedoPending ); |
|
m_bRedoPending = false; |
|
|
|
// Copy current data |
|
CChoreoScene *r = new CChoreoScene( this ); |
|
*r = *m_pScene; |
|
CVUndo *undo = m_UndoStack[ m_nUndoLevel - 1 ]; |
|
undo->redo = r; |
|
undo->rdescription = CopyString( description ); |
|
|
|
// Always redo here to reflect that someone has made a change |
|
redraw(); |
|
} |
|
|
|
void CChoreoView::WipeUndo( void ) |
|
{ |
|
while ( m_UndoStack.Size() > 0 ) |
|
{ |
|
CVUndo *u = m_UndoStack[ 0 ]; |
|
delete u->undo; |
|
delete u->redo; |
|
delete[] u->udescription; |
|
delete[] u->rdescription; |
|
delete u; |
|
m_UndoStack.Remove( 0 ); |
|
} |
|
m_nUndoLevel = 0; |
|
} |
|
|
|
void CChoreoView::WipeRedo( void ) |
|
{ |
|
// Wipe everything above level |
|
while ( m_UndoStack.Size() > m_nUndoLevel ) |
|
{ |
|
CVUndo *u = m_UndoStack[ m_nUndoLevel ]; |
|
delete u->undo; |
|
delete u->redo; |
|
delete[] u->udescription; |
|
delete[] u->rdescription; |
|
delete u; |
|
m_UndoStack.Remove( m_nUndoLevel ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : const char |
|
//----------------------------------------------------------------------------- |
|
const char *CChoreoView::GetUndoDescription( void ) |
|
{ |
|
if ( CanUndo() ) |
|
{ |
|
CVUndo *u = m_UndoStack[ m_nUndoLevel - 1 ]; |
|
return u->udescription; |
|
} |
|
return "???undo"; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : const char |
|
//----------------------------------------------------------------------------- |
|
const char *CChoreoView::GetRedoDescription( void ) |
|
{ |
|
if ( CanRedo() ) |
|
{ |
|
CVUndo *u = m_UndoStack[ m_nUndoLevel ]; |
|
return u->rdescription; |
|
} |
|
return "???redo"; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CChoreoView::CanUndo() |
|
{ |
|
return m_nUndoLevel != 0; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CChoreoView::CanRedo() |
|
{ |
|
return m_nUndoLevel != m_UndoStack.Size(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::OnGestureTool( void ) |
|
{ |
|
if ( m_pClickedEvent->GetEvent()->GetType() != CChoreoEvent::GESTURE ) |
|
return; |
|
|
|
g_pGestureTool->SetEvent( m_pClickedEvent->GetEvent() ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::OnExpressionTool( void ) |
|
{ |
|
if ( m_pClickedEvent->GetEvent()->GetType() != CChoreoEvent::FLEXANIMATION ) |
|
return; |
|
|
|
g_pExpressionTool->SetEvent( m_pClickedEvent->GetEvent() ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : CChoreoScene |
|
//----------------------------------------------------------------------------- |
|
CChoreoScene *CChoreoView::GetScene( void ) |
|
{ |
|
return m_pScene; |
|
} |
|
|
|
bool CChoreoView::CanPaste( void ) |
|
{ |
|
char const *copyfile = COPYPASTE_FILENAME; |
|
|
|
if ( !filesystem->FileExists( copyfile ) ) |
|
{ |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
void CChoreoView::CopyEvents( void ) |
|
{ |
|
if ( !m_pScene ) |
|
return; |
|
|
|
char const *copyfile = COPYPASTE_FILENAME; |
|
MakeFileWriteable( copyfile ); |
|
ExportVCDFile( copyfile ); |
|
} |
|
|
|
void CChoreoView::PasteEvents( void ) |
|
{ |
|
if ( !m_pScene ) |
|
return; |
|
|
|
if ( !CanPaste() ) |
|
return; |
|
|
|
char const *copyfile = COPYPASTE_FILENAME; |
|
|
|
ImportVCDFile( copyfile ); |
|
} |
|
|
|
void CChoreoView::ImportEvents( void ) |
|
{ |
|
if ( !m_pScene ) |
|
return; |
|
|
|
if ( !m_pClickedActor || !m_pClickedChannel ) |
|
return; |
|
|
|
char eventfile[ 512 ]; |
|
if ( !FacePoser_ShowOpenFileNameDialog( eventfile, sizeof( eventfile ), "scenes", "*.vce" ) ) |
|
return; |
|
|
|
char fullpathbuf[ 512 ]; |
|
char *fullpath = eventfile; |
|
if ( !Q_IsAbsolutePath( eventfile ) ) |
|
{ |
|
filesystem->RelativePathToFullPath( eventfile, "GAME", fullpathbuf, sizeof( fullpathbuf ) ); |
|
fullpath = fullpathbuf; |
|
} |
|
|
|
if ( !filesystem->FileExists( fullpath ) ) |
|
return; |
|
|
|
LoadScriptFile( fullpath ); |
|
|
|
DeselectAll(); |
|
|
|
SetDirty( true ); |
|
|
|
PushUndo( "Import Events" ); |
|
|
|
m_pScene->ImportEvents( tokenprocessor, m_pClickedActor->GetActor(), m_pClickedChannel->GetChannel() ); |
|
|
|
PushRedo( "Import Events" ); |
|
|
|
CreateSceneWidgets(); |
|
// Redraw |
|
InvalidateLayout(); |
|
|
|
Con_Printf( "Imported events from %s\n", fullpath ); |
|
} |
|
|
|
void CChoreoView::ExportEvents( void ) |
|
{ |
|
char eventfilename[ 512 ]; |
|
if ( !FacePoser_ShowSaveFileNameDialog( eventfilename, sizeof( eventfilename ), "scenes", "*.vce" ) ) |
|
return; |
|
|
|
Q_DefaultExtension( eventfilename, ".vce", sizeof( eventfilename ) ); |
|
|
|
Con_Printf( "Exporting events to %s\n", eventfilename ); |
|
|
|
// Write to file |
|
CUtlVector< CChoreoEvent * > events; |
|
|
|
// Find selected eventss |
|
for ( int i = 0; i < m_SceneActors.Size(); i++ ) |
|
{ |
|
CChoreoActorWidget *a = m_SceneActors[ i ]; |
|
if ( !a ) |
|
continue; |
|
|
|
for ( int j = 0; j < a->GetNumChannels(); j++ ) |
|
{ |
|
CChoreoChannelWidget *c = a->GetChannel( j ); |
|
if ( !c ) |
|
continue; |
|
|
|
for ( int k = 0; k < c->GetNumEvents(); k++ ) |
|
{ |
|
CChoreoEventWidget *e = c->GetEvent( k ); |
|
if ( !e ) |
|
continue; |
|
|
|
if ( !e->IsSelected() ) |
|
continue; |
|
|
|
CChoreoEvent *event = e->GetEvent(); |
|
if ( !event ) |
|
continue; |
|
|
|
events.AddToTail( event ); |
|
} |
|
} |
|
} |
|
|
|
if ( events.Size() > 0 ) |
|
{ |
|
m_pScene->ExportEvents( eventfilename, events ); |
|
} |
|
else |
|
{ |
|
Con_Printf( "No events selected\n" ); |
|
} |
|
} |
|
|
|
void CChoreoView::ExportVCDFile( char const *filename ) |
|
{ |
|
Con_Printf( "Exporting to %s\n", filename ); |
|
|
|
// Unmark everything |
|
m_pScene->MarkForSaveAll( false ); |
|
|
|
// Mark everything related to selected events |
|
for ( int i = 0; i < m_SceneActors.Size(); i++ ) |
|
{ |
|
CChoreoActorWidget *a = m_SceneActors[ i ]; |
|
if ( !a ) |
|
continue; |
|
|
|
for ( int j = 0; j < a->GetNumChannels(); j++ ) |
|
{ |
|
CChoreoChannelWidget *c = a->GetChannel( j ); |
|
if ( !c ) |
|
continue; |
|
|
|
for ( int k = 0; k < c->GetNumEvents(); k++ ) |
|
{ |
|
CChoreoEventWidget *e = c->GetEvent( k ); |
|
if ( !e ) |
|
continue; |
|
|
|
if ( !e->IsSelected() ) |
|
continue; |
|
|
|
CChoreoEvent *event = e->GetEvent(); |
|
if ( !event ) |
|
continue; |
|
|
|
event->SetMarkedForSave( true ); |
|
if ( event->GetChannel() ) |
|
{ |
|
event->GetChannel()->SetMarkedForSave( true ); |
|
} |
|
if ( event->GetActor() ) |
|
{ |
|
event->GetActor()->SetMarkedForSave( true ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
m_pScene->ExportMarkedToFile( filename ); |
|
} |
|
|
|
void CChoreoView::ImportVCDFile( char const *filename ) |
|
{ |
|
CChoreoScene *merge = LoadScene( filename ); |
|
if ( !merge ) |
|
{ |
|
Con_Printf( "Couldn't load from .vcd %s\n", filename ); |
|
return; |
|
} |
|
|
|
DeselectAll(); |
|
|
|
CUtlRBTree< CChoreoEvent *, int > oldEvents( 0, 0, DefLessFunc( CChoreoEvent * ) ); |
|
|
|
int i; |
|
for ( i = 0; i < m_SceneActors.Count(); ++i ) |
|
{ |
|
CChoreoActorWidget *actor = m_SceneActors[ i ]; |
|
if ( !actor ) |
|
continue; |
|
|
|
for ( int j = 0; j < actor->GetNumChannels(); j++ ) |
|
{ |
|
CChoreoChannelWidget *channel = actor->GetChannel( j ); |
|
if ( !channel ) |
|
continue; |
|
|
|
for ( int k = 0; k < channel->GetNumEvents(); k++ ) |
|
{ |
|
CChoreoEventWidget *event = channel->GetEvent( k ); |
|
if ( !event ) |
|
continue; |
|
|
|
oldEvents.Insert( event->GetEvent() ); |
|
} |
|
} |
|
} |
|
|
|
SetDirty( true ); |
|
|
|
PushUndo( "Merge/Import VCD" ); |
|
|
|
m_pScene->Merge( merge ); |
|
|
|
PushRedo( "Merge/Import VCD" ); |
|
|
|
DeleteSceneWidgets(); |
|
CreateSceneWidgets(); |
|
|
|
// Force scroll bars to recompute |
|
ForceScrollBarsToRecompute( false ); |
|
|
|
// Now walk through the "new" events and select everything that wasn't already there (all of the stuff that was "added" during the merge) |
|
for ( i = 0; i < m_SceneActors.Count(); ++i ) |
|
{ |
|
CChoreoActorWidget *actor = m_SceneActors[ i ]; |
|
if ( !actor ) |
|
continue; |
|
|
|
for ( int j = 0; j < actor->GetNumChannels(); j++ ) |
|
{ |
|
CChoreoChannelWidget *channel = actor->GetChannel( j ); |
|
if ( !channel ) |
|
continue; |
|
|
|
for ( int k = 0; k < channel->GetNumEvents(); k++ ) |
|
{ |
|
CChoreoEventWidget *event = channel->GetEvent( k ); |
|
if ( !event ) |
|
continue; |
|
|
|
if ( oldEvents.Find( event->GetEvent() ) == oldEvents.InvalidIndex() ) |
|
{ |
|
event->SetSelected( true ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
// Redraw |
|
InvalidateLayout(); |
|
|
|
Con_Printf( "Imported vcd '%s'\n", filename ); |
|
|
|
delete merge; |
|
|
|
redraw(); |
|
} |
|
|
|
void CChoreoView::ExportVCD() |
|
{ |
|
char scenefile[ 512 ]; |
|
if ( !FacePoser_ShowSaveFileNameDialog( scenefile, sizeof( scenefile ), "scenes", "*.vcd" ) ) |
|
{ |
|
return; |
|
} |
|
|
|
Q_DefaultExtension( scenefile, ".vcd", sizeof( scenefile ) ); |
|
|
|
ExportVCDFile( scenefile ); |
|
} |
|
|
|
void CChoreoView::ImportVCD() |
|
{ |
|
if ( !m_pScene ) |
|
return; |
|
|
|
if ( !m_pClickedActor || !m_pClickedChannel ) |
|
return; |
|
|
|
char scenefile[ 512 ]; |
|
if ( !FacePoser_ShowOpenFileNameDialog( scenefile, sizeof( scenefile ), "scenes", "*.vcd" ) ) |
|
{ |
|
return; |
|
} |
|
|
|
ImportVCDFile( scenefile ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CChoreoView::IsProcessing( void ) |
|
{ |
|
if ( !m_pScene ) |
|
return false; |
|
|
|
if ( m_flScrub != m_flScrubTarget ) |
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CChoreoView::ShouldProcessSpeak( void ) |
|
{ |
|
if ( !g_pControlPanel->AllToolsDriveSpeech() ) |
|
{ |
|
if ( !IsActiveTool() ) |
|
return false; |
|
} |
|
|
|
if ( IFacePoserToolWindow::IsAnyToolScrubbing() ) |
|
return true; |
|
|
|
if ( IFacePoserToolWindow::IsAnyToolProcessing() ) |
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *scene - |
|
// *event - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::ProcessSpeak( CChoreoScene *scene, CChoreoEvent *event ) |
|
{ |
|
if ( !ShouldProcessSpeak() ) |
|
return; |
|
|
|
Assert( event->GetType() == CChoreoEvent::SPEAK ); |
|
Assert( scene ); |
|
|
|
float t = scene->GetTime(); |
|
|
|
StudioModel *model = FindAssociatedModel( scene, event->GetActor() ); |
|
|
|
// See if we should trigger CC |
|
char soundname[ 512 ]; |
|
Q_strncpy( soundname, event->GetParameters(), sizeof( soundname ) ); |
|
|
|
float actualEndTime = event->GetEndTime(); |
|
|
|
if ( event->GetCloseCaptionType() == CChoreoEvent::CC_MASTER ) |
|
{ |
|
char tok[ CChoreoEvent::MAX_CCTOKEN_STRING ]; |
|
if ( event->GetPlaybackCloseCaptionToken( tok, sizeof( tok ) ) ) |
|
{ |
|
// Use the token as the sound name lookup, too. |
|
if ( event->IsUsingCombinedFile() && |
|
( event->GetNumSlaves() > 0 ) ) |
|
{ |
|
Q_strncpy( soundname, tok, sizeof( soundname ) ); |
|
actualEndTime = max( actualEndTime, event->GetLastSlaveEndTime() ); |
|
} |
|
} |
|
} |
|
|
|
CAudioMixer *mixer = event->GetMixer(); |
|
if ( !mixer || !sound->IsSoundPlaying( mixer ) ) |
|
{ |
|
CSoundParameters params; |
|
float volume = VOL_NORM; |
|
|
|
gender_t gender = GENDER_NONE; |
|
if (model) |
|
{ |
|
gender = soundemitter->GetActorGender( model->GetFileName() ); |
|
} |
|
|
|
if ( !Q_stristr( soundname, ".wav" ) && |
|
soundemitter->GetParametersForSound( soundname, params, gender ) ) |
|
{ |
|
volume = params.volume; |
|
} |
|
|
|
sound->PlaySound( |
|
model, |
|
volume, |
|
va( "sound/%s", FacePoser_TranslateSoundName( soundname, model ) ), |
|
&mixer ); |
|
event->SetMixer( mixer ); |
|
} |
|
|
|
mixer = event->GetMixer(); |
|
if ( !mixer ) |
|
return; |
|
|
|
mixer->SetDirection( m_flFrameTime >= 0.0f ); |
|
float starttime, endtime; |
|
starttime = event->GetStartTime(); |
|
endtime = actualEndTime; |
|
|
|
float soundtime = endtime - starttime; |
|
if ( soundtime <= 0.0f ) |
|
return; |
|
|
|
float f = ( t - starttime ) / soundtime; |
|
f = clamp( f, 0.0f, 1.0f ); |
|
|
|
// Compute sample |
|
float numsamples = (float)mixer->GetSource()->SampleCount(); |
|
|
|
int cursample = f * numsamples; |
|
cursample = clamp( cursample, 0, numsamples - 1 ); |
|
|
|
int realsample = mixer->GetSamplePosition(); |
|
|
|
int dsample = cursample - realsample; |
|
|
|
int samplelimit = mixer->GetSource()->SampleRate() * 0.02f; // don't shift until samples are off by this much |
|
if (IsScrubbing()) |
|
{ |
|
samplelimit = mixer->GetSource()->SampleRate() * 0.01f; // make it shorter tolerance when scrubbing |
|
} |
|
|
|
if ( abs( dsample ) > samplelimit ) |
|
{ |
|
mixer->SetSamplePosition( cursample, IsScrubbing() ); |
|
} |
|
mixer->SetActive( true ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *event - |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::ProcessSubscene( CChoreoScene *scene, CChoreoEvent *event ) |
|
{ |
|
Assert( event->GetType() == CChoreoEvent::SUBSCENE ); |
|
|
|
CChoreoScene *subscene = event->GetSubScene(); |
|
if ( !subscene ) |
|
return; |
|
|
|
if ( subscene->SimulationFinished() ) |
|
return; |
|
|
|
// Have subscenes think for appropriate time |
|
subscene->Think( m_flScrub ); |
|
} |
|
|
|
void CChoreoView::PositionControls() |
|
{ |
|
int topx = GetCaptionHeight() + SCRUBBER_HEIGHT; |
|
|
|
int bx = 2; |
|
int bw = 16; |
|
|
|
m_btnPlay->setBounds( bx, topx + 4, 16, 16 ); |
|
|
|
bx += bw + 2; |
|
|
|
m_btnPause->setBounds( bx, topx + 4, 16, 16 ); |
|
bx += bw + 2; |
|
m_btnStop->setBounds( bx, topx + 4, 16, 16 ); |
|
bx += bw + 2; |
|
m_pPlaybackRate->setBounds( bx, topx + 4, 100, 16 ); |
|
} |
|
|
|
void CChoreoView::SetChoreoFile( char const *filename ) |
|
{ |
|
strcpy( m_szChoreoFile, filename ); |
|
if ( m_szChoreoFile[ 0 ] ) |
|
{ |
|
char sz[ 256 ]; |
|
if ( IsFileWriteable( m_szChoreoFile ) ) |
|
{ |
|
Q_snprintf( sz, sizeof( sz ), " - %s", m_szChoreoFile ); |
|
} |
|
else |
|
{ |
|
Q_snprintf( sz, sizeof( sz ), " - %s [Read-Only]", m_szChoreoFile ); |
|
} |
|
SetSuffix( sz ); |
|
} |
|
else |
|
{ |
|
SetSuffix( "" ); |
|
} |
|
} |
|
|
|
char const *CChoreoView::GetChoreoFile( void ) const |
|
{ |
|
return m_szChoreoFile; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : rcHandle - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::GetScrubHandleRect( RECT& rcHandle, bool clipped ) |
|
{ |
|
float pixel = 0.0f; |
|
|
|
if ( m_pScene ) |
|
{ |
|
float currenttime = m_flScrub; |
|
float starttime = m_flStartTime; |
|
float endtime = m_flEndTime; |
|
|
|
float screenfrac = ( currenttime - starttime ) / ( endtime - starttime ); |
|
|
|
pixel = GetLabelWidth() + screenfrac * ( w2() - GetLabelWidth() ); |
|
|
|
if ( clipped ) |
|
{ |
|
pixel = clamp( pixel, SCRUBBER_HANDLE_WIDTH/2, w2() - SCRUBBER_HANDLE_WIDTH/2 ); |
|
} |
|
} |
|
|
|
rcHandle.left = pixel-SCRUBBER_HANDLE_WIDTH/2; |
|
rcHandle.right = pixel + SCRUBBER_HANDLE_WIDTH/2; |
|
rcHandle.top = 2 + GetCaptionHeight(); |
|
rcHandle.bottom = rcHandle.top + SCRUBBER_HANDLE_HEIGHT; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : rcArea - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::GetScrubAreaRect( RECT& rcArea ) |
|
{ |
|
rcArea.left = 0; |
|
rcArea.right = w2(); |
|
rcArea.top = 2 + GetCaptionHeight(); |
|
rcArea.bottom = rcArea.top + SCRUBBER_HEIGHT - 4; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : drawHelper - |
|
// rcHandle - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::DrawScrubHandle( CChoreoWidgetDrawHelper& drawHelper ) |
|
{ |
|
RECT rcHandle; |
|
GetScrubHandleRect( rcHandle, true ); |
|
|
|
HBRUSH br = CreateSolidBrush( RGB( 0, 150, 100 ) ); |
|
|
|
drawHelper.DrawFilledRect( br, rcHandle ); |
|
|
|
// |
|
char sz[ 32 ]; |
|
sprintf( sz, "%.3f", m_flScrub ); |
|
|
|
int len = drawHelper.CalcTextWidth( "Arial", 9, 500, sz ); |
|
|
|
RECT rcText = rcHandle; |
|
int textw = rcText.right - rcText.left; |
|
|
|
rcText.left += ( textw - len ) / 2; |
|
|
|
drawHelper.DrawColoredText( "Arial", 9, 500, RGB( 255, 255, 255 ), rcText, sz ); |
|
|
|
DeleteObject( br ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *event - |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CChoreoView::IsMouseOverScrubHandle( mxEvent *event ) |
|
{ |
|
RECT rcHandle; |
|
GetScrubHandleRect( rcHandle, true ); |
|
InflateRect( &rcHandle, 2, 2 ); |
|
|
|
POINT pt; |
|
pt.x = (short)event->x; |
|
pt.y = (short)event->y; |
|
if ( PtInRect( &rcHandle, pt ) ) |
|
{ |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
bool CChoreoView::IsMouseOverScrubArea( mxEvent *event ) |
|
{ |
|
RECT rcArea; |
|
GetScrubAreaRect( rcArea ); |
|
|
|
InflateRect( &rcArea, 2, 2 ); |
|
|
|
POINT pt; |
|
pt.x = (short)event->x; |
|
pt.y = (short)event->y; |
|
if ( PtInRect( &rcArea, pt ) ) |
|
{ |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
bool CChoreoView::IsScrubbing( void ) const |
|
{ |
|
bool scrubbing = ( m_nDragType == DRAGTYPE_SCRUBBER ) ? true : false; |
|
return scrubbing; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : dt - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::Think( float dt ) |
|
{ |
|
bool scrubbing = IFacePoserToolWindow::IsAnyToolScrubbing(); |
|
|
|
ScrubThink( dt, scrubbing, this ); |
|
} |
|
|
|
static int lastthinkframe = -1; |
|
void CChoreoView::ScrubThink( float dt, bool scrubbing, IFacePoserToolWindow *invoker ) |
|
{ |
|
// Make sure we don't get called more than once per frame |
|
int thisframe = g_MDLViewer->GetCurrentFrame(); |
|
if ( thisframe == lastthinkframe ) |
|
return; |
|
|
|
lastthinkframe = thisframe; |
|
|
|
if ( !m_pScene ) |
|
return; |
|
|
|
if ( m_flScrubTarget == m_flScrub && !scrubbing ) |
|
{ |
|
// Act like it's paused |
|
if ( IFacePoserToolWindow::ShouldAutoProcess() ) |
|
{ |
|
m_bSimulating = true; |
|
SceneThink( m_flScrub ); |
|
} |
|
|
|
if ( m_bSimulating && !m_bPaused ) |
|
{ |
|
//FinishSimulation(); |
|
} |
|
return; |
|
} |
|
|
|
// Make sure we're solving head turns during playback |
|
models->SetSolveHeadTurn( 1 ); |
|
|
|
if ( m_bPaused ) |
|
{ |
|
SceneThink( m_flScrub ); |
|
return; |
|
} |
|
|
|
// Make sure phonemes are loaded |
|
FacePoser_EnsurePhonemesLoaded(); |
|
|
|
if ( !m_bSimulating ) |
|
{ |
|
m_bSimulating = true; |
|
} |
|
|
|
float d = m_flScrubTarget - m_flScrub; |
|
int sign = d > 0.0f ? 1 : -1; |
|
|
|
float maxmove = dt * m_flPlaybackRate; |
|
|
|
float prevScrub = m_flScrub; |
|
|
|
if ( sign > 0 ) |
|
{ |
|
if ( d < maxmove ) |
|
{ |
|
m_flScrub = m_flScrubTarget; |
|
} |
|
else |
|
{ |
|
m_flScrub += maxmove; |
|
} |
|
} |
|
else |
|
{ |
|
if ( -d < maxmove ) |
|
{ |
|
m_flScrub = m_flScrubTarget; |
|
} |
|
else |
|
{ |
|
m_flScrub -= maxmove; |
|
} |
|
} |
|
|
|
m_flFrameTime = ( m_flScrub - prevScrub ); |
|
|
|
SceneThink( m_flScrub ); |
|
|
|
DrawScrubHandle(); |
|
|
|
if ( scrubbing ) |
|
{ |
|
g_pMatSysWindow->Frame(); |
|
} |
|
|
|
if ( invoker != g_pExpressionTool ) |
|
{ |
|
g_pExpressionTool->ForceScrubPositionFromSceneTime( m_flScrub ); |
|
} |
|
if ( invoker != g_pGestureTool ) |
|
{ |
|
g_pGestureTool->ForceScrubPositionFromSceneTime( m_flScrub ); |
|
} |
|
if ( invoker != g_pRampTool ) |
|
{ |
|
g_pRampTool->ForceScrubPositionFromSceneTime( m_flScrub ); |
|
} |
|
if ( invoker != g_pSceneRampTool ) |
|
{ |
|
g_pSceneRampTool->ForceScrubPositionFromSceneTime( m_flScrub ); |
|
} |
|
} |
|
|
|
void CChoreoView::DrawScrubHandle( void ) |
|
{ |
|
if ( !m_bCanDraw ) |
|
return; |
|
|
|
// Handle new time and |
|
RECT rcArea; |
|
GetScrubAreaRect( rcArea ); |
|
|
|
CChoreoWidgetDrawHelper drawHelper( this, rcArea, COLOR_CHOREO_BACKGROUND ); |
|
DrawScrubHandle( drawHelper ); |
|
} |
|
|
|
void CChoreoView::SetScrubTime( float t ) |
|
{ |
|
m_flScrub = t; |
|
|
|
m_bPaused = false; |
|
} |
|
|
|
void CChoreoView::SetScrubTargetTime( float t ) |
|
{ |
|
m_flScrubTarget = t; |
|
|
|
m_bPaused = false; |
|
} |
|
|
|
void CChoreoView::ClampTimeToSelectionInterval( float& timeval ) |
|
{ |
|
// FIXME hook this up later |
|
return; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : show - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::ShowButtons( bool show ) |
|
{ |
|
m_btnPlay->setVisible( show ); |
|
m_btnPause->setVisible( show ); |
|
m_btnStop->setVisible( show ); |
|
m_pPlaybackRate->setVisible( show ); |
|
} |
|
|
|
void CChoreoView::RememberSelectedEvents( CUtlVector< CChoreoEvent * >& list ) |
|
{ |
|
GetSelectedEvents( list ); |
|
} |
|
|
|
void CChoreoView::ReselectEvents( CUtlVector< CChoreoEvent * >& list ) |
|
{ |
|
for ( int i = 0; i < m_SceneActors.Size(); i++ ) |
|
{ |
|
CChoreoActorWidget *actor = m_SceneActors[ i ]; |
|
if ( !actor ) |
|
continue; |
|
|
|
for ( int j = 0; j < actor->GetNumChannels(); j++ ) |
|
{ |
|
CChoreoChannelWidget *channel = actor->GetChannel( j ); |
|
if ( !channel ) |
|
continue; |
|
|
|
for ( int k = 0; k < channel->GetNumEvents(); k++ ) |
|
{ |
|
CChoreoEventWidget *event = channel->GetEvent( k ); |
|
if ( !event ) |
|
continue; |
|
|
|
CChoreoEvent *check = event->GetEvent(); |
|
if ( list.Find( check ) != list.InvalidIndex() ) |
|
{ |
|
event->SetSelected( true ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
} |
|
|
|
void CChoreoView::OnChangeScale( void ) |
|
{ |
|
CChoreoScene *scene = m_pScene; |
|
if ( !scene ) |
|
{ |
|
return; |
|
} |
|
|
|
// Zoom time in / out |
|
CInputParams params; |
|
memset( ¶ms, 0, sizeof( params ) ); |
|
|
|
strcpy( params.m_szDialogTitle, "Change Zoom" ); |
|
strcpy( params.m_szPrompt, "New scale (e.g., 2.5x):" ); |
|
|
|
Q_snprintf( params.m_szInputText, sizeof( params.m_szInputText ), "%.2f", (float)GetTimeZoom( GetToolName() ) / 100.0f ); |
|
|
|
if ( !InputProperties( ¶ms ) ) |
|
return; |
|
|
|
SetTimeZoom( GetToolName(), clamp( (int)( 100.0f * atof( params.m_szInputText ) ), 1, MAX_TIME_ZOOM ), false ); |
|
|
|
// Force scroll bars to recompute |
|
ForceScrollBarsToRecompute( false ); |
|
|
|
CUtlVector< CChoreoEvent * > selected; |
|
RememberSelectedEvents( selected ); |
|
|
|
DeleteSceneWidgets(); |
|
CreateSceneWidgets(); |
|
|
|
ReselectEvents( selected ); |
|
|
|
InvalidateLayout(); |
|
Con_Printf( "Zoom factor %i %%\n", GetTimeZoom( GetToolName() ) ); |
|
} |
|
|
|
void CChoreoView::OnCheckSequenceLengths( void ) |
|
{ |
|
if ( !m_pScene ) |
|
return; |
|
|
|
Con_Printf( "Checking sequence durations...\n" ); |
|
|
|
bool changed = FixupSequenceDurations( m_pScene, true ); |
|
|
|
if ( !changed ) |
|
{ |
|
Con_Printf( " no changes...\n" ); |
|
return; |
|
} |
|
|
|
SetDirty( true ); |
|
|
|
PushUndo( "Check sequence lengths" ); |
|
|
|
FixupSequenceDurations( m_pScene, false ); |
|
|
|
PushRedo( "Check sequence lengths" ); |
|
|
|
InvalidateLayout(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *scene - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::InvalidateTrackLookup_R( CChoreoScene *scene ) |
|
{ |
|
// No need to undo since this data doesn't matter |
|
int c = scene->GetNumEvents(); |
|
for ( int i = 0; i < c; i++ ) |
|
{ |
|
CChoreoEvent *event = scene->GetEvent( i ); |
|
if ( !event ) |
|
continue; |
|
|
|
switch ( event->GetType() ) |
|
{ |
|
default: |
|
break; |
|
case CChoreoEvent::FLEXANIMATION: |
|
{ |
|
event->SetTrackLookupSet( false ); |
|
} |
|
break; |
|
case CChoreoEvent::SUBSCENE: |
|
{ |
|
CChoreoScene *sub = event->GetSubScene(); |
|
// NOTE: Don't bother loading it now if it's not on hand |
|
if ( sub ) |
|
{ |
|
InvalidateTrackLookup_R( sub ); |
|
} |
|
} |
|
break; |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Model changed so we'll have to re-index flex anim tracks |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::InvalidateTrackLookup( void ) |
|
{ |
|
if ( !m_pScene ) |
|
return; |
|
|
|
InvalidateTrackLookup_R( m_pScene ); |
|
} |
|
|
|
|
|
|
|
bool CChoreoView::IsRampOnly( void ) const |
|
{ |
|
return m_bRampOnly; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *scene - |
|
// *event - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::ProcessInterrupt( CChoreoScene *scene, CChoreoEvent *event ) |
|
{ |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *scene - |
|
// *event - |
|
//----------------------------------------------------------------------------- |
|
void CChoreoView::ProcessPermitResponses( CChoreoScene *scene, CChoreoEvent *event ) |
|
{ |
|
} |
|
|
|
void CChoreoView::ApplyBounds( int& mx, int& my ) |
|
{ |
|
if ( !m_bUseBounds ) |
|
return; |
|
|
|
mx = clamp( mx, m_nMinX, m_nMaxX ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns -1 if no event found |
|
// Input : *channel - |
|
// *e - |
|
// forward - |
|
// Output : float |
|
//----------------------------------------------------------------------------- |
|
float CChoreoView::FindNextEventTime( CChoreoEvent::EVENTTYPE type, CChoreoChannel *channel, CChoreoEvent *e, bool forward ) |
|
{ |
|
bool foundone = false; |
|
float bestTime = -1.0f; |
|
float bestGap = 999999.0f; |
|
|
|
int c = channel->GetNumEvents(); |
|
for ( int i = 0; i < c; i++ ) |
|
{ |
|
CChoreoEvent *test = channel->GetEvent( i ); |
|
if ( test->GetType() != type ) |
|
continue; |
|
|
|
if ( forward ) |
|
{ |
|
float dt = test->GetStartTime() - e->GetEndTime(); |
|
if ( dt <= 0.0f ) |
|
continue; |
|
|
|
if ( dt < bestGap ) |
|
{ |
|
foundone = true; |
|
bestGap = dt; |
|
bestTime = test->GetStartTime(); |
|
} |
|
} |
|
else |
|
{ |
|
float dt = e->GetStartTime() - test->GetEndTime(); |
|
if ( dt <= 0.0f ) |
|
continue; |
|
|
|
if ( dt < bestGap ) |
|
{ |
|
foundone = true; |
|
bestGap = dt; |
|
bestTime = test->GetEndTime(); |
|
} |
|
} |
|
} |
|
|
|
return bestTime; |
|
} |
|
|
|
void CChoreoView::CalcBounds( int movetype ) |
|
{ |
|
m_bUseBounds = false; |
|
m_nMinX = 0; |
|
m_nMaxX = 0; |
|
|
|
if ( !m_pClickedEvent ) |
|
return; |
|
|
|
switch ( movetype ) |
|
{ |
|
default: |
|
break; |
|
case DRAGTYPE_EVENT_MOVE: |
|
case DRAGTYPE_EVENT_STARTTIME: |
|
case DRAGTYPE_EVENT_ENDTIME: |
|
case DRAGTYPE_EVENT_STARTTIME_RESCALE: |
|
case DRAGTYPE_EVENT_ENDTIME_RESCALE: |
|
{ |
|
m_nMinX = GetPixelForTimeValue( 0 ); |
|
m_nMaxX = GetPixelForTimeValue( m_pScene->FindStopTime() ); |
|
|
|
CChoreoEvent *e = m_pClickedEvent->GetEvent(); |
|
|
|
m_bUseBounds = false; // e && e->GetType() == CChoreoEvent::GESTURE; |
|
// FIXME: use this for finding adjacent gesture edges (kenb) |
|
if ( m_bUseBounds ) |
|
{ |
|
CChoreoChannel *channel = e->GetChannel(); |
|
Assert( channel ); |
|
|
|
float forwardTime = FindNextEventTime( e->GetType(), channel, e, true ); |
|
float reverseTime = FindNextEventTime( e->GetType(), channel, e, false ); |
|
|
|
// Compute pixel for time |
|
int nextPixel = forwardTime != -1 ? GetPixelForTimeValue( forwardTime ) : m_nMaxX; |
|
int prevPixel = reverseTime != -1 ? GetPixelForTimeValue( reverseTime ) : m_nMinX; |
|
|
|
int startPixel = GetPixelForTimeValue( e->GetStartTime() ); |
|
int endPixel = GetPixelForTimeValue( e->GetEndTime() ); |
|
|
|
switch ( movetype ) |
|
{ |
|
case DRAGTYPE_EVENT_MOVE: |
|
{ |
|
m_nMinX = prevPixel + ( m_xStart - startPixel ) + 1; |
|
m_nMaxX = nextPixel - ( endPixel - m_xStart ) - 1; |
|
} |
|
break; |
|
case DRAGTYPE_EVENT_STARTTIME: |
|
case DRAGTYPE_EVENT_STARTTIME_RESCALE: |
|
{ |
|
m_nMinX = prevPixel + ( m_xStart - startPixel ) + 1; |
|
} |
|
break; |
|
case DRAGTYPE_EVENT_ENDTIME: |
|
case DRAGTYPE_EVENT_ENDTIME_RESCALE: |
|
{ |
|
m_nMaxX = nextPixel - ( endPixel - m_xStart ) - 1; |
|
} |
|
break; |
|
} |
|
} |
|
} |
|
break; |
|
} |
|
} |
|
|
|
bool CChoreoView::ShouldSelectEvent( SelectionParams_t ¶ms, CChoreoEvent *event ) |
|
{ |
|
if ( params.forward ) |
|
{ |
|
if ( event->GetStartTime() >= params.time ) |
|
return true; |
|
} |
|
else |
|
{ |
|
float endtime = event->HasEndTime() ? event->GetEndTime() : event->GetStartTime(); |
|
|
|
if ( endtime <= params.time ) |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
void CChoreoView::SelectEvents( SelectionParams_t& params ) |
|
{ |
|
if ( !m_pScene ) |
|
return; |
|
|
|
if ( !m_pClickedActor ) |
|
return; |
|
|
|
if ( params.type == SelectionParams_t::SP_CHANNEL && !m_pClickedChannel ) |
|
return; |
|
|
|
//CChoreoActor *actor = m_pClickedActor->GetActor(); |
|
CChoreoChannel *channel = m_pClickedChannel ? m_pClickedChannel->GetChannel() : NULL; |
|
|
|
for ( int i = 0; i < m_SceneActors.Size(); i++ ) |
|
{ |
|
CChoreoActorWidget *a = m_SceneActors[ i ]; |
|
if ( !a ) |
|
continue; |
|
|
|
//if ( a->GetActor() != actor ) |
|
// continue; |
|
|
|
for ( int j = 0; j < a->GetNumChannels(); j++ ) |
|
{ |
|
CChoreoChannelWidget *c = a->GetChannel( j ); |
|
if ( !c ) |
|
continue; |
|
|
|
if ( params.type == SelectionParams_t::SP_CHANNEL && |
|
c->GetChannel() != channel ) |
|
continue; |
|
|
|
if ( params.type == SelectionParams_t::SP_ACTIVE && |
|
!c->GetChannel()->GetActive() ) |
|
continue; |
|
|
|
for ( int k = 0; k < c->GetNumEvents(); k++ ) |
|
{ |
|
CChoreoEventWidget *e = c->GetEvent( k ); |
|
if ( !e ) |
|
continue; |
|
|
|
CChoreoEvent *event = e->GetEvent(); |
|
if ( !event ) |
|
continue; |
|
|
|
if ( !ShouldSelectEvent( params, event ) ) |
|
continue; |
|
|
|
e->SetSelected( true ); |
|
} |
|
} |
|
} |
|
|
|
// Now handle global events, too |
|
for ( int i = 0; i < m_SceneGlobalEvents.Count(); i++ ) |
|
{ |
|
CChoreoGlobalEventWidget *e = m_SceneGlobalEvents[ i ]; |
|
if ( !e ) |
|
continue; |
|
|
|
CChoreoEvent *event = e->GetEvent(); |
|
if ( !event ) |
|
continue; |
|
|
|
if ( !ShouldSelectEvent( params, event ) ) |
|
continue; |
|
|
|
e->SetSelected( true ); |
|
} |
|
|
|
redraw(); |
|
} |
|
|
|
void CChoreoView::SetTimeZoom( char const *tool, int tz, bool preserveFocus ) |
|
{ |
|
if ( !m_pScene ) |
|
return; |
|
|
|
// No change |
|
int oldZoom = GetTimeZoom( tool ); |
|
if ( tz == oldZoom ) |
|
return; |
|
|
|
SetDirty( true ); |
|
|
|
POINT pt; |
|
::GetCursorPos( &pt ); |
|
::ScreenToClient( (HWND)getHandle(), &pt ); |
|
|
|
// Now figure out time under cursor at old zoom scale |
|
float t = GetTimeValueForMouse( pt.x, true ); |
|
|
|
m_pScene->SetTimeZoom( tool, tz ); |
|
|
|
if ( preserveFocus ) |
|
{ |
|
RECT rc; |
|
GetClientRect( (HWND)getHandle(), &rc ); |
|
RECT rcClient = rc; |
|
rcClient.top += GetStartRow(); |
|
OffsetRect( &rcClient, 0, -m_nTopOffset ); |
|
m_flStartTime = m_flLeftOffset / GetPixelsPerSecond(); |
|
m_flEndTime = m_flStartTime + (float)( rcClient.right - GetLabelWidth() ) / GetPixelsPerSecond(); |
|
|
|
// Now figure out tie under pt.x |
|
float newT = GetTimeValueForMouse( pt.x, true ); |
|
if ( newT != t ) |
|
{ |
|
// We need to scroll over a bit |
|
float pps = GetPixelsPerSecond(); |
|
float movePixels = pps * ( newT - t ); |
|
|
|
m_flLeftOffset -= movePixels; |
|
if ( m_flLeftOffset < 0.0f ) |
|
{ |
|
//float fixup = - m_flLeftOffset; |
|
m_flLeftOffset = 0; |
|
} |
|
|
|
// float maxtime = m_pScene->FindStopTime(); |
|
float flApparentEndTime = max( m_pScene->FindStopTime(), 5.0f ) + 5.0f; |
|
if ( m_flEndTime > flApparentEndTime ) |
|
{ |
|
movePixels = pps * ( m_flEndTime - flApparentEndTime ); |
|
m_flLeftOffset = max( 0.0f, m_flLeftOffset - movePixels ); |
|
} |
|
} |
|
} |
|
|
|
// Deal with the slider |
|
RepositionHSlider(); |
|
redraw(); |
|
} |
|
|
|
int CChoreoView::GetTimeZoom( char const *tool ) |
|
{ |
|
if ( !m_pScene ) |
|
return 100; |
|
|
|
return m_pScene->GetTimeZoom( tool ); |
|
} |
|
|
|
void CChoreoView::CheckInsertTime( CChoreoEvent *e, float dt, float starttime, float endtime ) |
|
{ |
|
// Not influenced |
|
float eventend = e->HasEndTime() ? e->GetEndTime() : e->GetStartTime(); |
|
|
|
if ( eventend < starttime ) |
|
return; |
|
|
|
if ( e->GetStartTime() > starttime ) |
|
{ |
|
e->OffsetTime( dt ); |
|
e->SnapTimes(); |
|
} |
|
else if ( !e->IsFixedLength() && e->HasEndTime() ) // the event starts before start, but ends after start time, need to insert space into the event, act like user dragged end time |
|
{ |
|
float newduration = e->GetDuration() + dt; |
|
RescaleRamp( e, newduration ); |
|
switch ( e->GetType() ) |
|
{ |
|
default: |
|
break; |
|
case CChoreoEvent::GESTURE: |
|
{ |
|
e->RescaleGestureTimes( e->GetStartTime(), e->GetEndTime() + dt, true ); |
|
} |
|
break; |
|
case CChoreoEvent::FLEXANIMATION: |
|
{ |
|
RescaleExpressionTimes( e, e->GetStartTime(), e->GetEndTime() + dt ); |
|
} |
|
break; |
|
} |
|
e->OffsetEndTime( dt ); |
|
e->SnapTimes(); |
|
e->ResortRamp(); |
|
} |
|
|
|
switch ( e->GetType() ) |
|
{ |
|
default: |
|
break; |
|
case CChoreoEvent::SPEAK: |
|
{ |
|
// Try and load wav to get length |
|
CAudioSource *wave = sound->LoadSound( va( "sound/%s", FacePoser_TranslateSoundName( e ) ) ); |
|
if ( wave ) |
|
{ |
|
e->SetEndTime( e->GetStartTime() + wave->GetRunningLength() ); |
|
delete wave; |
|
} |
|
} |
|
break; |
|
case CChoreoEvent::SEQUENCE: |
|
{ |
|
CheckSequenceLength( e, false ); |
|
} |
|
break; |
|
case CChoreoEvent::GESTURE: |
|
{ |
|
CheckGestureLength( e, false ); |
|
} |
|
break; |
|
} |
|
} |
|
|
|
void CChoreoView::OnInsertTime() |
|
{ |
|
if ( !m_rgABPoints[ 0 ].active && |
|
!m_rgABPoints[ 1 ].active ) |
|
{ |
|
return; |
|
} |
|
|
|
Con_Printf( "OnInsertTime()\n" ); |
|
|
|
float starttime = m_rgABPoints[ 0 ].time; |
|
float endtime = m_rgABPoints[ 1 ].time; |
|
|
|
// Sort samples correctly |
|
if ( starttime > endtime ) |
|
{ |
|
float temp = starttime; |
|
starttime = endtime; |
|
endtime = temp; |
|
} |
|
|
|
float dt = endtime - starttime; |
|
if ( dt == 0.0f ) |
|
{ |
|
// Nothing to do... |
|
return; |
|
} |
|
|
|
SetDirty( true ); |
|
|
|
PushUndo( "Insert Time" ); |
|
|
|
for ( int i = 0; i < m_SceneActors.Size(); i++ ) |
|
{ |
|
CChoreoActorWidget *actor = m_SceneActors[ i ]; |
|
if ( !actor ) |
|
continue; |
|
|
|
for ( int j = 0; j < actor->GetNumChannels(); j++ ) |
|
{ |
|
CChoreoChannelWidget *channel = actor->GetChannel( j ); |
|
if ( !channel ) |
|
continue; |
|
|
|
for ( int k = 0; k < channel->GetNumEvents(); k++ ) |
|
{ |
|
CChoreoEventWidget *event = channel->GetEvent( k ); |
|
if ( !event ) |
|
continue; |
|
|
|
CChoreoEvent *e = event->GetEvent(); |
|
if ( !e ) |
|
continue; |
|
|
|
CheckInsertTime( e, dt, starttime, endtime ); |
|
} |
|
} |
|
} |
|
|
|
// Now handle global events, too |
|
for ( int i = 0; i < m_SceneGlobalEvents.Count(); i++ ) |
|
{ |
|
CChoreoGlobalEventWidget *event = m_SceneGlobalEvents[ i ]; |
|
if ( !event ) |
|
continue; |
|
|
|
CChoreoEvent *e = event->GetEvent(); |
|
if ( !e ) |
|
continue; |
|
|
|
CheckInsertTime( e, dt, starttime, endtime ); |
|
} |
|
|
|
PushRedo( "Insert Time" ); |
|
InvalidateLayout(); |
|
|
|
g_pExpressionTool->LayoutItems( true ); |
|
g_pExpressionTool->redraw(); |
|
g_pGestureTool->redraw(); |
|
g_pRampTool->redraw(); |
|
g_pSceneRampTool->redraw(); |
|
} |
|
|
|
void CChoreoView::CheckDeleteTime( CChoreoEvent *e, float dt, float starttime, float endtime, bool& deleteEvent ) |
|
{ |
|
deleteEvent = false; |
|
|
|
// Not influenced |
|
float eventend = e->HasEndTime() ? e->GetEndTime() : e->GetStartTime(); |
|
|
|
if ( eventend < starttime ) |
|
{ |
|
return; |
|
} |
|
|
|
// On right side of start mark, just shift left |
|
if ( e->GetStartTime() > starttime ) |
|
{ |
|
// If it has no duration and it's in the bounds then kill it. |
|
if ( !e->HasEndTime() && e->GetStartTime() < endtime ) |
|
{ |
|
deleteEvent = true; |
|
return; |
|
} |
|
else |
|
{ |
|
float shift = e->GetStartTime() - starttime; |
|
float maxoffset = min( dt, shift ); |
|
|
|
e->OffsetTime( -maxoffset ); |
|
e->SnapTimes(); |
|
} |
|
} |
|
else if ( !e->IsFixedLength() && e->HasEndTime() ) // the event starts before start, but ends after start time, need to insert space into the event, act like user dragged end time |
|
{ |
|
float shiftend = e->GetEndTime() - starttime; |
|
float maxoffset = min( dt, shiftend ); |
|
|
|
float newduration = e->GetDuration() - maxoffset; |
|
if ( newduration <= 0.0f ) |
|
{ |
|
deleteEvent = true; |
|
return; |
|
} |
|
else |
|
{ |
|
RescaleRamp( e, newduration ); |
|
switch ( e->GetType() ) |
|
{ |
|
default: |
|
break; |
|
case CChoreoEvent::GESTURE: |
|
{ |
|
e->RescaleGestureTimes( e->GetStartTime(), e->GetEndTime() - maxoffset, true ); |
|
} |
|
break; |
|
case CChoreoEvent::FLEXANIMATION: |
|
{ |
|
RescaleExpressionTimes( e, e->GetStartTime(), e->GetEndTime() - maxoffset ); |
|
} |
|
break; |
|
} |
|
e->OffsetEndTime( -maxoffset ); |
|
e->SnapTimes(); |
|
e->ResortRamp(); |
|
} |
|
} |
|
|
|
switch ( e->GetType() ) |
|
{ |
|
default: |
|
break; |
|
case CChoreoEvent::SPEAK: |
|
{ |
|
// Try and load wav to get length |
|
CAudioSource *wave = sound->LoadSound( va( "sound/%s", FacePoser_TranslateSoundName( e ) ) ); |
|
if ( wave ) |
|
{ |
|
e->SetEndTime( e->GetStartTime() + wave->GetRunningLength() ); |
|
delete wave; |
|
} |
|
} |
|
break; |
|
case CChoreoEvent::SEQUENCE: |
|
{ |
|
CheckSequenceLength( e, false ); |
|
} |
|
break; |
|
case CChoreoEvent::GESTURE: |
|
{ |
|
CheckGestureLength( e, false ); |
|
} |
|
break; |
|
} |
|
} |
|
|
|
void CChoreoView::OnDeleteTime() |
|
{ |
|
if ( !m_rgABPoints[ 0 ].active && |
|
!m_rgABPoints[ 1 ].active ) |
|
{ |
|
return; |
|
} |
|
|
|
Con_Printf( "OnDeleteTime()\n" ); |
|
|
|
float starttime = m_rgABPoints[ 0 ].time; |
|
float endtime = m_rgABPoints[ 1 ].time; |
|
|
|
// Sort samples correctly |
|
if ( starttime > endtime ) |
|
{ |
|
float temp = starttime; |
|
starttime = endtime; |
|
endtime = temp; |
|
} |
|
|
|
float dt = endtime - starttime; |
|
if ( dt == 0.0f ) |
|
{ |
|
// Nothing to do... |
|
return; |
|
} |
|
|
|
SetDirty( true ); |
|
|
|
PushUndo( "Delete Time" ); |
|
|
|
CUtlVector< CChoreoEventWidget * > deletions; |
|
CUtlVector< CChoreoGlobalEventWidget * > global_deletions; |
|
|
|
for ( int i = 0; i < m_SceneActors.Size(); i++ ) |
|
{ |
|
CChoreoActorWidget *actor = m_SceneActors[ i ]; |
|
if ( !actor ) |
|
continue; |
|
|
|
for ( int j = 0; j < actor->GetNumChannels(); j++ ) |
|
{ |
|
CChoreoChannelWidget *channel = actor->GetChannel( j ); |
|
if ( !channel ) |
|
continue; |
|
|
|
for ( int k = 0; k < channel->GetNumEvents(); k++ ) |
|
{ |
|
CChoreoEventWidget *event = channel->GetEvent( k ); |
|
if ( !event ) |
|
continue; |
|
|
|
CChoreoEvent *e = event->GetEvent(); |
|
if ( !e ) |
|
continue; |
|
|
|
bool deleteEvent = false; |
|
|
|
CheckDeleteTime( e, dt, starttime, endtime, deleteEvent ); |
|
|
|
if ( deleteEvent ) |
|
{ |
|
deletions.AddToTail( event ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
// Now handle global events, too |
|
for ( int i = 0; i < m_SceneGlobalEvents.Count(); i++ ) |
|
{ |
|
CChoreoGlobalEventWidget *event = m_SceneGlobalEvents[ i ]; |
|
if ( !event ) |
|
continue; |
|
|
|
CChoreoEvent *e = event->GetEvent(); |
|
if ( !e ) |
|
continue; |
|
|
|
bool deleteEvent = false; |
|
CheckDeleteTime( e, dt, starttime, endtime, deleteEvent ); |
|
|
|
if ( deleteEvent ) |
|
{ |
|
global_deletions.AddToTail( event ); |
|
} |
|
} |
|
|
|
for ( int i = 0; i < deletions.Count(); i++ ) |
|
{ |
|
CChoreoEventWidget *w = deletions[ i ]; |
|
|
|
CChoreoEvent *e = w->GetEvent(); |
|
CChoreoChannel *channel = e->GetChannel(); |
|
if ( channel ) |
|
{ |
|
channel->RemoveEvent( e ); |
|
} |
|
m_pScene->DeleteReferencedObjects( e ); |
|
} |
|
|
|
for ( int i = 0; i < global_deletions.Count(); i++ ) |
|
{ |
|
CChoreoGlobalEventWidget *w = global_deletions[ i ]; |
|
CChoreoEvent *e = w->GetEvent(); |
|
m_pScene->DeleteReferencedObjects( e ); |
|
} |
|
|
|
// Force scroll bars to recompute |
|
ForceScrollBarsToRecompute( false ); |
|
|
|
if ( deletions.Count() > 0 || global_deletions.Count() > 0 ) |
|
{ |
|
DeleteSceneWidgets(); |
|
CreateSceneWidgets(); |
|
} |
|
|
|
PushRedo( "Delete Time" ); |
|
|
|
InvalidateLayout(); |
|
|
|
g_pExpressionTool->LayoutItems( true ); |
|
g_pExpressionTool->redraw(); |
|
g_pGestureTool->redraw(); |
|
g_pRampTool->redraw(); |
|
g_pSceneRampTool->redraw(); |
|
} |
|
|
|
void CChoreoView::OnModelChanged() |
|
{ |
|
InvalidateTrackLookup(); |
|
// OnCheckSequenceLengths(); |
|
} |
|
|
|
void CChoreoView::SetShowCloseCaptionData( bool show ) |
|
{ |
|
m_bShowCloseCaptionData = show; |
|
} |
|
|
|
bool CChoreoView::GetShowCloseCaptionData( void ) const |
|
{ |
|
return m_bShowCloseCaptionData; |
|
} |
|
|
|
void CChoreoView::OnToggleCloseCaptionTags() |
|
{ |
|
m_bShowCloseCaptionData = !m_bShowCloseCaptionData; |
|
InvalidateLayout(); |
|
} |
|
|
|
|
|
|
|
static bool EventStartTimeLessFunc( CChoreoEvent * const &p1, CChoreoEvent * const &p2 ) |
|
{ |
|
CChoreoEvent *w1; |
|
CChoreoEvent *w2; |
|
|
|
w1 = const_cast< CChoreoEvent * >( p1 ); |
|
w2 = const_cast< CChoreoEvent * >( p2 ); |
|
|
|
return w1->GetStartTime() < w2->GetStartTime(); |
|
} |
|
|
|
bool CChoreoView::GenerateCombinedFile( char const *outfilename, char const *cctoken, gender_t gender, CUtlRBTree< CChoreoEvent * >& sorted ) |
|
{ |
|
CUtlVector< CombinerEntry > work; |
|
|
|
char actualfile[ 512 ]; |
|
soundemitter->GenderExpandString( gender, outfilename, actualfile, sizeof( actualfile ) ); |
|
if ( Q_strlen( actualfile ) <= 0 ) |
|
{ |
|
return false; |
|
} |
|
|
|
int i = sorted.FirstInorder(); |
|
if ( i != sorted.InvalidIndex() ) |
|
{ |
|
CChoreoEvent *e = sorted[ i ]; |
|
|
|
float startoffset = e->GetStartTime(); |
|
|
|
do |
|
{ |
|
e = sorted[ i ]; |
|
|
|
float curoffset = e->GetStartTime(); |
|
|
|
CombinerEntry ce; |
|
Q_snprintf( ce.wavefile, sizeof( ce.wavefile ), "sound/%s", FacePoser_TranslateSoundNameGender( e->GetParameters(), gender ) ); |
|
ce.startoffset = curoffset - startoffset; |
|
|
|
work.AddToTail( ce ); |
|
|
|
i = sorted.NextInorder( i ); |
|
} |
|
while ( i != sorted.InvalidIndex() ); |
|
} |
|
|
|
bool ok = soundcombiner->CombineSoundFiles( filesystem, actualfile, work ); |
|
if ( !ok ) |
|
{ |
|
Con_ErrorPrintf( "Failed to create combined sound '%s':'%s'\n", cctoken, actualfile ); |
|
return false; |
|
} |
|
Con_Printf( "Created combined sound '%s':'%s'\n", cctoken, actualfile ); |
|
return true; |
|
} |
|
|
|
bool CChoreoView::ValidateCombinedFileCheckSum( char const *outfilename, char const *cctoken, gender_t gender, CUtlRBTree< CChoreoEvent * >& sorted ) |
|
{ |
|
CUtlVector< CombinerEntry > work; |
|
|
|
char actualfile[ 512 ]; |
|
soundemitter->GenderExpandString( gender, outfilename, actualfile, sizeof( actualfile ) ); |
|
if ( Q_strlen( actualfile ) <= 0 ) |
|
{ |
|
return false; |
|
} |
|
|
|
int i = sorted.FirstInorder(); |
|
if ( i != sorted.InvalidIndex() ) |
|
{ |
|
CChoreoEvent *e = sorted[ i ]; |
|
|
|
float startoffset = e->GetStartTime(); |
|
|
|
do |
|
{ |
|
e = sorted[ i ]; |
|
|
|
float curoffset = e->GetStartTime(); |
|
|
|
CombinerEntry ce; |
|
Q_snprintf( ce.wavefile, sizeof( ce.wavefile ), "sound/%s", FacePoser_TranslateSoundNameGender( e->GetParameters(), gender ) ); |
|
ce.startoffset = curoffset - startoffset; |
|
|
|
work.AddToTail( ce ); |
|
|
|
i = sorted.NextInorder( i ); |
|
} |
|
while ( i != sorted.InvalidIndex() ); |
|
} |
|
|
|
return soundcombiner->IsCombinedFileChecksumValid( filesystem, actualfile, work ); |
|
} |
|
|
|
void SuggestCaption( char *dest, int destlen, CUtlVector< CChoreoEvent * >& events ) |
|
{ |
|
// Walk through events and concatenate current captions, or raw wav data if have any |
|
dest[ 0 ] = 0; |
|
|
|
int c = events.Count(); |
|
for ( int i = 0 ; i < c; ++i ) |
|
{ |
|
CChoreoEvent *e = events[ i ]; |
|
|
|
bool found = false; |
|
char tok[ CChoreoEvent::MAX_CCTOKEN_STRING ]; |
|
if ( e->GetPlaybackCloseCaptionToken( tok, sizeof( tok ) ) ) |
|
{ |
|
wchar_t *localized = g_pLocalize->Find( tok ); |
|
if ( localized ) |
|
{ |
|
found = true; |
|
|
|
char ansi[ 1024 ]; |
|
g_pLocalize->ConvertUnicodeToANSI( localized, ansi, sizeof( ansi ) ); |
|
Q_strncat( dest, ansi, destlen, COPY_ALL_CHARACTERS ); |
|
} |
|
} |
|
|
|
if ( !found ) |
|
{ |
|
// See if the wav file has data... |
|
CAudioSource *wave = sound->LoadSound( va( "sound/%s", FacePoser_TranslateSoundName( e ) ) ); |
|
if ( wave ) |
|
{ |
|
CSentence *sentence = wave->GetSentence(); |
|
if ( sentence ) |
|
{ |
|
Q_strncat( dest, sentence->GetText(), destlen, COPY_ALL_CHARACTERS ); |
|
found = true; |
|
} |
|
} |
|
} |
|
|
|
if ( found && Q_strlen( dest ) > 0 && i != c - 1 ) |
|
{ |
|
Q_strncat( dest, " ", destlen, COPY_ALL_CHARACTERS ); |
|
} |
|
} |
|
} |
|
|
|
void CChoreoView::OnCombineSpeakEvents() |
|
{ |
|
if ( !m_pScene ) |
|
return; |
|
|
|
CChoreoChannel *firstChannel = NULL; |
|
|
|
CUtlVector< CChoreoEvent * > selected; |
|
GetSelectedEvents( selected ); |
|
|
|
int c = selected.Count(); |
|
// Find the appropriate event by iterating across all actors and channels |
|
for ( int i = c - 1; i >= 0; --i ) |
|
{ |
|
CChoreoEvent *e = selected[ i ]; |
|
|
|
if ( e->GetType() != CChoreoEvent::SPEAK ) |
|
{ |
|
Con_ErrorPrintf( "Can't combine events, all events must be SPEAK events.\n" ); |
|
return; |
|
} |
|
|
|
if ( !firstChannel ) |
|
{ |
|
firstChannel = e->GetChannel(); |
|
} |
|
else if ( e->GetChannel() != firstChannel ) |
|
{ |
|
Con_ErrorPrintf( "Can't combine events, all events must reside in the same channel.\n" ); |
|
return; |
|
} |
|
} |
|
|
|
if ( selected.Count() < 2 ) |
|
{ |
|
Con_ErrorPrintf( "Can't combine events, must have at least two events selected.\n" ); |
|
return; |
|
} |
|
|
|
// Let the user pick a CC phrase |
|
CCloseCaptionLookupParams params; |
|
Q_strncpy( params.m_szDialogTitle, "Choose Close Caption Token", sizeof( params.m_szDialogTitle ) ); |
|
|
|
params.m_bPositionDialog = false; |
|
params.m_nLeft = 0; |
|
params.m_nTop = 0; |
|
|
|
char playbacktoken[ CChoreoEvent::MAX_CCTOKEN_STRING ]; |
|
if ( !selected[0]->GetPlaybackCloseCaptionToken( playbacktoken, sizeof( playbacktoken ) ) ) |
|
{ |
|
return; |
|
} |
|
|
|
if ( !Q_stristr( playbacktoken, "_cc" ) ) |
|
{ |
|
Q_strncpy( params.m_szCCToken, va( "%s_cc", playbacktoken ), sizeof( params.m_szCCToken ) ); |
|
} |
|
else |
|
{ |
|
Q_strncpy( params.m_szCCToken, va( "%s", playbacktoken ), sizeof( params.m_szCCToken ) ); |
|
} |
|
|
|
// User hit okay and value actually changed? |
|
if ( !CloseCaptionLookup( ¶ms ) && |
|
params.m_szCCToken[0] != 0 ) |
|
{ |
|
return; |
|
} |
|
|
|
// See if the token exists? |
|
StringIndex_t stringIndex = g_pLocalize->FindIndex( params.m_szCCToken ); |
|
if ( INVALID_LOCALIZE_STRING_INDEX == stringIndex ) |
|
{ |
|
// Add token to closecaption_english file. |
|
// Guess at string and ask user to confirm. |
|
CInputParams ip; |
|
memset( &ip, 0, sizeof( ip ) ); |
|
|
|
Q_strncpy( ip.m_szDialogTitle, "Add Close Caption", sizeof( ip.m_szDialogTitle ) ); |
|
Q_snprintf( ip.m_szPrompt, sizeof( ip.m_szPrompt ), "Token (%s):", params.m_szCCToken ); |
|
|
|
char suggested[ 2048 ]; |
|
|
|
SuggestCaption( suggested, sizeof( suggested ), selected ); |
|
|
|
Q_snprintf( ip.m_szInputText, sizeof( ip.m_szInputText ), "%s", suggested ); |
|
|
|
if ( !InputProperties( &ip ) ) |
|
{ |
|
Con_Printf( "Combining of sound events cancelled\n" ); |
|
return; |
|
} |
|
|
|
if ( Q_strlen( ip.m_szInputText ) == 0 ) |
|
{ |
|
Q_snprintf( ip.m_szInputText, sizeof( ip.m_szInputText ), "!!!%s", params.m_szCCToken ); |
|
} |
|
|
|
char const *captionFile = "resource/closecaption_english.txt"; |
|
|
|
if ( !filesystem->IsFileWritable( captionFile, "GAME" ) ) |
|
{ |
|
Warning( "Forcing %s to be writable!!!\n", captionFile ); |
|
MakeFileWriteable( captionFile ); |
|
} |
|
|
|
wchar_t unicode[ 2048 ]; |
|
g_pLocalize->ConvertANSIToUnicode( ip.m_szInputText, unicode, sizeof( unicode ) ); |
|
|
|
g_pLocalize->AddString( params.m_szCCToken, unicode, captionFile ); |
|
g_pLocalize->SaveToFile( captionFile ); |
|
} |
|
|
|
SetDirty( true ); |
|
|
|
PushUndo( "Combine Sound Events" ); |
|
|
|
c = selected.Count(); |
|
for ( int i = 0 ; i < c; ++i ) |
|
{ |
|
selected[ i ]->SetCloseCaptionToken( params.m_szCCToken ); |
|
} |
|
|
|
PushRedo( "Combine Sound Events" ); |
|
|
|
// Redraw |
|
InvalidateLayout(); |
|
|
|
Con_Printf( "Changed %i events to use close caption token '%s'\n", c, params.m_szCCToken ); |
|
|
|
// Sort the sounds by start time |
|
|
|
CUtlRBTree< CChoreoEvent * > sorted( 0, 0, EventStartTimeLessFunc ); |
|
|
|
// Sort items |
|
c = selected.Count(); |
|
bool genderwildcard = false; |
|
for ( int i = 0; i < c; i++ ) |
|
{ |
|
CChoreoEvent *e = selected[ i ]; |
|
sorted.Insert( e ); |
|
|
|
// Get the sound entry name and use it to look up the gender info |
|
// Look up the sound level from the soundemitter system |
|
if ( !genderwildcard ) |
|
{ |
|
genderwildcard = soundemitter->IsUsingGenderToken( e->GetParameters() ); |
|
} |
|
} |
|
|
|
|
|
char outfilename[ 512 ]; |
|
Q_memset( outfilename, 0, sizeof( outfilename ) ); |
|
|
|
CChoreoEvent *e = sorted[ sorted.FirstInorder() ]; |
|
|
|
// Update whether we use the $gender token |
|
e->SetCombinedUsingGenderToken( genderwildcard ); |
|
|
|
if ( !e->ComputeCombinedBaseFileName( outfilename, sizeof( outfilename ), genderwildcard ) ) |
|
{ |
|
Con_ErrorPrintf( "Unable to regenerate wav file name for combined sound\n" ); |
|
return; |
|
} |
|
|
|
int soundindex = soundemitter->GetSoundIndex( e->GetParameters() ); |
|
char const *scriptfile = soundemitter->GetSourceFileForSound( soundindex ); |
|
if ( !scriptfile || !scriptfile[0] ) |
|
{ |
|
Con_ErrorPrintf( "Unable to find existing script to use for new combined sound entry.\n" ); |
|
return; |
|
} |
|
|
|
// Create a new sound entry for this sound |
|
CAddSoundParams asp; |
|
Q_memset( &asp, 0, sizeof( asp ) ); |
|
Q_strncpy( asp.m_szDialogTitle, "Add Combined Sound Entry", sizeof( asp.m_szDialogTitle ) ); |
|
Q_strncpy( asp.m_szWaveFile, outfilename + Q_strlen( "sound/"), sizeof( asp.m_szWaveFile ) ); |
|
Q_strncpy( asp.m_szScriptName, scriptfile, sizeof( asp.m_szScriptName ) ); |
|
Q_strncpy( asp.m_szSoundName, params.m_szCCToken, sizeof( asp.m_szSoundName ) ); |
|
|
|
asp.m_bAllowExistingSound = true; |
|
asp.m_bReadOnlySoundName = true; |
|
|
|
if ( !AddSound( &asp, (HWND)g_MDLViewer->getHandle() ) ) |
|
{ |
|
return; |
|
} |
|
|
|
if ( genderwildcard ) |
|
{ |
|
GenerateCombinedFile( outfilename, params.m_szCCToken, GENDER_MALE, sorted ); |
|
GenerateCombinedFile( outfilename, params.m_szCCToken, GENDER_FEMALE, sorted ); |
|
} |
|
else |
|
{ |
|
GenerateCombinedFile( outfilename, params.m_szCCToken, GENDER_NONE, sorted ); |
|
} |
|
} |
|
|
|
bool CChoreoView::ValidateCombinedSoundCheckSum( CChoreoEvent *e ) |
|
{ |
|
if ( !e || e->GetType() != CChoreoEvent::SPEAK ) |
|
return false; |
|
|
|
bool genderwildcard = e->IsCombinedUsingGenderToken(); |
|
char outfilename[ 512 ]; |
|
Q_memset( outfilename, 0, sizeof( outfilename ) ); |
|
if ( !e->ComputeCombinedBaseFileName( outfilename, sizeof( outfilename ), genderwildcard ) ) |
|
{ |
|
Con_ErrorPrintf( "Unable to regenerate wav file name for combined sound (%s)\n", e->GetCloseCaptionToken() ); |
|
return false; |
|
} |
|
|
|
bool checksumvalid = false; |
|
|
|
CUtlRBTree< CChoreoEvent * > eventList( 0, 0, EventStartTimeLessFunc ); |
|
|
|
if ( !e->GetChannel()->GetSortedCombinedEventList( e->GetCloseCaptionToken(), eventList ) ) |
|
{ |
|
Con_ErrorPrintf( "Unable to generated combined event list (%s)\n", e->GetCloseCaptionToken() ); |
|
return false; |
|
} |
|
|
|
|
|
if ( genderwildcard ) |
|
{ |
|
checksumvalid = ValidateCombinedFileCheckSum( outfilename, e->GetCloseCaptionToken(), GENDER_MALE, eventList ); |
|
checksumvalid &= ValidateCombinedFileCheckSum( outfilename, e->GetCloseCaptionToken(), GENDER_FEMALE, eventList ); |
|
} |
|
else |
|
{ |
|
checksumvalid = ValidateCombinedFileCheckSum( outfilename, e->GetCloseCaptionToken(), GENDER_NONE, eventList ); |
|
} |
|
|
|
return checksumvalid; |
|
} |
|
|
|
void CChoreoView::OnRemoveSpeakEventFromGroup() |
|
{ |
|
if ( !m_pScene ) |
|
return; |
|
|
|
int i, c; |
|
|
|
CUtlVector< CChoreoEvent * > selected; |
|
CUtlVector< CChoreoEvent * > processlist; |
|
if ( GetSelectedEvents( selected ) > 0 ) |
|
{ |
|
|
|
int c = selected.Count(); |
|
// Find the appropriate event by iterating across all actors and channels |
|
for ( i = c - 1; i >= 0; --i ) |
|
{ |
|
CChoreoEvent *e = selected[ i ]; |
|
if ( e->GetType() != CChoreoEvent::SPEAK ) |
|
{ |
|
selected.Remove( i ); |
|
continue; |
|
} |
|
|
|
if ( e->GetCloseCaptionType() == CChoreoEvent::CC_DISABLED ) |
|
{ |
|
selected.Remove( i ); |
|
continue; |
|
} |
|
|
|
m_pClickedChannel->GetMasterAndSlaves( e, processlist ); |
|
} |
|
} |
|
else |
|
{ |
|
m_pClickedChannel->GetMasterAndSlaves( m_pClickedChannel->GetCaptionClickedEvent(), processlist ); |
|
} |
|
|
|
if ( selected.Count() < 1 ) |
|
{ |
|
Con_ErrorPrintf( "No eligible SPEAK event selected.\n" ); |
|
return; |
|
} |
|
|
|
SetDirty( true ); |
|
|
|
PushUndo( "Remove speak event(s)" ); |
|
|
|
c = processlist.Count(); |
|
for ( i = 0 ; i < c; ++i ) |
|
{ |
|
processlist[ i ]->SetCloseCaptionToken( "" ); |
|
processlist[ i ]->SetCloseCaptionType( CChoreoEvent::CC_MASTER ); |
|
processlist[ i ]->SetUsingCombinedFile( false ); |
|
processlist[ i ]->SetRequiredCombinedChecksum( 0 ); |
|
processlist[ i ]->SetNumSlaves( 0 ); |
|
processlist[ i ]->SetLastSlaveEndTime( 0.0f ); |
|
} |
|
|
|
PushRedo( "Remove speak event(s)" ); |
|
|
|
// Redraw |
|
InvalidateLayout(); |
|
Con_Printf( "Reverted %i events to use default close caption token\n", c ); |
|
} |
|
|
|
bool CChoreoView::AreSelectedEventsCombinable() |
|
{ |
|
CUtlVector< CChoreoEvent * > events; |
|
if ( GetSelectedEvents( events ) <= 0 ) |
|
return false; |
|
|
|
CChoreoChannel *firstChannel = NULL; |
|
|
|
CUtlVector< CChoreoEvent * > selected; |
|
GetSelectedEvents( selected ); |
|
|
|
int c = selected.Count(); |
|
// Find the appropriate event by iterating across all actors and channels |
|
for ( int i = c - 1; i >= 0; --i ) |
|
{ |
|
CChoreoEvent *e = selected[ i ]; |
|
|
|
if ( e->GetType() != CChoreoEvent::SPEAK ) |
|
{ |
|
return false; |
|
} |
|
|
|
if ( !firstChannel ) |
|
{ |
|
firstChannel = e->GetChannel(); |
|
} |
|
else if ( e->GetChannel() != firstChannel ) |
|
{ |
|
return false; |
|
} |
|
} |
|
return selected.Count() >= 2 ? true : false; |
|
} |
|
|
|
bool CChoreoView::AreSelectedEventsInSpeakGroup() |
|
{ |
|
CUtlVector< CChoreoEvent * > selected; |
|
if ( GetSelectedEvents( selected ) <= 0 ) |
|
{ |
|
if ( m_pClickedChannel ) |
|
{ |
|
CChoreoEvent *e = m_pClickedChannel->GetCaptionClickedEvent(); |
|
if ( e && e->GetCloseCaptionType() == CChoreoEvent::CC_MASTER && |
|
e->GetNumSlaves() >= 1 ) |
|
{ |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
int c = selected.Count(); |
|
// Find the appropriate event by iterating across all actors and channels |
|
for ( int i = c - 1; i >= 0; --i ) |
|
{ |
|
CChoreoEvent *e = selected[ i ]; |
|
if ( e->GetType() != CChoreoEvent::SPEAK ) |
|
{ |
|
selected.Remove( i ); |
|
continue; |
|
} |
|
|
|
if ( e->GetCloseCaptionType() == CChoreoEvent::CC_DISABLED ) |
|
{ |
|
selected.Remove( i ); |
|
continue; |
|
} |
|
} |
|
|
|
return selected.Count() >= 1 ? true : false; |
|
|
|
} |
|
|
|
void CChoreoView::OnChangeCloseCaptionToken( CChoreoEvent *e ) |
|
{ |
|
CCloseCaptionLookupParams params; |
|
Q_strncpy( params.m_szDialogTitle, "Close Caption Token Lookup", sizeof( params.m_szDialogTitle ) ); |
|
|
|
params.m_bPositionDialog = false; |
|
params.m_nLeft = 0; |
|
params.m_nTop = 0; |
|
// strcpy( params.m_szPrompt, "Choose model:" ); |
|
|
|
Q_strncpy( params.m_szCCToken, e->GetCloseCaptionToken(), sizeof( params.m_szCCToken ) ); |
|
|
|
// User hit okay and value actually changed? |
|
if ( CloseCaptionLookup( ¶ms ) && |
|
Q_stricmp( e->GetCloseCaptionToken(), params.m_szCCToken ) ) |
|
{ |
|
char oldToken[ CChoreoEvent::MAX_CCTOKEN_STRING ]; |
|
Q_strncpy( oldToken, e->GetCloseCaptionToken(), sizeof( oldToken ) ); |
|
|
|
CUtlVector< CChoreoEvent * > events; |
|
m_pClickedChannel->GetMasterAndSlaves( e, events ); |
|
|
|
if ( events.Count() < 2 ) |
|
{ |
|
Con_ErrorPrintf( "Can't combine events, must have at least two events selected.\n" ); |
|
} |
|
|
|
SetDirty( true ); |
|
|
|
PushUndo( "Change closecaption token" ); |
|
|
|
// Make the change... |
|
int c = events.Count(); |
|
for ( int i = 0 ; i < c; ++i ) |
|
{ |
|
events[i]->SetCloseCaptionToken( params.m_szCCToken ); |
|
} |
|
|
|
PushRedo( "Change closecaption token" ); |
|
|
|
InvalidateLayout(); |
|
|
|
Con_Printf( "Close Caption token for '%s' changed to '%s'\n", e->GetName(), params.m_szCCToken ); |
|
} |
|
} |
|
|
|
void CChoreoView::OnToggleCloseCaptionsForEvent() |
|
{ |
|
if ( !m_pClickedChannel ) |
|
{ |
|
return; |
|
} |
|
|
|
CChoreoEvent *e = m_pClickedChannel->GetCaptionClickedEvent(); |
|
|
|
if ( !e ) |
|
{ |
|
return; |
|
} |
|
|
|
CChoreoEvent::CLOSECAPTION newType = CChoreoEvent::CC_MASTER; |
|
// Can't mess with slave |
|
switch ( e->GetCloseCaptionType() ) |
|
{ |
|
default: |
|
case CChoreoEvent::CC_SLAVE: |
|
return; |
|
case CChoreoEvent::CC_MASTER: |
|
newType = CChoreoEvent::CC_DISABLED; |
|
break; |
|
case CChoreoEvent::CC_DISABLED: |
|
newType = CChoreoEvent::CC_MASTER; |
|
break; |
|
} |
|
|
|
SetDirty( true ); |
|
|
|
PushUndo( "Enable/disable captions" ); |
|
|
|
// Make the change... |
|
e->SetCloseCaptionType( newType ); |
|
|
|
PushRedo( "Enable/disable captions" ); |
|
|
|
InvalidateLayout(); |
|
|
|
Con_Printf( "Close Caption type for '%s' changed to '%s'\n", e->GetName(), CChoreoEvent::NameForCCType( newType ) ); |
|
|
|
} |
|
|
|
void CChoreoView::StopScene() |
|
{ |
|
SetScrubTargetTime( m_flScrub ); |
|
FinishSimulation(); |
|
sound->Flush(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : - |
|
//----------------------------------------------------------------------------- |
|
template <class T> |
|
void DeleteAllAndPurge( T &tree ) |
|
{ |
|
T::IndexType_t i; |
|
|
|
for ( i = tree.FirstInorder(); i != T::InvalidIndex(); i = tree.NextInorder( i ) ) |
|
{ |
|
delete tree[i]; |
|
} |
|
|
|
tree.Purge(); |
|
} |
|
|
|
void CChoreoView::OnPlaceNextSpeakEvent() |
|
{ |
|
CUtlVector< CChoreoEvent * > list; |
|
GetSelectedEvents( list ); |
|
if ( list.Count() != 1 ) |
|
{ |
|
Warning( "Can't place sound event, nothing selected\n" ); |
|
return; |
|
} |
|
|
|
CChoreoEvent *ev = list[ 0 ]; |
|
if ( ev->GetType() != CChoreoEvent::SPEAK ) |
|
{ |
|
Warning( "Can't place sound event, no previous sound event selected\n" ); |
|
return; |
|
} |
|
|
|
CChoreoChannelWidget *widget = FindChannelForEvent( ev ); |
|
if ( !widget ) |
|
{ |
|
Warning( "Can't place sound event, can't find channel widget for event\n" ); |
|
return; |
|
} |
|
|
|
CChoreoChannel *channel = widget->GetChannel(); |
|
if ( !channel ) |
|
{ |
|
Warning( "Can't place sound event, can't find channel for new event\n" ); |
|
return; |
|
} |
|
|
|
CUtlRBTree< char const *, int > m_SortedNames( 0, 0, NameLessFunc ); |
|
|
|
int c = soundemitter->GetSoundCount(); |
|
for ( int i = 0; i < c; i++ ) |
|
{ |
|
char const *name = soundemitter->GetSoundName( i ); |
|
if ( name && name[ 0 ] ) |
|
{ |
|
m_SortedNames.Insert( strdup( name ) ); |
|
} |
|
} |
|
|
|
int idx = m_SortedNames.Find( ev->GetParameters() ); |
|
if ( idx == m_SortedNames.InvalidIndex() ) |
|
{ |
|
Warning( "Can't place sound event, can't find '%s' in sound list\n", ev->GetParameters() ); |
|
DeleteAllAndPurge( m_SortedNames ); |
|
return; |
|
} |
|
|
|
int nextIdx = m_SortedNames.NextInorder( idx ); |
|
if ( nextIdx == m_SortedNames.InvalidIndex() ) |
|
{ |
|
Warning( "Can't place sound event, can't next sound after '%s' in sound list\n", ev->GetParameters() ); |
|
DeleteAllAndPurge( m_SortedNames ); |
|
return; |
|
} |
|
|
|
DeselectAll(); |
|
|
|
SetDirty( true ); |
|
|
|
PushUndo( "Place Next Speak Event" ); |
|
|
|
CChoreoEvent *event = m_pScene->AllocEvent(); |
|
Assert( event ); |
|
if ( event ) |
|
{ |
|
// Copy everything for source event |
|
*event = *ev; |
|
|
|
event->SetParameters( m_SortedNames[ nextIdx ] ); |
|
// Start it at the end time... |
|
event->SetStartTime( event->GetEndTime() ); |
|
event->SetResumeCondition( false ); |
|
event->ClearAllRelativeTags(); |
|
event->ClearAllTimingTags(); |
|
event->ClearAllAbsoluteTags( CChoreoEvent::PLAYBACK ); |
|
event->ClearAllAbsoluteTags( CChoreoEvent::ORIGINAL ); |
|
|
|
event->SetChannel( channel ); |
|
event->SetActor( channel->GetActor() ); |
|
|
|
// Try and load wav to get length |
|
CAudioSource *wave = sound->LoadSound( va( "sound/%s", FacePoser_TranslateSoundName( event ) ) ); |
|
if ( wave ) |
|
{ |
|
event->SetEndTime( event->GetStartTime() + wave->GetRunningLength() ); |
|
delete wave; |
|
} |
|
|
|
DeleteSceneWidgets(); |
|
|
|
// Add to appropriate channel |
|
channel->AddEvent( event ); |
|
|
|
CreateSceneWidgets(); |
|
|
|
CChoreoEventWidget *eventWidget = FindWidgetForEvent( event ); |
|
if ( eventWidget ) |
|
{ |
|
eventWidget->SetSelected( true ); |
|
} |
|
|
|
// Redraw |
|
InvalidateLayout(); |
|
} |
|
|
|
PushRedo( "Place Next Speak Event" ); |
|
|
|
DeleteAllAndPurge( m_SortedNames ); |
|
} |
|
|
|
enum |
|
{ |
|
FM_LEFT = 0, |
|
FM_RIGHT, |
|
FM_SMALLESTWIDE, |
|
FM_LARGESTWIDE |
|
}; |
|
|
|
static int FindMetric( int type, CUtlVector< CChoreoEvent * > &list, float& value ) |
|
{ |
|
float bestVal = 999999.0f; |
|
int bestIndex = -1; |
|
bool greater = true; |
|
|
|
switch ( type ) |
|
{ |
|
default: |
|
case FM_LEFT: |
|
case FM_SMALLESTWIDE: |
|
greater = false; |
|
break; |
|
case FM_RIGHT: |
|
case FM_LARGESTWIDE: |
|
bestVal = -bestVal; |
|
greater = true; |
|
break; |
|
} |
|
int c = list.Count(); |
|
for ( int i = 0; i < c; ++i ) |
|
{ |
|
CChoreoEvent *e = list[ i ]; |
|
if ( type != FM_LEFT && |
|
!e->HasEndTime() ) |
|
continue; |
|
|
|
float val; |
|
switch ( type ) |
|
{ |
|
default: |
|
case FM_LEFT: |
|
val = e->GetStartTime(); |
|
break; |
|
case FM_RIGHT: |
|
val = e->GetEndTime(); |
|
break; |
|
case FM_SMALLESTWIDE: |
|
case FM_LARGESTWIDE: |
|
val = e->GetDuration(); |
|
break; |
|
} |
|
|
|
if ( greater ) |
|
{ |
|
if ( val <= bestVal ) |
|
continue; |
|
} |
|
else |
|
{ |
|
if ( val >= bestVal ) |
|
continue; |
|
} |
|
|
|
bestVal = val; |
|
bestIndex = i; |
|
} |
|
|
|
value = bestVal; |
|
return bestIndex; |
|
} |
|
|
|
void CChoreoView::OnAlign( bool left ) |
|
{ |
|
CUtlVector< CChoreoEvent * > list; |
|
GetSelectedEvents( list ); |
|
|
|
if ( left ) |
|
{ |
|
for ( int i = 0; i < m_SceneGlobalEvents.Size(); i++ ) |
|
{ |
|
CChoreoGlobalEventWidget *event = m_SceneGlobalEvents[ i ]; |
|
if ( !event || !event->IsSelected() ) |
|
continue; |
|
|
|
list.AddToTail( event->GetEvent() ); |
|
} |
|
} |
|
|
|
int numSel = list.Count(); |
|
if ( numSel < 2 ) |
|
{ |
|
Warning( "Can't align, must have at least two events selected\n" ); |
|
return; |
|
} |
|
|
|
float value; |
|
int idx = FindMetric( left ? FM_LEFT : FM_RIGHT, list, value ); |
|
if ( idx == -1 ) |
|
{ |
|
return; |
|
} |
|
|
|
SetDirty( true ); |
|
|
|
char undotext[ 128 ]; |
|
Q_snprintf( undotext, sizeof( undotext ), "Align %s", left ? "Left" : "Right" ); |
|
PushUndo( undotext ); |
|
|
|
for ( int i = 0; i < numSel; ++i ) |
|
{ |
|
if ( i == idx ) |
|
continue; |
|
|
|
CChoreoEvent *e = list[ i ]; |
|
|
|
float newStartTime = left ? value : ( value - e->GetDuration() ); |
|
float offset = newStartTime - e->GetStartTime(); |
|
e->OffsetTime( offset ); |
|
} |
|
|
|
PushRedo( undotext ); |
|
|
|
InvalidateLayout(); |
|
} |
|
|
|
void CChoreoView::OnMakeSameSize( bool smallest ) |
|
{ |
|
CUtlVector< CChoreoEvent * > list; |
|
int numSel = GetSelectedEvents( list ); |
|
if ( numSel < 2 ) |
|
{ |
|
Warning( "Can't align, must have at least two events selected\n" ); |
|
return; |
|
} |
|
|
|
float value; |
|
int idx = FindMetric( smallest ? FM_SMALLESTWIDE : FM_LARGESTWIDE, list, value ); |
|
if ( idx == -1 ) |
|
{ |
|
return; |
|
} |
|
|
|
SetDirty( true ); |
|
|
|
char undotext[ 128 ]; |
|
Q_snprintf( undotext, sizeof( undotext ), "Size to %s", smallest ? "Smallest" : "Largest" ); |
|
PushUndo( undotext ); |
|
|
|
for ( int i = 0; i < numSel; ++i ) |
|
{ |
|
if ( i == idx ) |
|
continue; |
|
|
|
list[ i ]->SetEndTime( list[ i ]->GetStartTime() + value ); |
|
} |
|
|
|
PushRedo( undotext ); |
|
|
|
InvalidateLayout(); |
|
} |
|
|
|
void CChoreoView::SelectAllEventsInActor( CChoreoActorWidget *actor ) |
|
{ |
|
TraverseWidgets( &CChoreoView::SelectInActor, actor ); |
|
redraw(); |
|
} |
|
|
|
void CChoreoView::SelectAllEventsInChannel( CChoreoChannelWidget *channel ) |
|
{ |
|
TraverseWidgets( &CChoreoView::SelectInChannel, channel ); |
|
redraw(); |
|
} |
|
|
|
void CChoreoView::SelectInActor( CChoreoWidget *widget, CChoreoWidget *param1 ) |
|
{ |
|
CChoreoEventWidget *ev = dynamic_cast< CChoreoEventWidget * >( widget ); |
|
if ( !ev ) |
|
return; |
|
|
|
if ( ev->IsSelected() ) |
|
return; |
|
|
|
CChoreoChannel *ch = ev->GetEvent()->GetChannel(); |
|
if ( !ch ) |
|
return; |
|
CChoreoActor *actor = ch->GetActor(); |
|
if ( !actor ) |
|
return; |
|
|
|
CChoreoActorWidget *actorw = dynamic_cast< CChoreoActorWidget * >( param1 ); |
|
if ( !actorw ) |
|
return; |
|
|
|
if ( actorw->GetActor() != actor ) |
|
return; |
|
|
|
widget->SetSelected( true ); |
|
} |
|
|
|
void CChoreoView::SelectInChannel( CChoreoWidget *widget, CChoreoWidget *param1 ) |
|
{ |
|
CChoreoEventWidget *ev = dynamic_cast< CChoreoEventWidget * >( widget ); |
|
if ( !ev ) |
|
return; |
|
|
|
if ( ev->IsSelected() ) |
|
return; |
|
|
|
CChoreoChannel *ch = ev->GetEvent()->GetChannel(); |
|
if ( !ch ) |
|
return; |
|
|
|
CChoreoChannelWidget *chw = dynamic_cast< CChoreoChannelWidget * >( param1 ); |
|
if ( !chw ) |
|
return; |
|
|
|
if ( chw->GetChannel() != ch ) |
|
return; |
|
|
|
widget->SetSelected( true ); |
|
} |
|
|
|
|