mirror of
https://github.com/nillerusr/source-engine.git
synced 2024-12-22 14:16:50 +00:00
11648 lines
262 KiB
C++
11648 lines
262 KiB
C++
//========= 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 );
|
|
}
|
|
|